Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKai Maetzel2004-03-12 18:19:37 +0000
committerKai Maetzel2004-03-12 18:19:37 +0000
commitbdad092ab72bb499f55b6d6dd4577a2319295cc7 (patch)
treea95b607dba8f4a8043b3effc0cb36e2995e51c24 /org.eclipse.text/projection/org
parentda89adec08f45013179dab1423381b51de4e9b34 (diff)
downloadeclipse.platform.text-bdad092ab72bb499f55b6d6dd4577a2319295cc7.tar.gz
eclipse.platform.text-bdad092ab72bb499f55b6d6dd4577a2319295cc7.tar.xz
eclipse.platform.text-bdad092ab72bb499f55b6d6dd4577a2319295cc7.zip
Rework of projection documents
Diffstat (limited to 'org.eclipse.text/projection/org')
-rw-r--r--org.eclipse.text/projection/org/eclipse/jface/text/CoordinatesTranslator.java1
-rw-r--r--org.eclipse.text/projection/org/eclipse/jface/text/Fragment.java1
-rw-r--r--org.eclipse.text/projection/org/eclipse/jface/text/FragmentUpdater.java1
-rw-r--r--org.eclipse.text/projection/org/eclipse/jface/text/ProjectionDocument.java1
-rw-r--r--org.eclipse.text/projection/org/eclipse/jface/text/ProjectionDocumentManager.java1
-rw-r--r--org.eclipse.text/projection/org/eclipse/jface/text/ProjectionPosition.java3
-rw-r--r--org.eclipse.text/projection/org/eclipse/jface/text/ProjectionPositionUpdater.java1
-rw-r--r--org.eclipse.text/projection/org/eclipse/jface/text/ProjectionTextStore.java1
-rw-r--r--org.eclipse.text/projection/org/eclipse/jface/text/projection/Fragment.java27
-rw-r--r--org.eclipse.text/projection/org/eclipse/jface/text/projection/FragmentUpdater.java100
-rw-r--r--org.eclipse.text/projection/org/eclipse/jface/text/projection/IMinimalMapping.java49
-rw-r--r--org.eclipse.text/projection/org/eclipse/jface/text/projection/ProjectionDocument.java613
-rw-r--r--org.eclipse.text/projection/org/eclipse/jface/text/projection/ProjectionDocumentEvent.java42
-rw-r--r--org.eclipse.text/projection/org/eclipse/jface/text/projection/ProjectionDocumentManager.java248
-rw-r--r--org.eclipse.text/projection/org/eclipse/jface/text/projection/ProjectionMapping.java467
-rw-r--r--org.eclipse.text/projection/org/eclipse/jface/text/projection/ProjectionTextStore.java159
-rw-r--r--org.eclipse.text/projection/org/eclipse/jface/text/projection/Segment.java50
-rw-r--r--org.eclipse.text/projection/org/eclipse/jface/text/projection/SegmentUpdater.java130
18 files changed, 1894 insertions, 1 deletions
diff --git a/org.eclipse.text/projection/org/eclipse/jface/text/CoordinatesTranslator.java b/org.eclipse.text/projection/org/eclipse/jface/text/CoordinatesTranslator.java
index b2b5547a015..eb4af1d45da 100644
--- a/org.eclipse.text/projection/org/eclipse/jface/text/CoordinatesTranslator.java
+++ b/org.eclipse.text/projection/org/eclipse/jface/text/CoordinatesTranslator.java
@@ -20,6 +20,7 @@ import java.util.Comparator;
* document is considered the image document.<p>
* This class is for internal use only.
* @since 2.1
+ * @deprecated
*/
public class CoordinatesTranslator implements IDocumentInformationMapping {
diff --git a/org.eclipse.text/projection/org/eclipse/jface/text/Fragment.java b/org.eclipse.text/projection/org/eclipse/jface/text/Fragment.java
index f19078e2c00..ffaf01716d9 100644
--- a/org.eclipse.text/projection/org/eclipse/jface/text/Fragment.java
+++ b/org.eclipse.text/projection/org/eclipse/jface/text/Fragment.java
@@ -16,6 +16,7 @@ package org.eclipse.jface.text;
* This class is for internal use only.
*
* @since 2.1
+ * @deprecated
*/
public class Fragment extends Position {
diff --git a/org.eclipse.text/projection/org/eclipse/jface/text/FragmentUpdater.java b/org.eclipse.text/projection/org/eclipse/jface/text/FragmentUpdater.java
index e24c8efffc2..c8c0732d632 100644
--- a/org.eclipse.text/projection/org/eclipse/jface/text/FragmentUpdater.java
+++ b/org.eclipse.text/projection/org/eclipse/jface/text/FragmentUpdater.java
@@ -22,6 +22,7 @@ package org.eclipse.jface.text;
* This class is for internal use only.
*
* @since 2.1
+ * @deprecated
*/
public class FragmentUpdater extends DefaultPositionUpdater {
diff --git a/org.eclipse.text/projection/org/eclipse/jface/text/ProjectionDocument.java b/org.eclipse.text/projection/org/eclipse/jface/text/ProjectionDocument.java
index 5cd17f7e3d7..9cbf9dbcfcb 100644
--- a/org.eclipse.text/projection/org/eclipse/jface/text/ProjectionDocument.java
+++ b/org.eclipse.text/projection/org/eclipse/jface/text/ProjectionDocument.java
@@ -25,6 +25,7 @@ import java.util.Comparator;
* This class if for internal use only.
*
* @since 2.1
+ * @deprecated
*/
public final class ProjectionDocument extends AbstractDocument {
diff --git a/org.eclipse.text/projection/org/eclipse/jface/text/ProjectionDocumentManager.java b/org.eclipse.text/projection/org/eclipse/jface/text/ProjectionDocumentManager.java
index 51d825d6359..63c69bceafa 100644
--- a/org.eclipse.text/projection/org/eclipse/jface/text/ProjectionDocumentManager.java
+++ b/org.eclipse.text/projection/org/eclipse/jface/text/ProjectionDocumentManager.java
@@ -34,6 +34,7 @@ import java.util.Map;
* This class if for internal use only.
*
* @since 2.1
+ * @deprecated
*/
public final class ProjectionDocumentManager implements IDocumentListener, ISlaveDocumentManager {
diff --git a/org.eclipse.text/projection/org/eclipse/jface/text/ProjectionPosition.java b/org.eclipse.text/projection/org/eclipse/jface/text/ProjectionPosition.java
index 30fca736574..1724a1332d0 100644
--- a/org.eclipse.text/projection/org/eclipse/jface/text/ProjectionPosition.java
+++ b/org.eclipse.text/projection/org/eclipse/jface/text/ProjectionPosition.java
@@ -12,9 +12,10 @@ package org.eclipse.jface.text;
/**
* Represents the corresponding parent document range of a fragment of a <code>ProjectionDocument</code>.<p>
- * This calss is for internal use only.
+ * This class is for internal use only.
*
* @since 2.1
+ * @deprecated
*/
public class ProjectionPosition extends Position {
diff --git a/org.eclipse.text/projection/org/eclipse/jface/text/ProjectionPositionUpdater.java b/org.eclipse.text/projection/org/eclipse/jface/text/ProjectionPositionUpdater.java
index 166193caa14..1ad05dd4560 100644
--- a/org.eclipse.text/projection/org/eclipse/jface/text/ProjectionPositionUpdater.java
+++ b/org.eclipse.text/projection/org/eclipse/jface/text/ProjectionPositionUpdater.java
@@ -22,6 +22,7 @@ package org.eclipse.jface.text;
* This class is for internal use only.
*
* @since 2.1
+ * @deprecated
*/
public class ProjectionPositionUpdater extends DefaultPositionUpdater {
diff --git a/org.eclipse.text/projection/org/eclipse/jface/text/ProjectionTextStore.java b/org.eclipse.text/projection/org/eclipse/jface/text/ProjectionTextStore.java
index 288010c4aff..b163d040dda 100644
--- a/org.eclipse.text/projection/org/eclipse/jface/text/ProjectionTextStore.java
+++ b/org.eclipse.text/projection/org/eclipse/jface/text/ProjectionTextStore.java
@@ -18,6 +18,7 @@ package org.eclipse.jface.text;
* This class is for internal use only.
*
* @since 2.1
+ * @deprecated
*/
public class ProjectionTextStore implements ITextStore {
diff --git a/org.eclipse.text/projection/org/eclipse/jface/text/projection/Fragment.java b/org.eclipse.text/projection/org/eclipse/jface/text/projection/Fragment.java
new file mode 100644
index 00000000000..67449998115
--- /dev/null
+++ b/org.eclipse.text/projection/org/eclipse/jface/text/projection/Fragment.java
@@ -0,0 +1,27 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation 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 API and implementation
+ *******************************************************************************/
+package org.eclipse.jface.text.projection;
+
+import org.eclipse.jface.text.Position;
+
+/**
+ * Internal class. Do not use. Only public for testing purposes.
+ *
+ * @since 3.0
+ */
+public class Fragment extends Position {
+
+ public Segment segment;
+
+ public Fragment(int offset, int length) {
+ super(offset, length);
+ }
+}
diff --git a/org.eclipse.text/projection/org/eclipse/jface/text/projection/FragmentUpdater.java b/org.eclipse.text/projection/org/eclipse/jface/text/projection/FragmentUpdater.java
new file mode 100644
index 00000000000..7a503749ec3
--- /dev/null
+++ b/org.eclipse.text/projection/org/eclipse/jface/text/projection/FragmentUpdater.java
@@ -0,0 +1,100 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation 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 API and implementation
+ *******************************************************************************/
+
+package org.eclipse.jface.text.projection;
+
+import org.eclipse.jface.text.BadPositionCategoryException;
+import org.eclipse.jface.text.DefaultPositionUpdater;
+import org.eclipse.jface.text.DocumentEvent;
+import org.eclipse.jface.text.Position;
+
+
+/**
+ * The position updater used to adapt the fragments of a document. If an
+ * insertion happens at a fragment's offset, the fragment is extended rather
+ * than shifted. Also, the last fragment is extended if an insert operation
+ * happens at the end of the fragment.
+ * <p>
+ * Internal class. Do not use. Only public for testing purposes.
+ *
+ * @since 3.0
+ */
+public class FragmentUpdater extends DefaultPositionUpdater {
+
+ /** Indicates whether the position being updated represents the last fragment. */
+ private boolean fIsLast= false;
+
+ /**
+ * Creates the fragment updater for the given category.
+ *
+ * @param fragmentCategory the position category used for managing the fragments of a document
+ */
+ protected FragmentUpdater(String fragmentCategory) {
+ super(fragmentCategory);
+ }
+
+ /*
+ * @see org.eclipse.jface.text.IPositionUpdater#update(org.eclipse.jface.text.DocumentEvent)
+ */
+ public void update(DocumentEvent event) {
+
+ try {
+
+ Position[] category= event.getDocument().getPositions(getCategory());
+
+ 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];
+ fIsLast= (i == category.length -1);
+
+ fOriginalPosition.offset= fPosition.offset;
+ fOriginalPosition.length= fPosition.length;
+
+ if (notDeleted())
+ adaptToReplace();
+ }
+
+ } catch (BadPositionCategoryException x) {
+ // do nothing
+ }
+ }
+
+ /*
+ * @see org.eclipse.jface.text.DefaultPositionUpdater#adaptToInsert()
+ */
+ protected void adaptToInsert() {
+ int myStart= fPosition.offset;
+ int myEnd= Math.max(myStart, fPosition.offset + fPosition.length - (fIsLast || isAffectingReplace() ? 0 : 1));
+
+ if (myEnd < fOffset)
+ return;
+
+ if (fLength <= 0) {
+
+ if (myStart <= fOffset)
+ fPosition.length += fReplaceLength;
+ else
+ fPosition.offset += fReplaceLength;
+
+ } else {
+
+ if (myStart <= fOffset && fOriginalPosition.offset <= fOffset)
+ fPosition.length += fReplaceLength;
+ else
+ fPosition.offset += fReplaceLength;
+ }
+ }
+}
diff --git a/org.eclipse.text/projection/org/eclipse/jface/text/projection/IMinimalMapping.java b/org.eclipse.text/projection/org/eclipse/jface/text/projection/IMinimalMapping.java
new file mode 100644
index 00000000000..19cf110dcee
--- /dev/null
+++ b/org.eclipse.text/projection/org/eclipse/jface/text/projection/IMinimalMapping.java
@@ -0,0 +1,49 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation 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 API and implementation
+ *******************************************************************************/
+package org.eclipse.jface.text.projection;
+
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IRegion;
+
+/**
+ * Internal class. Do not use.
+ *
+ * @since 3.0
+ */
+interface IMinimalMapping {
+
+ /**
+ * @return
+ */
+ IRegion getCoverage();
+
+ /**
+ * @param region
+ * @return
+ */
+ IRegion toOriginRegion(IRegion region) throws BadLocationException;
+
+ /**
+ * @param offset
+ * @return
+ */
+ int toOriginOffset(int offset) throws BadLocationException;
+
+ /**
+ * @return
+ */
+ IRegion[] toExactOriginRegions(IRegion region)throws BadLocationException;
+
+ /**
+ * @return
+ */
+ int getImageLength();
+}
diff --git a/org.eclipse.text/projection/org/eclipse/jface/text/projection/ProjectionDocument.java b/org.eclipse.text/projection/org/eclipse/jface/text/projection/ProjectionDocument.java
new file mode 100644
index 00000000000..1bafeffe841
--- /dev/null
+++ b/org.eclipse.text/projection/org/eclipse/jface/text/projection/ProjectionDocument.java
@@ -0,0 +1,613 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation 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 API and implementation
+ *******************************************************************************/
+package org.eclipse.jface.text.projection;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jface.text.AbstractDocument;
+import org.eclipse.jface.text.Assert;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.BadPositionCategoryException;
+import org.eclipse.jface.text.DefaultLineTracker;
+import org.eclipse.jface.text.DocumentEvent;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IDocumentExtension;
+import org.eclipse.jface.text.IDocumentListener;
+import org.eclipse.jface.text.ILineTracker;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITextStore;
+import org.eclipse.jface.text.Position;
+import org.eclipse.jface.text.Region;
+
+
+
+/**
+ * A <code>ProjectionDocument</code> represents a projection of its master
+ * document. The contents of a projection document is a sequence of fragments of
+ * the master document, i.e. the projection document can be thought as being
+ * constructed from the master document by not copying the whole master document
+ * but omitting several ranges of the master document.
+ * <p>
+ * The projection document utilizes its master document as
+ * <code>ITextStore</code>.
+ * <p>
+ * API in progress. Do not yet use.
+ *
+ * @since 3.0
+ */
+public class ProjectionDocument extends AbstractDocument {
+
+ /** The master document */
+ private IDocument fMasterDocument;
+ /** The master document as document extension */
+ private IDocumentExtension fMasterDocumentExtension;
+ /** The fragments' position category */
+ private String fFragmentsCategory;
+ /** The segment's position category */
+ private String fSegmentsCategory;
+ /** The document event issued by the master document */
+ private DocumentEvent fMasterEvent;
+ /** The document event issued and to be issued by the projection document */
+ private ProjectionDocumentEvent fSlaveEvent;
+ /** Indicates whether the projection document initiated a master document update or not */
+ private boolean fIsUpdating= false;
+ /** Indicated whether the projection document is in auto expand mode nor not */
+ private boolean fIsAutoExpanding= false;
+ /** The position updater for the segments */
+ private SegmentUpdater fSegmentUpdater;
+ /** The projection mapping */
+ private ProjectionMapping fMapping;
+
+ /**
+ * Creates a projection document for the given master document.
+ *
+ * @param masterDocument the master document
+ * @param fragmentsCategory the document position category managing the master's fragments
+ * @param segmentsCategory the document position category managing the segments
+ */
+ public ProjectionDocument(IDocument masterDocument, String fragmentsCategory, String segmentsCategory) {
+ super();
+
+ fMasterDocument= masterDocument;
+ if (fMasterDocument instanceof IDocumentExtension)
+ fMasterDocumentExtension= (IDocumentExtension) fMasterDocument;
+
+ fFragmentsCategory= fragmentsCategory;
+ fSegmentsCategory= segmentsCategory;
+ fMapping= new ProjectionMapping(masterDocument, fragmentsCategory, this, segmentsCategory);
+
+ ITextStore s= new ProjectionTextStore(masterDocument, fMapping);
+ ILineTracker tracker= new DefaultLineTracker();
+
+ setTextStore(s);
+ setLineTracker(tracker);
+
+ completeInitialization();
+
+ initializeProjection();
+ tracker.set(s.get(0, s.getLength()));
+ }
+
+ private void internalError() {
+ throw new IllegalStateException();
+ }
+
+ protected final Position[] getFragments() {
+ try {
+ return fMasterDocument.getPositions(fFragmentsCategory);
+ } catch (BadPositionCategoryException e) {
+ internalError();
+ }
+ // unreachable
+ return null;
+ }
+
+ protected final Position[] getSegments() {
+ try {
+ return getPositions(fSegmentsCategory);
+ } catch (BadPositionCategoryException e) {
+ internalError();
+ }
+ // unreachable
+ return null;
+ }
+
+ /**
+ * Returns the projection mapping used by this document.
+ *
+ * @return the projection mapping used by this document
+ */
+ public ProjectionMapping getProjectionMapping(){
+ return fMapping;
+ }
+
+ /**
+ * Returns the master document of this projection document.
+ *
+ * @return the master document of this projection document
+ */
+ public IDocument getMasterDocument() {
+ return fMasterDocument;
+ }
+
+ /**
+ * Initializes the projection document from the master document based on
+ * the master's fragments.
+ */
+ private void initializeProjection() {
+
+ try {
+
+ addPositionCategory(fSegmentsCategory);
+ fSegmentUpdater= new SegmentUpdater(fSegmentsCategory);
+ addPositionUpdater(fSegmentUpdater);
+
+ int offset= 0;
+ Position[] fragments= getFragments();
+ for (int i= 0; i < fragments.length; i++) {
+ Fragment fragment= (Fragment) fragments[i];
+ Segment segment= new Segment(offset, fragment.getLength());
+ segment.fragment= fragment;
+ addPosition(fSegmentsCategory, segment);
+ offset += fragment.length;
+ }
+
+ } catch (BadPositionCategoryException x) {
+ internalError();
+ } catch (BadLocationException x) {
+ internalError();
+ }
+ }
+
+ private Segment createSegmentFor(Fragment fragment, int index) throws BadLocationException, BadPositionCategoryException {
+
+ int offset= 0;
+ if (index > 0) {
+ Position[] segments= getSegments();
+ Segment segment= (Segment) segments[index - 1];
+ offset= segment.getOffset() + segment.getLength();
+ }
+
+ Segment segment= new Segment(offset, 0);
+ segment.fragment= fragment;
+ fragment.segment= segment;
+ addPosition(fSegmentsCategory, segment);
+ return segment;
+ }
+
+ /**
+ * Adds the given range of the master document to this projection document.
+ *
+ * @param offsetInMaster offset of the master document range
+ * @param lengthInMaster length of the master document range
+ * @throws BadLocationException if the given range is invalid in the master document
+ */
+ private void internalAddMasterDocumentRange(int offsetInMaster, int lengthInMaster) throws BadLocationException {
+
+ if (lengthInMaster == 0)
+ return;
+
+ try {
+
+ Position[] fragments= getFragments();
+ int index= fMasterDocument.computeIndexInCategory(fFragmentsCategory, offsetInMaster);
+
+ Fragment left= null;
+ Fragment right= null;
+
+ if (index < fragments.length) {
+ if (offsetInMaster == fragments[index].offset)
+ throw new IllegalArgumentException("overlaps with existing fragment");
+ if (offsetInMaster + lengthInMaster == fragments[index].offset)
+ right= (Fragment) fragments[index];
+ }
+
+ if (0 < index && index <= fragments.length) {
+ Fragment fragment= (Fragment) fragments[index - 1];
+ if (fragment.includes(offsetInMaster))
+ throw new IllegalArgumentException("overlaps with existing fragment");
+ if (fragment.getOffset() + fragment.getLength() == offsetInMaster)
+ left= fragment;
+ }
+
+ // check for neighboring fragment
+ if (left != null && right != null) {
+
+ int endOffset= right.getOffset() + right.getLength();
+ left.setLength(endOffset - left.getOffset());
+ left.segment.setLength(left.segment.getLength() + right.segment.getLength());
+
+ removePosition(fSegmentsCategory, right.segment);
+ fMasterDocument.removePosition(fFragmentsCategory, right);
+
+ } else if (left != null) {
+ int endOffset= offsetInMaster +lengthInMaster;
+ left.setLength(endOffset - left.getOffset());
+ left.segment.markForStretch();
+
+ } else if (right != null) {
+ right.setOffset(right.getOffset() - lengthInMaster);
+ right.setLength(right.getLength() + lengthInMaster);
+ right.segment.markForStretch();
+
+ } else {
+ // create a new segment
+ Fragment fragment= new Fragment(offsetInMaster, lengthInMaster);
+ fMasterDocument.addPosition(fFragmentsCategory, fragment);
+ Segment segment= createSegmentFor(fragment, index);
+ segment.markForStretch();
+ }
+
+ int offsetInSlave= fMapping.toImageOffset(offsetInMaster);
+ Assert.isTrue(offsetInSlave != -1);
+
+ ProjectionDocumentEvent event= new ProjectionDocumentEvent(this, offsetInSlave, 0, fMasterDocument.get(offsetInMaster, lengthInMaster));
+ super.fireDocumentAboutToBeChanged(event);
+ getTracker().replace(event.getOffset(), event.getLength(), event.getText());
+ super.fireDocumentChanged(event);
+
+ } catch (BadPositionCategoryException x) {
+ internalError();
+ }
+ }
+
+ /**
+ * Finds the fragment of the master document that represents the given range.
+ *
+ * @param offsetInMaster the offset of the range in the master document
+ * @param lengthInMaster the length of the range in the master document
+ * @return the fragment representing the given master document range
+ */
+ private Fragment findFragment(int offsetInMaster, int lengthInMaster) {
+ Position[] fragments= getFragments();
+ for (int i= 0; i < fragments.length; i++) {
+ Fragment f= (Fragment) fragments[i];
+ if (f.getOffset() <= offsetInMaster && offsetInMaster + lengthInMaster <= f.getOffset() + f.getLength())
+ return f;
+ }
+ return null;
+ }
+
+ /**
+ * Removes the given range of the master document from this projection
+ * document.
+ *
+ * @param offsetInMaster the offset of the range in the master document
+ * @param lengthInMaster the length of the range in the master document
+ *
+ * @throws BadLocationException if the given range is not valid in the
+ * master document
+ * @throws IllegalArgumentException if the given range is not projected in
+ * this projection document or is not completely comprised by
+ * an existing fragment
+ */
+ private void internalRemoveMasterDocumentRange(int offsetInMaster, int lengthInMaster) throws BadLocationException {
+ try {
+
+ IRegion imageRegion= fMapping.toExactImageRegion(new Region(offsetInMaster, lengthInMaster));
+ if (imageRegion == null)
+ throw new IllegalArgumentException();
+
+ Fragment fragment= findFragment(offsetInMaster, lengthInMaster);
+ if (fragment == null)
+ throw new IllegalArgumentException();
+
+ if (fragment.getOffset() == offsetInMaster) {
+ fragment.setOffset(offsetInMaster + lengthInMaster);
+ fragment.setLength(fragment.getLength() - lengthInMaster);
+ } else if (fragment.getOffset() + fragment.getLength() == offsetInMaster + lengthInMaster) {
+ fragment.setLength(fragment.getLength() - lengthInMaster);
+ } else {
+ // split fragment into three fragments, let position updater remove it
+
+ // add fragment for the region to be removed
+ Fragment newFragment= new Fragment(offsetInMaster, lengthInMaster);
+ Segment segment= new Segment(imageRegion.getOffset(), imageRegion.getLength());
+ newFragment.segment= segment;
+ segment.fragment= newFragment;
+ fMasterDocument.addPosition(fFragmentsCategory, newFragment);
+ addPosition(fSegmentsCategory, segment);
+
+ // add fragment for the remainder right of the deleted range in the original fragment
+ int offset= offsetInMaster + lengthInMaster;
+ newFragment= new Fragment(offset, fragment.getOffset() + fragment.getLength() - offset);
+ offset= imageRegion.getOffset() + imageRegion.getLength();
+ segment= new Segment(offset, fragment.segment.getOffset() + fragment.segment.getLength() - offset);
+ newFragment.segment= segment;
+ segment.fragment= newFragment;
+ fMasterDocument.addPosition(fFragmentsCategory, newFragment);
+ addPosition(fSegmentsCategory, segment);
+
+ // adjust length of initial fragment (the left one)
+ fragment.setLength(offsetInMaster - fragment.getOffset());
+ fragment.segment.setLength(imageRegion.getOffset() - fragment.segment.getOffset());
+ }
+
+ ProjectionDocumentEvent event= new ProjectionDocumentEvent(this, imageRegion.getOffset(), imageRegion.getLength(), null);
+ super.fireDocumentAboutToBeChanged(event);
+ getTracker().replace(event.getOffset(), event.getLength(), event.getText());
+ super.fireDocumentChanged(event);
+
+ } catch (BadPositionCategoryException x) {
+ internalError();
+ }
+ }
+
+ private IRegion[] computeUnprojectedMasterRegions(int offsetInMaster, int lengthInMaster) throws BadLocationException {
+
+ IRegion[] fragments= null;
+ IRegion imageRegion= fMapping.toImageRegion(new Region(offsetInMaster, lengthInMaster));
+ if (imageRegion != null)
+ fragments= fMapping.toExactOriginRegions(imageRegion);
+
+ if (fragments == null || fragments.length == 0)
+ return new IRegion[] { new Region(offsetInMaster, lengthInMaster) };
+
+ List gaps= new ArrayList();
+
+ IRegion region= fragments[0];
+ if (offsetInMaster < region.getOffset())
+ gaps.add(new Region(offsetInMaster, region.getOffset() - offsetInMaster));
+
+ for (int i= 0; i < fragments.length - 1; i++) {
+ IRegion left= fragments[i];
+ IRegion right= fragments[i + 1];
+ int leftEnd= left.getOffset() + left.getLength();
+ if (leftEnd < right.getOffset())
+ gaps.add(new Region(leftEnd, right.getOffset() - leftEnd));
+ }
+
+ region= fragments[fragments.length - 1];
+ int leftEnd= region.getOffset() + region.getLength();
+ int rightEnd= offsetInMaster + lengthInMaster;
+ if (leftEnd < rightEnd)
+ gaps.add(new Region(leftEnd, rightEnd - leftEnd));
+
+ IRegion[] result= new IRegion[gaps.size()];
+ gaps.toArray(result);
+ return result;
+ }
+
+ /**
+ * Ensures that the given range of the master document is part of this
+ * projection document.
+ *
+ * @param offsetInMaster the offset of the master document range
+ * @param lengthInMaster the length of the master document range
+ */
+ public void addMasterDocumentRange(int offsetInMaster, int lengthInMaster) throws BadLocationException {
+
+ IRegion[] gaps= computeUnprojectedMasterRegions(offsetInMaster, lengthInMaster);
+ if (gaps == null)
+ return;
+
+ for (int i= 0; i < gaps.length; i++) {
+ IRegion gap= gaps[i];
+ internalAddMasterDocumentRange(gap.getOffset(), gap.getLength());
+ }
+ }
+
+ /**
+ * Ensures that the given range of the master document is not part of this
+ * projection document.
+ *
+ * @param offsetInMaster the offset of the master document range
+ * @param lengthInMaster the length of the master document range
+ */
+ public void removeMasterDocumentRange(int offsetInMaster, int lengthInMaster) throws BadLocationException {
+
+ IRegion[] fragments= null;
+ IRegion imageRegion= fMapping.toImageRegion(new Region(offsetInMaster, lengthInMaster));
+ if (imageRegion != null)
+ fragments= fMapping.toExactOriginRegions(imageRegion);
+
+ if (fragments == null || fragments.length == 0)
+ return;
+
+ for (int i= 0; i < fragments.length; i++) {
+ IRegion fragment= fragments[i];
+ internalRemoveMasterDocumentRange(fragment.getOffset(), fragment.getLength());
+ }
+ }
+
+ /*
+ * @see org.eclipse.jface.text.IDocument#replace(int, int, java.lang.String)
+ */
+ public void replace(int offset, int length, String text) throws BadLocationException {
+ try {
+ fIsUpdating= true;
+ if (fMasterDocumentExtension != null)
+ fMasterDocumentExtension.stopPostNotificationProcessing();
+
+ super.replace(offset, length, text);
+
+ } finally {
+ fIsUpdating= false;
+ if (fMasterDocumentExtension != null)
+ fMasterDocumentExtension.resumePostNotificationProcessing();
+ }
+ }
+
+ /*
+ * @see org.eclipse.jface.text.IDocument#set(java.lang.String)
+ */
+ public void set(String text) {
+ try {
+ fIsUpdating= true;
+ if (fMasterDocumentExtension != null)
+ fMasterDocumentExtension.stopPostNotificationProcessing();
+
+ super.set(text);
+
+ } finally {
+ fIsUpdating= false;
+ if (fMasterDocumentExtension != null)
+ fMasterDocumentExtension.resumePostNotificationProcessing();
+ }
+ }
+
+ /**
+ * Transforms a document event of the master document into a projection
+ * document based document event.
+ *
+ * @param masterEvent the master document event
+ * @return the slave document event
+ */
+ private ProjectionDocumentEvent normalize(DocumentEvent masterEvent) throws BadLocationException {
+ IRegion imageRegion= fMapping.toExactImageRegion(new Region(masterEvent.getOffset(), masterEvent.getLength()));
+ if (imageRegion != null)
+ return new ProjectionDocumentEvent(this, imageRegion.getOffset(), imageRegion.getLength(), masterEvent.getText(), masterEvent);
+ return null;
+ }
+
+ /**
+ * Ensures that when the master event effects this projection document, that the whole region described by the
+ * event is part of this projection document.
+ *
+ * @param masterEvent the master document event
+ * @return <code>true</code> if masterEvent affects this projection document
+ * @throws BadLocationException in case the master event is not valid
+ */
+ private boolean adaptProjectionToMasterChange(DocumentEvent masterEvent) throws BadLocationException {
+ if (!fIsUpdating || fIsAutoExpanding) {
+ IRegion region= new Region(masterEvent.getOffset(), masterEvent.getLength());
+ IRegion exactImage= fMapping.toExactImageRegion(region);
+ IRegion image= fMapping.toImageRegion(region);
+ if (exactImage == null && image != null) {
+ addMasterDocumentRange(masterEvent.getOffset(), masterEvent.getLength());
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * When called, this projection document is informed about a forthcoming
+ * change of its master document. This projection document checks whether
+ * the master document change affects it and if so informs all document
+ * listeners.
+ *
+ * @param masterEvent the master document event
+ */
+ public void masterDocumentAboutToBeChanged(DocumentEvent masterEvent) {
+ try {
+
+ boolean assertNotNull= adaptProjectionToMasterChange(masterEvent);
+ fSlaveEvent= normalize(masterEvent);
+ if (assertNotNull && fSlaveEvent == null)
+ internalError();
+
+ fMasterEvent= masterEvent;
+ if (fSlaveEvent != null)
+ delayedFireDocumentAboutToBeChanged();
+
+ } catch (BadLocationException e) {
+ internalError();
+ }
+ }
+
+ /**
+ * When called, this projection document is informed about a change of its
+ * master document. If this projection document is affected it informs all
+ * of its document listeners.
+ *
+ * @param masterEvent the master document event
+ */
+ public void masterDocumentChanged(DocumentEvent masterEvent) {
+ if ( !fIsUpdating && masterEvent == fMasterEvent) {
+ if (fSlaveEvent != null) {
+ try {
+ getTracker().replace(fSlaveEvent.getOffset(), fSlaveEvent.getLength(), fSlaveEvent.getText());
+ fireDocumentChanged(fSlaveEvent);
+ } catch (BadLocationException e) {
+ internalError();
+ }
+ } else {
+ ensureWellFormedSegmentation();
+ }
+ }
+ }
+
+ /*
+ * @see org.eclipse.jface.text.AbstractDocument#fireDocumentAboutToBeChanged(org.eclipse.jface.text.DocumentEvent)
+ */
+ protected void fireDocumentAboutToBeChanged(DocumentEvent event) {
+ // delay it until there is a notification from the master document
+ // at this point, it is expensive to construct the master document information
+ }
+
+ /**
+ * Fires the slave document event as about-to-be-changed event to all registered listeners.
+ */
+ private void delayedFireDocumentAboutToBeChanged() {
+ super.fireDocumentAboutToBeChanged(fSlaveEvent);
+ }
+
+ /**
+ * Ignores the given event and sends the semantically equal slave document event instead.
+ *
+ * @param event the event to be ignored
+ */
+ protected void fireDocumentChanged(DocumentEvent event) {
+ super.fireDocumentChanged(fSlaveEvent);
+ }
+
+ /*
+ * @see org.eclipse.jface.text.AbstractDocument#updateDocumentStructures(org.eclipse.jface.text.DocumentEvent)
+ */
+ protected void updateDocumentStructures(DocumentEvent event) {
+ super.updateDocumentStructures(event);
+ ensureWellFormedSegmentation();
+ }
+
+ private void ensureWellFormedSegmentation() {
+ Position[] segments= getSegments();
+ for (int i= 0; i < segments.length; i++) {
+ Segment segment= (Segment) segments[i];
+ if (segment.isDeleted()) {
+ try {
+ removePosition(fSegmentsCategory, segment);
+ fMasterDocument.removePosition(fFragmentsCategory, segment.fragment);
+ } catch (BadPositionCategoryException e) {
+ internalError();
+ }
+ } else if (i < segments.length - 1) {
+ Segment next= (Segment) segments[i + 1];
+ if (next.isDeleted())
+ continue;
+ Fragment fragment= segment.fragment;
+ if (fragment.getOffset() + fragment.getLength() == next.fragment.getOffset()) {
+ // join fragments and their corresponding segments
+ segment.setLength(segment.getLength() + next.getLength());
+ fragment.setLength(fragment.getLength() + next.fragment.getLength());
+ next.delete();
+ }
+ }
+ }
+ }
+
+ /*
+ * @see IDocumentExtension#registerPostNotificationReplace(IDocumentListener, IDocumentExtension.IReplace)
+ */
+ public void registerPostNotificationReplace(IDocumentListener owner, IDocumentExtension.IReplace replace) {
+ if (!fIsUpdating)
+ throw new UnsupportedOperationException();
+ super.registerPostNotificationReplace(owner, replace);
+ }
+
+ /**
+ * Sets the auto expand mode for this document.
+ */
+ public void setAutoExpandMode(boolean autoExpandMode) {
+ fIsAutoExpanding= autoExpandMode;
+ }
+}
diff --git a/org.eclipse.text/projection/org/eclipse/jface/text/projection/ProjectionDocumentEvent.java b/org.eclipse.text/projection/org/eclipse/jface/text/projection/ProjectionDocumentEvent.java
new file mode 100644
index 00000000000..f443d51773c
--- /dev/null
+++ b/org.eclipse.text/projection/org/eclipse/jface/text/projection/ProjectionDocumentEvent.java
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation 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 API and implementation
+ *******************************************************************************/
+package org.eclipse.jface.text.projection;
+
+import org.eclipse.jface.text.DocumentEvent;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.SlaveDocumentEvent;
+
+/**
+ * Internal class. Do not use. Only public for testing purposes.
+ *
+ * @since 3.0
+ */
+public class ProjectionDocumentEvent extends SlaveDocumentEvent {
+
+ public final static Object PROJECTION_CHANGE= new Object();
+ public final static Object CONTENT_CHANGE= new Object();
+
+ private Object fChangeType;
+
+ public ProjectionDocumentEvent(IDocument doc, int offset, int length, String text, DocumentEvent masterEvent) {
+ super(doc, offset, length, text, masterEvent);
+ fChangeType= CONTENT_CHANGE;
+ }
+
+ public ProjectionDocumentEvent(IDocument doc, int offset, int length, String text) {
+ super(doc, offset, length, text, null);
+ fChangeType= PROJECTION_CHANGE;
+ }
+
+ public Object getChangeType() {
+ return fChangeType;
+ }
+}
diff --git a/org.eclipse.text/projection/org/eclipse/jface/text/projection/ProjectionDocumentManager.java b/org.eclipse.text/projection/org/eclipse/jface/text/projection/ProjectionDocumentManager.java
new file mode 100644
index 00000000000..856753f2b7d
--- /dev/null
+++ b/org.eclipse.text/projection/org/eclipse/jface/text/projection/ProjectionDocumentManager.java
@@ -0,0 +1,248 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation 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 API and implementation
+ *******************************************************************************/
+package org.eclipse.jface.text.projection;
+
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.jface.text.BadPositionCategoryException;
+import org.eclipse.jface.text.DocumentEvent;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IDocumentInformationMapping;
+import org.eclipse.jface.text.IDocumentListener;
+import org.eclipse.jface.text.IPositionUpdater;
+import org.eclipse.jface.text.ISlaveDocumentManager;
+
+
+
+/**
+ * <code>ProjectionDocumentManager</code> is one particular implementation of
+ * <code>ISlaveDocumentManager</code>. This manager creates so called
+ * projection documents as slave documents for given master documents.
+ * <p>
+ * A projection document represents a particular projection of the master
+ * document and is accordingly adapted to changes of the master document. Vice
+ * versa, the master document is accordingly adapted to changes of its slave
+ * 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>
+ * API in progress. Do not yet use.
+ *
+ * @since 3.0
+ */
+public class ProjectionDocumentManager implements IDocumentListener, ISlaveDocumentManager {
+
+
+ /**
+ * Name of the position category used to keep track of the master
+ * document's fragments that correspond to the segments of the projection
+ * documents.
+ */
+ protected final static String FRAGMENTS_CATEGORY= "__fragmentsCategory"; //$NON-NLS-1$
+
+ /**
+ * Name of the position category used to keep track of the project
+ * document's segments that correspond to the fragments of the master
+ * documents.
+ */
+ protected final static String SEGMENTS_CATEGORY= "__segmentsCategory"; //$NON-NLS-1$
+
+
+ /** The position updater shared by all master documents which have projection documents */
+ private IPositionUpdater fFragmentsUpdater;
+ /** Registry for master documents and their projection documents. */
+ private Map fRegistry= new HashMap();
+
+
+ /**
+ * Returns the fragments updater. If necessary, it is dynamically created.
+ *
+ * @return the fragments updater
+ */
+ private IPositionUpdater getFragmentsUpdater() {
+ if (fFragmentsUpdater == null)
+ fFragmentsUpdater= new FragmentUpdater(FRAGMENTS_CATEGORY);
+ return fFragmentsUpdater;
+ }
+
+ /**
+ * Registers the given projection document for the given master document.
+ *
+ * @param master the master document
+ * @param projection the projection document
+ */
+ private void add(IDocument master, ProjectionDocument projection) {
+ List list= (List) fRegistry.get(master);
+ if (list == null) {
+ list= new ArrayList(1);
+ fRegistry.put(master, list);
+ }
+ list.add(projection);
+ }
+
+ /**
+ * Unregisters the given projection document from its master.
+ *
+ * @param master the master document
+ * @param projection the projection document
+ */
+ private void remove(IDocument master, ProjectionDocument projection) {
+ List list= (List) fRegistry.get(master);
+ if (list != null) {
+ list.remove(projection);
+ if (list.size() == 0)
+ fRegistry.remove(master);
+ }
+ }
+
+ /**
+ * Returns whether the given document is a master document.
+ *
+ * @param master the document
+ * @return <code>true</code> if the given document is a master document known to this manager
+ */
+ private boolean hasProjection(IDocument master) {
+ return (fRegistry.get(master) instanceof List);
+ }
+
+ /**
+ * Returns an iterator enumerating all projection documents registered for the given document or
+ * <code>null</code> if the document is not a known master document.
+ *
+ * @param master the document
+ * @return an iterator for all registered projection documents or <code>null</code>
+ */
+ private Iterator getProjectionsIterator(IDocument master) {
+ List list= (List) fRegistry.get(master);
+ if (list != null)
+ return list.iterator();
+ return null;
+ }
+
+ /**
+ * Informs all projection documents of the master document that issued the given document event.
+ *
+ * @param about indicates whether the change is about to happen or happened already
+ * @param masterEvent the document event which will be processed to inform the projection documents
+ */
+ protected void fireDocumentEvent(boolean about, DocumentEvent masterEvent) {
+ IDocument master= masterEvent.getDocument();
+ Iterator e= getProjectionsIterator(master);
+ if (e == null)
+ return;
+
+ while (e.hasNext()) {
+ ProjectionDocument document= (ProjectionDocument) e.next();
+ if (about)
+ document.masterDocumentAboutToBeChanged(masterEvent);
+ else
+ document.masterDocumentChanged(masterEvent);
+ }
+ }
+
+ /*
+ * @see org.eclipse.jface.text.IDocumentListener#documentChanged(org.eclipse.jface.text.DocumentEvent)
+ */
+ public void documentChanged(DocumentEvent event) {
+ fireDocumentEvent(false, event);
+ }
+
+ /*
+ * @see org.eclipse.jface.text.IDocumentListener#documentAboutToBeChanged(org.eclipse.jface.text.DocumentEvent)
+ */
+ public void documentAboutToBeChanged(DocumentEvent event) {
+ fireDocumentEvent(true, event);
+ }
+
+ /*
+ * @see org.eclipse.jface.text.ISlaveDocumentManager#createMasterSlaveMapping(org.eclipse.jface.text.IDocument)
+ */
+ public IDocumentInformationMapping createMasterSlaveMapping(IDocument slave) {
+ if (slave instanceof ProjectionDocument) {
+ ProjectionDocument projectionDocument= (ProjectionDocument) slave;
+ return projectionDocument.getProjectionMapping();
+ }
+ return null;
+ }
+
+ /*
+ * @see org.eclipse.jface.text.ISlaveDocumentManager#createSlaveDocument(org.eclipse.jface.text.IDocument)
+ */
+ public IDocument createSlaveDocument(IDocument master) {
+ if (!master.containsPositionCategory(FRAGMENTS_CATEGORY)) {
+ master.addPositionCategory(FRAGMENTS_CATEGORY);
+ master.addPositionUpdater(getFragmentsUpdater());
+ master.addDocumentListener(this);
+ }
+ ProjectionDocument slave= createProjectionDocument(master);
+ add(master, slave);
+ return slave;
+ }
+
+ /**
+ * Factory method for projection documents.
+ *
+ * @param master the master document
+ * @return the newly created projection document
+ */
+ protected ProjectionDocument createProjectionDocument(IDocument master) {
+ return new ProjectionDocument(master, FRAGMENTS_CATEGORY, SEGMENTS_CATEGORY);
+ }
+
+ /*
+ * @see org.eclipse.jface.text.ISlaveDocumentManager#freeSlaveDocument(org.eclipse.jface.text.IDocument)
+ */
+ public void freeSlaveDocument(IDocument slave) {
+ if (slave instanceof ProjectionDocument) {
+ ProjectionDocument projectionDocument= (ProjectionDocument) slave;
+ IDocument master= projectionDocument.getMasterDocument();
+ remove(master, projectionDocument);
+
+ try {
+ if (!hasProjection(master)) {
+ master.removeDocumentListener(this);
+ master.removePositionUpdater(getFragmentsUpdater());
+ master.removePositionCategory(FRAGMENTS_CATEGORY);
+ }
+ } catch (BadPositionCategoryException x) {
+ }
+ }
+ }
+
+ /*
+ * @see org.eclipse.jface.text.ISlaveDocumentManager#getMasterDocument(org.eclipse.jface.text.IDocument)
+ */
+ public IDocument getMasterDocument(IDocument slave) {
+ if (slave instanceof ProjectionDocument)
+ return ((ProjectionDocument) slave).getMasterDocument();
+ return null;
+ }
+
+ /*
+ * @see org.eclipse.jface.text.ISlaveDocumentManager#isSlaveDocument(org.eclipse.jface.text.IDocument)
+ */
+ public boolean isSlaveDocument(IDocument document) {
+ return (document instanceof ProjectionDocument);
+ }
+
+ /*
+ * @see org.eclipse.jface.text.ISlaveDocumentManager#setAutoExpandMode(org.eclipse.jface.text.IDocument, boolean)
+ */
+ public void setAutoExpandMode(IDocument slave, boolean autoExpanding) {
+ if (slave instanceof ProjectionDocument)
+ ((ProjectionDocument) slave).setAutoExpandMode(autoExpanding);
+ }
+}
diff --git a/org.eclipse.text/projection/org/eclipse/jface/text/projection/ProjectionMapping.java b/org.eclipse.text/projection/org/eclipse/jface/text/projection/ProjectionMapping.java
new file mode 100644
index 00000000000..07ca37ddddc
--- /dev/null
+++ b/org.eclipse.text/projection/org/eclipse/jface/text/projection/ProjectionMapping.java
@@ -0,0 +1,467 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation 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 API and implementation
+ *******************************************************************************/
+package org.eclipse.jface.text.projection;
+
+import org.eclipse.jface.text.Assert;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.BadPositionCategoryException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IDocumentInformationMapping;
+import org.eclipse.jface.text.IDocumentInformationMappingExtension;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.Position;
+import org.eclipse.jface.text.Region;
+
+/**
+ * Internal class. Do not use. Only public for testing purposes.
+ * <p>
+ * Implementation of <code>IDocumentInformationMapping</code> for the
+ * projection mapping between a master and a slave document.
+ *
+ * @since 3.0
+ */
+public class ProjectionMapping implements IDocumentInformationMapping , IDocumentInformationMappingExtension, IMinimalMapping {
+
+ private static final int LEFT= -1;
+ private static final int NONE= 0;
+ private static final int RIGHT= +1;
+
+ /** The master document */
+ private IDocument fMasterDocument;
+ /** The position category used to manage the projection fragments inside the master document */
+ private String fFragmentsCategory;
+ /** The projection document */
+ private IDocument fSlaveDocument;
+ /** The position category to manage the projection segments inside the slave document. */
+ private String fSegmentsCategory;
+
+
+ /**
+ * Creates a new mapping between the given parent document and the given projection document.
+ *
+ * @param masterDocument the master document
+ * @param fragmentsCategory the position category of the parent document used to manage the projected regions
+ * @param slaveDocument the slave document
+ * @param segmentsCategory the position category of the projection document used to manage the fragments
+ */
+ public ProjectionMapping(IDocument masterDocument, String fragmentsCategory, IDocument slaveDocument, String segmentsCategory) {
+ fMasterDocument= masterDocument;
+ fFragmentsCategory= fragmentsCategory;
+ fSlaveDocument= slaveDocument;
+ fSegmentsCategory= segmentsCategory;
+ }
+
+ private Position[] getSegments() {
+ try {
+ return fSlaveDocument.getPositions(fSegmentsCategory);
+ } catch (BadPositionCategoryException e) {
+ }
+
+ return new Position[0];
+ }
+
+ private Position[] getFragments() {
+ try {
+ return fMasterDocument.getPositions(fFragmentsCategory);
+ } catch (BadPositionCategoryException e) {
+ }
+
+ return new Position[0];
+ }
+
+ private int findSegmentIndex(int offset) throws BadLocationException {
+ Position[] segments= getSegments();
+ if (segments.length == 0) {
+ if (offset > 0)
+ throw new BadLocationException();
+ return -1;
+ }
+
+ try {
+ int index= fSlaveDocument.computeIndexInCategory(fSegmentsCategory, offset);
+ if (index == segments.length && offset > (segments[index-1].offset + segments[index-1].length))
+ throw new BadLocationException();
+
+ if (index < segments.length && offset == segments[index].offset)
+ return index;
+
+ if (index > 0)
+ index--;
+
+ return index;
+
+ } catch (BadPositionCategoryException e) {
+ throw new IllegalStateException();
+ }
+ }
+
+ private Segment findSegment(int offset) throws BadLocationException {
+
+ if (offset < 0 || getImageLength() < offset)
+ throw new BadLocationException();
+
+ int index= findSegmentIndex(offset);
+ if (index == -1) {
+
+ Segment s= new Segment(0, 0);
+ Fragment f= new Fragment(0, 0);
+ s.fragment= f;
+ f.segment= s;
+ return s;
+ }
+
+ Position[] segments= getSegments();
+ return (Segment) segments[index];
+ }
+
+ private int findFragmentIndex(int offset, int extensionDirection) throws BadLocationException {
+ try {
+
+ Position[] fragments= getFragments();
+ if (fragments.length == 0)
+ return -1;
+
+ int index= fMasterDocument.computeIndexInCategory(fFragmentsCategory, offset);
+
+ if (index < fragments.length && offset == fragments[index].offset)
+ return index;
+
+ if (0 < index && index <= fragments.length && fragments[index - 1].includes(offset))
+ return index - 1;
+
+ switch(extensionDirection) {
+ case LEFT:
+ return Math.max(index - 1, 0);
+ case RIGHT:
+ return Math.min(index, fragments.length - 1);
+ }
+
+ return -1;
+
+ } catch (BadPositionCategoryException e) {
+ throw new IllegalStateException();
+ }
+ }
+
+ private Fragment findFragment(int offset) throws BadLocationException {
+
+ int length= fMasterDocument.getLength();
+ if (offset < 0 || length < offset)
+ throw new BadLocationException();
+
+ int index= findFragmentIndex(offset, NONE);
+ Position[] fragments= getFragments();
+ if (index == -1) {
+ if (fragments.length > 0) {
+ Fragment last= (Fragment) fragments[fragments.length - 1];
+ if (last.getOffset() + last.getLength() == offset)
+ return last;
+ }
+ return null;
+ }
+ return (Fragment) fragments[index];
+ }
+
+ private Fragment[] findFragments(IRegion region, boolean exact) throws BadLocationException {
+ int offset= region.getOffset();
+ if (offset < 0 || fMasterDocument.getLength() < offset)
+ throw new BadLocationException();
+
+ int inclusiveEndOffset= region.getOffset() + region.getLength() - 1;
+ if (inclusiveEndOffset < 0 || fMasterDocument.getLength() < inclusiveEndOffset)
+ throw new BadLocationException();
+
+ int startIndex= findFragmentIndex(offset, exact ? NONE : LEFT);
+ if (startIndex == -1)
+ return new Fragment[0];
+
+ int endIndex= findFragmentIndex(inclusiveEndOffset, exact ? NONE : RIGHT);
+ if (endIndex == -1)
+ return new Fragment[0];
+
+ Position[] fragments= getFragments();
+ while (startIndex <= endIndex && !fragments[startIndex].overlapsWith(region.getOffset(), region.getLength()))
+ ++startIndex;
+
+ while (endIndex >= startIndex && !fragments[endIndex].overlapsWith(region.getOffset(), region.getLength()))
+ --endIndex;
+
+ int length= Math.max(0, endIndex - startIndex + 1);
+ Fragment[] result= new Fragment[length];
+ for (int i= 0; i < length; i++)
+ result[i]= (Fragment) fragments[startIndex + i];
+ return result;
+ }
+
+ private IRegion toImageRegion(IRegion originRegion, boolean exact) throws BadLocationException {
+ if (originRegion.getLength() == 0) {
+ int imageOffset= toImageOffset(originRegion.getOffset());
+ return imageOffset == -1 ? null : new Region(imageOffset, 0);
+ }
+
+ Fragment[] fragments= findFragments(originRegion, exact);
+ if (fragments.length > 0) {
+
+ // translate start offset
+ Fragment fragment= fragments[0];
+ int originOffset= originRegion.getOffset();
+ int relative= originOffset - fragment.getOffset();
+ if (relative < 0) {
+ Assert.isTrue(!exact);
+ relative= 0;
+ }
+ int imageOffset= fragment.segment.getOffset() + relative;
+
+ // translate end offset
+ fragment= fragments[fragments.length - 1];
+ int exclusiveOriginEndOffset= originRegion.getOffset() + originRegion.getLength();
+ relative= exclusiveOriginEndOffset - fragment.getOffset();
+ if (relative > fragment.getLength()) {
+ Assert.isTrue(!exact);
+ int delta= relative - fragment.getLength();
+ relative -= delta;
+ }
+ int exclusiveImageEndOffset= fragment.segment.getOffset() + relative;
+
+ return new Region(imageOffset, exclusiveImageEndOffset - imageOffset);
+ }
+
+ return null;
+ }
+
+ private IRegion createOriginStartRegion(Segment original, int offsetShift) {
+ return new Region(original.fragment.getOffset() + offsetShift, original.fragment.getLength() - offsetShift);
+ }
+
+ private IRegion createOriginRegion(Segment origin) {
+ return new Region(origin.fragment.getOffset(), origin.fragment.getLength());
+ }
+
+ private IRegion createOriginEndRegion(Segment original, int lengthReduction) {
+ return new Region(original.fragment.getOffset(), original.fragment.getLength() - lengthReduction);
+ }
+
+ private IRegion getIntersectingRegion(IRegion left, IRegion right) {
+ int offset= Math.max(left.getOffset(), right.getOffset());
+ int exclusiveEndOffset= Math.min(left.getOffset() + left.getLength(), right.getOffset() + right.getLength());
+ if (exclusiveEndOffset < offset)
+ return null;
+ return new Region(offset, exclusiveEndOffset - offset);
+ }
+
+ /*
+ * @see org.eclipse.jface.text.IDocumentInformationMapping#getCoverage()
+ */
+ public IRegion getCoverage() {
+ Position[] fragments= getFragments();
+ if (fragments != null && fragments.length > 0) {
+ Position first=fragments[0];
+ Position last= fragments[fragments.length -1];
+ return new Region(first.offset, (last.offset + last.length) - first.offset);
+ }
+ return new Region(0, 0);
+ }
+
+ /*
+ * @see org.eclipse.jface.text.IDocumentInformationMapping#toOriginOffset(int)
+ */
+ public int toOriginOffset(int imageOffset) throws BadLocationException {
+ Segment segment= findSegment(imageOffset);
+ int relative= imageOffset - segment.offset;
+ return segment.fragment.offset + relative;
+ }
+
+ /*
+ * @see org.eclipse.jface.text.IDocumentInformationMapping#toOriginRegion(org.eclipse.jface.text.IRegion)
+ */
+ public IRegion toOriginRegion(IRegion imageRegion) throws BadLocationException {
+ int imageOffset= imageRegion.getOffset();
+ int imageLength= imageRegion.getLength();
+
+ if (imageLength == 0) {
+ if (imageOffset == 0 && getImageLength() == 0)
+ return new Region(0, fMasterDocument.getLength());
+ return new Region(toOriginOffset(imageOffset), 0);
+ }
+
+ int originOffset= toOriginOffset(imageOffset);
+ int inclusiveImageEndOffset= imageOffset + imageLength -1;
+ int inclusiveOriginEndOffset= toOriginOffset(inclusiveImageEndOffset);
+
+ return new Region(originOffset, (inclusiveOriginEndOffset + 1) - originOffset);
+ }
+
+ /*
+ * @see org.eclipse.jface.text.IDocumentInformationMapping#toOriginLines(int)
+ */
+ public IRegion toOriginLines(int imageLine) throws BadLocationException {
+ IRegion imageRegion= fSlaveDocument.getLineInformation(imageLine);
+ IRegion originRegion= toOriginRegion(imageRegion);
+
+ int originStartLine= fMasterDocument.getLineOfOffset(originRegion.getOffset());
+ if (originRegion.getLength() == 0)
+ return new Region(originStartLine, 1);
+
+ int inclusiveOriginEndOffset= originRegion.getOffset() + originRegion.getLength() -1;
+ int originEndLine= fMasterDocument.getLineOfOffset(inclusiveOriginEndOffset);
+ return new Region(originStartLine, (originEndLine + 1) - originStartLine);
+ }
+
+ /*
+ * @see org.eclipse.jface.text.IDocumentInformationMapping#toOriginLine(int)
+ */
+ public int toOriginLine(int imageLine) throws BadLocationException {
+ IRegion lines= toOriginLines(imageLine);
+ return (lines.getLength() > 1 ? -1 : lines.getOffset());
+ }
+
+ /*
+ * @see org.eclipse.jface.text.IDocumentInformationMapping#toImageOffset(int)
+ */
+ public int toImageOffset(int originOffset) throws BadLocationException {
+ Fragment fragment= findFragment(originOffset);
+ if (fragment != null) {
+ int relative= originOffset - fragment.offset;
+ return fragment.segment.offset + relative;
+ }
+ return -1;
+ }
+
+ /*
+ * @see org.eclipse.jface.text.IDocumentInformationMappingExtension#toExactImageRegion(org.eclipse.jface.text.IRegion)
+ */
+ public IRegion toExactImageRegion(IRegion originRegion) throws BadLocationException {
+ return toImageRegion(originRegion, true);
+ }
+
+ /*
+ * @see org.eclipse.jface.text.IDocumentInformationMapping#toImageRegion(org.eclipse.jface.text.IRegion)
+ */
+ public IRegion toImageRegion(IRegion originRegion) throws BadLocationException {
+ return toImageRegion(originRegion, false);
+ }
+
+ /*
+ * @see org.eclipse.jface.text.IDocumentInformationMapping#toImageLine(int)
+ */
+ public int toImageLine(int originLine) throws BadLocationException {
+ IRegion originRegion= fMasterDocument.getLineInformation(originLine);
+ IRegion imageRegion= toImageRegion(originRegion);
+ if (imageRegion == null) {
+ int imageOffset= toImageOffset(originRegion.getOffset());
+ if (imageOffset > -1)
+ imageRegion= new Region(imageOffset, 0);
+ else
+ return -1;
+ }
+
+ int startLine= fSlaveDocument.getLineOfOffset(imageRegion.getOffset());
+ if (imageRegion.getLength() == 0)
+ return startLine;
+
+ int endLine= fSlaveDocument.getLineOfOffset(imageRegion.getOffset() + imageRegion.getLength());
+ if (endLine != startLine)
+ throw new IllegalStateException();
+
+ return startLine;
+ }
+
+ /*
+ * @see org.eclipse.jface.text.IDocumentInformationMapping#toClosestImageLine(int)
+ */
+ public int toClosestImageLine(int originLine) throws BadLocationException {
+ try {
+
+ int imageLine= toImageLine(originLine);
+ if (imageLine > -1)
+ return imageLine;
+
+ Position[] fragments= getFragments();
+ if (fragments.length == 0)
+ return -1;
+
+ IRegion originLineRegion= fMasterDocument.getLineInformation(originLine);
+ int index= fMasterDocument.computeIndexInCategory(fFragmentsCategory, originLineRegion.getOffset());
+
+ if (0 < index && index < fragments.length) {
+ Fragment left= (Fragment) fragments[index - 1];
+ int leftDistance= originLineRegion.getOffset() - (left.getOffset() + left.getLength());
+ Fragment right= (Fragment) fragments[index];
+ int rightDistance= right.getOffset() - (originLineRegion.getOffset() + originLineRegion.getLength());
+
+ if (leftDistance <= rightDistance)
+ originLine= fMasterDocument.getLineOfOffset(left.getOffset() + Math.max(left.getLength() - 1, 0));
+ else
+ originLine= fMasterDocument.getLineOfOffset(right.getOffset());
+
+ } else if (index == 0) {
+ Fragment right= (Fragment) fragments[index];
+ originLine= fMasterDocument.getLineOfOffset(right.getOffset());
+ } else if (index == fragments.length) {
+ Fragment left= (Fragment) fragments[index - 1];
+ originLine= fMasterDocument.getLineOfOffset(left.getOffset() + left.getLength());
+ }
+
+ return toImageLine(originLine);
+
+ } catch (BadPositionCategoryException x) {
+ }
+
+ return -1;
+ }
+
+ /*
+ * @see org.eclipse.jface.text.IDocumentInformationMappingExtension#toExactOriginRegions(org.eclipse.jface.text.IRegion)
+ */
+ public IRegion[] toExactOriginRegions(IRegion imageRegion) throws BadLocationException {
+
+ if (imageRegion.getLength() == 0)
+ return new IRegion[] { new Region(toOriginOffset(imageRegion.getOffset()), 0) };
+
+ int endOffset= imageRegion.getOffset() + imageRegion.getLength();
+ Position[] segments= getSegments();
+ int firstIndex= findSegmentIndex(imageRegion.getOffset());
+ int lastIndex= findSegmentIndex(endOffset - 1);
+
+ int resultLength= lastIndex - firstIndex + 1;
+ IRegion[] result= new IRegion[resultLength];
+
+ // first
+ result[0]= createOriginStartRegion((Segment) segments[firstIndex], imageRegion.getOffset() - segments[firstIndex].getOffset());
+ // middles
+ for (int i= 1; i < resultLength - 1; i++)
+ result[i]= createOriginRegion((Segment) segments[firstIndex + i]);
+ // last
+ Segment last= (Segment) segments[lastIndex];
+ int segmentEndOffset= last.getOffset() + last.getLength();
+ IRegion lastRegion= createOriginEndRegion(last, segmentEndOffset - endOffset);
+ if (resultLength > 1) {
+ // first != last
+ result[resultLength - 1]= lastRegion;
+ } else {
+ // merge first and last
+ result[0]= getIntersectingRegion(result[0], lastRegion);
+ }
+
+ return result;
+ }
+
+ /*
+ * @see org.eclipse.jface.text.IDocumentInformationMappingExtension#getImageLength()
+ */
+ public int getImageLength() {
+ Position[] segments= getSegments();
+ int length= 0;
+ for (int i= 0; i < segments.length; i++)
+ length += segments[i].length;
+ return length;
+ }
+}
diff --git a/org.eclipse.text/projection/org/eclipse/jface/text/projection/ProjectionTextStore.java b/org.eclipse.text/projection/org/eclipse/jface/text/projection/ProjectionTextStore.java
new file mode 100644
index 00000000000..8a6d492f5e8
--- /dev/null
+++ b/org.eclipse.text/projection/org/eclipse/jface/text/projection/ProjectionTextStore.java
@@ -0,0 +1,159 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation 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 API and implementation
+ *******************************************************************************/
+
+package org.eclipse.jface.text.projection;
+
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITextStore;
+import org.eclipse.jface.text.Region;
+
+/**
+ * * Internal class. Do not use. Only public for testing purposes.
+ * <p>
+ * A text store representing the projection defined by the given document
+ * information mapping.
+ *
+ * @since 3.0
+ */
+public class ProjectionTextStore implements ITextStore {
+
+ /**
+ * Implementation of <code>IRegion</code> that can be reused
+ * by setting the offset and the length.
+ */
+ private static class ReusableRegion implements IRegion {
+
+ private int fOffset;
+ private int fLength;
+
+ /*
+ * @see org.eclipse.jface.text.IRegion#getLength()
+ */
+ public int getLength() {
+ return fLength;
+ }
+
+ /*
+ * @see org.eclipse.jface.text.IRegion#getOffset()
+ */
+ public int getOffset() {
+ return fOffset;
+ }
+
+ /**
+ * Updates this region.
+ *
+ * @param offset the new offset
+ * @param length the new length
+ */
+ public void update(int offset, int length) {
+ fOffset= offset;
+ fLength= length;
+ }
+ }
+
+ /** The master document */
+ private IDocument fMasterDocument;
+ /** The document information mapping */
+ private IMinimalMapping fMapping;
+ /** Internal region used for querying the mapping. */
+ private ReusableRegion fReusableRegion= new ReusableRegion();
+
+
+ /**
+ * Creates a new projection text store for the given master document and
+ * the given document information mapping.
+ *
+ * @param masterDocument the master document
+ * @param mapping the document information mapping
+ */
+ public ProjectionTextStore(IDocument masterDocument, IMinimalMapping mapping) {
+ fMasterDocument= masterDocument;
+ fMapping= mapping;
+ }
+
+ private void internalError() {
+ throw new IllegalStateException();
+ }
+
+ /*
+ * @see org.eclipse.jface.text.ITextStore#set(java.lang.String)
+ */
+ public void set(String contents) {
+
+ IRegion masterRegion= fMapping.getCoverage();
+ if (masterRegion == null)
+ internalError();
+
+ try {
+ fMasterDocument.replace(masterRegion.getOffset(), masterRegion.getLength(), contents);
+ } catch (BadLocationException e) {
+ internalError();
+ }
+ }
+
+ /*
+ * @see org.eclipse.jface.text.ITextStore#replace(int, int, java.lang.String)
+ */
+ public void replace(int offset, int length, String text) {
+ fReusableRegion.update(offset, length);
+ try {
+ IRegion masterRegion= fMapping.toOriginRegion(fReusableRegion);
+ fMasterDocument.replace(masterRegion.getOffset(), masterRegion.getLength(), text);
+ } catch (BadLocationException e) {
+ internalError();
+ }
+ }
+
+ /*
+ * @see org.eclipse.jface.text.ITextStore#getLength()
+ */
+ public int getLength() {
+ return fMapping.getImageLength();
+ }
+
+ /*
+ * @see org.eclipse.jface.text.ITextStore#get(int)
+ */
+ public char get(int offset) {
+ try {
+ int originOffset= fMapping.toOriginOffset(offset);
+ return fMasterDocument.getChar(originOffset);
+ } catch (BadLocationException e) {
+ internalError();
+ }
+
+ // unreachable
+ return (char) 0;
+ }
+
+ /*
+ * @see ITextStore#get(int, int)
+ */
+ public String get(int offset, int length) {
+ try {
+ IRegion[] fragments= fMapping.toExactOriginRegions(new Region(offset, length));
+ StringBuffer buffer= new StringBuffer();
+ for (int i= 0; i < fragments.length; i++) {
+ IRegion fragment= fragments[i];
+ buffer.append(fMasterDocument.get(fragment.getOffset(), fragment.getLength()));
+ }
+ return buffer.toString();
+ } catch (BadLocationException e) {
+ internalError();
+ }
+
+ // unreachable
+ return null;
+ }
+}
diff --git a/org.eclipse.text/projection/org/eclipse/jface/text/projection/Segment.java b/org.eclipse.text/projection/org/eclipse/jface/text/projection/Segment.java
new file mode 100644
index 00000000000..febbc27252b
--- /dev/null
+++ b/org.eclipse.text/projection/org/eclipse/jface/text/projection/Segment.java
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation 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 API and implementation
+ *******************************************************************************/
+package org.eclipse.jface.text.projection;
+
+import org.eclipse.jface.text.Position;
+
+/**
+ * Internal class. Do not use. Only public for testing purposes.
+ *
+ * @since 3.0
+ */
+public class Segment extends Position {
+
+ public Fragment fragment;
+ public boolean isMarkedForStretch;
+ public boolean isMarkedForShift;
+
+ public Segment(int offset, int length) {
+ super(offset, length);
+ }
+
+ public void markForStretch() {
+ isMarkedForStretch= true;
+ }
+
+ public boolean isMarkedForStretch() {
+ return isMarkedForStretch;
+ }
+
+ public void markForShift() {
+ isMarkedForShift= true;
+ }
+
+ public boolean isMarkedForShift() {
+ return isMarkedForShift;
+ }
+
+ public void clearMark() {
+ isMarkedForStretch= false;
+ isMarkedForShift= false;
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.text/projection/org/eclipse/jface/text/projection/SegmentUpdater.java b/org.eclipse.text/projection/org/eclipse/jface/text/projection/SegmentUpdater.java
new file mode 100644
index 00000000000..cb242e32efa
--- /dev/null
+++ b/org.eclipse.text/projection/org/eclipse/jface/text/projection/SegmentUpdater.java
@@ -0,0 +1,130 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation 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 API and implementation
+ *******************************************************************************/
+package org.eclipse.jface.text.projection;
+
+import org.eclipse.jface.text.Assert;
+import org.eclipse.jface.text.BadPositionCategoryException;
+import org.eclipse.jface.text.DefaultPositionUpdater;
+import org.eclipse.jface.text.DocumentEvent;
+import org.eclipse.jface.text.Position;
+
+
+/**
+ * Internal class. Do not use. Only public for testing purposes.
+ * <p>
+ * The position updater used to adapt the segments of a projection document to
+ * changes of the master document. If an insertion happens at a segment's
+ * offset, the segment is extended rather than shifted. Also, the last segment
+ * is extended if an insert operation happens at the end of the segment.
+ *
+ * @since 3.0
+ */
+public class SegmentUpdater extends DefaultPositionUpdater {
+
+ private Segment fNextSegment= null;
+ private boolean fIsProjectionChange= false;
+
+ /**
+ * Creates the segment updater for the given category.
+ *
+ * @param segmentCategory the position category used for managing the segments of a projection document
+ */
+ protected SegmentUpdater(String segmentCategory) {
+ super(segmentCategory);
+ }
+
+ /*
+ * @see org.eclipse.jface.text.IPositionUpdater#update(org.eclipse.jface.text.DocumentEvent)
+ */
+ public void update(DocumentEvent event) {
+
+ Assert.isTrue(event instanceof ProjectionDocumentEvent);
+ fIsProjectionChange= ((ProjectionDocumentEvent) event).getChangeType() == ProjectionDocumentEvent.PROJECTION_CHANGE;
+
+ try {
+
+ Position[] category= event.getDocument().getPositions(getCategory());
+
+ 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];
+ Assert.isTrue(fPosition instanceof Segment);
+
+ if (i < category.length - 1)
+ fNextSegment= (Segment) category[i + 1];
+ else
+ fNextSegment= null;
+
+ fOriginalPosition.offset= fPosition.offset;
+ fOriginalPosition.length= fPosition.length;
+
+ if (notDeleted())
+ adaptToReplace();
+
+ if (fPosition.getLength() == 0)
+ fPosition.isDeleted= true;
+ }
+
+ } catch (BadPositionCategoryException x) {
+ // do nothing
+ }
+ }
+
+ /*
+ * @see org.eclipse.jface.text.DefaultPositionUpdater#adaptToInsert()
+ */
+ protected void adaptToInsert() {
+
+ Segment segment= (Segment) fPosition;
+ int myStart= segment.offset;
+ int myEnd= segment.offset + segment.length - (segment.isMarkedForStretch || fNextSegment == null || isAffectingReplace() ? 0 : 1);
+ myEnd= Math.max(myStart, myEnd);
+ int yoursStart= fOffset;
+
+ try {
+
+ if (myEnd < yoursStart)
+ return;
+
+ if (segment.isMarkedForStretch) {
+ Assert.isTrue(fIsProjectionChange);
+ segment.isMarkedForShift= false;
+ if (fNextSegment != null) {
+ fNextSegment.isMarkedForShift= true;
+ fNextSegment.isMarkedForStretch= false;
+ }
+ }
+
+ if (fLength <= 0) {
+
+ if (myStart < (yoursStart + (segment.isMarkedForShift ? 0 : 1)))
+ fPosition.length += fReplaceLength;
+ else
+ fPosition.offset += fReplaceLength;
+
+ } else {
+
+ if (myStart <= yoursStart && fOriginalPosition.offset <= yoursStart)
+ fPosition.length += fReplaceLength;
+ else
+ fPosition.offset += fReplaceLength;
+ }
+
+ } finally {
+ segment.clearMark();
+ }
+ }
+}

Back to the top