diff options
author | Kai Maetzel | 2002-12-03 13:53:47 +0000 |
---|---|---|
committer | Kai Maetzel | 2002-12-03 13:53:47 +0000 |
commit | 12a19d1dcedb0e74ec7448608e801550e9ddf756 (patch) | |
tree | 666fd0e3d69aeaa61cae9fc0a44a95a6eb6614c1 /org.eclipse.text | |
parent | cca5d0bee1158d234c5366d10b4dcb9126932563 (diff) | |
download | eclipse.platform.text-12a19d1dcedb0e74ec7448608e801550e9ddf756.tar.gz eclipse.platform.text-12a19d1dcedb0e74ec7448608e801550e9ddf756.tar.xz eclipse.platform.text-12a19d1dcedb0e74ec7448608e801550e9ddf756.zip |
projection
Diffstat (limited to 'org.eclipse.text')
19 files changed, 2027 insertions, 73 deletions
diff --git a/org.eclipse.text/.classpath b/org.eclipse.text/.classpath index d8b3fa0d8f2..16fb22f551c 100644 --- a/org.eclipse.text/.classpath +++ b/org.eclipse.text/.classpath @@ -1,6 +1,8 @@ <?xml version="1.0" encoding="UTF-8"?> <classpath> <classpathentry kind="src" path="src"/> + <classpathentry kind="src" path="projection"/> <classpathentry kind="var" path="JRE_LIB" rootpath="JRE_SRCROOT" sourcepath="JRE_SRC"/> + <classpathentry kind="src" path="/org.junit"/> <classpathentry kind="output" path="bin"/> </classpath> diff --git a/org.eclipse.text/.project b/org.eclipse.text/.project index 9e0b25af586..e6a88f34e13 100644 --- a/org.eclipse.text/.project +++ b/org.eclipse.text/.project @@ -3,6 +3,7 @@ <name>org.eclipse.text</name> <comment></comment> <projects> + <project>org.junit</project> </projects> <buildSpec> <buildCommand> diff --git a/org.eclipse.text/build.properties b/org.eclipse.text/build.properties index 04e9be9a51b..03e53ece0c8 100644 --- a/org.eclipse.text/build.properties +++ b/org.eclipse.text/build.properties @@ -1,4 +1,5 @@ bin.includes = plugin.properties,\ plugin.xml,\ *.jar -source.text.jar = src/ +source.text.jar = src/,\ + projection/ diff --git a/org.eclipse.text/projection/org/eclipse/jface/text/CoordinatesTranslator.java b/org.eclipse.text/projection/org/eclipse/jface/text/CoordinatesTranslator.java new file mode 100644 index 00000000000..f4c7c3017f8 --- /dev/null +++ b/org.eclipse.text/projection/org/eclipse/jface/text/CoordinatesTranslator.java @@ -0,0 +1,288 @@ +/********************************************************************** +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.Arrays; +import java.util.Comparator; + +/** + * + */ +public class CoordinatesTranslator implements IDocumentInformationMapping { + + private IDocument fParentDocument; + private String fParentCategory; + private ProjectionDocument fProjectionDocument; + private String fProjectionCategory; + + public CoordinatesTranslator(IDocument parent, String parentCategory, ProjectionDocument projection, String projectionCategory) { + fParentDocument= parent; + fParentCategory= parentCategory; + fProjectionDocument= projection; + fProjectionCategory= projectionCategory; + } + + /* (non-Javadoc) + * @see org.eclipse.jface.text.IDocumentInformationMapping#toOriginOffset(int) + */ + public int toOriginOffset(int imageOffset) throws BadLocationException { + Fragment fragment= (Fragment) getPositionOfOffset(fProjectionDocument, ProjectionDocument.FRAGMENT_CATEGORY, imageOffset); + if (fragment == null) { + if (imageOffset == 0) + return 0; + throw new BadLocationException(); + } + + int relative= imageOffset - fragment.offset; + return fragment.getOrigin().offset + relative; + } + + /* (non-Javadoc) + * @see org.eclipse.jface.text.IDocumentInformationMapping#toOriginRegion(org.eclipse.jface.text.IRegion) + */ + public IRegion toOriginRegion(IRegion imageRegion) throws BadLocationException { + + int projectionOffset= imageRegion.getOffset(); + int projectionLength= imageRegion.getLength(); + + if (projectionLength == 0) { + if (projectionOffset == 0 && projectionLength == fProjectionDocument.getLength()) + return new Region(0, fParentDocument.getLength()); + return new Region(toOriginOffset(projectionOffset), 0); + } + + int o1= toOriginOffset(projectionOffset); + int o2= toOriginOffset(projectionOffset + projectionLength -1); + return new Region(o1, o2 - o1 + 1); + } + + /* (non-Javadoc) + * @see org.eclipse.jface.text.IDocumentInformationMapping#toOriginLines(int) + */ + public IRegion toOriginLines(int imageLine) throws BadLocationException { + + IRegion projectionDocumentRegion= fProjectionDocument.getLineInformation(imageLine); + IRegion parentDocumentRegion= toOriginRegion(projectionDocumentRegion); + + int startLine= fParentDocument.getLineOfOffset(parentDocumentRegion.getOffset()); + if (parentDocumentRegion.getLength() == 0) + return new Region(startLine, 0); + + int endLine= fParentDocument.getLineOfOffset(parentDocumentRegion.getOffset() + parentDocumentRegion.getLength() -1); + return new Region(startLine, endLine - startLine); + } + + /* (non-Javadoc) + * @see org.eclipse.jface.text.IDocumentInformationMapping#toOriginLine(int) + */ + public int toOriginLine(int imageLine) throws BadLocationException { + IRegion lines= toOriginLines(imageLine); + if (lines.getLength() > 0) + throw new IllegalStateException(); + return lines.getOffset(); + } + + public int toImageOffset(int originOffset) throws BadLocationException { + ProjectionPosition projection= (ProjectionPosition) getPositionOfOffset(fParentDocument, ProjectionDocumentManager.PROJECTION_DOCUMENTS, originOffset); + if (projection != null) + return translateOffset(projection, originOffset, projection.getFragment()); + // not included + return -1; + } + + public IRegion toImageRegion(IRegion originRegion) throws BadLocationException { + + if (originRegion.getLength() == 0) { + int projectionOffset= toImageOffset(originRegion.getOffset()); + return projectionOffset == -1 ? null : new Region(projectionOffset, 0); + } + + Position[] positions= getPositionsOfRange(fParentDocument, ProjectionDocumentManager.PROJECTION_DOCUMENTS, originRegion, null); + if (positions != null && positions.length > 0) { + ProjectionPosition projection= (ProjectionPosition) positions[0]; + + int offset= originRegion.getOffset(); + int length= originRegion.getLength(); + + int delta= projection.getOffset() - offset; + if (delta > 0) { + offset += delta; + length -= delta; + } + int start= translateOffset(projection, offset, projection.getFragment()); + + projection= (ProjectionPosition) positions[positions.length -1]; + int decrease= 0; + int endOffset= offset + length; + if (length > 0) + decrease= 1; + endOffset -= decrease; + + delta= endOffset - (projection.getOffset() + Math.max(projection.getLength() -1, 0)); + if (delta > 0) + endOffset -= delta; + + int end= translateOffset(projection, endOffset, projection.getFragment()); + return new Region(start, end - start + decrease); + } + + return null; + } + + /* (non-Javadoc) + * @see org.eclipse.jface.text.IDocumentInformationMapping#toImageLine(int) + */ + public int toImageLine(int originLine) throws BadLocationException { + + IRegion parentDocumentRegion= fParentDocument.getLineInformation(originLine); + IRegion projectionDocumentRegion= toImageRegion(parentDocumentRegion); + if (projectionDocumentRegion == null) + return -1; + + int startLine= fProjectionDocument.getLineOfOffset(projectionDocumentRegion.getOffset()); + if (projectionDocumentRegion.getLength() == 0) + return startLine; + + int endLine= fProjectionDocument.getLineOfOffset(projectionDocumentRegion.getOffset() + projectionDocumentRegion.getLength() -1); + if (endLine != startLine) + throw new IllegalStateException(); + + return startLine; + } + + + //----------------------------------------------------------------------------------------------------------------------------------- + + + private int translateOffset(Position origin, int originOffset, Position target) { + int relative= originOffset - origin.offset; + return target.offset + relative; + } + + private Position getPositionOfOffset(IDocument document, String category, int offset) throws BadLocationException { + try { + int index= getPositionIndexOfOffset(document, category, offset, 0); + if (index > -1) { + Position[] positions= document.getPositions(category); + return positions[index]; + } + } catch (BadPositionCategoryException x) { + } + return null; + } + + private Position[] getPositionsOfRange(IDocument document, String category, IRegion range, Comparator comparator) { + + int offset= range.getOffset(); + int length= range.getLength(); + + try { + + int start= getPositionIndexOfOffset(document, category, offset, length); + int end= getPositionIndexOfOffset(document, category, offset + length -1, 1 - length); + + if (start > -1 && end > -1) { + + Position[] positions= document.getPositions(category); + + if (start == end) + return new Position[] { positions[start] }; + + Position[] result= new Position[end - start + 1]; + for (int i= start; i <= end; i++) + result[i - start]= positions[i]; + + if (comparator != null) + Arrays.sort(result, comparator); + + return result; + } + + } catch (BadPositionCategoryException e) { + } catch (BadLocationException e) { + } + + return new Position[0]; + } + + private int getPositionIndexOfOffset(IDocument document, String category, int offset, int direction ) throws BadPositionCategoryException, BadLocationException{ + + Position[] positions= document.getPositions(category); + if (positions != null && positions.length > 0) { + + // test for inclusion + int index= document.computeIndexInCategory(category, offset); + if (index < positions.length && positions[index].includes(offset)) + return index; + if (index > 0 && positions[index -1].includes(offset)) + return index -1; + + // find next accorrding to direction + if (direction != 0) { + if (direction > 0) { + if (index < positions.length && positions[index].overlapsWith(offset, direction)) + return index; + } else { + if (index > 0 && positions[index -1].overlapsWith(offset + direction, -direction)) + return index -1; + } + } + } + + return -1; + } + + /* (non-Javadoc) + * @see org.eclipse.jface.text.IDocumentInformationMapping#getCoverage() + */ + public IRegion getCoverage() { + Position coverage= fProjectionDocument.getParentDocumentCoverage(); + return new Region(coverage.getOffset(), coverage.getLength()); + } + + /* (non-Javadoc) + * @see org.eclipse.jface.text.IDocumentInformationMapping#toClosestImageLine(int) + */ + public int toClosestImageLine(int originLine) throws BadLocationException { + try { + + int modelLineOffset= fParentDocument.getLineOffset(originLine); + int index= fParentDocument.computeIndexInCategory(ProjectionDocumentManager.PROJECTION_DOCUMENTS, modelLineOffset); + Position[] projections= fParentDocument.getPositions(ProjectionDocumentManager.PROJECTION_DOCUMENTS); + + if (index < projections.length) { + Position p= projections[index -1]; + int delta1= modelLineOffset - (p.getOffset() + p.getLength()); + p= projections[index]; + int delta2= modelLineOffset - (p.getOffset() + p.getLength()); + if (delta1 < delta2) { + p= projections[index -1]; + originLine= fParentDocument.getLineOfOffset(p.getOffset() + Math.max(p.getLength() -1, 0)); + } else { + originLine= fParentDocument.getLineOfOffset(p.getOffset()); + } + } else if (projections.length > 0) { + Position p= projections[index -1]; + originLine= fParentDocument.getLineOfOffset(p.getOffset() + Math.max(p.getLength() -1, 0)); + } else { + return 0; + } + + return toImageLine(originLine); + + } catch (BadLocationException x) { + } catch (BadPositionCategoryException x) { + } + + return 0; + } +} diff --git a/org.eclipse.text/projection/org/eclipse/jface/text/Fragment.java b/org.eclipse.text/projection/org/eclipse/jface/text/Fragment.java new file mode 100644 index 00000000000..22ee665eee6 --- /dev/null +++ b/org.eclipse.text/projection/org/eclipse/jface/text/Fragment.java @@ -0,0 +1,43 @@ +/********************************************************************** +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 org.eclipse.jface.text.Position; + +/** + * Fragment.java + */ +public class Fragment extends Position { + + private Position fOrigin; + + public Fragment(int offset, int length, Position origin) { + super(offset, length); + fOrigin= origin; + } + +// public Fragment(int offset, int length) { +// super(offset, length); +// } + + /** + * Returns the fOrigin. + * @return Position + */ + public Position getOrigin() { + return fOrigin; + } + + public void setOrigin(Position origin) { + fOrigin= origin; + } +} diff --git a/org.eclipse.text/projection/org/eclipse/jface/text/FragmentUpdater.java b/org.eclipse.text/projection/org/eclipse/jface/text/FragmentUpdater.java new file mode 100644 index 00000000000..7233a2ce57d --- /dev/null +++ b/org.eclipse.text/projection/org/eclipse/jface/text/FragmentUpdater.java @@ -0,0 +1,104 @@ +/********************************************************************** +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; + + +/** + * The position updater used to adapt the fragments of a projection document to + * changes of the master document. + */ +public class FragmentUpdater extends DefaultPositionUpdater { + + private boolean fShiftMode= false; + private boolean fIsLast= false; + + /** + * Creates the fragment updater. + */ + protected FragmentUpdater(String fragmentCategory) { + super(fragmentCategory); + } + + public void enableShiftMode(boolean enable) { + fShiftMode= enable; + } + + /** + * If an insertion happens at a fragment's offset, the fragment is extended + * rather than shifted. Also, the last fragment is extended if an inserted + * happends at the end of the fragment. + */ + protected void adaptToInsert() { + + if (fShiftMode) { + super.adaptToInsert(); + return; + } + + int myStart= fPosition.offset; + int myEnd= fPosition.offset + fPosition.length - (fIsLast ? 0 : 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; + } + } + + /* + * @see IPositionUpdater#update(DocumentEvent event) + */ + 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 + } + } +};
\ No newline at end of file diff --git a/org.eclipse.text/projection/org/eclipse/jface/text/ProjectionDocument.java b/org.eclipse.text/projection/org/eclipse/jface/text/ProjectionDocument.java new file mode 100644 index 00000000000..9191c9186d4 --- /dev/null +++ b/org.eclipse.text/projection/org/eclipse/jface/text/ProjectionDocument.java @@ -0,0 +1,745 @@ +/********************************************************************** +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.Arrays; +import java.util.Comparator; + + + +/** + * A patch document represent a projection of its parent document. + * The patch 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. + * + * TODO: sorting Fragments on creation/joining + * + * @see ITextStore + */ +public final class ProjectionDocument extends AbstractDocument { + + final public static String FRAGMENT_CATEGORY= "__fragment_category"; + + /** The parent document */ + private IDocument fParentDocument; + /** The parent document as document extension */ + private IDocumentExtension fExtension; + /** The position category defining the projection */ + private String fProjectionCategory; + /** The document event issued by the parent document */ + private DocumentEvent fParentEvent; + /** The document event issued and to be issued by the patch document */ + private SlaveDocumentEvent fEvent; + /** Indicates whether the patch document initiated a parent document update or not */ + private boolean fIsUpdating= false; + /** The fragment updater */ + private FragmentUpdater fFragmentUpdater= new FragmentUpdater(FRAGMENT_CATEGORY); + + /** + * Creates a projection document of the given parent document. + * + * @param parentDocument the parent Document + * @param projectionCategory the document position category whose positions define the projection of the parent document + */ + public ProjectionDocument(IDocument parentDocument, String projectionCategory) { + super(); + + fParentDocument= parentDocument; + if (fParentDocument instanceof IDocumentExtension) + fExtension= (IDocumentExtension) fParentDocument; + + ITextStore s= new ProjectionTextStore(this); + ILineTracker tracker= new DefaultLineTracker(); + + setTextStore(s); + setLineTracker(tracker); + + completeInitialization(); + + initializeProjection(projectionCategory); + tracker.set(s.get(0, s.getLength())); + } + + /** + * Initializes the projection document from the parent document based on the given + * projection category. + * @param projectionCategory the document position category whose positions define the projection of the parent document + */ + private void initializeProjection(String projectionCategory) { + + fProjectionCategory= projectionCategory; + + try { + + addPositionCategory(FRAGMENT_CATEGORY); + addPositionUpdater(fFragmentUpdater); + + int offset= 0; + Position[] patch= fParentDocument.getPositions(fProjectionCategory); + for (int i= 0; i < patch.length; i++) { + Position p= patch[i]; + addPosition(FRAGMENT_CATEGORY, new Fragment(offset, p.length, p)); + offset += p.length; + } + + } catch (BadPositionCategoryException x) { + } catch (BadLocationException x) { + } + } + + /** + * Creates a fragment from a postion of the parent document. + * @param parentPosition a position of the parent document + * @return the fragment representing the range given by the parent position + */ + public Fragment createFragment(Position parentPosition) { + try { + + int index= fParentDocument.computeIndexInCategory(fProjectionCategory, parentPosition.offset); + if (index <= 0) + return new Fragment(0, parentPosition.length, parentPosition); + + Position[] fragments= getPositions(FRAGMENT_CATEGORY); + Position p= fragments[index -1]; + return new Fragment(p.offset + p.length, parentPosition.length, parentPosition); + + } catch (BadPositionCategoryException e) { + } catch (BadLocationException e) { + } + + return null; + } + + private int getPositionOfOffset(IDocument document, String category, int offset, int direction ) throws BadPositionCategoryException, BadLocationException{ + + Position[] positions= document.getPositions(category); + if (positions != null && positions.length > 0) { + + // test for inclusion + int index= document.computeIndexInCategory(category, offset); + if (index < positions.length && positions[index].includes(offset)) + return index; + if (index > 0 && positions[index -1].includes(offset)) + return index -1; + + // find next accorrding to direction + if (direction != 0) { + if (direction > 0) { + if (index < positions.length && positions[index].overlapsWith(offset, direction)) + return index; + } else { + if (index > 0 && positions[index -1].overlapsWith(offset + direction, -direction)) + return index -1; + } + } + } + + return -1; + } + + /** + * Returns the position which is used to manage a parent + * document range represented in this projection document and that + * includes or is close to the given parent document offset. The distance + * is computed based on the given direction hint. + * + * @param offsetInParent the parent document offset + * @param direction the direction hint used for computing the distance + * @return position the parent document position including or near to the parent document offset + */ + private Position getParentDocumentPositionOfOffset(int offsetInParent, int direction ) { + try { + + int index= getPositionOfOffset(fParentDocument, fProjectionCategory, offsetInParent, direction); + if (index > -1) { + Position[] positions= fParentDocument.getPositions(fProjectionCategory); + return positions[index]; + } + + } catch (BadPositionCategoryException x) { + } catch (BadLocationException x) { + } + + return null; + } + + /** + * Returns the offset in the projection document corresponding to the + * given parent document offset. + * + * @param offsetInParent the parent document offset + * @return the projection document offset corresponding to the given parent document offset + */ + private int toProjectionDocumentOffset(int offsetInParent, int direction) { + + Position p= getParentDocumentPositionOfOffset(offsetInParent, direction); + if (p == null) + return -1; + + int relative= offsetInParent - p.offset; + + if (direction > 0) { + if (relative < 0) + relative= 0; + } else if (direction < 0) { + if (relative >= p.length) + relative= p.length -1; + } + + Fragment f= findCorrespondingFragment(p); + return f.offset + relative; + } + + /** + * Creates a position describing the projection document range corresponding to + * the given parent document range. + * + * @param offsetInParent the parent document offset + * @param lengthInParent the parent document lengh + * @return position describing the projection document range corresponding to the given parent document range + */ + public Position computeProjectionDocumentPosition(int offsetInParent, int lengthInParent) { + + Position p= getParentDocumentCoverage(); + if (p != null) { + + if (p.overlapsWith(offsetInParent, lengthInParent)) { + + int o1= toProjectionDocumentOffset(offsetInParent, lengthInParent); + if (o1 == -1) + return null; + + if (lengthInParent == 0) + return new Position(o1, 0); + + int o2= toProjectionDocumentOffset(offsetInParent + lengthInParent -1, 1 - lengthInParent); + if (o2 == -1) + return null; + + return new Position(o1, o2 - o1 + 1); + + } else if (p.getOffset() + p.getLength() == offsetInParent + lengthInParent) { + + Position[] fragments= getFragmentation(); + if (fragments != null && fragments.length > 0) { + Position last= fragments[fragments.length -1]; + return new Position(last.getOffset() + last.getLength()); + } + } + } + + return null; + } + + public int toParentDocumentOffset(int offset) throws BadLocationException { + Fragment fragment= getFragmentOfOffset(offset); + + if (fragment == null) { + +// if (offset == 0) +// return 0; +// throw new BadLocationException(); + + Position[] fragmentation= getFragmentation(); + if (fragmentation != null && fragmentation.length > 0) { + Fragment last= (Fragment) fragmentation[fragmentation.length -1]; + if (last.offset + last.length == offset) { + Position origin= last.getOrigin(); + return origin.offset + origin.length; + } + } + + throw new BadLocationException(); + } + + int relative= offset - fragment.offset; + return fragment.getOrigin().offset + relative; + } + + public IRegion computeParentDocumentRegion(int offset, int length) throws BadLocationException { + + if (length == 0) { + if (offset == 0 && length == getLength()) + return new Region(0, fParentDocument.getLength()); + return new Region(toParentDocumentOffset(offset), 0); + } + + int o1= toParentDocumentOffset(offset); + int o2= toParentDocumentOffset(offset + length -1); + return new Region(o1, o2 - o1 + 1); + } + + /** + * Removes all fragments and thus empties this projection document. + */ + public void removeAllFragments() { + Position[] projection= getProjection(); + if (projection == null) + return; + + for (int i= 0; i < projection.length; i++) { + try { + removeFragment(projection[i]); + } catch (BadLocationException e) { + } + } + } + + /** + * Add a new fragment of the parent document to this projection document. + * + * @param offsetInParent offset of the parent document range + * @param lengthInParent length of the parent document range + * @return returns the position representing the parent document range in this projection document + * @throws BadLocationException + */ + public void addFragment(int offsetInParent, int lengthInParent) throws BadLocationException { + + if (lengthInParent == 0) + return; + + try { + + ProjectionPosition p= new ProjectionPosition(this, offsetInParent, lengthInParent); + fParentDocument.addPosition(fProjectionCategory, p); + + Fragment fragment= createFragment(p); + p.setFragment(fragment); + fireDocumentProjectionChanged(new DocumentEvent(this, fragment.offset, 0, fParentDocument.get(offsetInParent, lengthInParent))); + addPosition(FRAGMENT_CATEGORY, fragment); + + + getTracker().set(getStore().get(0, getStore().getLength())); + + } catch (BadPositionCategoryException x) { + } + + } + + public void joinFragments() { + try { + while (joinTwoFragments()) {} + } catch (BadPositionCategoryException x) { + } + } + + private boolean joinTwoFragments() throws BadPositionCategoryException { + Position[] projection= getProjection(); + if (projection != null && projection.length > 0) { + Position previous= projection[0]; + for (int i= 1; i < projection.length; i++) { + Position current= projection[i]; + if (previous.offset + previous.length == current.offset) { + join(previous, current); + return true; + } + previous= current; + } + } + return false; + } + + private void join(Position p1, Position p2) throws BadPositionCategoryException { + // remove p2 + Fragment fragment= findCorrespondingFragment(p2); + removePosition(FRAGMENT_CATEGORY, fragment); + fParentDocument.removePosition(fProjectionCategory, p2); + // extend p1 by length of p2 + fragment= findCorrespondingFragment(p1); + fragment.length += p2.length; + p1.length += p2.length; + } + + /** + * Removes the given parent document range from this projection document. + * + * @param parentPosition the position representing the parent document range + * @throws BadLocationException + */ + public void removeFragment(Position parentPosition) throws BadLocationException { + try { + + Fragment fragment= findCorrespondingFragment(parentPosition); + if (fragment != null) { + removePosition(FRAGMENT_CATEGORY, fragment); + fParentDocument.removePosition(fProjectionCategory, parentPosition); + fireDocumentProjectionChanged(new DocumentEvent(this, fragment.offset, fragment.length, null)); + getTracker().set(getStore().get(0, getStore().getLength())); + } + + } catch (BadPositionCategoryException x) { + } + } + + public Position[] getAffectedFragments(int offsetInParent, int lengthInParent) { + + Position p= computeProjectionDocumentPosition(offsetInParent, lengthInParent); + if (p == null) + return null; + + Fragment[] f= getFragmentsOfRange(p.offset, p.length); + if (f == null) + return null; + + Position[] result= new Position[f.length]; + for (int i= 0; i < f.length; i++) + result[i]= f[i].getOrigin(); + return result; + } + + /** + * Finds the fragment that represents the given parent document range in this projection document. + * + * @param parentPosition the parent document range + * @return the fragment representing the given parent document range + */ + private Fragment findCorrespondingFragment(Position parentPosition) { + try { + Position[] fragments= getPositions(FRAGMENT_CATEGORY); + for (int i= 0; i < fragments.length; i++) { + Fragment f= (Fragment) fragments[i]; + if (parentPosition.equals(f.getOrigin())) + return f; + } + } catch (BadPositionCategoryException x) { + } + + return null; + } + + /** + * Returns the fragment that contains the given offset. + * + * @param offset the offset + * @return the fragment that contains the given offset + */ + protected Fragment getFragmentOfOffset(int offset) throws BadLocationException { + try { + int index= getPositionOfOffset(this, FRAGMENT_CATEGORY, offset, 0); + if (index > -1) { + Position[] fragments= getPositions(FRAGMENT_CATEGORY); + return (Fragment) fragments[index]; + } + } catch (BadPositionCategoryException x) { + } + return null; + } + + /** + * Returns the minimal consecutive list of fragments that completely covers the given range. + * + * @param offset the offset of the range + * @param length the length of the range + * @return the minimal consecutive list of fragments convering the given range + */ + protected Fragment[] getFragmentsOfRange(int offset, int length) { + + try { + + int start= getPositionOfOffset(this, FRAGMENT_CATEGORY, offset, length); + int end= getPositionOfOffset(this, FRAGMENT_CATEGORY, offset + length -1, 1 - length); + + if (start > -1 && end > -1) { + + Position[] positions= getPositions(FRAGMENT_CATEGORY); + + if (start == end) + return new Fragment[] { (Fragment) positions[start] }; + + Fragment[] result= new Fragment[end - start + 1]; + for (int i= start; i <= end; i++) + result[i - start]= (Fragment) positions[i]; + sortFragments(result); + return result; + } + + } catch (BadPositionCategoryException e) { + } catch (BadLocationException e) { + } + + return new Fragment[0]; + } + + /** + * Sorts a list of fragments based on the sequence of their origins in the parent document. + * @param result + */ + private void sortFragments(Object[] result) { + + Comparator comparator= new Comparator() { + + public int compare(Object o1, Object o2) { + Fragment f1= (Fragment) o1; + Fragment f2= (Fragment) o2; + return f1.getOrigin().getOffset() - f2.getOrigin().getOffset(); + } + + public boolean equals(Object obj) { + return false; + } + }; + + Arrays.sort(result, comparator); + } + + /** + * Returns the minimal range of the parent document that covers all fragments. + * + * @return a position describing the minimal parent document range covering all fragments + */ + public Position getParentDocumentCoverage() { + Position[] projection= getProjection(); + if (projection != null && projection.length > 0) { + Position first=projection[0]; + Position last= projection[projection.length -1]; + return new Position(first.offset, last.offset - first.offset + last.length); + } + return new Position(0, 0); + } + + private void fireDocumentProjectionChanged(DocumentEvent event) { + fFragmentUpdater.enableShiftMode(true); + try { + updatePositions(event); + } finally { + fFragmentUpdater.enableShiftMode(false); + } + } + + /** + * Returns parent document + * + * @return the parent document + */ + public IDocument getParentDocument() { + return fParentDocument; + } + + /** + * Returns the ranges of the parent document covered by this patch document. + * + * @return the child document's parent document range + */ + public Position[] getProjection() { + try { + return fParentDocument.getPositions(fProjectionCategory); + } catch (BadPositionCategoryException x) { + } + return null; + } + + public Position[] getFragmentation() { + try { + + Position[] fragmentation= getPositions(FRAGMENT_CATEGORY); + sortFragments(fragmentation); + return fragmentation; + + } catch (BadPositionCategoryException x) { + } + return null; + } + + /** + * Transforms a document event of the parent document into a patch document + * based document event. + * + * @param e the parent document event + * @return the child document event + */ + private SlaveDocumentEvent normalize(DocumentEvent e) { + + Position c= computeProjectionDocumentPosition(e.getOffset(), e.getLength()); + + if (c != null) { + if (c.length == 0) { + int insertLength= e.getText() == null ? 0 : e.getText().length(); + if (insertLength == 0) + return null; + } + return new SlaveDocumentEvent(this, c.offset, c.length, e.getText(), e); + } + + return null; + } + + /** + * When called this patch document is informed about a forthcoming change + * of its parent document. This child document checks whether the parent + * document change affects it and if so informs all document listeners. + * + * @param event the parent document event + */ + public void parentDocumentAboutToBeChanged(DocumentEvent event) { + fParentEvent= event; + fEvent= normalize(event); + if (fEvent != null) + delayedFireDocumentAboutToBeChanged(); + } + + /** + * 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.getOffset(), fEvent.getLength(), fEvent.getText()); + 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); + } + + + /** + * Convenience method for hiding the specified region of the document. + *
* @param document
* @param offsetInParent
* @param lengthInParent
*/ + public void hide(int offsetInParent, int lengthInParent) { + + IDocument parent= getParentDocument(); + Position[] effected= getAffectedFragments(offsetInParent, lengthInParent); + + try { + + if (effected == null) { + // populate new document with two new fragments, the left and the right of the hidden region + int end= offsetInParent + lengthInParent; + addFragment(0, offsetInParent); + addFragment(end, parent.getLength() - end); + } else if (effected.length == 1) { + // the only affected fragment must be splitted into two + Position fragment= effected[0]; + removeFragment(fragment); + addFragment(fragment.offset, offsetInParent - fragment.offset); + int secondOffset= offsetInParent + lengthInParent; + addFragment(secondOffset, fragment.offset + fragment.length - secondOffset); + } else { + // first expand and than collapse + internalShow(offsetInParent, lengthInParent, effected); + hide(offsetInParent, lengthInParent); + } + + joinFragments(); + + } catch (BadLocationException x) { + } + } + + public void show(int offsetInParent, int lengthInParent) { + + Position[] effected= getAffectedFragments(offsetInParent, lengthInParent); + if (effected == null || effected.length == 0) { + try { + addFragment(offsetInParent, lengthInParent); + joinFragments(); + } catch (BadLocationException x) { + } + return; + } + + internalShow(offsetInParent, lengthInParent, effected); + joinFragments(); + + } + + private void internalShow(int offsetInParent, int lengthInParent, Position[] effected) { + try { + + int size= effected.length; + for (int i= 0; i < size; i++) + removeFragment(effected[i]); + + int offset= Math.min(offsetInParent, effected[0].offset); + int end= Math.max(offsetInParent + lengthInParent, effected[size -1].offset + effected[size -1].length); + addFragment(offset, end - offset); + + } catch (BadLocationException x) { + } + } +}
\ No newline at end of file diff --git a/org.eclipse.text/projection/org/eclipse/jface/text/ProjectionDocumentManager.java b/org.eclipse.text/projection/org/eclipse/jface/text/ProjectionDocumentManager.java new file mode 100644 index 00000000000..a039af8c598 --- /dev/null +++ b/org.eclipse.text/projection/org/eclipse/jface/text/ProjectionDocumentManager.java @@ -0,0 +1,287 @@ +/********************************************************************** +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; + + + +/** + * 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 ProjectionDocumentManager implements IDocumentListener, ISlaveDocumentManager { + + + /** + * Name of the position categories used to keep track of the child + * documents offset ranges into the parent document. + */ + public final static String PROJECTION_DOCUMENTS= "__projectiondocuments"; //$NON-NLS-1$ + + +// /** +// * The child document partitioner uses the parent document to answer all questions. +// */ +// static class ChildPartitioner implements IDocumentPartitioner { +// +// protected ProjectionDocument 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 ProjectionDocument); +// fChildDocument= (ProjectionDocument) childDocument; +// fParentDocument= fChildDocument.getParentDocument(); +// } +// }; + + + + /** The position updater shared by all documents which have projection documents */ + private IPositionUpdater fProjectionPositionUpdater; + + private Map fRegistar= new HashMap(); + + + /** + * Returns the projection position updater. If necessary, it is dynamically created. + * + * @return the child position updater + */ + protected IPositionUpdater getProjectionPositionUpdater() { + if (fProjectionPositionUpdater == null) + fProjectionPositionUpdater= new FragmentUpdater(PROJECTION_DOCUMENTS); + return fProjectionPositionUpdater; + } + + private void add(IDocument parent, ProjectionDocument projection) { + List list= (List) fRegistar.get(parent); + if (list == null) { + list= new ArrayList(1); + fRegistar.put(parent, list); + } + list.add(projection); + } + + private void remove(IDocument parent, ProjectionDocument projection) { + List list= (List) fRegistar.get(parent); + if (list != null) { + list.remove(projection); + if (list.size() == 0) + fRegistar.remove(parent); + } + } + + private boolean hasProjection(IDocument parent) { + return (fRegistar.get(parent) instanceof List); + } + + private Iterator getProjectionsIterator(IDocument parent) { + List list= (List) fRegistar.get(parent); + if (list != null) + return list.iterator(); + return null; + } + + /** + * 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) { + IDocument parent= event.getDocument(); + Iterator e= getProjectionsIterator(parent); + if (e == null) + return; + + while (e.hasNext()) { + ProjectionDocument document= (ProjectionDocument) e.next(); + if (about) + document.parentDocumentAboutToBeChanged(event); + else + document.parentDocumentChanged(event); + } + } + + /* + * @see IDocumentListener#documentChanged(DocumentEvent) + */ + public void documentChanged(DocumentEvent event) { + fireDocumentEvent(false, event); + } + + /* + * @see IDocumentListener#documentAboutToBeChanged(DocumentEvent) + */ + public void documentAboutToBeChanged(DocumentEvent event) { + fireDocumentEvent(true, event); + } + + /* (non-Javadoc) + * @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 new CoordinatesTranslator(projectionDocument.getParentDocument(), PROJECTION_DOCUMENTS, projectionDocument, ProjectionDocument.FRAGMENT_CATEGORY); + } + return null; + } + + /* (non-Javadoc) + * @see org.eclipse.jface.text.ISlaveDocumentManager#createSlaveDocument(org.eclipse.jface.text.IDocument) + */ + public IDocument createSlaveDocument(IDocument master) { + if (!master.containsPositionCategory(PROJECTION_DOCUMENTS)) { + master.addPositionCategory(PROJECTION_DOCUMENTS); + master.addPositionUpdater(getProjectionPositionUpdater()); + master.addDocumentListener(this); + } + + ProjectionDocument slave= new ProjectionDocument(master, PROJECTION_DOCUMENTS); +// IDocumentPartitioner partitioner= new ChildPartitioner(); +// child.setDocumentPartitioner(partitioner); +// partitioner.connect(child); + + add(master, slave); + return slave; + } + + /* (non-Javadoc) + * @see org.eclipse.jface.text.ISlaveDocumentManager#freeSlaveDocument(org.eclipse.jface.text.IDocument) + */ + public void freeSlaveDocument(IDocument slave) { + + if ( !(slave instanceof ProjectionDocument)) + return; + + ProjectionDocument projectionDocument= (ProjectionDocument) slave; + +// childDocument.getDocumentPartitioner().disconnect(); + + IDocument parent= projectionDocument.getParentDocument(); + remove(parent, projectionDocument); + + try { + if (!hasProjection(parent)) { + parent.removeDocumentListener(this); + parent.removePositionUpdater(getProjectionPositionUpdater()); + parent.removePositionCategory(PROJECTION_DOCUMENTS); + } + } catch (BadPositionCategoryException x) { + } + } + + /* (non-Javadoc) + * @see org.eclipse.jface.text.ISlaveDocumentManager#getMasterDocument(org.eclipse.jface.text.IDocument) + */ + public IDocument getMasterDocument(IDocument slave) { + if (slave instanceof ProjectionDocument) + return ((ProjectionDocument) slave).getParentDocument(); + return null; + } + + /* (non-Javadoc) + * @see org.eclipse.jface.text.ISlaveDocumentManager#isSlaveDocument(org.eclipse.jface.text.IDocument) + */ + public boolean isSlaveDocument(IDocument document) { + return (document instanceof ProjectionDocument); + } +}
\ No newline at end of file diff --git a/org.eclipse.text/projection/org/eclipse/jface/text/ProjectionPosition.java b/org.eclipse.text/projection/org/eclipse/jface/text/ProjectionPosition.java new file mode 100644 index 00000000000..14b0eacdcf1 --- /dev/null +++ b/org.eclipse.text/projection/org/eclipse/jface/text/ProjectionPosition.java @@ -0,0 +1,42 @@ +/********************************************************************** +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; + +/** + * ProjectionPosition.java + */ +public class ProjectionPosition extends Position { + + private IDocument fProjectionDocument; + private Fragment fFragment; + + public ProjectionPosition(IDocument projectionDocument, int offset, int length) { + super(offset, length); + fProjectionDocument= projectionDocument; + } + + public void setFragment(Fragment fragment) { + fFragment= fragment; + } + + public Fragment getFragment() { + return fFragment; + } + +// /** +// * 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); +// } +} diff --git a/org.eclipse.text/projection/org/eclipse/jface/text/ProjectionPositionUpdater.java b/org.eclipse.text/projection/org/eclipse/jface/text/ProjectionPositionUpdater.java new file mode 100644 index 00000000000..a82cf4ce5d3 --- /dev/null +++ b/org.eclipse.text/projection/org/eclipse/jface/text/ProjectionPositionUpdater.java @@ -0,0 +1,51 @@ +package org.eclipse.jface.text; + +import org.eclipse.jface.text.DefaultPositionUpdater; + +/** + * The position updater used to adapt the positions representing + * the child document ranges to changes of the parent document. + */ + +public class ProjectionPositionUpdater extends DefaultPositionUpdater { + + /** + * Creates the position updated. + */ + protected ProjectionPositionUpdater(String category) { + super(category); + } + + /** + * 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; + } +} diff --git a/org.eclipse.text/projection/org/eclipse/jface/text/ProjectionTextStore.java b/org.eclipse.text/projection/org/eclipse/jface/text/ProjectionTextStore.java new file mode 100644 index 00000000000..467578330fc --- /dev/null +++ b/org.eclipse.text/projection/org/eclipse/jface/text/ProjectionTextStore.java @@ -0,0 +1,132 @@ +/********************************************************************** +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; + + +/** + * ProjectionTextStore.java + */ +public class ProjectionTextStore implements ITextStore { + + private ProjectionDocument fProjectionDocument; + + public ProjectionTextStore(ProjectionDocument projectionDocument) { + fProjectionDocument= projectionDocument; + } + + private int computeParentDocumentOffset(int offset) { + try { + return fProjectionDocument.toParentDocumentOffset(offset); + } catch (BadLocationException x) { + throw new RuntimeException(); + } + } + + /* + * @see ITextStore#set + */ + public void set(String contents) { + + try { + + Position[] projection= fProjectionDocument.getProjection(); + if (projection != null && projection.length > 0) { + Position first=projection[0]; + Position last= projection[projection.length -1]; + int length= last.offset - first.offset + last.length; + getParentDocument().replace(first.getOffset(), length, contents); + } else { + getParentDocument().set(contents); + } + + } catch (BadLocationException x) { + } + } + + /* + * @see ITextStore#replace + */ + public void replace(int offset, int length, String text) { + + try { + + int endoffset= length > 0 ? offset + length -1 : offset; + int o2= computeParentDocumentOffset(endoffset); + if (length > 0) + ++ o2; + + offset= computeParentDocumentOffset(offset); + length= o2 - offset; + + getParentDocument().replace(offset, length, text); + + } catch (BadLocationException x) { + // ignored as surrounding document should have handled this + } + } + + /* + * @see ITextStore#getLength + */ + public int getLength() { + Position[] projection= fProjectionDocument.getProjection(); + if (projection == null || projection.length == 0) + return 0; + + int length= 0; + for (int i= 0; i < projection.length; i++) + length += projection[i].length; + return length; + } + + /* + * @see ITextStore#get + */ + public String get(int offset, int length) { + try { + + Fragment[] fragments= fProjectionDocument.getFragmentsOfRange(offset, length); + if (fragments == null || fragments.length == 0) + return ""; + + StringBuffer buffer= new StringBuffer(); + for (int i= 0; i < fragments.length; i++) { + Position p= fragments[i].getOrigin(); + buffer.append(getParentDocument().get(p.offset, p.length)); + } + + offset -= fragments[0].offset; + return buffer.substring(offset, offset + length); + + } catch (BadLocationException x) { + } + + return null; + } + + private IDocument getParentDocument() { + return fProjectionDocument.getParentDocument(); + } + + /* + * @see ITextStore#get + */ + public char get(int offset) { + try { + int o= computeParentDocumentOffset(offset); + return getParentDocument().getChar(o); + } catch (BadLocationException x) { + } + + return (char) 0; + } +}
\ No newline at end of file diff --git a/org.eclipse.text/scripts/exportplugin.xml b/org.eclipse.text/scripts/exportplugin.xml index 870cc45dc6b..ba29a6de216 100644 --- a/org.eclipse.text/scripts/exportplugin.xml +++ b/org.eclipse.text/scripts/exportplugin.xml @@ -23,6 +23,7 @@ <copy file="plugin.properties" todir="${dest}"/> <zip zipfile="${dest}/textsrc.zip"> <fileset dir="src" /> + <fileset dir="projection"/> </zip> </target> </project> diff --git a/org.eclipse.text/src/org/eclipse/jface/text/ChildDocument.java b/org.eclipse.text/src/org/eclipse/jface/text/ChildDocument.java index 7d2185c3302..c534dd30d96 100644 --- a/org.eclipse.text/src/org/eclipse/jface/text/ChildDocument.java +++ b/org.eclipse.text/src/org/eclipse/jface/text/ChildDocument.java @@ -177,7 +177,7 @@ public final class ChildDocument extends AbstractDocument { if (offset + length > fRange.getLength()) length= fRange.getLength() - offset; - return new ChildDocumentEvent(this, offset, length, e.fText, e); + return new SlaveDocumentEvent(this, offset, length, e.fText, e); } /** diff --git a/org.eclipse.text/src/org/eclipse/jface/text/ChildDocumentEvent.java b/org.eclipse.text/src/org/eclipse/jface/text/ChildDocumentEvent.java deleted file mode 100644 index cc5e52976b5..00000000000 --- a/org.eclipse.text/src/org/eclipse/jface/text/ChildDocumentEvent.java +++ /dev/null @@ -1,39 +0,0 @@ -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 index 2b9ed9a4b19..ecf02967892 100644 --- a/org.eclipse.text/src/org/eclipse/jface/text/ChildDocumentManager.java +++ b/org.eclipse.text/src/org/eclipse/jface/text/ChildDocumentManager.java @@ -1,10 +1,15 @@ -package org.eclipse.jface.text; +/********************************************************************** +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 -/* - * (c) Copyright IBM Corp. 2000, 2001. - * All Rights Reserved. - */ +Contributors: + IBM Corporation - Initial implementation +**********************************************************************/ +package org.eclipse.jface.text; @@ -19,7 +24,7 @@ package org.eclipse.jface.text; * * For internal use only. */ -public final class ChildDocumentManager implements IDocumentListener { +public final class ChildDocumentManager implements IDocumentListener, ISlaveDocumentManager { /** @@ -210,47 +215,45 @@ public final class ChildDocumentManager implements IDocumentListener { 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 + /* (non-Javadoc) + * @see org.eclipse.jface.text.ISlaveDocumentManager#createSlaveDocument(org.eclipse.jface.text.IDocument) */ - public ChildDocument createChildDocument(IDocument parent, int offset, int length) throws BadLocationException { - - if (!parent.containsPositionCategory(CHILDDOCUMENTS)) { - parent.addPositionCategory(CHILDDOCUMENTS); - parent.addPositionUpdater(getChildPositionUpdater()); - parent.addDocumentListener(this); + public IDocument createSlaveDocument(IDocument master) { + + if (!master.containsPositionCategory(CHILDDOCUMENTS)) { + master.addPositionCategory(CHILDDOCUMENTS); + master.addPositionUpdater(getChildPositionUpdater()); + master.addDocumentListener(this); } - - ChildPosition pos= new ChildPosition(parent, offset, length); + + ChildPosition pos= new ChildPosition(master, 0, 0); try { - parent.addPosition(CHILDDOCUMENTS, pos); + master.addPosition(CHILDDOCUMENTS, pos); } catch (BadPositionCategoryException x) { // cannot happen + } catch (BadLocationException x) { + // (0, 0) is OK } - - ChildDocument child= new ChildDocument(parent, pos); + + ChildDocument child= new ChildDocument(master, 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 + /* (non-Javadoc) + * @see org.eclipse.jface.text.ISlaveDocumentManager#freeSlaveDocument(org.eclipse.jface.text.IDocument) */ - public void freeChildDocument(ChildDocument childDocument) { + public void freeSlaveDocument(IDocument slave) { + + if (! (slave instanceof ChildDocument)) + return; + + ChildDocument childDocument= (ChildDocument) slave; childDocument.getDocumentPartitioner().disconnect(); @@ -270,6 +273,31 @@ public final class ChildDocumentManager implements IDocumentListener { } } + /* (non-Javadoc) + * @see org.eclipse.jface.text.ISlaveDocumentManager#createMasterSlaveMapping(org.eclipse.jface.text.IDocument) + */ + public IDocumentInformationMapping createMasterSlaveMapping(IDocument slave) { + if (slave instanceof ChildDocument) + return new ParentChildMapping((ChildDocument) slave); + return null; + } + + /* (non-Javadoc) + * @see org.eclipse.jface.text.ISlaveDocumentManager#getMasterDocument(org.eclipse.jface.text.IDocument) + */ + public IDocument getMasterDocument(IDocument slave) { + if (slave instanceof ChildDocument) + return ((ChildDocument) slave).getParentDocument(); + return null; + } + + /* (non-Javadoc) + * @see org.eclipse.jface.text.ISlaveDocumentManager#isSlaveDocument(org.eclipse.jface.text.IDocument) + */ + public boolean isSlaveDocument(IDocument document) { + return (document instanceof ChildDocument); + } + /** * Informs all child documents of the document which issued this document event. * diff --git a/org.eclipse.text/src/org/eclipse/jface/text/IDocumentInformationMapping.java b/org.eclipse.text/src/org/eclipse/jface/text/IDocumentInformationMapping.java new file mode 100644 index 00000000000..6fead880e86 --- /dev/null +++ b/org.eclipse.text/src/org/eclipse/jface/text/IDocumentInformationMapping.java @@ -0,0 +1,27 @@ +package org.eclipse.jface.text; + + + +public interface IDocumentInformationMapping { + + IRegion getCoverage(); + + + int toOriginOffset(int imageOffset) throws BadLocationException; + + IRegion toOriginRegion(IRegion imageRegion) throws BadLocationException; + + IRegion toOriginLines(int imageLine) throws BadLocationException; + + int toOriginLine(int imageLine) throws BadLocationException; + + + + int toImageOffset(int originOffset) throws BadLocationException; + + IRegion toImageRegion(IRegion originRegion) throws BadLocationException; + + int toImageLine(int originLine) throws BadLocationException; + + int toClosestImageLine(int originLine) throws BadLocationException; +}
\ No newline at end of file diff --git a/org.eclipse.text/src/org/eclipse/jface/text/ISlaveDocumentManager.java b/org.eclipse.text/src/org/eclipse/jface/text/ISlaveDocumentManager.java new file mode 100644 index 00000000000..d74731d20af --- /dev/null +++ b/org.eclipse.text/src/org/eclipse/jface/text/ISlaveDocumentManager.java @@ -0,0 +1,57 @@ +/********************************************************************** +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; + +/** + * Slave documents are documents that use a text store which is based on a + * master document. + */ +public interface ISlaveDocumentManager { + + /** + * Creates a new slave document for the given master document. + * + * @param master + * @return IDocument + */ + IDocument createSlaveDocument(IDocument master); + + /** + * Frees the given slave document. + * + * @param slave + */ + void freeSlaveDocument(IDocument slave); + + /** + * Creates a new mapping between the given slave document and its master. + * + * @param slaveDocument + * @return IDocumentInformationMapping + */ + IDocumentInformationMapping createMasterSlaveMapping(IDocument slave); + + /** + * Returns the master of the given slave document. + * + * @param slave + * @return IDocument + */ + IDocument getMasterDocument(IDocument slave); + + /** + * Method isSlaveDocument. + * @param document + * @return boolean + */ + boolean isSlaveDocument(IDocument document); +}
\ No newline at end of file diff --git a/org.eclipse.text/src/org/eclipse/jface/text/ParentChildMapping.java b/org.eclipse.text/src/org/eclipse/jface/text/ParentChildMapping.java new file mode 100644 index 00000000000..d47e2c9c796 --- /dev/null +++ b/org.eclipse.text/src/org/eclipse/jface/text/ParentChildMapping.java @@ -0,0 +1,139 @@ +package org.eclipse.jface.text; + +/** + * ParentChildMapping.java + */ +public class ParentChildMapping implements IDocumentInformationMapping { + + private IDocument fParentDocument; + private ChildDocument fChildDocument; + + + public ParentChildMapping(ChildDocument childDocument) { + fParentDocument= childDocument.getParentDocument(); + fChildDocument= childDocument; + } + + /* (non-Javadoc) + * @see org.eclipse.jface.text.IDocumentInformationMapping#getCoverage() + */ + public IRegion getCoverage() { + Position p= fChildDocument.getParentDocumentRange(); + return new Region(p.getOffset(), p.getLength()); + } + + /* (non-Javadoc) + * @see org.eclipse.jface.text.IDocumentInformationMapping#toOriginOffset(int) + */ + public int toOriginOffset(int imageOffset) throws BadLocationException { + int anchorOffset= fChildDocument.getParentDocumentRange().getOffset(); + return anchorOffset + imageOffset; + } + + /* (non-Javadoc) + * @see org.eclipse.jface.text.IDocumentInformationMapping#toOriginRegion(org.eclipse.jface.text.IRegion) + */ + public IRegion toOriginRegion(IRegion imageRegion) throws BadLocationException { + int anchorOffset= fChildDocument.getParentDocumentRange().getOffset(); + return new Region(anchorOffset + imageRegion.getOffset(), imageRegion.getLength()); + } + + /* (non-Javadoc) + * @see org.eclipse.jface.text.IDocumentInformationMapping#toOriginLines(int) + */ + public IRegion toOriginLines(int imageLine) throws BadLocationException { + IRegion imageDocumentRegion= fChildDocument.getLineInformation(imageLine); + IRegion originDocumentRegion= toOriginRegion(imageDocumentRegion); + + int startLine= fParentDocument.getLineOfOffset(originDocumentRegion.getOffset()); + if (originDocumentRegion.getLength() == 0) + return new Region(startLine, 0); + + int endLine= fParentDocument.getLineOfOffset(originDocumentRegion.getOffset() + originDocumentRegion.getLength() -1); + return new Region(startLine, endLine - startLine); + } + + /* (non-Javadoc) + * @see org.eclipse.jface.text.IDocumentInformationMapping#toOriginLine(int) + */ + public int toOriginLine(int imageLine) throws BadLocationException { + int anchorOffset= fChildDocument.getParentDocumentRange().getOffset(); + return fParentDocument.getLineOfOffset(anchorOffset) + imageLine; + } + + /* (non-Javadoc) + * @see org.eclipse.jface.text.IDocumentInformationMapping#toImageOffset(int) + */ + public int toImageOffset(int originOffset) throws BadLocationException { + Position p= fChildDocument.getParentDocumentRange(); + if (p.includes(originOffset)) + return originOffset - p.getOffset(); + return -1; + } + + /* (non-Javadoc) + * @see org.eclipse.jface.text.IDocumentInformationMapping#toImageRegion(org.eclipse.jface.text.IRegion) + */ + public IRegion toImageRegion(IRegion originRegion) throws BadLocationException { + + int offset= originRegion.getOffset(); + int length= originRegion.getLength(); + + if (length < 0) { + length= -length; + offset -= length; + } + + Position p= fChildDocument.getParentDocumentRange(); + if (p.overlapsWith(offset, length)) { + + if (offset < p.getOffset()) + offset= p.getOffset(); + + int end= offset + length; + int e= p.getOffset() + p.getLength(); + if (end > e) + end= e; + + offset -= p.getOffset(); + end -= p.getOffset(); + + if (originRegion.getLength() < 0) + return new Region(end, offset - end); + return new Region(offset, end - offset); + } + + return null; + } + + /* (non-Javadoc) + * @see org.eclipse.jface.text.IDocumentInformationMapping#toImageLine(int) + */ + public int toImageLine(int originLine) throws BadLocationException { + int anchorOffset= fChildDocument.getParentDocumentRange().getOffset(); + int startLine= fParentDocument.getLineOfOffset(anchorOffset); + + int imageLine= originLine - startLine; + if (imageLine < 0 || imageLine > fChildDocument.getNumberOfLines()) + return -1; + return imageLine; + } + + /* (non-Javadoc) + * @see org.eclipse.jface.text.IDocumentInformationMapping#toClosestImageLine(int) + */ + public int toClosestImageLine(int originLine) throws BadLocationException { + int anchorOffset= fChildDocument.getParentDocumentRange().getOffset(); + int startLine= fParentDocument.getLineOfOffset(anchorOffset); + + int imageLine= originLine - startLine; + if (imageLine < 0) + return 0; + + int maxLine= fChildDocument.getNumberOfLines(); + if (imageLine > maxLine) + return maxLine; + + return imageLine; + } +} diff --git a/org.eclipse.text/src/org/eclipse/jface/text/SlaveDocumentEvent.java b/org.eclipse.text/src/org/eclipse/jface/text/SlaveDocumentEvent.java new file mode 100644 index 00000000000..6563524707f --- /dev/null +++ b/org.eclipse.text/src/org/eclipse/jface/text/SlaveDocumentEvent.java @@ -0,0 +1,45 @@ +/********************************************************************** +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 slave document event represents a master document event as a slave-relative + * document event. It also carries the master document event. + */ +public class SlaveDocumentEvent extends DocumentEvent { + + /** The master document event */ + private DocumentEvent fMasterEvent; + + /** + * Creates a new slave document event. + * + * @param doc the slave document + * @param offset the offset in the slave document + * @param length the length in the slave document + * @param text the substitution text + * @param masterEvent the master document event + */ + public SlaveDocumentEvent(IDocument doc, int offset, int length, String text, DocumentEvent masterEvent) { + super(doc, offset, length, text); + fMasterEvent= masterEvent; + } + + /** + * Returns this event's master event. + * + * @return this event's master event + */ + public DocumentEvent getMasterEvent() { + return fMasterEvent; + } +}
\ No newline at end of file |