Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'org.eclipse.text')
-rw-r--r--org.eclipse.text/.classpath6
-rw-r--r--org.eclipse.text/.cvsignore1
-rw-r--r--org.eclipse.text/.project17
-rw-r--r--org.eclipse.text/build.properties4
-rw-r--r--org.eclipse.text/plugin.properties2
-rw-r--r--org.eclipse.text/plugin.xml14
-rw-r--r--org.eclipse.text/scripts/exportplugin.xml28
-rw-r--r--org.eclipse.text/src/org/eclipse/jface/text/AbstractDocument.java1177
-rw-r--r--org.eclipse.text/src/org/eclipse/jface/text/AbstractLineTracker.java502
-rw-r--r--org.eclipse.text/src/org/eclipse/jface/text/Assert.java165
-rw-r--r--org.eclipse.text/src/org/eclipse/jface/text/BadLocationException.java30
-rw-r--r--org.eclipse.text/src/org/eclipse/jface/text/BadPositionCategoryException.java32
-rw-r--r--org.eclipse.text/src/org/eclipse/jface/text/ChildDocument.java290
-rw-r--r--org.eclipse.text/src/org/eclipse/jface/text/ChildDocumentEvent.java39
-rw-r--r--org.eclipse.text/src/org/eclipse/jface/text/ChildDocumentManager.java312
-rw-r--r--org.eclipse.text/src/org/eclipse/jface/text/ConfigurableLineTracker.java58
-rw-r--r--org.eclipse.text/src/org/eclipse/jface/text/DefaultLineTracker.java74
-rw-r--r--org.eclipse.text/src/org/eclipse/jface/text/DefaultPositionUpdater.java219
-rw-r--r--org.eclipse.text/src/org/eclipse/jface/text/Document.java71
-rw-r--r--org.eclipse.text/src/org/eclipse/jface/text/DocumentEvent.java84
-rw-r--r--org.eclipse.text/src/org/eclipse/jface/text/GapTextStore.java253
-rw-r--r--org.eclipse.text/src/org/eclipse/jface/text/IDocument.java567
-rw-r--r--org.eclipse.text/src/org/eclipse/jface/text/IDocumentExtension.java81
-rw-r--r--org.eclipse.text/src/org/eclipse/jface/text/IDocumentListener.java33
-rw-r--r--org.eclipse.text/src/org/eclipse/jface/text/IDocumentPartitioner.java104
-rw-r--r--org.eclipse.text/src/org/eclipse/jface/text/IDocumentPartitionerExtension.java37
-rw-r--r--org.eclipse.text/src/org/eclipse/jface/text/IDocumentPartitioningListener.java27
-rw-r--r--org.eclipse.text/src/org/eclipse/jface/text/IDocumentPartitioningListenerExtension.java33
-rw-r--r--org.eclipse.text/src/org/eclipse/jface/text/ILineTracker.java132
-rw-r--r--org.eclipse.text/src/org/eclipse/jface/text/IPositionUpdater.java33
-rw-r--r--org.eclipse.text/src/org/eclipse/jface/text/IRegion.java32
-rw-r--r--org.eclipse.text/src/org/eclipse/jface/text/ITextStore.java57
-rw-r--r--org.eclipse.text/src/org/eclipse/jface/text/ITypedRegion.java24
-rw-r--r--org.eclipse.text/src/org/eclipse/jface/text/Line.java63
-rw-r--r--org.eclipse.text/src/org/eclipse/jface/text/Position.java181
-rw-r--r--org.eclipse.text/src/org/eclipse/jface/text/Region.java63
-rw-r--r--org.eclipse.text/src/org/eclipse/jface/text/SequentialRewriteTextStore.java273
-rw-r--r--org.eclipse.text/src/org/eclipse/jface/text/TextUtilities.java117
-rw-r--r--org.eclipse.text/src/org/eclipse/jface/text/TypedPosition.java68
-rw-r--r--org.eclipse.text/src/org/eclipse/jface/text/TypedRegion.java56
40 files changed, 5359 insertions, 0 deletions
diff --git a/org.eclipse.text/.classpath b/org.eclipse.text/.classpath
new file mode 100644
index 000000000..d8b3fa0d8
--- /dev/null
+++ b/org.eclipse.text/.classpath
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="var" path="JRE_LIB" rootpath="JRE_SRCROOT" sourcepath="JRE_SRC"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/org.eclipse.text/.cvsignore b/org.eclipse.text/.cvsignore
new file mode 100644
index 000000000..c5e82d745
--- /dev/null
+++ b/org.eclipse.text/.cvsignore
@@ -0,0 +1 @@
+bin \ No newline at end of file
diff --git a/org.eclipse.text/.project b/org.eclipse.text/.project
new file mode 100644
index 000000000..9e0b25af5
--- /dev/null
+++ b/org.eclipse.text/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.eclipse.text</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/org.eclipse.text/build.properties b/org.eclipse.text/build.properties
new file mode 100644
index 000000000..04e9be9a5
--- /dev/null
+++ b/org.eclipse.text/build.properties
@@ -0,0 +1,4 @@
+bin.includes = plugin.properties,\
+ plugin.xml,\
+ *.jar
+source.text.jar = src/
diff --git a/org.eclipse.text/plugin.properties b/org.eclipse.text/plugin.properties
new file mode 100644
index 000000000..460170dca
--- /dev/null
+++ b/org.eclipse.text/plugin.properties
@@ -0,0 +1,2 @@
+pluginName= Text
+providerName= Eclipse.org
diff --git a/org.eclipse.text/plugin.xml b/org.eclipse.text/plugin.xml
new file mode 100644
index 000000000..ae9d701e3
--- /dev/null
+++ b/org.eclipse.text/plugin.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<plugin
+ id="org.eclipse.text"
+ name="%pluginName"
+ version="2.1.0"
+ provider-name="%providerName">
+
+ <runtime>
+ <library name="text.jar">
+ <export name="*"/>
+ </library>
+ </runtime>
+
+</plugin>
diff --git a/org.eclipse.text/scripts/exportplugin.xml b/org.eclipse.text/scripts/exportplugin.xml
new file mode 100644
index 000000000..870cc45dc
--- /dev/null
+++ b/org.eclipse.text/scripts/exportplugin.xml
@@ -0,0 +1,28 @@
+<project name="Export Text" default="export" basedir="..">
+ <target name="init">
+ <tstamp/>
+ <property name="destdir" value="../../plugin-export" />
+ <property name="plugin" value="org.eclipse.text" />
+ <property name="version" value="_2.1.0" />
+ <property name="dest" value="${destdir}/${plugin}${version}" />
+ </target>
+
+ <target name="build" depends="init">
+ <eclipse.incrementalBuild project="${plugin}" kind="incr"/>
+ </target>
+
+ <target name="export" depends="build">
+ <mkdir dir="${destdir}" />
+ <delete dir="${dest}" />
+ <mkdir dir="${dest}" />
+ <jar
+ jarfile="${dest}/text.jar"
+ basedir="bin"
+ />
+ <copy file="plugin.xml" todir="${dest}"/>
+ <copy file="plugin.properties" todir="${dest}"/>
+ <zip zipfile="${dest}/textsrc.zip">
+ <fileset dir="src" />
+ </zip>
+ </target>
+</project>
diff --git a/org.eclipse.text/src/org/eclipse/jface/text/AbstractDocument.java b/org.eclipse.text/src/org/eclipse/jface/text/AbstractDocument.java
new file mode 100644
index 000000000..8e822aa7e
--- /dev/null
+++ b/org.eclipse.text/src/org/eclipse/jface/text/AbstractDocument.java
@@ -0,0 +1,1177 @@
+/**********************************************************************
+Copyright (c) 2000, 2002 IBM Corp. and others.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Common Public License v1.0
+which accompanies this distribution, and is available at
+http://www.eclipse.org/legal/cpl-v10.html
+
+Contributors:
+ IBM Corporation - Initial implementation
+**********************************************************************/
+
+package org.eclipse.jface.text;
+
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * Abstract implementation of <code>IDocument</code>.
+ * Implements the complete contract of <code>IDocument</code> and <code>IDocumentExtension</code>.
+ * An <code>AbstractDocument</code> supports the following implementation plug-ins:
+ * <ul>
+ * <li> a text store for storing and managing the document's content
+ * <li> a line tracker to map character positions to line numbers and vice versa
+ * </ul>
+ * This class must be subclassed. Subclasses must configure which implementation
+ * plug-ins the document should use. Subclasses are not intended to overwrite existing methods.
+ *
+ * @see IDocument
+ * @see ITextStore
+ * @see ILineTracker
+ */
+public abstract class AbstractDocument implements IDocument, IDocumentExtension {
+
+ /** The document's text store */
+ private ITextStore fStore;
+ /** The document's line tracker */
+ private ILineTracker fTracker;
+ /** The document's partitioner */
+ private IDocumentPartitioner fDocumentPartitioner;
+ /**
+ * The document's partitioner casted to <code>IDocumentPartitionerExtension</code>.
+ * @since 2.0
+ */
+ private IDocumentPartitionerExtension fDocumentPartitionerExtension;
+ /** The registered document listeners */
+ private List fDocumentListeners;
+ /** The registered prenotified document listeners */
+ private List fPrenotifiedDocumentListeners;
+ /** The registered document partitioning listeners */
+ private List fDocumentPartitioningListeners;
+ /** All positions managed by the document */
+ private Map fPositions;
+ /** All registered document position updaters */
+ private List fPositionUpdaters;
+
+ /**
+ * The list of post notification changes
+ * @since 2.0
+ */
+ private List fPostNotificationChanges;
+ /**
+ * The reentrance count for post notification changes.
+ * @since 2.0
+ */
+ private int fReentranceCount= 0;
+ /**
+ * Indicates whether post notification change processing has been stopped.
+ * @since 2.0
+ */
+ private int fStoppedCount= 0;
+
+ /**
+ * The default constructor does not perform any configuration
+ * but leaves it to the clients who must first initialize the
+ * implementation plug-ins and then call <code>completeInitialization</code>.
+ * Results in the construction of an empty document.
+ */
+ protected AbstractDocument() {
+ }
+
+
+ //--- accessor to fields -------------------------------
+
+ /**
+ * Returns the document's text store. Assumes that the
+ * document has been initialized with a text store.
+ *
+ * @return the document's text store
+ */
+ protected ITextStore getStore() {
+ Assert.isNotNull(fStore);
+ return fStore;
+ }
+
+ /**
+ * Returns the document's line tracker. Assumes that the
+ * document has been initialized with a line tracker.
+ *
+ * @return the document's line tracker
+ */
+ protected ILineTracker getTracker() {
+ Assert.isNotNull(fTracker);
+ return fTracker;
+ }
+
+ /**
+ * Returns the document's document listeners.
+ *
+ * @return the document's document listeners
+ */
+ protected List getDocumentListeners() {
+ return fDocumentListeners;
+ }
+
+ /**
+ * Returns the document's partitioning listeners .
+ *
+ * @return the document's partitioning listeners
+ */
+ protected List getDocumentPartitioningListeners() {
+ return fDocumentPartitioningListeners;
+ }
+
+ /**
+ * Returns all positions managed by the document grouped by category.
+ *
+ * @return the document's positions
+ */
+ protected Map getDocumentManagedPositions() {
+ return fPositions;
+ }
+
+ /*
+ * @see IDocument#getDocumentPartitioner
+ */
+ public IDocumentPartitioner getDocumentPartitioner() {
+ return fDocumentPartitioner;
+ }
+
+
+
+ //--- implementation configuration interface ------------
+
+ /**
+ * Sets the document's text store.
+ * Must be called first inside the constructor.
+ *
+ * @param store the document's text store
+ */
+ protected void setTextStore(ITextStore store) {
+ fStore= store;
+ }
+
+ /**
+ * Sets the document's line tracker.
+ * Must be called first inside the constructor.
+ *
+ * @param tracker the document's line tracker
+ */
+ protected void setLineTracker(ILineTracker tracker) {
+ fTracker= tracker;
+ }
+
+ /*
+ * @see IDocument#setDocumentPartitioner
+ */
+ public void setDocumentPartitioner(IDocumentPartitioner partitioner) {
+ fDocumentPartitioner= partitioner;
+ if (fDocumentPartitioner instanceof IDocumentPartitionerExtension)
+ fDocumentPartitionerExtension= (IDocumentPartitionerExtension) fDocumentPartitioner;
+
+ fireDocumentPartitioningChanged(new Region(0, getLength()));
+ }
+
+ /**
+ * Initializes document listeners, positions, and position updaters.
+ * Must be called inside the constructor after the implementation plug-ins
+ * have been set.
+ */
+ protected void completeInitialization() {
+
+ fPositions= new HashMap();
+ fPositionUpdaters= new ArrayList();
+ fDocumentListeners= new ArrayList();
+ fPrenotifiedDocumentListeners= new ArrayList();
+ fDocumentPartitioningListeners= new ArrayList();
+
+ addPositionCategory(DEFAULT_CATEGORY);
+ addPositionUpdater(new DefaultPositionUpdater(DEFAULT_CATEGORY));
+ }
+
+
+ //-------------------------------------------------------
+
+ /*
+ * @see IDocument#addDocumentListener
+ */
+ public void addDocumentListener(IDocumentListener listener) {
+ Assert.isNotNull(listener);
+ if (! fDocumentListeners.contains(listener))
+ fDocumentListeners.add(listener);
+ }
+
+ /*
+ * @see IDocument#removeDocumentListener
+ */
+ public void removeDocumentListener(IDocumentListener listener) {
+ Assert.isNotNull(listener);
+ fDocumentListeners.remove(listener);
+ }
+
+ /*
+ * @see IDocument#addPrenotifiedDocumentListener(IDocumentListener)
+ */
+ public void addPrenotifiedDocumentListener(IDocumentListener listener) {
+ Assert.isNotNull(listener);
+ if (! fPrenotifiedDocumentListeners.contains(listener))
+ fPrenotifiedDocumentListeners.add(listener);
+ }
+
+ /*
+ * @see IDocument#removePrenotifiedDocumentListener(IDocumentListener)
+ */
+ public void removePrenotifiedDocumentListener(IDocumentListener listener) {
+ Assert.isNotNull(listener);
+ fPrenotifiedDocumentListeners.remove(listener);
+ }
+
+ /*
+ * @see IDocument#addDocumentPartitioningListener
+ */
+ public void addDocumentPartitioningListener(IDocumentPartitioningListener listener) {
+ Assert.isNotNull(listener);
+ if (! fDocumentPartitioningListeners.contains(listener))
+ fDocumentPartitioningListeners.add(listener);
+ }
+
+ /*
+ * @see IDocument#removeDocumentPartitioningListener
+ */
+ public void removeDocumentPartitioningListener(IDocumentPartitioningListener listener) {
+ Assert.isNotNull(listener);
+ fDocumentPartitioningListeners.remove(listener);
+ }
+
+ /*
+ * @see IDocument#addPosition
+ */
+ public void addPosition(String category, Position position) throws BadLocationException, BadPositionCategoryException {
+
+ if ((0 > position.offset) || (0 > position.length) || (position.offset + position.length > getLength()))
+ throw new BadLocationException();
+
+ if (category == null)
+ throw new BadPositionCategoryException();
+
+ List list= (List) fPositions.get(category);
+ if (list == null)
+ throw new BadPositionCategoryException();
+
+ list.add(computeIndexInPositionList(list, position.offset), position);
+ }
+
+ /*
+ * @see IDocument#addPosition
+ */
+ public void addPosition(Position position) throws BadLocationException {
+ try {
+ addPosition(DEFAULT_CATEGORY, position);
+ } catch (BadPositionCategoryException e) {
+ }
+ }
+
+ /*
+ * @see IDocument#addPositionCategory
+ */
+ public void addPositionCategory(String category) {
+
+ if (category == null)
+ return;
+
+ if (!containsPositionCategory(category))
+ fPositions.put(category, new ArrayList());
+ }
+
+ /*
+ * @see IDocument#addPositionUpdater
+ */
+ public void addPositionUpdater(IPositionUpdater updater) {
+ insertPositionUpdater(updater, fPositionUpdaters.size());
+ }
+
+ /*
+ * @see IDocument#containsPosition
+ */
+ public boolean containsPosition(String category, int offset, int length) {
+
+ if (category == null)
+ return false;
+
+ List list= (List) fPositions.get(category);
+ if (list == null)
+ return false;
+
+ int size= list.size();
+ if (size == 0)
+ return false;
+
+ int index= computeIndexInPositionList(list, offset);
+ if (index < size) {
+ Position p= (Position) list.get(index);
+ while (p != null && p.offset == offset) {
+ if (p.length == length)
+ return true;
+ ++ index;
+ p= (index < size) ? (Position) list.get(index) : null;
+ }
+ }
+
+ return false;
+ }
+
+ /*
+ * @see IDocument#containsPositionCategory
+ */
+ public boolean containsPositionCategory(String category) {
+ if (category != null)
+ return fPositions.containsKey(category);
+ return false;
+ }
+
+
+ /**
+ * Computes the index in the list of positions at which a position with the given
+ * offset would be inserted. The position is supposed to become the first in this list
+ * of all positions with the same offset.
+ *
+ * @param positions the list in which the index is computed
+ * @param offset the offset for which the index is computed
+ * @return the computed index
+ *
+ * @see IDocument#computeIndexInCategory(String, int)
+ */
+ protected int computeIndexInPositionList(List positions, int offset) {
+
+ if (positions.size() == 0)
+ return 0;
+
+ int left= 0;
+ int right= positions.size() -1;
+ int mid= 0;
+ Position p= null;
+
+ while (left < right) {
+
+ mid= (left + right) / 2;
+
+ p= (Position) positions.get(mid);
+ if (offset < p.getOffset()) {
+ if (left == mid)
+ right= left;
+ else
+ right= mid -1;
+ } else if (offset > p.getOffset()) {
+ if (right == mid)
+ left= right;
+ else
+ left= mid +1;
+ } else if (offset == p.getOffset()) {
+ left= right= mid;
+ }
+
+ }
+
+ int pos= left;
+ p= (Position) positions.get(pos);
+ if (offset > p.getOffset()) {
+ // append to the end
+ pos++;
+ } else {
+ // entry will became the first of all entries with the same offset
+ do {
+ --pos;
+ if (pos < 0)
+ break;
+ p= (Position) positions.get(pos);
+ } while (offset == p.getOffset());
+ ++pos;
+ }
+
+ Assert.isTrue(0 <= pos && pos <= positions.size());
+
+ return pos;
+ }
+
+
+ /*
+ * @see IDocument#computeIndexInCategory
+ */
+ public int computeIndexInCategory(String category, int offset) throws BadLocationException, BadPositionCategoryException {
+
+ if (0 > offset || offset > getLength())
+ throw new BadLocationException();
+
+ List c= (List) fPositions.get(category);
+ if (c == null)
+ throw new BadPositionCategoryException();
+
+ return computeIndexInPositionList(c, offset);
+ }
+
+ /**
+ * Fires the document partitioning changed notification to all registered
+ * document partitioning listeners. Uses a robust iterator.
+ * @deprecated use <code>fireDocumentPartitioningChanged(IRegion)</code> instead
+ */
+ protected void fireDocumentPartitioningChanged() {
+
+ if (fDocumentPartitioningListeners != null && fDocumentPartitioningListeners.size() > 0) {
+
+ List list= new ArrayList(fDocumentPartitioningListeners);
+ Iterator e= list.iterator();
+ while (e.hasNext()) {
+ IDocumentPartitioningListener l= (IDocumentPartitioningListener) e.next();
+ l.documentPartitioningChanged(this);
+ }
+ }
+ }
+
+ /**
+ * Fires the document partitioning changed notification to all registered
+ * document partitioning listeners. Uses a robust iterator.
+ *
+ * @param region the region in which partitioning has changed
+ * @see IDocumentPartitioningListenerExtension
+ * @since 2.0
+ */
+ protected void fireDocumentPartitioningChanged(IRegion region) {
+
+ if (fDocumentPartitioningListeners != null && fDocumentPartitioningListeners.size() > 0) {
+
+ List list= new ArrayList(fDocumentPartitioningListeners);
+ Iterator e= list.iterator();
+ while (e.hasNext()) {
+ IDocumentPartitioningListener l= (IDocumentPartitioningListener) e.next();
+ if (l instanceof IDocumentPartitioningListenerExtension)
+ ((IDocumentPartitioningListenerExtension) l).documentPartitioningChanged(this, region);
+ else
+ l.documentPartitioningChanged(this);
+ }
+ }
+ }
+
+ /**
+ * Fires the given document event to all registers document listeners informing them
+ * about the forthcoming document manipulation. Uses a robust iterator.
+ *
+ * @param event the event to be sent out
+ */
+ protected void fireDocumentAboutToBeChanged(DocumentEvent event) {
+
+ // IDocumentExtension
+ if (fReentranceCount == 0)
+ flushPostNotificationChanges();
+
+ if (fDocumentPartitioner != null)
+ fDocumentPartitioner.documentAboutToBeChanged(event);
+
+ if (fPrenotifiedDocumentListeners.size() > 0) {
+
+ List list= new ArrayList(fPrenotifiedDocumentListeners);
+ Iterator e= list.iterator();
+ while (e.hasNext()) {
+ IDocumentListener l= (IDocumentListener) e.next();
+ l.documentAboutToBeChanged(event);
+ }
+ }
+
+ if (fDocumentListeners.size() > 0) {
+
+ List list= new ArrayList(fDocumentListeners);
+ Iterator e= list.iterator();
+ while (e.hasNext()) {
+ IDocumentListener l= (IDocumentListener) e.next();
+ l.documentAboutToBeChanged(event);
+ }
+ }
+ }
+
+ /**
+ * Updates document partitioning and document positions according to the
+ * specification given by the document event.
+ *
+ * @param event the document event describing the change to which structures must be adapted
+ */
+ protected void updateDocumentStructures(DocumentEvent event) {
+ boolean partitioningChanged= false;
+ IRegion changedRegion= null;
+
+ if (fDocumentPartitioner != null) {
+ if (fDocumentPartitionerExtension != null) {
+ changedRegion= fDocumentPartitionerExtension.documentChanged2(event);
+ partitioningChanged= (changedRegion != null);
+ } else
+ partitioningChanged= fDocumentPartitioner.documentChanged(event);
+ }
+
+ if (fPositions.size() > 0)
+ updatePositions(event);
+
+ if (partitioningChanged)
+ fireDocumentPartitioningChanged(changedRegion);
+ }
+
+ /**
+ * Updates the internal document structures and informs all document listeners.
+ * Uses a robust iterator. <p>
+ * Executes all registered post notification replace operation.
+ *
+ * @param event the document event to be sent out
+ * @see IDocumentExtension
+ */
+ protected void fireDocumentChanged(DocumentEvent event) {
+ updateDocumentStructures(event);
+
+ if (fPrenotifiedDocumentListeners.size() > 0) {
+
+ List list= new ArrayList(fPrenotifiedDocumentListeners);
+ Iterator e= list.iterator();
+ while (e.hasNext()) {
+ IDocumentListener l= (IDocumentListener) e.next();
+ l.documentChanged(event);
+ }
+ }
+
+ if (fDocumentListeners.size() > 0) {
+
+ List list= new ArrayList(fDocumentListeners);
+ Iterator e= list.iterator();
+ while (e.hasNext()) {
+ IDocumentListener l= (IDocumentListener) e.next();
+ l.documentChanged(event);
+ }
+ }
+
+ // IDocumentExtension
+ ++ fReentranceCount;
+ try {
+ if (fReentranceCount == 1)
+ executePostNotificationChanges();
+ } finally {
+ -- fReentranceCount;
+ }
+ }
+
+ /*
+ * @see IDocument#getChar
+ */
+ public char getChar(int pos) throws BadLocationException {
+ if ((0 > pos) || (pos >= getLength()))
+ throw new BadLocationException();
+ return getStore().get(pos);
+ }
+
+ /*
+ * @see IDocument#getContentType
+ */
+ public String getContentType(int offset) throws BadLocationException {
+ if ((0 > offset) || (offset > getLength()))
+ throw new BadLocationException();
+
+ if (fDocumentPartitioner == null)
+ return DEFAULT_CONTENT_TYPE;
+
+ return fDocumentPartitioner.getContentType(offset);
+ }
+
+ /*
+ * @see IDocument#getLegalContentTypes
+ */
+ public String[] getLegalContentTypes() {
+ if (fDocumentPartitioner == null)
+ return new String[] { DEFAULT_CONTENT_TYPE };
+ return fDocumentPartitioner.getLegalContentTypes();
+ }
+
+ /*
+ * @see IDocument#getLength
+ */
+ public int getLength() {
+ return getStore().getLength();
+ }
+
+ /*
+ * @see IDocument#getLineDelimiter
+ */
+ public String getLineDelimiter(int line) throws BadLocationException {
+ return getTracker().getLineDelimiter(line);
+ }
+
+ /*
+ * @see IDocument#getLegalLineDelimiters
+ */
+ public String[] getLegalLineDelimiters() {
+ return getTracker().getLegalLineDelimiters();
+ }
+
+ /*
+ * @see IDocument#getLineLength
+ */
+ public int getLineLength(int line) throws BadLocationException {
+ return getTracker().getLineLength(line);
+ }
+
+ /*
+ * @see IDocument#getLineOfOffset
+ */
+ public int getLineOfOffset(int pos) throws BadLocationException {
+ return getTracker().getLineNumberOfOffset(pos);
+ }
+
+ /*
+ * @see IDocument#getLineOffset
+ */
+ public int getLineOffset(int line) throws BadLocationException {
+ return getTracker().getLineOffset(line);
+ }
+
+ /*
+ * @see IDocument#getLineInformation
+ */
+ public IRegion getLineInformation(int line) throws BadLocationException {
+ return getTracker().getLineInformation(line);
+ }
+
+ /*
+ * @see IDocument#getLineInformationOfOffset
+ */
+ public IRegion getLineInformationOfOffset(int offset) throws BadLocationException {
+ return getTracker().getLineInformationOfOffset(offset);
+ }
+
+ /*
+ * @see IDocument#getNumberOfLines
+ */
+ public int getNumberOfLines() {
+ return getTracker().getNumberOfLines();
+ }
+
+ /*
+ * @see IDocument#getNumberOfLines(int, int)
+ */
+ public int getNumberOfLines(int offset, int length) throws BadLocationException {
+ return getTracker().getNumberOfLines(offset, length);
+ }
+
+ /*
+ * @see IDocument#computeNumberOfLines(String)
+ */
+ public int computeNumberOfLines(String text) {
+ return getTracker().computeNumberOfLines(text);
+ }
+
+ /*
+ * @see IDocument#getPartition
+ */
+ public ITypedRegion getPartition(int offset) throws BadLocationException {
+ if ((0 > offset) || (offset > getLength()))
+ throw new BadLocationException();
+
+ if (fDocumentPartitioner == null)
+ return new TypedRegion(0, getLength(), DEFAULT_CONTENT_TYPE);
+
+ return fDocumentPartitioner.getPartition(offset);
+ }
+
+ /*
+ * @see IDocument#computePartitioning
+ */
+ public ITypedRegion[] computePartitioning(int offset, int length) throws BadLocationException {
+ if ((0 > offset) || (0 > length) || (offset + length > getLength()))
+ throw new BadLocationException();
+
+ if (fDocumentPartitioner == null)
+ return new TypedRegion[] { new TypedRegion(offset, length, DEFAULT_CONTENT_TYPE) };
+
+ return fDocumentPartitioner.computePartitioning(offset, length);
+ }
+
+ /*
+ * @see IDocument#getPositions
+ */
+ public Position[] getPositions(String category) throws BadPositionCategoryException {
+
+ if (category == null)
+ throw new BadPositionCategoryException();
+
+ List c= (List) fPositions.get(category);
+ if (c == null)
+ throw new BadPositionCategoryException();
+
+ Position[] positions= new Position[c.size()];
+ c.toArray(positions);
+ return positions;
+ }
+
+ /*
+ * @see IDocument#getPositionCategories
+ */
+ public String[] getPositionCategories() {
+ String[] categories= new String[fPositions.size()];
+ Iterator keys= fPositions.keySet().iterator();
+ for (int i= 0; i < categories.length; i++)
+ categories[i]= (String) keys.next();
+ return categories;
+ }
+
+ /*
+ * @see IDocument#getPositionUpdaters
+ */
+ public IPositionUpdater[] getPositionUpdaters() {
+ IPositionUpdater[] updaters= new IPositionUpdater[fPositionUpdaters.size()];
+ fPositionUpdaters.toArray(updaters);
+ return updaters;
+ }
+
+ /*
+ * @see IDocument#get
+ */
+ public String get() {
+ return getStore().get(0, getLength());
+ }
+
+ /*
+ * @see IDocument#get
+ */
+ public String get(int pos, int length) throws BadLocationException {
+ int myLength= getLength();
+ if ((0 > pos) || (0 > length) || (pos + length > myLength))
+ throw new BadLocationException();
+ return getStore().get(pos, length);
+ }
+
+ /*
+ * @see IDocument#insertPositionUpdater
+ */
+ public void insertPositionUpdater(IPositionUpdater updater, int index) {
+
+ for (int i= fPositionUpdaters.size() - 1; i >= 0; i--) {
+ if (fPositionUpdaters.get(i) == updater)
+ return;
+ }
+
+ if (index == fPositionUpdaters.size())
+ fPositionUpdaters.add(updater);
+ else
+ fPositionUpdaters.add(index, updater);
+ }
+
+ /*
+ * @see IDocument#removePosition
+ */
+ public void removePosition(String category, Position position) throws BadPositionCategoryException {
+
+ if (position == null)
+ return;
+
+ if (category == null)
+ throw new BadPositionCategoryException();
+
+ List c= (List) fPositions.get(category);
+ if (c == null)
+ throw new BadPositionCategoryException();
+
+ // remove based on identity not equality
+ int size= c.size();
+ for (int i= 0; i < size; i++) {
+ if (position == c.get(i)) {
+ c.remove(i);
+ return;
+ }
+ }
+ }
+
+ /*
+ * @see IDocument#removePosition
+ */
+ public void removePosition(Position position) {
+ try {
+ removePosition(DEFAULT_CATEGORY, position);
+ } catch (BadPositionCategoryException e) {
+ }
+ }
+
+ /*
+ * @see IDocument#removePositionCategory
+ */
+ public void removePositionCategory(String category) throws BadPositionCategoryException {
+
+ if (category == null)
+ return;
+
+ if ( !containsPositionCategory(category))
+ throw new BadPositionCategoryException();
+
+ fPositions.remove(category);
+ }
+
+ /*
+ * @see IDocument#removePositionUpdater
+ */
+ public void removePositionUpdater(IPositionUpdater updater) {
+ for (int i= fPositionUpdaters.size() - 1; i >= 0; i--) {
+ if (fPositionUpdaters.get(i) == updater) {
+ fPositionUpdaters.remove(i);
+ return;
+ }
+ }
+ }
+
+ /*
+ * @see IDocument#replace
+ */
+ public void replace(int pos, int length, String text) throws BadLocationException {
+ if ((0 > pos) || (0 > length) || (pos + length > getLength()))
+ throw new BadLocationException();
+
+ DocumentEvent e= new DocumentEvent(this, pos, length, text);
+ fireDocumentAboutToBeChanged(e);
+
+ getStore().replace(pos, length, text);
+ getTracker().replace(pos, length, text);
+
+ fireDocumentChanged(e);
+ }
+
+ /*
+ * @see IDocument#set
+ */
+ public void set(String text) {
+ int length= getStore().getLength();
+ DocumentEvent e= new DocumentEvent(this, 0, length, text);
+ fireDocumentAboutToBeChanged(e);
+
+ getStore().set(text);
+ getTracker().set(text);
+
+ fireDocumentChanged(e);
+ }
+
+ /**
+ * Updates all positions of all categories to the change
+ * described by the document event. All registered document
+ * updaters are called in the sequence they have been arranged.
+ * Uses a robust iterator.
+ *
+ * @param event the document event describing the change to which to adapt the positions
+ */
+ protected void updatePositions(DocumentEvent event) {
+ List list= new ArrayList(fPositionUpdaters);
+ Iterator e= list.iterator();
+ while (e.hasNext()) {
+ IPositionUpdater u= (IPositionUpdater) e.next();
+ u.update(event);
+ }
+ }
+
+ /*
+ * @see IDocument#search
+ */
+ public int search(int startPosition, String findString, boolean forwardSearch, boolean caseSensitive, boolean wholeWord) throws BadLocationException {
+
+ if (findString == null || findString.length() == 0)
+ return -1;
+
+ ITextStore store= getStore();
+
+ if (startPosition < -1 || startPosition > store.getLength())
+ throw new BadLocationException();
+
+ if (!caseSensitive)
+ findString= findString.toLowerCase();
+
+ char[] fs= new char[findString.length()];
+ findString.getChars(0, fs.length, fs, 0);
+
+
+ if (forwardSearch) {
+ if (startPosition == -1)
+ startPosition= 0;
+ int end= getLength();
+ while (startPosition < end) {
+ int pos= indexOf(store, fs, startPosition, caseSensitive);
+ if (!wholeWord || pos == -1 || isWholeWord(store, pos, pos + fs.length)) {
+ return pos;
+ }
+ startPosition= pos + 1;
+ }
+ } else {
+ if (startPosition == -1)
+ startPosition= getLength();
+ while (startPosition >= 0) {
+ int pos= lastIndexOf(store, fs, startPosition, caseSensitive);
+ if (!wholeWord || pos == -1 || isWholeWord(store, pos, pos + fs.length)) {
+ return pos;
+ }
+ startPosition= pos - 1;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Returns the first index greater than <code>fromIndex</code> at which <code>str</code>
+ * can be found in the <code>store</code>.
+ *
+ * @param store the text store to search
+ * @param str the string to search
+ * @param fromIndex the start offset
+ * @param caseSensitive <code>true</code> if capitalization should be honored, <code>false</code> otherwise
+ * @return the offset greater than the start offset at which the search string has been found
+ */
+ static private int indexOf(ITextStore store, char[] str, int fromIndex, boolean caseSensitive) {
+ int count= store.getLength();
+
+ if (fromIndex >= count)
+ return -1;
+
+ if (fromIndex < 0)
+ fromIndex= 0;
+
+ int strLen= str.length;
+ if (strLen == 0) // empty string always matches
+ return fromIndex;
+
+ char first= str[0];
+ int i= fromIndex;
+ int max= count - strLen;
+
+ restart:
+ while (true) {
+
+ // Look for first character
+ if (caseSensitive) {
+ while (i <= max && store.get(i) != first)
+ i++;
+ } else {
+ while (i <= max && Character.toLowerCase(store.get(i)) != first)
+ i++;
+ }
+
+ if (i > max)
+ return -1;
+
+ // Found first character
+ int j= i + 1;
+ int end= j + strLen - 1;
+ int k= 1;
+ if (caseSensitive) {
+ while (j < end) {
+ if (store.get(j++) != str[k++]) {
+ i++;
+ continue restart;
+ }
+ }
+ } else {
+ while (j < end) {
+ if (Character.toLowerCase(store.get(j++)) != str[k++]) {
+ i++;
+ continue restart;
+ }
+ }
+ }
+
+ return i; // Found
+ }
+ }
+
+ /**
+ * Returns the first index smaller than <code>fromIndex</code> at which <code>str</code>
+ * can be found in the <code>store</code>.
+ *
+ * @param store the text store to search
+ * @param str the string to search
+ * @param fromIndex the start offset
+ * @param caseSensitive <code>true</code> if capitalization should be honored, <code>false</code> otherwise
+ * @return the offset smaller than the start offset at which the search string has been found
+ */
+ static private int lastIndexOf(ITextStore store, char[] str, int fromIndex, boolean caseSensitive) {
+
+ if (fromIndex < 0)
+ return -1;
+
+ int count= store.getLength();
+ int strLen= str.length;
+ int rightIndex= count - strLen;
+
+ if (fromIndex > rightIndex)
+ fromIndex= rightIndex;
+
+ if (strLen == 0) // empty string always matches
+ return fromIndex;
+
+ int strLastIndex= strLen - 1;
+ char strLastChar= str[strLastIndex];
+ int min= strLen - 1;
+ int i= min + fromIndex;
+
+ restart:
+ while (true) {
+
+ // Look for the last character
+ if (caseSensitive) {
+ while (i >= min && store.get(i) != strLastChar)
+ i--;
+ } else {
+ while (i >= min && Character.toLowerCase(store.get(i)) != strLastChar)
+ i--;
+ }
+
+ if (i < min)
+ return -1;
+
+ // Found last character
+ int j= i - 1;
+ int start= j - (strLen - 1);
+ int k= strLastIndex - 1;
+
+ if (caseSensitive) {
+ while (j > start) {
+ if (store.get(j--) != str[k--]) {
+ i--;
+ continue restart;
+ }
+ }
+ } else {
+ while (j > start) {
+ if (Character.toLowerCase(store.get(j--)) != str[k--]) {
+ i--;
+ continue restart;
+ }
+ }
+ }
+
+ return start + 1; /* Found whole string. */
+ }
+ }
+
+ /**
+ * Tests if the substring is a whole word.
+ *
+ * @param store the store in which to find the string
+ * @param from the substring start offset
+ * @param to the substring endoffset
+ * @return <code>true</code> if the string is a whole word, otherwise <code>false</code>
+ */
+ private static boolean isWholeWord(ITextStore store, int from, int to) {
+
+ if (from > 0) {
+ char ch= store.get(from-1);
+ if (Character.isLetterOrDigit(ch) || ch == '_') {
+ return false;
+ }
+ }
+ if (to < store.getLength()) {
+ char ch= store.get(to);
+ if (Character.isLetterOrDigit(ch) || ch == '_' ) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+
+ // ---------- implementation of IDocumentExtension --------------
+
+ /**
+ * Inner class to bundle a registered post notifcation replace operation together with its
+ * owner.
+ *
+ * @since 2.0
+ */
+ static private class RegisteredReplace {
+ /** The owner of this replace operation. */
+ IDocumentListener fOwner;
+ /** The replace operation */
+ IDocumentExtension.IReplace fReplace;
+
+ /**
+ * Creates a new bundle object.
+ */
+ RegisteredReplace(IDocumentListener owner, IDocumentExtension.IReplace replace) {
+ fOwner= owner;
+ fReplace= replace;
+ }
+ };
+
+ /**
+ * Flushs all registered post notification changes.
+ *
+ * @since 2.0
+ */
+ private void flushPostNotificationChanges() {
+ if (fPostNotificationChanges != null)
+ fPostNotificationChanges.clear();
+ }
+
+ /**
+ * Executes all registered post notification changes. The process is
+ * repeated until no new post notification changes are added.
+ *
+ * @since 2.0
+ */
+ private void executePostNotificationChanges() {
+
+ if (fStoppedCount > 0)
+ return;
+
+ while (fPostNotificationChanges != null) {
+ List changes= fPostNotificationChanges;
+ fPostNotificationChanges= null;
+
+ Iterator e= changes.iterator();
+ while (e.hasNext()) {
+ RegisteredReplace replace = (RegisteredReplace) e.next();
+ replace.fReplace.perform(this, replace.fOwner);
+ }
+ }
+ }
+
+ /*
+ * @see IDocumentExtension#registerPostNotificationReplace(IDocumentListener, IDocumentExtension.IReplace)
+ * @since 2.0
+ */
+ public void registerPostNotificationReplace(IDocumentListener owner, IDocumentExtension.IReplace replace) {
+ if (fPostNotificationChanges == null)
+ fPostNotificationChanges= new ArrayList(1);
+ fPostNotificationChanges.add(new RegisteredReplace(owner, replace));
+ }
+
+ /*
+ * @see IDocumentExtension#stopPostNotificationProcessing()
+ * @since 2.0
+ */
+ public void stopPostNotificationProcessing() {
+ ++ fStoppedCount;
+ }
+
+ /*
+ * @see IDocumentExtension#resumePostNotificationProcessing()
+ * @since 2.0
+ */
+ public void resumePostNotificationProcessing() {
+ -- fStoppedCount;
+ if (fStoppedCount == 0 && fReentranceCount == 0)
+ executePostNotificationChanges();
+ }
+
+ /*
+ * @see IDocumentExtension#startSequentialRewrite(boolean)
+ * @since 2.0
+ */
+ public void startSequentialRewrite(boolean normalized) {
+ }
+
+ /*
+ * @see IDocumentExtension#stopSequentialRewrite()
+ * @since 2.0
+ */
+ public void stopSequentialRewrite() {
+ }
+}
diff --git a/org.eclipse.text/src/org/eclipse/jface/text/AbstractLineTracker.java b/org.eclipse.text/src/org/eclipse/jface/text/AbstractLineTracker.java
new file mode 100644
index 000000000..f74f3051e
--- /dev/null
+++ b/org.eclipse.text/src/org/eclipse/jface/text/AbstractLineTracker.java
@@ -0,0 +1,502 @@
+package org.eclipse.jface.text;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+
+/**
+ * Abstract implementation of <code>ILineTracker</code>. It lets the
+ * definition of line delimiters to subclasses. Assuming that '\n' is
+ * the only line delimiter, this abstract implementation defines the
+ * following line scheme:
+ * <ul>
+ * <li> "" -> [0,0]
+ * <li> "a" -> [0,1]
+ * <li> "\n" -> [0,1], [1,0]
+ * <li> "a\n" -> [0,2], [2,0]
+ * <li> "a\nb" -> [0,2], [2,1]
+ * <li> "a\nbc\n" -> [0,2], [2,3], [5,0]
+ * </ul>
+ * This class must be subclassed.
+ */
+public abstract class AbstractLineTracker implements ILineTracker {
+
+
+ /**
+ * Combines the information of the occurence of a line delimiter.
+ * <code>delimiterIndex</code> is the index where a line delimiter
+ * starts, whereas <code>delimiterLength</code>, indicates the length
+ * of the delimiter.
+ */
+ protected static class DelimiterInfo {
+ public int delimiterIndex;
+ public int delimiterLength;
+ public String delimiter;
+ };
+
+
+ /** The line information */
+ private List fLines= new ArrayList();
+ /** The length of the tracked text */
+ private int fTextLength;
+
+
+ /**
+ * Creates a new line tracker.
+ */
+ protected AbstractLineTracker() {
+ }
+
+ /**
+ * Binary search for the line at a given offset.
+ *
+ * @param offset the offset whose line should be found
+ * @return the line of the offset
+ */
+ private int findLine(int offset) {
+
+ if (fLines.size() == 0)
+ return -1;
+
+ int left= 0;
+ int right= fLines.size() -1;
+ int mid= 0;
+ Line line= null;
+
+ while (left < right) {
+
+ mid= (left + right) / 2;
+
+ line= (Line) fLines.get(mid);
+ if (offset < line.offset) {
+ if (left == mid)
+ right= left;
+ else
+ right= mid -1;
+ } else if (offset > line.offset) {
+ if (right == mid)
+ left= right;
+ else
+ left= mid +1;
+ } else if (offset == line.offset) {
+ left= right= mid;
+ }
+ }
+
+ line= (Line) fLines.get(left);
+ if (line.offset > offset)
+ -- left;
+ return left;
+ }
+
+ /**
+ * Returns the number of lines covered by the specified text range.
+ *
+ * @param startLine the line where the text range starts
+ * @param offset the start offset of the text range
+ * @param length the length of the text range
+ * @return the number of lines covered by this text range
+ * @exception BadLocationException if range is undefined in this tracker
+ */
+ private int getNumberOfLines(int startLine, int offset, int length) throws BadLocationException {
+
+ if (length == 0)
+ return 1;
+
+ int target= offset + length;
+
+ Line l= (Line) fLines.get(startLine);
+
+ if (l.delimiter == null)
+ return 1;
+
+ if (l.offset + l.length > target)
+ return 1;
+
+ if (l.offset + l.length == target)
+ return 2;
+
+ return getLineNumberOfOffset(target) - startLine + 1;
+ }
+
+ /*
+ * @see ILineTracker#getLineLength
+ */
+ public int getLineLength(int line) throws BadLocationException {
+
+ int lines= fLines.size();
+
+ if (line < 0 || line > lines)
+ throw new BadLocationException();
+
+ if (lines == 0 || lines == line)
+ return 0;
+
+ Line l= (Line) fLines.get(line);
+ return l.length;
+ }
+
+ /*
+ * @see ILineTracker#getLineNumberOfOffset
+ */
+ public int getLineNumberOfOffset(int position) throws BadLocationException {
+
+ if (position > fTextLength)
+ throw new BadLocationException();
+
+ if (position == fTextLength) {
+
+ int lastLine= fLines.size() - 1;
+ if (lastLine < 0)
+ return 0;
+
+ Line l= (Line) fLines.get(lastLine);
+ return (l.delimiter != null ? lastLine + 1 : lastLine);
+ }
+
+ return findLine(position);
+ }
+
+ /*
+ * @see ILineTracker#getLineInformationOfOffset
+ */
+ public IRegion getLineInformationOfOffset(int position) throws BadLocationException {
+ if (position > fTextLength)
+ throw new BadLocationException();
+
+ if (position == fTextLength) {
+ int size= fLines.size();
+ if (size == 0)
+ return new Region(0, 0);
+ Line l= (Line) fLines.get(size - 1);
+ return (l.delimiter != null ? new Line(fTextLength, 0) : new Line(fTextLength - l.length, l.length));
+ }
+
+ return getLineInformation(findLine(position));
+ }
+
+ /*
+ * @see ILineTracker#getLineInformation
+ */
+ public IRegion getLineInformation(int line) throws BadLocationException {
+
+ int lines= fLines.size();
+
+ if (line < 0 || line > lines)
+ throw new BadLocationException();
+
+ if (lines == 0)
+ return new Line(0, 0);
+
+ if (line == lines) {
+ Line l= (Line) fLines.get(line - 1);
+ return new Line(l.offset + l.length, 0);
+ }
+
+ Line l= (Line) fLines.get(line);
+ return (l.delimiter != null ? new Line(l.offset, l.length - l.delimiter.length()) : l);
+ }
+
+ /*
+ * @see ILineTracker#getLineOffset
+ */
+ public int getLineOffset(int line) throws BadLocationException {
+
+ int lines= fLines.size();
+
+ if (line < 0 || line > lines)
+ throw new BadLocationException();
+
+ if (lines == 0)
+ return 0;
+
+ if (line == lines) {
+ Line l= (Line) fLines.get(line - 1);
+ return l.offset + l.length;
+ }
+
+ Line l= (Line) fLines.get(line);
+ return l.offset;
+ }
+
+ /*
+ * @see ILineTracker#getNumberOfLines
+ */
+ public int getNumberOfLines() {
+
+ int lines= fLines.size();
+
+ if (lines == 0)
+ return 1;
+
+ Line l= (Line) fLines.get(lines - 1);
+ return (l.delimiter != null ? lines + 1 : lines);
+ }
+
+ /*
+ * @see ILineTracker#getNumberOfLines(int, int)
+ */
+ public int getNumberOfLines(int position, int length) throws BadLocationException {
+
+ if (position < 0 || position + length > fTextLength)
+ throw new BadLocationException();
+
+ if (length == 0) // optimization
+ return 1;
+
+ return getNumberOfLines(getLineNumberOfOffset(position), position, length);
+ }
+
+ /*
+ * @see ILineTracker#computeNumberOfLines(String)
+ */
+ public int computeNumberOfLines(String text) {
+ int count= 0;
+ int start= 0;
+ DelimiterInfo delimiterInfo= nextDelimiterInfo(text, start);
+ while (delimiterInfo != null && delimiterInfo.delimiterIndex > -1) {
+ ++count;
+ start= delimiterInfo.delimiterIndex + delimiterInfo.delimiterLength;
+ delimiterInfo= nextDelimiterInfo(text, start);
+ }
+ return count;
+ }
+
+
+ /* ----------------- manipulation ------------------------------ */
+
+
+ /**
+ * Returns the info of the first delimiter found in the given
+ * text starting at the given offset.
+ *
+ * @param text the text to be searched
+ * @param offset the offset in the given text
+ * @return the info of the first found delimiter or <code>null</code> if
+ * there is no such info
+ */
+ protected abstract DelimiterInfo nextDelimiterInfo(String text, int offset);
+
+
+ /**
+ * Creates the line structure for the given text. Newly created lines
+ * are inserted into the line structure starting at the given
+ * position. Returns the number of newly created lines.
+ *
+ * @param text the text for which to create a line structure
+ * @param insertPosition the position at which the newly created lines are inserted
+ * into the tracker's line structure
+ * @param offset the offset of all newly created lines
+ * @return the number of newly created lines
+ */
+ private int createLines(String text, int insertPosition, int offset) {
+
+ int count= 0;
+ int start= 0;
+ DelimiterInfo delimiterInfo= nextDelimiterInfo(text, 0);
+
+
+ while (delimiterInfo != null && delimiterInfo.delimiterIndex > -1) {
+
+ int index= delimiterInfo.delimiterIndex + (delimiterInfo.delimiterLength - 1);
+
+ if (insertPosition + count >= fLines.size())
+ fLines.add(new Line(offset + start, offset + index, delimiterInfo.delimiter));
+ else
+ fLines.add(insertPosition + count, new Line(offset + start, offset + index, delimiterInfo.delimiter));
+
+ ++count;
+ start= index + 1;
+ delimiterInfo= nextDelimiterInfo(text, start);
+ }
+
+ if (start < text.length()) {
+ if (insertPosition + count < fLines.size()) {
+ // there is a line below the current
+ Line l= (Line) fLines.get(insertPosition + count);
+ int delta= text.length() - start;
+ l.offset -= delta;
+ l.length += delta;
+ } else {
+ fLines.add(new Line(offset + start, offset + text.length() - 1, null));
+ ++count;
+ }
+ }
+
+ return count;
+ }
+
+ /**
+ * Keeps track of the line information when text is inserted.
+ * Returns the number of inserted lines.
+ *
+ * @param lineNumber the line at which the insert happens
+ * @param offset at which the insert happens
+ * @param text the inserted text
+ * @return the number of inserted lines
+ * @exception BadLocationException if offset is invalid in this tracker
+ */
+ private int insert(int lineNumber, int offset, String text) throws BadLocationException {
+
+ if (text == null || text.length() == 0)
+ return 0;
+
+ fTextLength += text.length();
+
+ int size= fLines.size();
+
+ if (size == 0 || lineNumber >= size)
+ return createLines(text, size, offset);
+
+ Line line= (Line) fLines.get(lineNumber);
+ DelimiterInfo delimiterInfo= nextDelimiterInfo(text, 0);
+ if (delimiterInfo == null || delimiterInfo.delimiterIndex == -1) {
+ line.length += text.length();
+ return 0;
+ }
+
+
+ // as there is a line break, split line but do so only if rest of line is not of length 0
+ int restLength= line.offset + line.length - offset;
+ if (restLength > 0) {
+ // determine start and end of the second half of the splitted line
+ Line lineRest= new Line(offset, restLength);
+ lineRest.delimiter= line.delimiter;
+ // shift it by the inserted text
+ lineRest.offset += text.length();
+ // and insert in line structure
+ fLines.add(lineNumber + 1, lineRest);
+ }
+
+ // adapt the beginning of the splitted line
+ line.delimiter= delimiterInfo.delimiter;
+ int nextStart= offset + delimiterInfo.delimiterIndex + delimiterInfo.delimiterLength;
+ line.length= nextStart - line.offset;
+
+ // insert lines for the remaining text
+ text= text.substring(delimiterInfo.delimiterIndex + delimiterInfo.delimiterLength);
+ return createLines(text, lineNumber + 1, nextStart) + 1;
+ }
+
+ /**
+ * Keeps track of the line information when text is removed. Returns
+ * whether the line at which the deletion start will thereby be deleted.
+ *
+ * @param lineNumber the lineNumber at which the deletion starts
+ * @param offset the offset of the first deleted character
+ * @param length the number of deleted characters
+ * @return whethere the start line of the deletion has been deleted
+ * @exception BadLocationException if position is unkown to the tracker
+ */
+ private boolean remove(int lineNumber, int offset, int length) throws BadLocationException {
+
+ if (length == 0)
+ return false;
+
+ int removedLineEnds= getNumberOfLines(lineNumber, offset, length) - 1;
+ Line line= (Line) fLines.get(lineNumber);
+
+ if ((lineNumber == fLines.size() - 1) && removedLineEnds > 0) {
+ line.length -= length;
+ line.delimiter= null;
+ } else {
+
+ ++ lineNumber;
+ for (int i= 1; i <= removedLineEnds; i++) {
+
+ if (lineNumber == fLines.size()) {
+ line.delimiter= null;
+ break;
+ }
+
+ Line line2= (Line) fLines.get(lineNumber);
+ line.length += line2.length;
+ line.delimiter= line2.delimiter;
+ fLines.remove(lineNumber);
+ }
+ line.length -= length;
+ }
+
+ fTextLength -= length;
+
+ if (line.length == 0) {
+ fLines.remove(line);
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Adapts the offset of all lines with line numbers greater than the specified
+ * one to the given delta.
+ *
+ * @param lineNumber the line number after which to start
+ * @param delta the offset delta to be applied
+ */
+ private void adaptLineOffsets(int lineNumber, int delta) {
+ int size= fLines.size();
+ for (int i= lineNumber + 1; i < size; i++) {
+ Line l= (Line) fLines.get(i);
+ l.offset += delta;
+ }
+ }
+
+ /*
+ * @see ILineTracker#replace
+ */
+ public void replace(int position, int length, String text) throws BadLocationException {
+
+ int lineNumber= getLineNumberOfOffset(position);
+ int insertLineNumber= lineNumber;
+
+ if (remove(lineNumber, position, length))
+ -- lineNumber;
+
+ lineNumber += insert(insertLineNumber, position, text);
+
+ int delta= -length;
+ if (text != null)
+ delta= text.length() + delta;
+
+ if (delta != 0)
+ adaptLineOffsets(lineNumber, delta);
+ }
+
+ /*
+ * @see ILineTracker#set
+ */
+ public void set(String text) {
+ fLines.clear();
+ if (text != null) {
+ fTextLength= text.length();
+ createLines(text, 0, 0);
+ }
+ }
+
+ /*
+ * @see ILineTracker#getLineDelimiter
+ */
+ public String getLineDelimiter(int line) throws BadLocationException {
+
+ int lines= fLines.size();
+
+ if (line < 0 || line > lines)
+ throw new BadLocationException();
+
+ if (lines == 0)
+ return null;
+
+ if (line == lines)
+ return null;
+
+ Line l= (Line) fLines.get(line);
+ return l.delimiter;
+ }
+}
diff --git a/org.eclipse.text/src/org/eclipse/jface/text/Assert.java b/org.eclipse.text/src/org/eclipse/jface/text/Assert.java
new file mode 100644
index 000000000..02cfc73a5
--- /dev/null
+++ b/org.eclipse.text/src/org/eclipse/jface/text/Assert.java
@@ -0,0 +1,165 @@
+package org.eclipse.jface.text;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+/**
+ * <code>Assert</code> is useful for for embedding runtime sanity checks
+ * in code. The static predicate methods all test a condition and throw some
+ * type of unchecked exception if the condition does not hold.
+ * <p>
+ * Assertion failure exceptions, like most runtime exceptions, are
+ * thrown when something is misbehaving. Assertion failures are invariably
+ * unspecified behavior; consequently, clients should never rely on
+ * these being thrown (or not thrown). <b>If you find yourself in the
+ * position where you need to catch an assertion failure, you have most
+ * certainly written your program incorrectly.</b>
+ * </p>
+ * <p>
+ * Note that an <code>assert</code> statement is slated to be added to the
+ * Java language in JDK 1.4, rending this class obsolete.
+ * </p>
+ */
+public final class Assert {
+
+ /**
+ * <code>AssertionFailedException</code> is a runtime exception thrown
+ * by some of the methods in <code>Assert</code>.
+ * <p>
+ * This class is not declared public to prevent some misuses; programs that catch
+ * or otherwise depend on assertion failures are susceptible to unexpected
+ * breakage when assertions in the code are added or removed.
+ * </p>
+ */
+ private static class AssertionFailedException extends RuntimeException {
+
+ /**
+ * Constructs a new exception.
+ */
+ public AssertionFailedException() {
+ }
+
+ /**
+ * Constructs a new exception with the given message.
+ */
+ public AssertionFailedException(String detail) {
+ super(detail);
+ }
+ }
+/* This class is not intended to be instantiated. */
+private Assert() {
+}
+/**
+ * Asserts that an argument is legal. If the given boolean is
+ * not <code>true</code>, an <code>IllegalArgumentException</code>
+ * is thrown.
+ *
+ * @param expression the outcome of the check
+ * @return <code>true</code> if the check passes (does not return
+ * if the check fails)
+ * @exception IllegalArgumentException if the legality test failed
+ */
+public static boolean isLegal(boolean expression) {
+ // succeed as quickly as possible
+ if (expression) {
+ return true;
+ }
+ return isLegal(expression, "");//$NON-NLS-1$
+}
+/**
+ * Asserts that an argument is legal. If the given boolean is
+ * not <code>true</code>, an <code>IllegalArgumentException</code>
+ * is thrown.
+ * The given message is included in that exception, to aid debugging.
+ *
+ * @param expression the outcome of the check
+ * @param message the message to include in the exception
+ * @return <code>true</code> if the check passes (does not return
+ * if the check fails)
+ * @exception IllegalArgumentException if the legality test failed
+ */
+public static boolean isLegal(boolean expression, String message) {
+ if (!expression)
+ throw new IllegalArgumentException("assertion failed; " + message); //$NON-NLS-1$
+ return expression;
+}
+/**
+ * Asserts that the given object is not <code>null</code>. If this
+ * is not the case, some kind of unchecked exception is thrown.
+ * <p>
+ * As a general rule, parameters passed to API methods must not be
+ * <code>null</code> unless <b>explicitly</b> allowed in the method's
+ * specification. Similarly, results returned from API methods are never
+ * <code>null</code> unless <b>explicitly</b> allowed in the method's
+ * specification. Implementations are encouraged to make regular use of
+ * <code>Assert.isNotNull</code> to ensure that <code>null</code>
+ * parameters are detected as early as possible.
+ * </p>
+ *
+ * @param object the value to test
+ * @exception Throwable an unspecified unchecked exception if the object
+ * is <code>null</code>
+ */
+public static void isNotNull(Object object) {
+ // succeed as quickly as possible
+ if (object != null) {
+ return;
+ }
+ isNotNull(object, "");//$NON-NLS-1$
+}
+/**
+ * Asserts that the given object is not <code>null</code>. If this
+ * is not the case, some kind of unchecked exception is thrown.
+ * The given message is included in that exception, to aid debugging.
+ * <p>
+ * As a general rule, parameters passed to API methods must not be
+ * <code>null</code> unless <b>explicitly</b> allowed in the method's
+ * specification. Similarly, results returned from API methods are never
+ * <code>null</code> unless <b>explicitly</b> allowed in the method's
+ * specification. Implementations are encouraged to make regular use of
+ * <code>Assert.isNotNull</code> to ensure that <code>null</code>
+ * parameters are detected as early as possible.
+ * </p>
+ *
+ * @param object the value to test
+ * @param message the message to include in the exception
+ * @exception Throwable an unspecified unchecked exception if the object
+ * is <code>null</code>
+ */
+public static void isNotNull(Object object, String message) {
+ if (object == null)
+ throw new AssertionFailedException("null argument;" + message);//$NON-NLS-1$
+}
+/**
+ * Asserts that the given boolean is <code>true</code>. If this
+ * is not the case, some kind of unchecked exception is thrown.
+ *
+ * @param expression the outcome of the check
+ * @return <code>true</code> if the check passes (does not return
+ * if the check fails)
+ */
+public static boolean isTrue(boolean expression) {
+ // succeed as quickly as possible
+ if (expression) {
+ return true;
+ }
+ return isTrue(expression, "");//$NON-NLS-1$
+}
+/**
+ * Asserts that the given boolean is <code>true</code>. If this
+ * is not the case, some kind of unchecked exception is thrown.
+ * The given message is included in that exception, to aid debugging.
+ *
+ * @param expression the outcome of the check
+ * @param message the message to include in the exception
+ * @return <code>true</code> if the check passes (does not return
+ * if the check fails)
+ */
+public static boolean isTrue(boolean expression, String message) {
+ if (!expression)
+ throw new AssertionFailedException("Assertion failed: "+message);//$NON-NLS-1$
+ return expression;
+}
+}
diff --git a/org.eclipse.text/src/org/eclipse/jface/text/BadLocationException.java b/org.eclipse.text/src/org/eclipse/jface/text/BadLocationException.java
new file mode 100644
index 000000000..17f208044
--- /dev/null
+++ b/org.eclipse.text/src/org/eclipse/jface/text/BadLocationException.java
@@ -0,0 +1,30 @@
+package org.eclipse.jface.text;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+
+/**
+ * Indicates the attempt to access a non-existing position.
+ * The attempt has been performed on a text store such as a document or string.
+ */
+public class BadLocationException extends Exception {
+
+ /**
+ * Creates a new bad location exception.
+ */
+ public BadLocationException() {
+ super();
+ }
+
+ /**
+ * Creates a new bad location exception.
+ *
+ * @param message the exception message
+ */
+ public BadLocationException(String message) {
+ super(message);
+ }
+}
diff --git a/org.eclipse.text/src/org/eclipse/jface/text/BadPositionCategoryException.java b/org.eclipse.text/src/org/eclipse/jface/text/BadPositionCategoryException.java
new file mode 100644
index 000000000..ba853d861
--- /dev/null
+++ b/org.eclipse.text/src/org/eclipse/jface/text/BadPositionCategoryException.java
@@ -0,0 +1,32 @@
+package org.eclipse.jface.text;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+
+/**
+ * Indicates the attempt to access a non-existing position
+ * category in a document.
+ *
+ * @see IDocument
+ */
+public class BadPositionCategoryException extends Exception {
+
+ /**
+ * Creates a new bad position category exception.
+ */
+ public BadPositionCategoryException() {
+ super();
+ }
+
+ /**
+ * Creates a new bad position category exception.
+ *
+ * @param message the exception's message
+ */
+ public BadPositionCategoryException(String message) {
+ super(message);
+ }
+}
diff --git a/org.eclipse.text/src/org/eclipse/jface/text/ChildDocument.java b/org.eclipse.text/src/org/eclipse/jface/text/ChildDocument.java
new file mode 100644
index 000000000..7d2185c33
--- /dev/null
+++ b/org.eclipse.text/src/org/eclipse/jface/text/ChildDocument.java
@@ -0,0 +1,290 @@
+/**********************************************************************
+Copyright (c) 2000, 2002 IBM Corp. and others.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Common Public License v1.0
+which accompanies this distribution, and is available at
+http://www.eclipse.org/legal/cpl-v10.html
+
+Contributors:
+ IBM Corporation - Initial implementation
+**********************************************************************/
+
+package org.eclipse.jface.text;
+
+
+
+/**
+ * A child document represent a range of its parent document.
+ * The child document is always in sync with its parent document
+ * by utilizing the parent document as its <code>ITextStore</code>.
+ * This class is for internal use only.
+ *
+ * @see ITextStore
+ */
+public final class ChildDocument extends AbstractDocument {
+
+
+ /**
+ * Implements ITextStore based on IDocument.
+ */
+ class TextStore implements ITextStore {
+
+ /*
+ * @see ITextStore#set
+ */
+ public void set(String txt) {
+ try {
+ fParentDocument.replace(fRange.getOffset(), fRange.getLength(), txt);
+ } catch (BadLocationException x) {
+ // cannot happen
+ }
+ }
+
+ /*
+ * @see ITextStore#replace
+ */
+ public void replace(int offset, int length, String txt) {
+ try {
+ fParentDocument.replace(fRange.getOffset() + offset, length, txt);
+ } catch (BadLocationException x) {
+ // ignored as surrounding document should have handled this
+ }
+ }
+
+ /*
+ * @see ITextStore#getLength
+ */
+ public int getLength() {
+ return fRange.getLength();
+ }
+
+ /*
+ * @see ITextStore#get
+ */
+ public String get(int offset, int length) {
+ try {
+ return fParentDocument.get(fRange.getOffset() + offset, length);
+ } catch (BadLocationException x) {
+ }
+
+ return null;
+ }
+
+ /*
+ * @see ITextStore#get
+ */
+ public char get(int offset) {
+ try {
+ return fParentDocument.getChar(fRange.getOffset() + offset);
+ } catch (BadLocationException x) {
+ }
+
+ return (char) 0;
+ }
+ };
+
+
+
+ /** The parent document */
+ private IDocument fParentDocument;
+ /**
+ * The parent document as document extension
+ * @since 2.0
+ */
+ private IDocumentExtension fExtension;
+
+ /** The section inside the parent document */
+ private Position fRange;
+ /** The document event issued by the parent document */
+ private DocumentEvent fParentEvent;
+ /** The document event issued and to be issued by the child document */
+ private DocumentEvent fEvent;
+ /** Indicates whether the child document initiated a parent document update or not */
+ private boolean fIsUpdating= false;
+
+ /**
+ * Creates a child document for the given range of the given parent document.
+ *
+ * @param parentDocument the parent Document
+ * @param range the parent document range covered by the child document
+ */
+ public ChildDocument(IDocument parentDocument, Position range) {
+ super();
+
+ fParentDocument= parentDocument;
+ if (fParentDocument instanceof IDocumentExtension)
+ fExtension= (IDocumentExtension) fParentDocument;
+
+ fRange= range;
+
+ ITextStore s= new TextStore();
+ ILineTracker tracker= new DefaultLineTracker();
+ tracker.set(s.get(0, fRange.getLength()));
+
+ setTextStore(s);
+ setLineTracker(tracker);
+
+ completeInitialization();
+ }
+
+ /**
+ * Sets the child document's parent document range.
+ *
+ * @param offset the offset of the parent document range
+ * @param length the length of the parent document range
+ */
+ public void setParentDocumentRange(int offset, int length) throws BadLocationException {
+
+ if (offset < 0 || length < 0 || offset + length > fParentDocument.getLength())
+ throw new BadLocationException();
+
+ fRange.setOffset(offset);
+ fRange.setLength(length);
+
+ getTracker().set(fParentDocument.get(offset, length));
+ }
+
+ /**
+ * Returns parent document
+ *
+ * @return the parent document
+ */
+ public IDocument getParentDocument() {
+ return fParentDocument;
+ }
+
+ /**
+ * Returns the range of the parent document covered by this child document.
+ *
+ * @return the child document's parent document range
+ */
+ public Position getParentDocumentRange() {
+ return fRange;
+ }
+
+ /**
+ * Transforms a document event of the parent document into a child document
+ * based document event.
+ *
+ * @param e the parent document event
+ * @return the child document event
+ */
+ private DocumentEvent normalize(DocumentEvent e) {
+
+ int delta= e.getOffset() - fRange.getOffset();
+ int offset= delta < 0 ? 0 : delta;
+ int length= delta < 0 ? e.fLength + delta : e.fLength;
+ if (offset + length > fRange.getLength())
+ length= fRange.getLength() - offset;
+
+ return new ChildDocumentEvent(this, offset, length, e.fText, e);
+ }
+
+ /**
+ * When called this child document is informed about a forthcoming change
+ * of its parent document. This child document checks whether the parent
+ * document changed affects it and if so informs all document listeners.
+ *
+ * @param event the parent document event
+ */
+ public void parentDocumentAboutToBeChanged(DocumentEvent event) {
+
+ fParentEvent= event;
+
+ if (fRange.overlapsWith(event.fOffset, event.fLength)) {
+ fEvent= normalize(event);
+ delayedFireDocumentAboutToBeChanged();
+ } else
+ fEvent= null;
+ }
+
+ /**
+ * When called this child document is informed about a change of its parent document.
+ * If this child document is affected it informs all of its document listeners.
+ *
+ * @param event the parent document event
+ */
+ public void parentDocumentChanged(DocumentEvent event) {
+ if ( !fIsUpdating && event == fParentEvent && fEvent != null) {
+ try {
+ getTracker().replace(fEvent.fOffset, fEvent.fLength, fEvent.fText);
+ fireDocumentChanged(fEvent);
+ } catch (BadLocationException x) {
+ Assert.isLegal(false);
+ }
+ }
+ }
+
+ /*
+ * @see AbstractDocument#fireDocumentAboutToBeChanged
+ */
+ protected void fireDocumentAboutToBeChanged(DocumentEvent event) {
+ // delay it until there is a notification from the parent document
+ // otherwise there it is expensive to construct the parent document information
+ }
+
+ /**
+ * Fires the child document event as about-to-be-changed event to all
+ * registed listeners.
+ */
+ private void delayedFireDocumentAboutToBeChanged() {
+ super.fireDocumentAboutToBeChanged(fEvent);
+ }
+
+ /**
+ * Ignores the given event and sends the similar child document event instead.
+ *
+ * @param event the event to be ignored
+ */
+ protected void fireDocumentChanged(DocumentEvent event) {
+ super.fireDocumentChanged(fEvent);
+ }
+
+ /*
+ * @see IDocument#replace(int, int, String)
+ * @since 2.0
+ */
+ public void replace(int offset, int length, String text) throws BadLocationException {
+ try {
+ fIsUpdating= true;
+ if (fExtension != null)
+ fExtension.stopPostNotificationProcessing();
+
+ super.replace(offset, length, text);
+
+ } finally {
+ fIsUpdating= false;
+ if (fExtension != null)
+ fExtension.resumePostNotificationProcessing();
+ }
+ }
+
+ /*
+ * @see IDocument#set(String)
+ * @since 2.0
+ */
+ public void set(String text) {
+ try {
+ fIsUpdating= true;
+ if (fExtension != null)
+ fExtension.stopPostNotificationProcessing();
+
+ super.set(text);
+
+ } finally {
+ fIsUpdating= false;
+ if (fExtension != null)
+ fExtension.resumePostNotificationProcessing();
+ }
+ }
+
+ /*
+ * @see IDocumentExtension#registerPostNotificationReplace(IDocumentListener, IDocumentExtension.IReplace)
+ * @since 2.0
+ */
+ public void registerPostNotificationReplace(IDocumentListener owner, IDocumentExtension.IReplace replace) {
+ if (!fIsUpdating)
+ throw new UnsupportedOperationException();
+ super.registerPostNotificationReplace(owner, replace);
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.text/src/org/eclipse/jface/text/ChildDocumentEvent.java b/org.eclipse.text/src/org/eclipse/jface/text/ChildDocumentEvent.java
new file mode 100644
index 000000000..cc5e52976
--- /dev/null
+++ b/org.eclipse.text/src/org/eclipse/jface/text/ChildDocumentEvent.java
@@ -0,0 +1,39 @@
+package org.eclipse.jface.text;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+/**
+ * A child document event represents a parent document event as a
+ * child-relative document event. It also carries the original event.
+ */
+public class ChildDocumentEvent extends DocumentEvent {
+
+ /** The parent document event */
+ private DocumentEvent fParentEvent;
+
+ /**
+ * Creates a new child document event.
+ *
+ * @param doc the child document
+ * @param offset the offset in the child document
+ * @param length the length in the child document
+ * @param text the substitution text
+ * @param parentEvent the parent Event
+ */
+ public ChildDocumentEvent(IDocument doc, int offset, int length, String text, DocumentEvent parentEvent) {
+ super(doc, offset, length, text);
+ fParentEvent= parentEvent;
+ }
+
+ /**
+ * Returns this event's parent event.
+ *
+ * @return this event's parent event
+ */
+ public DocumentEvent getParentEvent() {
+ return fParentEvent;
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.text/src/org/eclipse/jface/text/ChildDocumentManager.java b/org.eclipse.text/src/org/eclipse/jface/text/ChildDocumentManager.java
new file mode 100644
index 000000000..2b9ed9a4b
--- /dev/null
+++ b/org.eclipse.text/src/org/eclipse/jface/text/ChildDocumentManager.java
@@ -0,0 +1,312 @@
+package org.eclipse.jface.text;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+
+
+
+/**
+ * Manages a set of child documents for given parent documents.
+ * A child document represents a particular range of the parent
+ * document and is accordingly adapted to changes of the parent document.
+ * Vice versa, the parent document is accordingly adapted to changes of
+ * its child documents. The manager does not maintain any particular management
+ * structure but utilizes mechanisms given by <code>IDocument</code> such
+ * as position categories and position updaters. <p>
+ *
+ * For internal use only.
+ */
+public final class ChildDocumentManager implements IDocumentListener {
+
+
+ /**
+ * Name of the position categories used to keep track of the child
+ * documents offset ranges into the parent document.
+ */
+ public final static String CHILDDOCUMENTS= "__childdocuments"; //$NON-NLS-1$
+
+
+ /**
+ * Positions which are used to mark the child documents offset ranges into
+ * the parent documents. This position uses as bidirectional reference as
+ * it knows the child document as well as the parent document.
+ */
+ static class ChildPosition extends Position {
+
+ public IDocument fParentDocument;
+ public ChildDocument fChildDocument;
+
+ public ChildPosition(IDocument parentDocument, int offset, int length) {
+ super(offset, length);
+ fParentDocument= parentDocument;
+ }
+
+ /**
+ * Changed to be compatible to the position updater behavior
+ * @see Position#overlapsWith(int, int)
+ */
+ public boolean overlapsWith(int offset, int length) {
+ boolean append= (offset == this.offset + this.length) && length == 0;
+ return append || super.overlapsWith(offset, length);
+ }
+ };
+
+
+ /**
+ * The position updater used to adapt the positions representing
+ * the child document ranges to changes of the parent document.
+ */
+ static class ChildPositionUpdater extends DefaultPositionUpdater {
+
+ /**
+ * Creates the position updated.
+ */
+ protected ChildPositionUpdater() {
+ super(CHILDDOCUMENTS);
+ }
+
+ /**
+ * Child document ranges cannot be deleted other then by calling
+ * freeChildDocument.
+ */
+ protected boolean notDeleted() {
+ return true;
+ }
+
+ /**
+ * If an insertion happens at a child document's start offset, the
+ * position is extended rather than shifted. Also, if something is added
+ * right behind the end of the position, the position is extended rather
+ * than kept stable.
+ */
+ protected void adaptToInsert() {
+
+ int myStart= fPosition.offset;
+ int myEnd= fPosition.offset + fPosition.length;
+ myEnd= Math.max(myStart, myEnd);
+
+ int yoursStart= fOffset;
+ int yoursEnd= fOffset + fReplaceLength -1;
+ yoursEnd= Math.max(yoursStart, yoursEnd);
+
+ if (myEnd < yoursStart)
+ return;
+
+ if (myStart <= yoursStart)
+ fPosition.length += fReplaceLength;
+ else
+ fPosition.offset += fReplaceLength;
+ }
+ };
+
+ /**
+ * The child document partitioner uses the parent document to answer all questions.
+ */
+ static class ChildPartitioner implements IDocumentPartitioner {
+
+ protected ChildDocument fChildDocument;
+ protected IDocument fParentDocument;
+
+ protected ChildPartitioner() {
+ }
+
+ /*
+ * @see IDocumentPartitioner#getPartition(int)
+ */
+ public ITypedRegion getPartition(int offset) {
+ try {
+ offset += fChildDocument.getParentDocumentRange().getOffset();
+ return fParentDocument.getPartition(offset);
+ } catch (BadLocationException x) {
+ }
+
+ return null;
+ }
+
+ /*
+ * @see IDocumentPartitioner#computePartitioning(int, int)
+ */
+ public ITypedRegion[] computePartitioning(int offset, int length) {
+ try {
+ offset += fChildDocument.getParentDocumentRange().getOffset();
+ return fParentDocument.computePartitioning(offset, length);
+ } catch (BadLocationException x) {
+ }
+
+ return null;
+ }
+
+ /*
+ * @see IDocumentPartitioner#getContentType(int)
+ */
+ public String getContentType(int offset) {
+ try {
+ offset += fChildDocument.getParentDocumentRange().getOffset();
+ return fParentDocument.getContentType(offset);
+ } catch (BadLocationException x) {
+ }
+
+ return null;
+ }
+
+ /*
+ * @see IDocumentPartitioner#getLegalContentTypes()
+ */
+ public String[] getLegalContentTypes() {
+ return fParentDocument.getLegalContentTypes();
+ }
+
+ /*
+ * @see IDocumentPartitioner#documentChanged(DocumentEvent)
+ */
+ public boolean documentChanged(DocumentEvent event) {
+ // ignore as the parent does this for us
+ return false;
+ }
+
+ /*
+ * @see IDocumentPartitioner#documentAboutToBeChanged(DocumentEvent)
+ */
+ public void documentAboutToBeChanged(DocumentEvent event) {
+ // ignore as the parent does this for us
+ }
+
+ /*
+ * @see IDocumentPartitioner#disconnect()
+ */
+ public void disconnect() {
+ fChildDocument= null;
+ fParentDocument= null;
+ }
+
+ /*
+ * @see IDocumentPartitioner#connect(IDocument)
+ */
+ public void connect(IDocument childDocument) {
+ Assert.isTrue(childDocument instanceof ChildDocument);
+ fChildDocument= (ChildDocument) childDocument;
+ fParentDocument= fChildDocument.getParentDocument();
+ }
+ };
+
+
+
+ /** The position updater shared by all documents which have child documents */
+ private IPositionUpdater fChildPositionUpdater;
+
+
+
+ /**
+ * Returns the child position updater. If necessary, it is dynamically created.
+ *
+ * @return the child position updater
+ */
+ protected IPositionUpdater getChildPositionUpdater() {
+ if (fChildPositionUpdater == null)
+ fChildPositionUpdater= new ChildPositionUpdater();
+ return fChildPositionUpdater;
+ }
+
+ /**
+ * Creates and returns a new child document for the specified range of the given parent document.
+ * The created child document is initialized with a child document partitioner.
+ *
+ * @param parent the parent document
+ * @param offset the offset of the parent document range
+ * @param length the length of the parent document range
+ * @exception BadLocationException if the specified range is invalid in the parent document
+ */
+ public ChildDocument createChildDocument(IDocument parent, int offset, int length) throws BadLocationException {
+
+ if (!parent.containsPositionCategory(CHILDDOCUMENTS)) {
+ parent.addPositionCategory(CHILDDOCUMENTS);
+ parent.addPositionUpdater(getChildPositionUpdater());
+ parent.addDocumentListener(this);
+ }
+
+ ChildPosition pos= new ChildPosition(parent, offset, length);
+ try {
+ parent.addPosition(CHILDDOCUMENTS, pos);
+ } catch (BadPositionCategoryException x) {
+ // cannot happen
+ }
+
+ ChildDocument child= new ChildDocument(parent, pos);
+ IDocumentPartitioner partitioner= new ChildPartitioner();
+ child.setDocumentPartitioner(partitioner);
+ partitioner.connect(child);
+
+ pos.fChildDocument= child;
+
+ return child;
+ }
+
+ /**
+ * Disconnects the given child document from it's parent document and frees
+ * all resources which are no longer needed.
+ *
+ * @param childDocument the child document to be freed
+ */
+ public void freeChildDocument(ChildDocument childDocument) {
+
+ childDocument.getDocumentPartitioner().disconnect();
+
+ ChildPosition pos= (ChildPosition) childDocument.getParentDocumentRange();
+ IDocument parent= pos.fParentDocument;
+
+ try {
+ parent.removePosition(CHILDDOCUMENTS, pos);
+ Position[] category= parent.getPositions(CHILDDOCUMENTS);
+ if (category.length == 0) {
+ parent.removeDocumentListener(this);
+ parent.removePositionUpdater(getChildPositionUpdater());
+ parent.removePositionCategory(CHILDDOCUMENTS);
+ }
+ } catch (BadPositionCategoryException x) {
+ // cannot happen
+ }
+ }
+
+ /**
+ * Informs all child documents of the document which issued this document event.
+ *
+ * @param about indicates whether the change is about to happen or alread happend
+ * @param event the document event which will be processed to inform child documents
+ */
+ protected void fireDocumentEvent(boolean about, DocumentEvent event) {
+ try {
+
+ IDocument parent= event.getDocument();
+ Position[] children= parent.getPositions(CHILDDOCUMENTS);
+ for (int i= 0; i < children.length; i++) {
+ Object o= children[i];
+ if (o instanceof ChildPosition) {
+ ChildPosition pos= (ChildPosition) o;
+ if (about)
+ pos.fChildDocument.parentDocumentAboutToBeChanged(event);
+ else
+ pos.fChildDocument.parentDocumentChanged(event);
+ }
+ }
+ } catch (BadPositionCategoryException x) {
+ // cannot happen
+ }
+ }
+
+ /*
+ * @see IDocumentListener#documentChanged(DocumentEvent)
+ */
+ public void documentChanged(DocumentEvent event) {
+ fireDocumentEvent(false, event);
+ }
+
+ /*
+ * @see IDocumentListener#documentAboutToBeChanged(DocumentEvent)
+ */
+ public void documentAboutToBeChanged(DocumentEvent event) {
+ fireDocumentEvent(true, event);
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.text/src/org/eclipse/jface/text/ConfigurableLineTracker.java b/org.eclipse.text/src/org/eclipse/jface/text/ConfigurableLineTracker.java
new file mode 100644
index 000000000..817bd9cdf
--- /dev/null
+++ b/org.eclipse.text/src/org/eclipse/jface/text/ConfigurableLineTracker.java
@@ -0,0 +1,58 @@
+package org.eclipse.jface.text;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+
+
+/**
+ * Standard implementation of a generic <code>ILineTracker</code>.
+ * The line tracker can be configured with the set of legal line delimiters.
+ * Line delimiters are unconstrainted. The line delimiters are used to
+ * compute the tracker's line structure. In the case of overlapping line delimiters,
+ * the longest line delimiter is given precedence of the shorter ones.<p>
+ * This class is not intended to be subclassed.
+ */
+public class ConfigurableLineTracker extends AbstractLineTracker {
+
+
+ /** The strings which are considered being the line delimiter */
+ private String[] fDelimiters;
+ /** A predefined delimiter info which is always reused as return value */
+ private DelimiterInfo fDelimiterInfo= new DelimiterInfo();
+
+
+ /**
+ * Creates a standard line tracker for the given line delimiters.
+ *
+ * @param legalLineDelimiters the tracker's legal line delimiters,
+ * may not be <code>null</code> and must be longer than 0
+ */
+ public ConfigurableLineTracker(String[] legalLineDelimiters) {
+ Assert.isTrue(legalLineDelimiters != null && legalLineDelimiters.length > 0);
+ fDelimiters= legalLineDelimiters;
+ }
+
+ /*
+ * @see ILineDelimiter@getLegalLineDelimiters
+ */
+ public String[] getLegalLineDelimiters() {
+ return fDelimiters;
+ }
+
+ /*
+ * @see AbstractLineTracker#nextDelimiterInfo(String, int)
+ */
+ protected DelimiterInfo nextDelimiterInfo(String text, int offset) {
+ int[] info= TextUtilities.indexOf(fDelimiters, text, offset);
+ if (info[0] == -1)
+ return null;
+
+ fDelimiterInfo.delimiterIndex= info[0];
+ fDelimiterInfo.delimiter= fDelimiters[info[1]];
+ fDelimiterInfo.delimiterLength= fDelimiterInfo.delimiter.length();
+ return fDelimiterInfo;
+ }
+}
diff --git a/org.eclipse.text/src/org/eclipse/jface/text/DefaultLineTracker.java b/org.eclipse.text/src/org/eclipse/jface/text/DefaultLineTracker.java
new file mode 100644
index 000000000..4f4b0eca9
--- /dev/null
+++ b/org.eclipse.text/src/org/eclipse/jface/text/DefaultLineTracker.java
@@ -0,0 +1,74 @@
+package org.eclipse.jface.text;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+
+
+/**
+ * Standard implementation of <code>ILineTracker</code>.
+ * The line tracker considers the three common line
+ * delimiters which are '\n', '\r', '\r\n'.<p>
+ * This class is not intended to be subclassed.
+ */
+public class DefaultLineTracker extends AbstractLineTracker {
+
+ /** The predefined delimiters of this tracker */
+ public final static String[] DELIMITERS= { "\r", "\n", "\r\n" }; //$NON-NLS-3$ //$NON-NLS-1$ //$NON-NLS-2$
+ /** A predefined delimiter info which is always reused as return value */
+ private DelimiterInfo fDelimiterInfo= new DelimiterInfo();
+
+
+ /**
+ * Creates a standard line tracker.
+ */
+ public DefaultLineTracker() {
+ }
+
+ /*
+ * @see ILineDelimiter@getLegalLineDelimiters
+ */
+ public String[] getLegalLineDelimiters() {
+ return DELIMITERS;
+ }
+
+ /*
+ * @see AbstractLineTracker#nextDelimiterInfo(String, int)
+ */
+ protected DelimiterInfo nextDelimiterInfo(String text, int offset) {
+
+ char ch;
+ int length= text.length();
+ for (int i= offset; i < length; i++) {
+
+ ch= text.charAt(i);
+ if (ch == '\r') {
+
+ if (i + 1 < length) {
+ if (text.charAt(i + 1) == '\n') {
+ fDelimiterInfo.delimiter= DELIMITERS[2];
+ fDelimiterInfo.delimiterIndex= i;
+ fDelimiterInfo.delimiterLength= 2;
+ return fDelimiterInfo;
+ }
+ }
+
+ fDelimiterInfo.delimiter= DELIMITERS[0];
+ fDelimiterInfo.delimiterIndex= i;
+ fDelimiterInfo.delimiterLength= 1;
+ return fDelimiterInfo;
+
+ } else if (ch == '\n') {
+
+ fDelimiterInfo.delimiter= DELIMITERS[1];
+ fDelimiterInfo.delimiterIndex= i;
+ fDelimiterInfo.delimiterLength= 1;
+ return fDelimiterInfo;
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/org.eclipse.text/src/org/eclipse/jface/text/DefaultPositionUpdater.java b/org.eclipse.text/src/org/eclipse/jface/text/DefaultPositionUpdater.java
new file mode 100644
index 000000000..c5044eb3f
--- /dev/null
+++ b/org.eclipse.text/src/org/eclipse/jface/text/DefaultPositionUpdater.java
@@ -0,0 +1,219 @@
+package org.eclipse.jface.text;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+
+/**
+ * Default implementation of <code>IPositionUpdater</code>.
+ * A default position updater must be configured with the position category
+ * whose positions it will update. Other position categories are not affected
+ * by this updater.<p>
+ * This implementation follows the following specification:
+ * <ul>
+ * <li> Inserting or deleting text before the position shifts the position accordingly.
+ * <li> Inserting text at the position offset shifts the position accordingly.
+ * <li> Inserting or deleting text completely surrounded by the position shrinks or stretches the position.
+ * <li> Inserting or deleting text after a position does not affect the position.
+ * <li> Deleting text which completly contains the position deletes the position.
+ * <li> Replacing text overlapping with the position considered as a sequence of first
+ * deleting the replaced text and afterwards inserting the new text. Thus, a
+ * position might first be shifted and shrink and then be stretched.
+ * </ul>
+ * This class can be used as is or be adapted by subclasses. Fields are protected to
+ * allow subclasses direct access. Because of the frequency with which position updaters
+ * are used this is a performance decision.
+ */
+public class DefaultPositionUpdater implements IPositionUpdater {
+
+ /** The position category the updater draws responsible for */
+ private String fCategory;
+
+ /** Caches the currently investigated position */
+ protected Position fPosition;
+ /** Remembers the original state of the investigated position */
+ protected Position fOriginalPosition= new Position(0, 0);
+ /** Caches the offset of the replaced text */
+ protected int fOffset;
+ /** Caches the length of the replaced text */
+ protected int fLength;
+ /** Caches the length of the newly inserted text */
+ protected int fReplaceLength;
+ /** Catches the document */
+ protected IDocument fDocument;
+
+
+ /**
+ * Creates a new default positon updater for the given category.
+ *
+ * @param category the category the updater is responsible for
+ */
+ public DefaultPositionUpdater(String category) {
+ fCategory= category;
+ }
+
+ /**
+ * Returns the category this updater is resonsible for.
+ *
+ * @return the category this updater is resonsible for
+ */
+ protected String getCategory() {
+ return fCategory;
+ }
+
+ /**
+ * Adapts the currently investigated position to an insertion.
+ */
+ protected void adaptToInsert() {
+
+ int myStart= fPosition.offset;
+ int myEnd= fPosition.offset + fPosition.length -1;
+ myEnd= Math.max(myStart, myEnd);
+
+ int yoursStart= fOffset;
+ int yoursEnd= fOffset + fReplaceLength -1;
+ yoursEnd= Math.max(yoursStart, yoursEnd);
+
+ if (myEnd < yoursStart)
+ return;
+
+ if (fLength <= 0) {
+
+ if (myStart < yoursStart)
+ fPosition.length += fReplaceLength;
+ else
+ fPosition.offset += fReplaceLength;
+
+ } else {
+
+ if (myStart <= yoursStart && fOriginalPosition.offset <= yoursStart)
+ fPosition.length += fReplaceLength;
+ else
+ fPosition.offset += fReplaceLength;
+ }
+ }
+
+ /**
+ * Adapts the currently investigated position to a deletion.
+ */
+ protected void adaptToRemove() {
+
+ int myStart= fPosition.offset;
+ int myEnd= fPosition.offset + fPosition.length -1;
+ myEnd= Math.max(myStart, myEnd);
+
+ int yoursStart= fOffset;
+ int yoursEnd= fOffset + fLength -1;
+ yoursEnd= Math.max(yoursStart, yoursEnd);
+
+ if (myEnd < yoursStart)
+ return;
+
+ if (myStart <= yoursStart) {
+
+ if (yoursEnd <= myEnd)
+ fPosition.length -= fLength;
+ else
+ fPosition.length -= (myEnd - yoursStart +1);
+
+ } else if (yoursStart < myStart) {
+
+ if (yoursEnd < myStart)
+ fPosition.offset -= fLength;
+ else {
+ fPosition.offset -= (myStart - yoursStart);
+ fPosition.length -= (yoursEnd - myStart +1);
+ }
+
+ }
+
+ // validate position to allowed values
+ if (fPosition.offset < 0)
+ fPosition.offset= 0;
+
+ if (fPosition.length < 0)
+ fPosition.length= 0;
+ }
+
+ /**
+ * Adapts the currently investigated position to the replace operation.
+ * First it checks whether the change replaces the whole range of the position.
+ * If not, it performs first the deletion of the previous text and afterwards
+ * the insertion of the new text.
+ */
+ protected void adaptToReplace() {
+
+ if (fPosition.offset == fOffset && fPosition.length == fLength && fPosition.length > 0) {
+
+ // replace the whole range of the position
+ fPosition.length += (fReplaceLength - fLength);
+ if (fPosition.length < 0) {
+ fPosition.offset += fPosition.length;
+ fPosition.length= 0;
+ }
+
+ } else {
+
+ if (fLength > 0)
+ adaptToRemove();
+
+ if (fReplaceLength > 0)
+ adaptToInsert();
+ }
+ }
+
+ /**
+ * Determines whether the currently investigated position has been deleted by
+ * the replace operation specified in the current event. If so, it deletes
+ * the position and removes it from the document's position category.
+ *
+ * @return <code>true</code> if position has been deleted
+ */
+ protected boolean notDeleted() {
+
+ if (fOffset < fPosition.offset && (fPosition.offset + fPosition.length < fOffset + fLength)) {
+
+ fPosition.delete();
+
+ try {
+ fDocument.removePosition(fCategory, fPosition);
+ } catch (BadPositionCategoryException x) {
+ }
+
+ return false;
+ }
+
+ return true;
+ }
+
+ /*
+ * @see IPositionUpdater#update(DocumentEvent event)
+ */
+ public void update(DocumentEvent event) {
+
+ try {
+
+ Position[] category= event.getDocument().getPositions(fCategory);
+
+ fOffset= event.getOffset();
+ fLength= event.getLength();
+ fReplaceLength= (event.getText() == null ? 0 : event.getText().length());
+ fDocument= event.getDocument();
+
+ for (int i= 0; i < category.length; i++) {
+
+ fPosition= category[i];
+ fOriginalPosition.offset= fPosition.offset;
+ fOriginalPosition.length= fPosition.length;
+
+ if (notDeleted())
+ adaptToReplace();
+ }
+
+ } catch (BadPositionCategoryException x) {
+ // do nothing
+ }
+ }
+}
diff --git a/org.eclipse.text/src/org/eclipse/jface/text/Document.java b/org.eclipse.text/src/org/eclipse/jface/text/Document.java
new file mode 100644
index 000000000..688e32832
--- /dev/null
+++ b/org.eclipse.text/src/org/eclipse/jface/text/Document.java
@@ -0,0 +1,71 @@
+/**********************************************************************
+Copyright (c) 2000, 2002 IBM Corp. and others.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Common Public License v1.0
+which accompanies this distribution, and is available at
+http://www.eclipse.org/legal/cpl-v10.html
+
+Contributors:
+ IBM Corporation - Initial implementation
+**********************************************************************/
+
+package org.eclipse.jface.text;
+
+
+/**
+ * Default document implementation. Uses a gap text store as text store and
+ * installs a line tracker considering the following strings as line delimiters
+ * "\n", "\r", "\r\n". The document is ready to use. It has a default position
+ * category for which a default position updater is installed.
+ *
+ * @see GapTextStore
+ */
+public class Document extends AbstractDocument {
+
+
+ /**
+ * Creates a new empty document.
+ */
+ public Document() {
+ super();
+ setTextStore(new GapTextStore(50, 300));
+ setLineTracker(new DefaultLineTracker());
+ completeInitialization();
+ }
+
+ /**
+ * Creates a new document with the given initial content.
+ *
+ * @param initialContent the document's initial content
+ */
+ public Document(String initialContent) {
+ super();
+ setTextStore(new GapTextStore(50, 300));
+ setLineTracker(new DefaultLineTracker());
+ getStore().set(initialContent);
+ getTracker().set(initialContent);
+ completeInitialization();
+ }
+
+ /*
+ * @see IDocumentExtension#startSequentialRewrite(boolean)
+ * @since 2.0
+ */
+ public void startSequentialRewrite(boolean normalized) {
+ ITextStore store= new SequentialRewriteTextStore(getStore());
+ setTextStore(store);
+ }
+
+ /*
+ * @see IDocumentExtension#stopSequentialRewrite()
+ * @since 2.0
+ */
+ public void stopSequentialRewrite() {
+ if (getStore() instanceof SequentialRewriteTextStore) {
+ SequentialRewriteTextStore srws= (SequentialRewriteTextStore) getStore();
+ ITextStore source= srws.getSourceStore();
+ setTextStore(source);
+ srws.dispose();
+ }
+ }
+}
diff --git a/org.eclipse.text/src/org/eclipse/jface/text/DocumentEvent.java b/org.eclipse.text/src/org/eclipse/jface/text/DocumentEvent.java
new file mode 100644
index 000000000..ea9aa5518
--- /dev/null
+++ b/org.eclipse.text/src/org/eclipse/jface/text/DocumentEvent.java
@@ -0,0 +1,84 @@
+package org.eclipse.jface.text;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+
+
+/**
+ * Specification of changes applied to documents.
+ * All changes are represented as replace commands, i.e.
+ * specifying a document range whose text gets replaced with different text.
+ * In addition to this information, the event also contains the changed document.
+ *
+ * @see IDocument
+ */
+public class DocumentEvent {
+
+ /** The changed document */
+ public IDocument fDocument;
+ /** The document offset */
+ public int fOffset;
+ /** Length of the replaced document text */
+ public int fLength;
+ /** Text inserted into the document */
+ public String fText;
+
+ /**
+ * Creates a new document event.
+ *
+ * @param doc the changed document
+ * @param offset the offset of the replaced text
+ * @param length the length of the replaced text
+ * @param text the substitution text
+ */
+ public DocumentEvent(IDocument doc, int offset, int length, String text) {
+
+ Assert.isNotNull(doc);
+ Assert.isTrue(offset >= 0);
+ Assert.isTrue(length >= 0);
+
+ fDocument= doc;
+ fOffset= offset;
+ fLength= length;
+ fText= text;
+ }
+
+ /**
+ * Returns the changed document.
+ *
+ * @return the changed document
+ */
+ public IDocument getDocument() {
+ return fDocument;
+ }
+
+ /**
+ * Returns the offset of the change
+ *
+ * @return the offset of the change
+ */
+ public int getOffset() {
+ return fOffset;
+ }
+
+ /**
+ * Returns the length of the replaced text.
+ *
+ * @return the length of the replaced text
+ */
+ public int getLength() {
+ return fLength;
+ }
+
+ /**
+ * Returns the text that has been inserted.
+ *
+ * @return the text that has been inserted
+ */
+ public String getText() {
+ return fText;
+ }
+}
diff --git a/org.eclipse.text/src/org/eclipse/jface/text/GapTextStore.java b/org.eclipse.text/src/org/eclipse/jface/text/GapTextStore.java
new file mode 100644
index 000000000..6a8d8827c
--- /dev/null
+++ b/org.eclipse.text/src/org/eclipse/jface/text/GapTextStore.java
@@ -0,0 +1,253 @@
+package org.eclipse.jface.text;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+
+
+/**
+ * Implements a gap managing text store. The gap text store
+ * relies on the assumption that subsequent changes of a document are co-located.
+ * The start of the gap is always moved to the location of the last change. The
+ * size of the gap varies between the low water mark and the high water mark. <p>
+ * This class is not intended to be subclassed.
+ */
+public class GapTextStore implements ITextStore {
+
+ /** The store's content */
+ private char[] fContent= new char[0];
+ /** Starting index of the gap */
+ private int fGapStart= -1;
+ /** End index of the gap */
+ private int fGapEnd= -1;
+
+ /** The high water mark. If the gap is larger than this, it will be shrunken */
+ private int fHighWatermark;
+ /** The low water mark, If this gap is smaller than this, it will be extended */
+ private int fLowWatermark;
+
+ /**
+ * Creates a new empty text store using the specified low and high watermarks.
+ *
+ * @param lowWatermark if this gap is ever smaller than this, it will automatically be extended
+ * @param highWatermark if the gap is ever larger than this, it will automatically be shrunken
+ */
+ public GapTextStore(int lowWatermark, int highWatermark) {
+ Assert.isTrue(lowWatermark < highWatermark);
+ fLowWatermark= lowWatermark;
+ fHighWatermark= highWatermark;
+ }
+
+ /**
+ * Adjusts the gap so that is at the right offset and capable of handling
+ * the addition of a specified number of characters without having to be shifted.
+ * The <code>sizeHint</code> represents the range that will be filled afterwards.
+ * If the gap is already at the right offset, it must only be
+ * resized if it will be no longer between the low and high watermark. However,
+ * on delete (sizeHint &lt; 0) at the edges of the gap, the gap is only enlarged.
+ *
+ * @param offset the offset at which the change happens
+ * @param sizeHint the number of character which will be inserted
+ */
+ private void adjustGap(int offset, int sizeHint) {
+
+ if (offset == fGapStart) {
+ int size= (fGapEnd - fGapStart) - sizeHint;
+ if (fLowWatermark <= size && size <= fHighWatermark)
+ return;
+ }
+
+ moveAndResizeGap(offset, sizeHint);
+ }
+
+ /**
+ * Moves the gap to the specified offset and adjust its size to the
+ * anticipated change size. The given size represents the expected
+ * range of the gap that will be filled after the gap has been moved.
+ * Thus the gap is resized to actual size + the specified size and
+ * moved to the given offset.
+ *
+ * @param offset the offset where the gap is moved to
+ * @param size the anticipated size of the change
+ */
+ private void moveAndResizeGap(int offset, int size) {
+
+ char[] content= null;
+ int oldSize= fGapEnd - fGapStart;
+ int newSize= fHighWatermark + size;
+
+
+ if (newSize < 0) {
+
+ if (oldSize > 0) {
+ content= new char[fContent.length - oldSize];
+ System.arraycopy(fContent, 0, content, 0, fGapStart);
+ System.arraycopy(fContent, fGapEnd, content, fGapStart, content.length - fGapStart);
+ fContent= content;
+ }
+ fGapStart= fGapEnd= offset;
+ return;
+ }
+
+
+ content= new char[fContent.length + (newSize - oldSize)];
+
+ int newGapStart= offset;
+ int newGapEnd= newGapStart + newSize;
+
+ if (oldSize == 0) {
+
+ System.arraycopy(fContent, 0, content, 0, newGapStart);
+ System.arraycopy(fContent, newGapStart, content, newGapEnd, content.length - newGapEnd);
+
+ } else if (newGapStart < fGapStart) {
+
+ int delta= fGapStart - newGapStart;
+ System.arraycopy(fContent, 0, content, 0, newGapStart);
+ System.arraycopy(fContent, newGapStart, content, newGapEnd, delta);
+ System.arraycopy(fContent, fGapEnd, content, newGapEnd + delta, fContent.length - fGapEnd);
+
+ } else {
+
+ int delta= newGapStart - fGapStart;
+ System.arraycopy(fContent, 0, content, 0, fGapStart);
+ System.arraycopy(fContent, fGapEnd, content, fGapStart, delta);
+ System.arraycopy(fContent, fGapEnd + delta, content, newGapEnd, content.length - newGapEnd);
+ }
+
+
+ fContent= content;
+ fGapStart= newGapStart;
+ fGapEnd= newGapEnd;
+ }
+
+ /*
+ * @see ITextStore#get
+ */
+ public char get(int offset) {
+
+ if (offset < fGapStart)
+ return fContent[offset];
+
+ int gapLength= fGapEnd - fGapStart;
+ return fContent[offset + gapLength];
+ }
+
+ /*
+ * @see ITextStore#get
+ */
+ public String get(int offset, int length) {
+
+ int end= offset + length;
+
+ if (fContent == null)
+ return ""; //$NON-NLS-1$
+
+ if (end <= fGapStart)
+ return new String(fContent, offset, length);
+
+ if (fGapStart < offset) {
+ int gapLength= fGapEnd - fGapStart;
+ return new String(fContent, offset + gapLength , length);
+ }
+
+ StringBuffer buf= new StringBuffer();
+ buf.append(fContent, offset, fGapStart - offset);
+ buf.append(fContent, fGapEnd, end - fGapStart);
+ return buf.toString();
+ }
+
+ /*
+ * @see ITextStore#getLength
+ */
+ public int getLength() {
+ int length= fGapEnd - fGapStart;
+ return (fContent.length - length);
+ }
+
+
+ /*
+ * @see ITextStore#replace
+ */
+ public void replace(int offset, int length, String text) {
+
+ int textLength= (text == null ? 0 : text.length());
+
+ // handle delete at the edges of the gap
+ if (textLength == 0) {
+ if (offset <= fGapStart && offset + length >= fGapStart && fGapStart > -1 && fGapEnd > -1) {
+ length -= fGapStart - offset;
+ fGapStart= offset;
+ fGapEnd += length;
+ return;
+ }
+ }
+
+ // move gap
+ adjustGap(offset + length, textLength - length);
+
+ // overwrite
+ int min= Math.min(textLength, length);
+ for (int i= offset, j= 0; i < offset + min; i++, j++)
+ fContent[i]= text.charAt(j);
+
+ if (length > textLength) {
+ // enlarge the gap
+ fGapStart -= (length - textLength);
+ } else if (textLength > length) {
+ // shrink gap
+ fGapStart += (textLength - length);
+ for (int i= length; i < textLength; i++)
+ fContent[offset + i]= text.charAt(i);
+ }
+ }
+
+ /**
+ * Sets the content to <code>text</code> and removes the gap
+ * since there are no sensible predictions about
+ * where the next change will occur.
+ * @see ITextStore#set
+ */
+ public void set(String text) {
+
+ if (text == null)
+ text= ""; //$NON-NLS-1$
+
+ fContent= text.toCharArray();
+
+ fGapStart= -1;
+ fGapEnd= -1;
+ }
+
+ /**
+ * Returns a copy of the content of this text store.
+ * For internal use only.
+ *
+ * @return a copy of the content of this text store
+ */
+ protected String getContentAsString() {
+ return new String(fContent);
+ }
+
+ /**
+ * Returns the start index of the gap managed by this text store.
+ * For internal use only.
+ *
+ * @return the start index of the gap managed by this text store
+ */
+ protected int getGapStartIndex() {
+ return fGapStart;
+ }
+
+ /**
+ * Returns the end index of the gap managed by this text store.
+ * For internal use only.
+ *
+ * @return the end index of the gap managed by this text store
+ */
+ protected int getGapEndIndex() {
+ return fGapEnd;
+ }
+}
diff --git a/org.eclipse.text/src/org/eclipse/jface/text/IDocument.java b/org.eclipse.text/src/org/eclipse/jface/text/IDocument.java
new file mode 100644
index 000000000..4c5beaea1
--- /dev/null
+++ b/org.eclipse.text/src/org/eclipse/jface/text/IDocument.java
@@ -0,0 +1,567 @@
+package org.eclipse.jface.text;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+
+import java.util.List;
+
+
+/**
+ * An <code>IDocument</code> represents text providing support for
+ * <ul>
+ * <li> text manipulation
+ * <li> positions
+ * <li> partitions
+ * <li> line information
+ * <li> search
+ * <li> document change listeners
+ * <li> document partition change listeners
+ * </ul>
+ *
+ * A document allows to set its content and to manipulate it. For manipulation
+ * a document provides the <code>replace</code> method which substitutes a given
+ * string for a specified text range in the document. On each document change, all
+ * registered document listeners are informed exactly once.
+ *
+ * Positions are stickers to the document's text that are updated when the
+ * document is changed. Positions are updated by <code>IPositionUpdater</code>s. Position
+ * updaters are managed as a list. The list defines the sequence in which position
+ * updaters are invoked.This way, position updaters may rely on each other.
+ * Positions are grouped into categories. A category is a ordered list of positions.
+ * the document defines the order of position in a category based on the position's offset
+ * based on the implementation of the method <code>computeIndexInCategory</code>.
+ * Each document must support a default position category whose name is specified by this
+ * interface.<p>
+ *
+ * A document can be considered consisting of a sequence of not overlapping partitions.
+ * A partition is defined by its offset, its length, and its type. Partitions are
+ * updated on every document manipulation and ensured to be up-to-date when the document
+ * listeners are informed. A document uses an <code>IDocumentPartitioner</code> to
+ * manage its partitions. A document may be unpartitioned which happens when there is no
+ * partitioner. In this case, the document is considered as one singloe partition of a
+ * default type. The default type is specified by this interface. If a document change
+ * changes the document's partitioning all registered partitioning listeners are
+ * informed exactly once.<p>
+ *
+ * An <code>IDocument</code> uses an <code>ILineTracker</code> to map line numbers and character
+ * positions onto each other based on the document's line delimiters. When moving text
+ * between documents using different line delimiters, the text must be converted to
+ * use the target document's line delimiters. <p>
+ *
+ * <code>IDocument</code> throws <code>BadLocationException</code> if the parameters of
+ * queries or manipulation requests are not inside the bounds of the document. The purpose
+ * of this style of exception handling is
+ * <ul>
+ * <li> prepare document for multi-thread access
+ * <li> allow clients to implement backtracking recovery methods
+ * <li> prevent clients from upfront contract checking when dealing with documents.
+ * </ul>
+ * Clients may implement this interface or use the default implementation provided
+ * by <code>AbstractDocument</code> and <code>Document</code>.
+ *
+ * @see Position
+ * @see IPositionUpdater
+ * @see IDocumentPartitioner
+ * @see ILineTracker
+ * @see IDocumentListener
+ * @see IDocumentPartitioningListener
+ */
+public interface IDocument {
+
+
+ /**
+ * The identifier of the default position category.
+ */
+ final static String DEFAULT_CATEGORY= "__dflt_position_category"; //$NON-NLS-1$
+
+ /**
+ * The identifier of the default partition content type.
+ */
+ final static String DEFAULT_CONTENT_TYPE= "__dftl_partition_content_type"; //$NON-NLS-1$
+
+
+
+
+ /* --------------- text access and manipulation --------------------------- */
+
+ /**
+ * Returns the character at the given document offset in this document.
+ *
+ * @param offset a document offset
+ * @return the character at the offset
+ * @exception BadLocationException if the offset is invalid in this document
+ */
+ char getChar(int offset) throws BadLocationException;
+
+ /**
+ * Returns the number of characters in this document.
+ *
+ * @return the number of characters in this document
+ */
+ int getLength();
+
+ /**
+ * Returns this document's complete text.
+ *
+ * @return the document's complete text
+ */
+ String get();
+
+ /**
+ * Returns this document's text for the specified range.
+ *
+ * @param offset the document offset
+ * @param length the length of the specified range
+ * @return the document's text for the specified range
+ * @exception BadLocationException if the range is invalid in this document
+ */
+ String get(int offset, int length) throws BadLocationException;
+
+ /**
+ * Replaces the content of the document with the given text.
+ * Sends a <code>DocumentEvent</code> to all registered <code>IDocumentListener</code>.
+ * This method is a convenience method for <code>
+ * replace(0, getLength(), text)</code>.
+ *
+ * @param text the new content of the document
+ *
+ * @see DocumentEvent
+ * @see IDocumentListener
+ */
+ void set(String text);
+
+ /**
+ * Subsitutes the given text for the specified document range.
+ * Sends a <code>DocumentEvent</code> to all registered <code>IDocumentListener</code>.
+ *
+ * @param offset the document offset
+ * @param length the length of the specified range
+ * @param text the substitution text
+ * @exception BadLocationException if the offset is invalid in this document
+ *
+ * @see DocumentEvent
+ * @see IDocumentListener
+ */
+ void replace(int offset, int length, String text) throws BadLocationException;
+
+ /**
+ * Registers the document listener with the document. After registration
+ * the IDocumentListener is informed about each change of this document.
+ * If the listener is already registered nothing happens.<p>
+ * An <code>IDocumentListener</code> may call back to this method
+ * when being inside a document notification.
+ *
+ * @param listener the listener to be registered
+ */
+ void addDocumentListener(IDocumentListener listener);
+
+ /**
+ * Removes the listener from the document's list of document listeners.
+ * If the listener is not registered with the document nothing happens.<p>
+ * An <code>IDocumentListener</code> may call back to this method
+ * when being inside a document notification.
+ *
+ * @param listener the listener to be removed
+ */
+ void removeDocumentListener(IDocumentListener listener);
+
+ /**
+ * Adds the given document listener as one which is notified before
+ * those document listeners added with <code>addDocumentListener</code>
+ * are notified. If the given listener is also registered using
+ * <code>addDocumentListener</code> it will be notified twice.
+ * If the listener is already registered nothing happens.<p>
+ *
+ * This method is not for public use, it may only be called by
+ * implementers of <code>IDocumentAdapter</code> and only if those
+ * implementers need to implement <code>IDocumentListener</code>.
+ *
+ * @param documentAdapter the listener to be added as prenotified document listener
+ */
+ void addPrenotifiedDocumentListener(IDocumentListener documentAdapter);
+
+ /**
+ * Removes the given document listener from the document's list of
+ * prenotified document listeners. If the listener is not registered
+ * with the document nothing happens. <p>
+ *
+ * This method is not for public use, it may only be called by
+ * implementers of <code>IDocumentAdapter</code> and only if those
+ * implementers need to implement <code>IDocumentListener</code>.
+ *
+ * @param documentAdapter the listener to be removed
+ *
+ * @see #addPrenotifiedDocumentListener(IDocumentListener)
+ */
+ void removePrenotifiedDocumentListener(IDocumentListener documentAdapter);
+
+
+
+ /* -------------------------- positions ----------------------------------- */
+
+ /**
+ * Adds a new position category to the document. If the position category
+ * already exists nothing happens.
+ *
+ * @param category the category to be added
+ */
+ void addPositionCategory(String category);
+
+ /**
+ * Deletes the position category from the document. All positions
+ * in this category are thus deleted as well.
+ *
+ * @param category the category to be removed
+ * @exception BadPositionCategoryException if category is undefined in this document
+ */
+ void removePositionCategory(String category) throws BadPositionCategoryException;
+
+ /**
+ * Returns all position categories of this document. This
+ * includes the default position category.
+ *
+ * @return the document's position categories
+ */
+ String[] getPositionCategories();
+
+ /**
+ * Checks the presence of the specified position category.
+ *
+ * @param category the category to check
+ * @return <code>true</code> if category is defined
+ */
+ boolean containsPositionCategory(String category);
+
+ /**
+ * Adds the position to the document's default position category.
+ * This is a convenience method for <code>addPosition(DEFAULT_CATEGORY, position)</code>.
+ *
+ * @param position the position to be added
+ * @exception BadLocationException if position describes an invalid range in this document
+ */
+ void addPosition(Position position) throws BadLocationException;
+
+ /**
+ * Removes the given position from the document's default position category.
+ * This is a convenience method for <code>removePosition(DEFAULT_CATEGORY, position)</code>.
+ *
+ * @param position the position to be removed
+ */
+ void removePosition(Position position);
+
+ /**
+ * Adds the position to the specified position category of the document.
+ * A position that has been added to a position category is updated on each
+ * change applied to the document. Positions may be added multiple times.
+ * The order of the category is maintained.
+ *
+ * @param category the category to which to add
+ * @param position the position to be added
+ * @exception BadLocationException if position describes an invalid range in this document
+ * @exception BadPositionCategoryException if the category is undefined in this document
+ */
+ void addPosition(String category, Position position) throws BadLocationException, BadPositionCategoryException;
+
+ /**
+ * Removes the given position from the specified position category.
+ * If the position is not part of the specified category nothing happens.
+ * If the position has been added multiple times, only the first occurence is deleted.
+ *
+ * @param category the category from which to delete
+ * @param position the position to be deleted
+ * @exception BadPositionCategoryException if category is undefined in this document
+ */
+ void removePosition(String category, Position position) throws BadPositionCategoryException;
+
+ /**
+ * Returns all positions of the given position category.
+ * The positions are ordered according to the category's order.
+ * Manipulating this list does not affect the document, but manipulating the
+ * position does affect the document.
+ *
+ * @param category the category
+ * @return the list of all positions
+ * @exception BadPositionCategoryException if category is undefined in this document
+ */
+ Position[] getPositions(String category) throws BadPositionCategoryException;
+
+ /**
+ * Determines whether a position described by the parameters is managed by this document.
+ *
+ * @param category the category to check
+ * @param offset the offset of the position to find
+ * @param length the length of the position to find
+ * @return <code>true</code> if position is found
+ */
+ boolean containsPosition(String category, int offset, int length);
+
+ /**
+ * Computes the index at which a <code>Position</code> with the
+ * specified offset would be inserted into the given category. As the
+ * ordering inside a category only depends on the offset, the index must be
+ * choosen to be the first of all positions with the same offset.
+ *
+ * @param category the category in which would be added
+ * @param offset the position offset to be considered
+ * @return the index into the category
+ * @exception BadLocationException if offset is invalid in this document
+ * @exception BadPositionCategoryException if category is undefined in this document
+ */
+ int computeIndexInCategory(String category, int offset) throws BadLocationException, BadPositionCategoryException;
+
+ /**
+ * Appends a new position updater to the document's list of position updaters.
+ * Position updaters may be added multiple times.<p>
+ * An <code>IPositionUpdater</code> may call back to this method
+ * when being inside a document notification.
+ *
+ * @param updater the updater to be added
+ */
+ void addPositionUpdater(IPositionUpdater updater);
+
+ /**
+ * Removes the position updater from the document's list of position updaters.
+ * If the position updater has multiple occurences only the first occurence is
+ * removed. If the position updater is not registered with this document, nothing
+ * happens.<p>
+ * An <code>IPositionUpdater</code> may call back to this method
+ * when being inside a document notification.
+ *
+ * @param updater the updater to be removed
+ */
+ void removePositionUpdater(IPositionUpdater updater);
+
+ /**
+ * Inserts the position updater at the specified index in the document's
+ * list of position updaters. Positions updaters may be inserted multiple times.<p>
+ * An <code>IPositionUpdater</code> may call back to this method
+ * when being inside a document notification.
+ *
+ * @param updater the updater to be inserted
+ * @param index the index in the document's updater list
+ */
+ void insertPositionUpdater(IPositionUpdater updater, int index);
+
+ /**
+ * Returns the list of position updaters attached to the document.
+ *
+ * @return the list of position updaters
+ */
+ IPositionUpdater[] getPositionUpdaters();
+
+
+
+
+ /* -------------------------- partitions ---------------------------------- */
+
+ /**
+ * Returns the set of legal content types of document partitions.
+ * This set can be empty. The set can contain more content types than
+ * contained by the result of <code>getPartitioning(0, getLength())</code>.
+ *
+ * @return the set of legal content types
+ */
+ String[] getLegalContentTypes();
+
+ /**
+ * Returns the type of the document partition containing the given offset.
+ * This is a convenience method for <code>getPartition(offset).getType()</code>.
+ *
+ * @param offset the document offset
+ * @return the partition type
+ * @exception BadLocationException if offset is invalid in this document
+ */
+ String getContentType(int offset) throws BadLocationException;
+
+ /**
+ * Returns the document partition in which the position is located.
+ *
+ * @param offset the document offset
+ * @return a specification of the partition
+ * @exception BadLocationException if offset is invalid in this document
+ */
+ ITypedRegion getPartition(int offset) throws BadLocationException;
+
+ /**
+ * Computes the partitioning of the given document range using the
+ * document's partitioner.
+ *
+ * @param offset the document offset at which the range starts
+ * @param length the length of the document range
+ * @return a specification of the range's partitioning
+ * @exception BadLocationException if the range is invalid in this document
+ */
+ ITypedRegion[] computePartitioning(int offset, int length) throws BadLocationException;
+
+ /**
+ * Registers the document partitioning listener with the document. After registration
+ * the document partitioning listener is informed about each partition change
+ * cause by a document manipulation or by changing the document's partitioner.
+ * If a document partitioning listener is also
+ * a document listener, the following notification sequence is guaranteed if a
+ * document manipulation changes the document partitioning:
+ * <ul>
+ * <li>listener.documentAboutToBeChanged(DocumentEvent);
+ * <li>listener.documentPartitioningChanged();
+ * <li>listener.documentChanged(DocumentEvent);
+ * </ul>
+ * If the listener is already registered nothing happens.<p>
+ * An <code>IDocumentPartitioningListener</code> may call back to this method
+ * when being inside a document notification.
+ *
+ * @param listener the listener to be added
+ */
+ void addDocumentPartitioningListener(IDocumentPartitioningListener listener);
+
+ /**
+ * Removes the listener from this document's list of document partitioning
+ * listeners. If the listener is not registered with the document nothing
+ * happens.<p>
+ * An <code>IDocumentPartitioningListener</code> may call back to this method
+ * when being inside a document notification.
+ *
+ * @param listener the listener to be removed
+ */
+ void removeDocumentPartitioningListener(IDocumentPartitioningListener listener);
+
+ /**
+ * Sets this document's partitioner. The caller of this method is responsible for
+ * disconnecting the document's old partitioner from the document and to
+ * connect the new partitioner to the document. Informs all document partitioning
+ * listeners about this change.
+ *
+ * @param the document's new partitioner
+ *
+ * @see IDocumentPartitioningListener
+ */
+ void setDocumentPartitioner(IDocumentPartitioner partitioner);
+
+ /**
+ * Returns this document's partitioner.
+ *
+ * @return this document's partitioner
+ */
+ IDocumentPartitioner getDocumentPartitioner();
+
+
+
+ /* ---------------------- line information -------------------------------- */
+
+ /**
+ * Returns the length of the given line including the line's delimiter.
+ *
+ * @param line the line of interest
+ * @return the length of the line
+ * @exception BadLocationException if the line number is invalid in this document
+ */
+ int getLineLength(int line) throws BadLocationException;
+
+ /**
+ * Returns the number of the line at which the character of the specified position is located.
+ * The first line has the line number 0. A new line starts directly after a line
+ * delimiter. <code>(pos == document length)</code> is a valid argument also there is no
+ * corresponding character.
+ *
+ * @param offset the document offset
+ * @return the number of the line
+ * @exception BadLocationException if the offset is invalid in this document
+ */
+ int getLineOfOffset(int offset) throws BadLocationException;
+
+ /**
+ * Determines the offset of the first character of the given line.
+ *
+ * @param line the line of interest
+ * @return the document offset
+ * @exception BadLocationException if the line number is invalid in this document
+ */
+ int getLineOffset(int line) throws BadLocationException;
+
+ /**
+ * Returns a description of the specified line. The line is described by its
+ * offset and its length excluding the line's delimiter.
+ *
+ * @param line the line of interest
+ * @return a line description
+ * @exception BadLocationException if the line number is invalid in this document
+ */
+ IRegion getLineInformation(int line) throws BadLocationException;
+
+ /**
+ * Returns a description of the line at the given offset.
+ * The description contains the offset and the length of the line
+ * excluding the line's delimiter.
+ *
+ * @param offset the offset whose line should be described
+ * @return a region describing the line
+ * @exception BadLocationException if offset is invalid in this document
+ */
+ IRegion getLineInformationOfOffset(int offset) throws BadLocationException;
+
+ /**
+ * Returns the number of lines in this document
+ *
+ * @return the number of lines in this document
+ */
+ int getNumberOfLines();
+
+ /**
+ * Returns the number of lines which are occupied by a given text range.
+ *
+ * @param offset the offset of the specified text range
+ * @param length the length of the specified text range
+ * @return the number of lines occupied by the specified range
+ * @exception BadLocationException if specified range is invalid in this tracker
+ */
+ int getNumberOfLines(int offset, int length) throws BadLocationException;
+
+ /**
+ * Computes the number of lines in the given text. For a given
+ * implementer of this interface this method returns the same
+ * result as <code>set(text); getNumberOfLines()</code>.
+ *
+ * @param text the text whose number of lines should be computed
+ * @return the number of lines in the given text
+ */
+ int computeNumberOfLines(String text);
+
+
+ /* ------------------ line delimiter conversion --------------------------- */
+
+ /**
+ * Returns the document's legal line delimiters.
+ *
+ * @return the document's legal line delimiters
+ */
+ String[] getLegalLineDelimiters();
+
+ /**
+ * Returns the line delimiter of that line.
+ *
+ * @param line the line of interest
+ * @return the line delimiter of that line
+ * @exception BadLocationException if the line number is invalid in this document
+ */
+ String getLineDelimiter(int line) throws BadLocationException;
+
+
+
+ /* ---------------------------- search ------------------------------------ */
+
+ /**
+ * Returns the offset of a given search string in the document based on a set of search criteria.
+ *
+ * @param startOffset document offset at which search starts
+ * @param findString the string to find
+ * @param forwardSearch the search direction
+ * @param caseSensitive indicates whether lower and upper case should be distinguished
+ * @param wholeWord indicates whether the findString should be limited by white spaces as
+ * defined by Character.isWhiteSpace
+ * @return the offset of the first occurence of findString based on the parameters
+ * @exception BadLocationException if startOffset is an invalid document offset
+ */
+ int search(int startOffset, String findString, boolean forwardSearch, boolean caseSensitive, boolean wholeWord) throws BadLocationException;
+}
diff --git a/org.eclipse.text/src/org/eclipse/jface/text/IDocumentExtension.java b/org.eclipse.text/src/org/eclipse/jface/text/IDocumentExtension.java
new file mode 100644
index 000000000..2986e184b
--- /dev/null
+++ b/org.eclipse.text/src/org/eclipse/jface/text/IDocumentExtension.java
@@ -0,0 +1,81 @@
+/**********************************************************************
+Copyright (c) 2000, 2002 IBM Corp. and others.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Common Public License v1.0
+which accompanies this distribution, and is available at
+http://www.eclipse.org/legal/cpl-v10.html
+
+Contributors:
+ IBM Corporation - Initial implementation
+**********************************************************************/
+
+package org.eclipse.jface.text;
+
+/**
+ * Extension interface for <code>IDocument</code>. <p>
+ * It introduces the notion of sequentially rewriting a document. This is to tell a
+ * document that a sequence of non-overlapping replace operation is about to be
+ * performed.<p>
+ * Is also introduces the concept of post notification replaces. This is, a document
+ * listener who is informed about a document change can cause a derived document
+ * change. As the listener is not allowed to directly modify the document, it can
+ * register a replace operation that is performed directly after all document listeners
+ * have been notified.
+ *
+ * @since 2.0
+ */
+public interface IDocumentExtension {
+
+ /**
+ * Interface for a post notification replace operation.
+ */
+ public interface IReplace {
+
+ /**
+ * Executes the replace operation on the given document.
+ * @param document the document to be changed
+ * @param owner the owner of this replace operation
+ */
+ void perform(IDocument document, IDocumentListener owner);
+ };
+
+ /**
+ * Callback for document listeners to be used inside <code>documentChanged</code>
+ * to register a post notification replace operation on the document notifying them.
+ *
+ * @param owner the owner of the replace operation
+ * @param the replace operation to be executed
+ * @exception UnsupportedOperationException if <code>registerPostNotificationReplace</code>
+ * is not supported by this document
+ */
+ void registerPostNotificationReplace(IDocumentListener owner, IReplace replace) throws UnsupportedOperationException;
+
+ /**
+ * Stops the processing of registered post notification replace operations until
+ * <code>resumePostNotificationProcessing</code> is called.
+ */
+ void stopPostNotificationProcessing();
+
+ /**
+ * Resumes the processing of post notification replace operations. If the queue of registered
+ * <code>IDocumentExtension.IReplace</code> objects is not empty, they are immediately processed if the
+ * document is not inside a replace operation. If the document is inside a replace operation,
+ * they are processed directly after the replace operation has finished.
+ */
+ void resumePostNotificationProcessing();
+
+ /**
+ * Tells the document that it is about to be sequentially rewritten. That is a
+ * sequence of non-overlapping replace operations will be performed on it. The
+ * <code>normalize</code> flag indicates whether the rewrite is preformed from
+ * the start of the document to its end or from an arbitrary start offset.
+ *
+ * @param normalize <code>true</code> if performed from the start to the end of the document
+ */
+ void startSequentialRewrite(boolean normalize);
+
+ /**
+ * Tells the document that the sequential rewrite has been finished.
+ */
+ void stopSequentialRewrite();
+} \ No newline at end of file
diff --git a/org.eclipse.text/src/org/eclipse/jface/text/IDocumentListener.java b/org.eclipse.text/src/org/eclipse/jface/text/IDocumentListener.java
new file mode 100644
index 000000000..dc957f81f
--- /dev/null
+++ b/org.eclipse.text/src/org/eclipse/jface/text/IDocumentListener.java
@@ -0,0 +1,33 @@
+package org.eclipse.jface.text;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+
+/**
+ * Interface for objects which are interested in getting informed about
+ * document changes. A listener is informed about document changes before
+ * they are applied and after they have been applied. It is ensured that
+ * the document event passed into the listener is the same for the two
+ * notifications, i.e. the two document events can be checked using object identity.
+ * Clients may implement this interface.
+ */
+public interface IDocumentListener {
+
+
+ /**
+ * The manipulation described by the document event will be performed.
+ *
+ * @param event the document event describing the document change
+ */
+ void documentAboutToBeChanged(DocumentEvent event);
+
+ /**
+ * The manipulation described by the document event has been performed.
+ *
+ * @param event the document event describing the document change
+ */
+ void documentChanged(DocumentEvent event);
+}
diff --git a/org.eclipse.text/src/org/eclipse/jface/text/IDocumentPartitioner.java b/org.eclipse.text/src/org/eclipse/jface/text/IDocumentPartitioner.java
new file mode 100644
index 000000000..d1cff732d
--- /dev/null
+++ b/org.eclipse.text/src/org/eclipse/jface/text/IDocumentPartitioner.java
@@ -0,0 +1,104 @@
+package org.eclipse.jface.text;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+
+/**
+ * A document partitioner divides a document into a set
+ * of disjoint text partitions. Each partition has a content type, an
+ * offset, and a length. The document partitioner is connected to one document
+ * and informed about all changes of this document before any of the
+ * document's document listeners. A document partitioner can thus
+ * incrementally update on the receipt of a document change event.
+ * Clients may implement this interface or use the standard
+ * implementation <code>RuleBasedDocumentPartitioner</code>.
+ *
+ * @see IDocument
+ */
+public interface IDocumentPartitioner {
+
+ /**
+ * Connects the partitioner to a document.
+ * Connect indicates the begin of the usage of the receiver
+ * as partitioner of the given document. Thus, resources the partitioner
+ * needs to be operational for this document should be allocated.<p>
+ * The caller of this method must ensure that this partitioner is
+ * also set as the document's document partitioner.
+ *
+ * @param document the document to be connected to
+ */
+ void connect(IDocument document);
+
+ /**
+ * Disconnects the partitioner from the document it is connected to.
+ * Disconnect indicates the end of the usage of the receiver as
+ * partitioner of the connected document. Thus, resources the partitioner
+ * needed to be operation for its connected document should be deallocated.<p>
+ * The caller of this method should also must ensure that this partitioner is
+ * no longer the document's partitioner.
+ */
+ void disconnect();
+
+ /**
+ * Informs about a forthcoming document change. Will be called by the
+ * connected document and is not intended to be used by clients
+ * other than the connected document.
+ *
+ * @param event the event describing the forthcoming change
+ */
+ void documentAboutToBeChanged(DocumentEvent event);
+
+ /**
+ * The document has been changed. The partitioner updates
+ * the document's partitioning and returns whether the structure of the
+ * document partitioning has been changed, i.e. whether partitions
+ * have been added or removed. Will be called by the connected document and
+ * is not intended to be used by clients other than the connected document.
+ *
+ * @param event the event describing the document change
+ * @return <code>true</code> if partitioning changed
+ */
+ boolean documentChanged(DocumentEvent event);
+
+ /**
+ * Returns the set of all legal content types of this partitioner.
+ * I.e. any result delivered by this partitioner may not contain a content type
+ * which would not be included in this method's result.
+ *
+ * @return the set of legal content types
+ */
+ String[] getLegalContentTypes();
+
+ /**
+ * Returns the content type of the partition containing the
+ * given offset in the connected document. There must be a
+ * document connected to this partitioner.
+ *
+ * @param offset the offset in the connected document
+ * @return the content type of the offset's partition
+ */
+ String getContentType(int offset);
+
+ /**
+ * Returns the partitioning of the given range of the connected
+ * document. There must be a document connected to this partitioner.
+ *
+ * @param offset the offset of the range of interest
+ * @param length the length of the range of interest
+ * @return the partitioning of the range
+ */
+ ITypedRegion[] computePartitioning(int offset, int length);
+
+ /**
+ * Returns the partition containing the given offset of
+ * the connected document. There must be a document connected to this
+ * partitioner.
+ *
+ * @param offset the offset for which to determine the partition
+ * @return the partition containing the offset
+ */
+ ITypedRegion getPartition(int offset);
+}
diff --git a/org.eclipse.text/src/org/eclipse/jface/text/IDocumentPartitionerExtension.java b/org.eclipse.text/src/org/eclipse/jface/text/IDocumentPartitionerExtension.java
new file mode 100644
index 000000000..34eda1f98
--- /dev/null
+++ b/org.eclipse.text/src/org/eclipse/jface/text/IDocumentPartitionerExtension.java
@@ -0,0 +1,37 @@
+/**********************************************************************
+Copyright (c) 2000, 2002 IBM Corp. and others.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Common Public License v1.0
+which accompanies this distribution, and is available at
+http://www.eclipse.org/legal/cpl-v10.html
+
+Contributors:
+ IBM Corporation - Initial implementation
+**********************************************************************/
+
+package org.eclipse.jface.text;
+
+
+/**
+ * Extension interface for <code>IDocumentPartitioner</code>. Extends the original
+ * concept of a document partitioner by returning the minimal region that includes all
+ * partition changes causes by the invocation of the document partitioner.
+ *
+ * @since 2.0
+
+*/
+public interface IDocumentPartitionerExtension {
+
+ /**
+ * The document has been changed. The partitioner updates
+ * the document's partitioning and returns in which region the
+ * partition types have changed. This method always returns
+ * the surrounding region. Will be called by the connected document
+ * and is not intended to be used by clients other than the connected
+ * document.
+ *
+ * @param event the event describing the document change
+ * @return the region of the document in which the partition type changed
+ */
+ IRegion documentChanged2(DocumentEvent event);
+}
diff --git a/org.eclipse.text/src/org/eclipse/jface/text/IDocumentPartitioningListener.java b/org.eclipse.text/src/org/eclipse/jface/text/IDocumentPartitioningListener.java
new file mode 100644
index 000000000..59e2cbabc
--- /dev/null
+++ b/org.eclipse.text/src/org/eclipse/jface/text/IDocumentPartitioningListener.java
@@ -0,0 +1,27 @@
+package org.eclipse.jface.text;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+
+/**
+ * Interface of objects which are interested in getting informed
+ * about changes of a document's partitioning. Clients may
+ * implement this interface.
+ *
+ * @see IDocument
+ * @see IDocumentPartitioner
+ */
+public interface IDocumentPartitioningListener {
+
+ /**
+ * The partitioning of the given document changed.
+ *
+ * @param document the document whose partitioning changed
+ *
+ * @see IDocument#addDocumentPartitioningListener
+ */
+ void documentPartitioningChanged(IDocument document);
+}
diff --git a/org.eclipse.text/src/org/eclipse/jface/text/IDocumentPartitioningListenerExtension.java b/org.eclipse.text/src/org/eclipse/jface/text/IDocumentPartitioningListenerExtension.java
new file mode 100644
index 000000000..3ae89d441
--- /dev/null
+++ b/org.eclipse.text/src/org/eclipse/jface/text/IDocumentPartitioningListenerExtension.java
@@ -0,0 +1,33 @@
+/**********************************************************************
+Copyright (c) 2000, 2002 IBM Corp. and others.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Common Public License v1.0
+which accompanies this distribution, and is available at
+http://www.eclipse.org/legal/cpl-v10.html
+
+Contributors:
+ IBM Corporation - Initial implementation
+**********************************************************************/
+
+package org.eclipse.jface.text;
+
+
+/**
+ * Extension interface for <code>IDocumentPartitioningListener</code>. Extends the original
+ * partitioning listener concept by telling the listener the minimal region that comprises all
+ * partitioning changes.
+ *
+ * @see org.eclipse.jface.text.IDocumentPartitionerExtension
+ * @since 2.0
+ */
+public interface IDocumentPartitioningListenerExtension {
+
+ /**
+ * The partitioning of the given document changed in the given region.
+ *
+ * @param document the document whose partitioning changed
+ * @param region the region in which the partitioning changed
+ * @see IDocument#addDocumentPartitioningListener
+ */
+ void documentPartitioningChanged(IDocument document, IRegion region);
+}
diff --git a/org.eclipse.text/src/org/eclipse/jface/text/ILineTracker.java b/org.eclipse.text/src/org/eclipse/jface/text/ILineTracker.java
new file mode 100644
index 000000000..e800841e5
--- /dev/null
+++ b/org.eclipse.text/src/org/eclipse/jface/text/ILineTracker.java
@@ -0,0 +1,132 @@
+package org.eclipse.jface.text;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+
+import java.util.Iterator;
+
+/**
+ * A line tracker maps character positions to line numbers and vice versa.
+ * Initially the line tracker is informed about its underlying text which
+ * in order to initialize the mapping information. After that, the line
+ * tracker is informed about all changes of the underlying text allowing
+ * for incremental updates of the mapping information. It is the client's
+ * responsibility to actively inform the line tacker about text changes.
+ * For example, when using a line tracker in combination with a document
+ * the document controls the line tracker.<p>
+ * Clients may implement this interface or use the standard implementation
+ * <code>DefaultLineTracker</code>.
+ */
+public interface ILineTracker {
+
+ /**
+ * Returns the strings this tracker considers as legal line delimiters.
+ *
+ * @return the legal line delimiters
+ */
+ String[] getLegalLineDelimiters();
+
+ /**
+ * Returns the line delimiter of the specified line. Returns null if the
+ * line is not closed with a line delimiter.
+ *
+ * @param line the line whose line delimiter is queried
+ * @return the line's delimiter or <code>null</code> if line does not have a delimiter
+ * @exception BadLocationException if the line number is invalid in this tracker's line structure
+ */
+ String getLineDelimiter(int line) throws BadLocationException;
+
+ /**
+ * Computes the number of lines in the given text.
+ *
+ * @param text the text whose number of lines should be computed
+ * @return the number of lines in the given text
+ */
+ int computeNumberOfLines(String text);
+
+ /**
+ * Returns the number of lines.
+ *
+ * @return the number of lines in this tracker's line structure
+ */
+ int getNumberOfLines();
+
+ /**
+ * Returns the number of lines which are occupied by a given text range.
+ *
+ * @param offset the offset of the specified text range
+ * @param length the length of the specified text range
+ * @return the number of lines occupied by the specified range
+ * @exception BadLocationException if specified range is unknown to this tracker
+ */
+ int getNumberOfLines(int offset, int length) throws BadLocationException;
+
+ /**
+ * Returns the position of the first character of the specified line.
+ *
+ * @param line the line of interest
+ * @return offset of the first character of the line
+ * @exception BadLocationException if the line is unknown to this tracker
+ */
+ int getLineOffset(int line) throws BadLocationException;
+
+ /**
+ * Returns length of the specified line including the line's delimiter.
+ *
+ * @param line the line of interest
+ * @return the length of the line
+ * @exception BadLocationException if line is unknown to this tracker
+ */
+ int getLineLength(int line) throws BadLocationException;
+
+ /**
+ * Returns the line number the character at the given offset belongs to.
+ *
+ * @param offset the offset whose line number to be determined
+ * @return the number of the line the offset is on
+ * @exception BadLocationException if the offset is invalid in this tracker
+ */
+ int getLineNumberOfOffset(int offset) throws BadLocationException;
+
+ /**
+ * Returns a line description of the line at the given offset.
+ * The description contains the start offset and the length of the line
+ * excluding the line's delimiter.
+ *
+ * @param offset the offset whose line should be described
+ * @return a region describing the line
+ * @exception BadLocationException if offset is invalid in this tracker
+ */
+ IRegion getLineInformationOfOffset(int offset) throws BadLocationException;
+
+ /**
+ * Returns a line description of the given line. The description
+ * contains the start offset and the length of the line excluding the line's
+ * delimiter.
+ *
+ * @param line the line that should be described
+ * @return a region describing the line
+ * @exception BadLocationException if line is unknown to this tracker
+ */
+ IRegion getLineInformation(int line) throws BadLocationException;
+
+ /**
+ * Informs the line tracker about the specified change in the tracked text.
+ *
+ * @param offset the offset of the replaced text
+ * @param length the length of the replaced text
+ * @param text the substitution text
+ * @exception BadLocationException if specified range is unknown to this tracker
+ */
+ void replace(int offset, int length, String text) throws BadLocationException;
+
+ /**
+ * Sets the tracked text to the specified text.
+ *
+ * @param text the new tracked text
+ */
+ void set(String text);
+}
diff --git a/org.eclipse.text/src/org/eclipse/jface/text/IPositionUpdater.java b/org.eclipse.text/src/org/eclipse/jface/text/IPositionUpdater.java
new file mode 100644
index 000000000..590b57db4
--- /dev/null
+++ b/org.eclipse.text/src/org/eclipse/jface/text/IPositionUpdater.java
@@ -0,0 +1,33 @@
+package org.eclipse.jface.text;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+/**
+ * A position updater is responsible for adapting document positions.
+ * When installed on a document, the position updater updates the
+ * document's positions to changes applied to this document. Document
+ * updaters can be selective, i.e. they might only update positions of
+ * a certain category.<p>
+ * Position updaters are of primary importance for the definition of
+ * the semantics of positions.<p>
+ * Clients may implement this interface or use the standard implementation
+ * <code>DefaultPositionUpdater</code>.
+ *
+ * @see IDocument
+ * @see Position
+ */
+public interface IPositionUpdater {
+
+ /**
+ * Adapts positions to the change specified by the document event.
+ * It is ensured that the document's partitioning has been adapted to
+ * this document change and that all the position updaters which have
+ * a smaller index in the document's position updater list have been called.
+ *
+ * @param event the document event describing the document change
+ */
+ void update(DocumentEvent event);
+}
diff --git a/org.eclipse.text/src/org/eclipse/jface/text/IRegion.java b/org.eclipse.text/src/org/eclipse/jface/text/IRegion.java
new file mode 100644
index 000000000..d3edc6740
--- /dev/null
+++ b/org.eclipse.text/src/org/eclipse/jface/text/IRegion.java
@@ -0,0 +1,32 @@
+package org.eclipse.jface.text;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+/**
+ * A region describes a certain range in an indexed text store.
+ * Text stores are for example documents or strings. A region is
+ * defined by its offset into the text store and its length.<p>
+ * A region is considered a value object. Its offset or length
+ * do not change over time. <p>
+ * Clients may implement this interface or use the standard implementation
+ * <code>Region</code>.
+ */
+public interface IRegion {
+
+ /**
+ * Returns the length of the region.
+ *
+ * @return the length of the region
+ */
+ int getLength();
+
+ /**
+ * Returns the offset of the region.
+ *
+ * @return the offset of the region
+ */
+ int getOffset();
+}
diff --git a/org.eclipse.text/src/org/eclipse/jface/text/ITextStore.java b/org.eclipse.text/src/org/eclipse/jface/text/ITextStore.java
new file mode 100644
index 000000000..a82894bdf
--- /dev/null
+++ b/org.eclipse.text/src/org/eclipse/jface/text/ITextStore.java
@@ -0,0 +1,57 @@
+package org.eclipse.jface.text;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+/**
+ * Interface for storing and managing text.
+ * Provides access to the stored text and allows to manipulate it.
+ * Clients may implement this interface.
+ */
+public interface ITextStore {
+
+ /**
+ * Returns the character at the specified offset.
+ *
+ * @param offset the offset in this text store
+ * @return the character at this offset
+ */
+ char get(int offset);
+
+ /**
+ * Returns the text of the specified character range.
+ *
+ * @param offset the offset of the range
+ * @param length the length of the range
+ * @return the text of the range
+ */
+ String get(int offset, int length);
+
+ /**
+ * Returns number of characters stored in this text store.
+ *
+ * @return the number of characters stored in this text store
+ */
+ int getLength();
+
+ /**
+ * Replaces the specified character range with the given text.
+ * <code>replace(getLength(), 0, "some text")</code> is a valid
+ * call and appends text to the end of the text store.
+ *
+ * @param offset the offset of the range to be replaced
+ * @param length the number of characters to be replaced
+ * @param text the substitution text
+ */
+ void replace(int offset, int length, String text);
+
+ /**
+ * Replace the content of the text store with the given text.
+ * Convenience method for <code>replace(0, getLength(), text</code>.
+ *
+ * @param text the new content of the text store
+ */
+ void set(String text);
+}
diff --git a/org.eclipse.text/src/org/eclipse/jface/text/ITypedRegion.java b/org.eclipse.text/src/org/eclipse/jface/text/ITypedRegion.java
new file mode 100644
index 000000000..9cd573a83
--- /dev/null
+++ b/org.eclipse.text/src/org/eclipse/jface/text/ITypedRegion.java
@@ -0,0 +1,24 @@
+package org.eclipse.jface.text;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+
+/**
+ * Describes a region of an indexed text store such as document or string.
+ * The region consists of offset, length, and type. The type is defines as
+ * a string. A typed region can, e.g., be used to described document partitions.
+ * Clients may implement this interface or use the standard impementation
+ * <code>TypedRegion</code>.
+ */
+public interface ITypedRegion extends IRegion {
+
+ /**
+ * Returns the content type of the region.
+ *
+ * @return the content type of the region
+ */
+ String getType();
+} \ No newline at end of file
diff --git a/org.eclipse.text/src/org/eclipse/jface/text/Line.java b/org.eclipse.text/src/org/eclipse/jface/text/Line.java
new file mode 100644
index 000000000..ab39bfd17
--- /dev/null
+++ b/org.eclipse.text/src/org/eclipse/jface/text/Line.java
@@ -0,0 +1,63 @@
+package org.eclipse.jface.text;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+
+/**
+ * Describes a line as a particular number of characters beginning at
+ * a particular offset, consisting of a particular number of characters,
+ * and being closed with a particular line delimiter.
+ */
+class Line implements IRegion {
+
+ /** The offset of the line */
+ public int offset;
+ /** The length of the line */
+ public int length;
+ /** The delimiter of this line */
+ public String delimiter;
+
+ /**
+ * Creates a new Line.
+ *
+ * @param offset the offset of the line
+ * @param end the last including character offset of the line
+ * @param delimiter the line's delimiter
+ */
+ public Line(int offset, int end, String delimiter) {
+ this.offset= offset;
+ this.length= (end - offset) +1;
+ this.delimiter= delimiter;
+ }
+
+ /**
+ * Creates a new Line.
+ *
+ * @param offset the offset of the line
+ * @param length the length of the line
+ */
+ public Line(int offset, int length) {
+ this.offset= offset;
+ this.length= length;
+ this.delimiter= null;
+ }
+
+ /*
+ * @see IRegion#getOffset()
+ */
+ public int getOffset() {
+ return offset;
+ }
+
+ /*
+ * @see IRegion#getLength()
+ */
+ public int getLength() {
+ return length;
+ }
+}
+
+
diff --git a/org.eclipse.text/src/org/eclipse/jface/text/Position.java b/org.eclipse.text/src/org/eclipse/jface/text/Position.java
new file mode 100644
index 000000000..08737da6d
--- /dev/null
+++ b/org.eclipse.text/src/org/eclipse/jface/text/Position.java
@@ -0,0 +1,181 @@
+/**********************************************************************
+Copyright (c) 2000, 2002 IBM Corp. and others.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Common Public License v1.0
+which accompanies this distribution, and is available at
+http://www.eclipse.org/legal/cpl-v10.html
+
+Contributors:
+ IBM Corporation - Initial implementation
+**********************************************************************/
+
+package org.eclipse.jface.text;
+
+
+/**
+ * Positions describe text ranges of a document and are
+ * adapted to changes applied to that document. The text range
+ * is specified by an offset and a length. Positions can be marked as
+ * deleted. Deleted positions are considered to no longer represent
+ * a valid text range in the managing document. <p>
+ * Positions attached to documents are ususally updated by position updaters.
+ * Because position updaters are freely defineable and because of the
+ * frequence in which they are used, the fields of a position are made
+ * publicly accessible. Clients other than position updaters are not
+ * allowed to access these public fields.
+ *
+ * @see IDocument
+ */
+public class Position {
+
+ /** The offset of the position */
+ public int offset;
+ /** The length of the position */
+ public int length;
+ /** Indicates whether the position has been deleted */
+ public boolean isDeleted;
+
+ /**
+ * Creates a new position with the given offset and length 0.
+ *
+ * @param offset the position offset, must be >= 0
+ */
+ public Position(int offset) {
+ this(offset, 0);
+ }
+
+ /**
+ * Creates a new position with the given offset and length.
+ *
+ * @param offset the position offset, must be >= 0
+ * @param length the position length, must be >= 0
+ */
+ public Position(int offset, int length) {
+ Assert.isTrue(offset >= 0);
+ Assert.isTrue(length >= 0);
+ this.offset= offset;
+ this.length= length;
+ }
+
+ /*
+ * @see Object#hashCode
+ */
+ public int hashCode() {
+ int deleted= isDeleted ? 0 : 1;
+ return (offset << 24) | (length << 16) | deleted;
+ }
+
+ /**
+ * Marks this position as deleted.
+ */
+ public void delete() {
+ isDeleted= true;
+ }
+
+ /**
+ * Marks this position as not deleted.
+ * @since 2.0
+ */
+ public void undelete() {
+ isDeleted= false;
+ }
+
+ /*
+ * @see Object#equals
+ */
+ public boolean equals(Object other) {
+ if (other instanceof Position) {
+ Position rp= (Position) other;
+ return (rp.offset == offset) && (rp.length == length);
+ }
+ return super.equals(other);
+ }
+
+ /**
+ * Returns the length of this position.
+ *
+ * @return the length of this position
+ */
+ public int getLength() {
+ return length;
+ }
+
+ /**
+ * Returns the offset of this position.
+ *
+ * @return the length of this position
+ */
+ public int getOffset() {
+ return offset;
+ }
+
+ /**
+ * Checks whether the given offset is inside
+ * of this position's text range.
+ *
+ * @param offset the offset to check
+ * @return <code>true</code> if offset is inside of this position
+ */
+ public boolean includes(int offset) {
+
+ if (isDeleted)
+ return false;
+
+ return (this.offset <= offset) && (offset < this.offset + length);
+ }
+
+ /**
+ * Checks whether the intersection of the given text range
+ * and the text range represented by this position is empty
+ * or not.
+ *
+ * @param offset the offset of the range to check
+ * @param length the length of the range to check
+ * @return <code>true</code> if intersection is not empty
+ */
+ public boolean overlapsWith(int offset, int length) {
+
+ if (isDeleted)
+ return false;
+
+ int end= offset + length;
+ int thisEnd= this.offset + this.length;
+
+ if (length > 0) {
+ if (this.length > 0)
+ return this.offset < end && offset < thisEnd;
+ return offset <= this.offset && this.offset < end;
+ }
+
+ if (this.length > 0)
+ return this.offset <= offset && offset < thisEnd;
+ return this.offset == offset;
+ }
+
+ /**
+ * Returns whether this position has been deleted or not.
+ *
+ * @return <code>true</code> if position has been deleted
+ */
+ public boolean isDeleted() {
+ return isDeleted;
+ }
+
+ /**
+ * Changes the length of this position to the given length.
+ *
+ * @param length the new length of this position
+ */
+ public void setLength(int length) {
+ this.length= length;
+ }
+
+ /**
+ * Changes the offset of this position to the given offset.
+ *
+ * @param offset the new offset of this position
+ */
+ public void setOffset(int offset) {
+ this.offset= offset;
+ }
+}
diff --git a/org.eclipse.text/src/org/eclipse/jface/text/Region.java b/org.eclipse.text/src/org/eclipse/jface/text/Region.java
new file mode 100644
index 000000000..98a65d9bb
--- /dev/null
+++ b/org.eclipse.text/src/org/eclipse/jface/text/Region.java
@@ -0,0 +1,63 @@
+package org.eclipse.jface.text;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+
+/**
+ * A default implementation of the <code>IRegion</code> interface.
+ */
+public class Region implements IRegion {
+
+ /** The region offset */
+ private int fOffset;
+ /** The region length */
+ private int fLength;
+
+ /**
+ * Create a new region.
+ *
+ * @param offset the offset of the region
+ * @param length the length of the region
+ */
+ public Region(int offset, int length) {
+ fOffset= offset;
+ fLength= length;
+ }
+
+ /*
+ * @see IRegion#getLength
+ */
+ public int getLength() {
+ return fLength;
+ }
+
+ /*
+ * @see IRegion#getOffset
+ */
+ public int getOffset() {
+ return fOffset;
+ }
+
+ /**
+ * Two regions are equal if they have the same offset and length.
+ *
+ * @see Object#equals
+ */
+ public boolean equals(Object o) {
+ if (o instanceof IRegion) {
+ IRegion r= (IRegion) o;
+ return r.getOffset() == fOffset && r.getLength() == fLength;
+ }
+ return false;
+ }
+
+ /*
+ * @see Object#hashCode
+ */
+ public int hashCode() {
+ return (fOffset << 24) | (fLength << 16);
+ }
+}
diff --git a/org.eclipse.text/src/org/eclipse/jface/text/SequentialRewriteTextStore.java b/org.eclipse.text/src/org/eclipse/jface/text/SequentialRewriteTextStore.java
new file mode 100644
index 000000000..9856a272c
--- /dev/null
+++ b/org.eclipse.text/src/org/eclipse/jface/text/SequentialRewriteTextStore.java
@@ -0,0 +1,273 @@
+/**********************************************************************
+Copyright (c) 2000, 2002 IBM Corp. and others.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Common Public License v1.0
+which accompanies this distribution, and is available at
+http://www.eclipse.org/legal/cpl-v10.html
+
+Contributors:
+ IBM Corporation - Initial implementation
+**********************************************************************/
+
+package org.eclipse.jface.text;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+
+/**
+ * A text store that optimizes a given source text store for sequential rewriting.
+ * While rewritten it keeps a list of replace command that serve as patches for
+ * the source store. Only on request, the source store is indeed manipulated
+ * by applying the patch commands to the source text store.
+ *
+ * @since 2.0
+ */
+public class SequentialRewriteTextStore implements ITextStore {
+
+ /**
+ * A buffered replace command.
+ */
+ private static class Replace {
+ public int newOffset;
+ public final int offset;
+ public final int length;
+ public final String text;
+
+ public Replace(int offset, int newOffset, int length, String text) {
+ this.newOffset= newOffset;
+ this.offset= offset;
+ this.length= length;
+ this.text= text;
+ }
+ }
+
+ /** The list of buffered replacements. */
+ private List fReplaceList;
+ /** The source text store */
+ private ITextStore fSource;
+ /** A flag to enforce sequential access. */
+ private static final boolean ASSERT_SEQUENTIALITY= false;
+
+
+ /**
+ * Creates a new sequential rewrite store for the given source store.
+ *
+ * @param source the source text store
+ */
+ public SequentialRewriteTextStore(ITextStore source) {
+ fReplaceList= new LinkedList();
+ fSource= source;
+ }
+
+ /**
+ * Returns the source store of this rewrite store.
+ *
+ * @return the source store of this rewrite store
+ */
+ public ITextStore getSourceStore() {
+ commit();
+ return fSource;
+ }
+
+ /*
+ * @see ITextStore#replace(int, int, String)
+ */
+ public void replace(int offset, int length, String text) {
+
+ if (fReplaceList.size() == 0) {
+ fReplaceList.add(new Replace(offset, offset, length, text));
+
+ } else {
+ Replace firstReplace= (Replace) fReplaceList.get(0);
+ Replace lastReplace= (Replace) fReplaceList.get(fReplaceList.size() - 1);
+
+ // backward
+ if (offset + length <= firstReplace.newOffset) {
+ int delta= text.length() - length;
+ if (delta != 0) {
+ for (Iterator i= fReplaceList.iterator(); i.hasNext(); ) {
+ Replace replace= (Replace) i.next();
+ replace.newOffset += delta;
+ }
+ }
+
+ fReplaceList.add(0, new Replace(offset, offset, length, text));
+
+ // forward
+ } else if (offset >= lastReplace.newOffset + lastReplace.text.length()) {
+ int delta= getDelta(lastReplace);
+ fReplaceList.add(new Replace(offset - delta, offset, length, text));
+
+ } else if (ASSERT_SEQUENTIALITY) {
+ throw new IllegalArgumentException();
+
+ } else {
+ commit();
+ fSource.replace(offset, length, text);
+ }
+ }
+ }
+
+ /*
+ * @see ITextStore#set(String)
+ */
+ public void set(String text) {
+ fSource.set(text);
+ fReplaceList.clear();
+ }
+
+ /*
+ * @see ITextStore#get(int, int)
+ */
+ public String get(int offset, int length) {
+
+ if (fReplaceList.size() == 0) {
+ return fSource.get(offset, length);
+
+ } else {
+ Replace firstReplace= (Replace) fReplaceList.get(0);
+ Replace lastReplace= (Replace) fReplaceList.get(fReplaceList.size() - 1);
+
+ // before
+ if (offset + length <= firstReplace.newOffset) {
+ return fSource.get(offset, length);
+
+ // after
+ } else if (offset >= lastReplace.newOffset + lastReplace.text.length()) {
+ int delta= getDelta(lastReplace);
+ return fSource.get(offset - delta, length);
+
+ } else if (ASSERT_SEQUENTIALITY) {
+ throw new IllegalArgumentException();
+
+ } else {
+
+ int delta= 0;
+ for (Iterator i= fReplaceList.iterator(); i.hasNext(); ) {
+ Replace replace= (Replace) i.next();
+
+ if (offset + length < replace.newOffset) {
+ return fSource.get(offset - delta, length);
+
+ } else if (offset >= replace.newOffset && offset + length <= replace.newOffset + replace.text.length()) {
+ return replace.text.substring(offset - replace.newOffset, length);
+
+ } else if (offset >= replace.newOffset + replace.text.length()) {
+ delta= getDelta(replace);
+ continue;
+
+ } else {
+ commit();
+ return fSource.get(offset, length);
+ }
+ }
+
+ return fSource.get(offset - delta, length);
+ }
+ }
+ }
+
+ /**
+ * Returns the difference between the offset in the source store and the "same" offset in the
+ * rewrite store after the replace operation.
+ *
+ * @param replace the replace command
+ */
+ private static final int getDelta(Replace replace) {
+ return replace.newOffset - replace.offset + replace.text.length() - replace.length;
+ }
+
+ /*
+ * @see ITextStore#get(int)
+ */
+ public char get(int offset) {
+ if (fReplaceList.size() == 0) {
+ return fSource.get(offset);
+
+ } else {
+ Replace firstReplace= (Replace) fReplaceList.get(0);
+ Replace lastReplace= (Replace) fReplaceList.get(fReplaceList.size() - 1);
+
+ // before
+ if (offset < firstReplace.newOffset) {
+ return fSource.get(offset);
+
+ // after
+ } else if (offset >= lastReplace.newOffset + lastReplace.text.length()) {
+ int delta= getDelta(lastReplace);
+ return fSource.get(offset - delta);
+
+ } else if (ASSERT_SEQUENTIALITY) {
+ throw new IllegalArgumentException();
+
+ } else {
+
+ int delta= 0;
+ for (Iterator i= fReplaceList.iterator(); i.hasNext(); ) {
+ Replace replace= (Replace) i.next();
+
+ if (offset < replace.newOffset)
+ return fSource.get(offset - delta);
+
+ else if (offset < replace.newOffset + replace.text.length())
+ return replace.text.charAt(offset - replace.newOffset);
+
+ delta= getDelta(replace);
+ }
+
+ return fSource.get(offset - delta);
+ }
+ }
+ }
+
+ /*
+ * @see ITextStore#getLength()
+ */
+ public int getLength() {
+ if (fReplaceList.size() == 0) {
+ return fSource.getLength();
+
+ } else {
+ Replace lastReplace= (Replace) fReplaceList.get(fReplaceList.size() - 1);
+ return fSource.getLength() + getDelta(lastReplace);
+ }
+ }
+
+ /**
+ * Disposes this rewrite store.
+ */
+ public void dispose() {
+ fReplaceList= null;
+ fSource= null;
+ }
+
+ /**
+ * Commits all buffered replace commands.
+ */
+ private void commit() {
+
+ if (fReplaceList.size() == 0)
+ return;
+
+ StringBuffer buffer= new StringBuffer();
+
+ int delta= 0;
+ for (Iterator i= fReplaceList.iterator(); i.hasNext(); ) {
+ Replace replace= (Replace) i.next();
+
+ int offset= buffer.length() - delta;
+ buffer.append(fSource.get(offset, replace.offset - offset));
+ buffer.append(replace.text);
+ delta= getDelta(replace);
+ }
+
+ int offset= buffer.length() - delta;
+ buffer.append(fSource.get(offset, fSource.getLength() - offset));
+
+ fSource.set(buffer.toString());
+ fReplaceList.clear();
+ }
+
+}
diff --git a/org.eclipse.text/src/org/eclipse/jface/text/TextUtilities.java b/org.eclipse.text/src/org/eclipse/jface/text/TextUtilities.java
new file mode 100644
index 000000000..9aea7a4e0
--- /dev/null
+++ b/org.eclipse.text/src/org/eclipse/jface/text/TextUtilities.java
@@ -0,0 +1,117 @@
+package org.eclipse.jface.text;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+
+/**
+ * Collection of text functions.
+ */
+public class TextUtilities {
+
+
+ public final static String[] fgDelimiters= new String[] { "\n", "\r", "\r\n" }; //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-1$
+
+
+ /**
+ * Determines which one of fgDelimiters appears first in the list. If none of them the
+ * hint is returned.
+ */
+ public static String determineLineDelimiter(String text, String hint) {
+ try {
+ int[] info= indexOf(fgDelimiters, text, 0);
+ return fgDelimiters[info[1]];
+ } catch (ArrayIndexOutOfBoundsException x) {
+ }
+ return hint;
+ }
+
+ /**
+ * Returns the position in the string greater than offset
+ * of the longest matching search string.
+ */
+ public static int[] indexOf(String[] searchStrings, String text, int offset) {
+
+ int[] result= { -1, -1 };
+ int zeroIndex= -1;
+
+ for (int i= 0; i < searchStrings.length; i++) {
+
+ int length= searchStrings[i].length();
+
+ if (length == 0) {
+ zeroIndex= i;
+ continue;
+ }
+
+ int index= text.indexOf(searchStrings[i], offset);
+ if (index >= 0) {
+
+ if (result[0] == -1) {
+ result[0]= index;
+ result[1]= i;
+ } else if (index < result[0]) {
+ result[0]= index;
+ result[1]= i;
+ } else if (index == result[0] && length > searchStrings[result[1]].length()) {
+ result[0]= index;
+ result[1]= i;
+ }
+ }
+ }
+
+ if (zeroIndex > -1 && result[0] == -1) {
+ result[0]= 0;
+ result[1]= zeroIndex;
+ }
+
+ return result;
+ }
+
+ /**
+ * Returns the longest search string with which the given text ends.
+ */
+ public static int endsWith(String[] searchStrings, String text) {
+
+ int index= -1;
+
+ for (int i= 0; i < searchStrings.length; i++) {
+ if (text.endsWith(searchStrings[i])) {
+ if (index == -1 || searchStrings[i].length() > searchStrings[index].length())
+ index= i;
+ }
+ }
+
+ return index;
+ }
+
+ /**
+ * Returns the longest search string with which the given text starts.
+ */
+ public static int startsWith(String[] searchStrings, String text) {
+
+ int index= -1;
+
+ for (int i= 0; i < searchStrings.length; i++) {
+ if (text.startsWith(searchStrings[i])) {
+ if (index == -1 || searchStrings[i].length() > searchStrings[index].length())
+ index= i;
+ }
+ }
+
+ return index;
+ }
+
+ /**
+ * Returns whether the text equals one of the given compare strings.
+ */
+ public static int equals(String[] compareStrings, String text) {
+ for (int i= 0; i < compareStrings.length; i++) {
+ if (text.equals(compareStrings[i]))
+ return i;
+ }
+ return -1;
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.text/src/org/eclipse/jface/text/TypedPosition.java b/org.eclipse.text/src/org/eclipse/jface/text/TypedPosition.java
new file mode 100644
index 000000000..bec400808
--- /dev/null
+++ b/org.eclipse.text/src/org/eclipse/jface/text/TypedPosition.java
@@ -0,0 +1,68 @@
+package org.eclipse.jface.text;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+
+/**
+ * Convenience class for position like typed regions.
+ */
+public class TypedPosition extends Position {
+
+ /** The type of the region described by this position */
+ private String fType;
+
+ /**
+ * Creates a position along the given specification.
+ *
+ * @param offset the offset of this position
+ * @param length the length of this position
+ * @param type the type of this position
+ */
+ public TypedPosition(int offset, int length, String type) {
+ super(offset, length);
+ fType= type;
+ }
+
+ /**
+ * Creates a position based on the typed region.
+ *
+ * @param region the typed region
+ */
+ public TypedPosition(ITypedRegion region) {
+ super(region.getOffset(), region.getLength());
+ fType= region.getType();
+ }
+
+ /**
+ * Returns the type of the position
+ *
+ * @return the type of this position
+ */
+ public String getType() {
+ return fType;
+ }
+
+ /*
+ * @see Object#equals
+ */
+ public boolean equals(Object o) {
+ if (o instanceof TypedPosition) {
+ if (super.equals(o)) {
+ TypedPosition p= (TypedPosition) o;
+ return (fType == null && p.getType() == null) || fType.equals(p.getType());
+ }
+ }
+ return false;
+ }
+
+ /*
+ * @see Object#hashCode
+ */
+ public int hashCode() {
+ int type= fType == null ? 0 : fType.hashCode();
+ return super.hashCode() | type;
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.text/src/org/eclipse/jface/text/TypedRegion.java b/org.eclipse.text/src/org/eclipse/jface/text/TypedRegion.java
new file mode 100644
index 000000000..f45eeb653
--- /dev/null
+++ b/org.eclipse.text/src/org/eclipse/jface/text/TypedRegion.java
@@ -0,0 +1,56 @@
+package org.eclipse.jface.text;
+
+/*
+ * (c) Copyright IBM Corp. 2000, 2001.
+ * All Rights Reserved.
+ */
+
+
+/**
+ * Default implementation of <code>ITypedRegion</code>.
+ */
+public class TypedRegion extends Region implements ITypedRegion {
+
+ /** The region's type */
+ private String fType;
+
+ /**
+ * Creates a typed region based on the given specification.
+ *
+ * @param offset the region's offset
+ * @param length the region's length
+ * @param type the region's type
+ */
+ public TypedRegion(int offset, int length, String type) {
+ super(offset, length);
+ fType= type;
+ }
+
+ /*
+ * @see ITypedRegion#getType()
+ */
+ public String getType() {
+ return fType;
+ }
+
+ /**
+ * Two typed positions are equal if they have the same offset, length, and type.
+ *
+ * @see Object#equals
+ */
+ public boolean equals(Object o) {
+ if (o instanceof TypedRegion) {
+ TypedRegion r= (TypedRegion) o;
+ return super.equals(r) && ((fType == null && r.getType() == null) || fType.equals(r.getType()));
+ }
+ return false;
+ }
+
+ /*
+ * @see Object#hashCode
+ */
+ public int hashCode() {
+ int type= fType == null ? 0 : fType.hashCode();
+ return super.hashCode() | type;
+ }
+} \ No newline at end of file

Back to the top