diff dwtx/jface/text/projection/ProjectionMapping.d @ 129:eb30df5ca28b

Added JFace Text sources
author Frank Benoit <benoit@tionex.de>
date Sat, 23 Aug 2008 19:10:48 +0200
parents
children c4fb132a086c
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/text/projection/ProjectionMapping.d	Sat Aug 23 19:10:48 2008 +0200
@@ -0,0 +1,710 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2008 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.jface.text.projection.ProjectionMapping;
+
+import dwt.dwthelper.utils;
+
+
+import dwtx.core.runtime.Assert;
+import dwtx.jface.text.BadLocationException;
+import dwtx.jface.text.BadPositionCategoryException;
+import dwtx.jface.text.IDocument;
+import dwtx.jface.text.IDocumentInformationMapping;
+import dwtx.jface.text.IDocumentInformationMappingExtension;
+import dwtx.jface.text.IDocumentInformationMappingExtension2;
+import dwtx.jface.text.IRegion;
+import dwtx.jface.text.Position;
+import dwtx.jface.text.Region;
+
+
+/**
+ * Internal class. Do not use. Only public for testing purposes.
+ * <p>
+ * Implementation of {@link dwtx.jface.text.IDocumentInformationMapping}
+ * for the projection mapping between a master and a slave document.
+ *
+ * @since 3.0
+ * @noinstantiate This class is not intended to be instantiated by clients.
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class ProjectionMapping : IDocumentInformationMapping , IDocumentInformationMappingExtension, IDocumentInformationMappingExtension2, 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;
+    /** Cached segments */
+    private Position[] fCachedSegments;
+    /** Cached fragments */
+    private Position[] fCachedFragments;
+
+    /**
+     * 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;
+    }
+
+    /**
+     * Notifies this projection mapping that there was a projection change.
+     */
+    public void projectionChanged() {
+        fCachedSegments= null;
+        fCachedFragments= null;
+    }
+
+    private Position[] getSegments() {
+        if (fCachedSegments is null) {
+            try {
+                fCachedSegments= fSlaveDocument.getPositions(fSegmentsCategory);
+            } catch (BadPositionCategoryException e) {
+                return new Position[0];
+            }
+        }
+        return fCachedSegments;
+    }
+
+    private Position[] getFragments() {
+        if (fCachedFragments is null) {
+            try {
+                fCachedFragments= fMasterDocument.getPositions(fFragmentsCategory);
+            } catch (BadPositionCategoryException e) {
+                return new Position[0];
+            }
+        }
+        return fCachedFragments;
+    }
+
+    private int findSegmentIndex(int offset) throws BadLocationException {
+        Position[] segments= getSegments();
+        if (segments.length is 0) {
+            if (offset > 0)
+                throw new BadLocationException();
+            return -1;
+        }
+
+        try {
+            int index= fSlaveDocument.computeIndexInCategory(fSegmentsCategory, offset);
+            if (index is segments.length && offset > exclusiveEnd(segments[index-1]))
+                throw new BadLocationException();
+
+            if (index < segments.length && offset is segments[index].offset)
+                return index;
+
+            if (index > 0)
+                index--;
+
+            return index;
+
+        } catch (BadPositionCategoryException e) {
+            throw new IllegalStateException();
+        }
+    }
+
+    private Segment findSegment(int offset) throws BadLocationException {
+
+        checkImageOffset(offset);
+
+        int index= findSegmentIndex(offset);
+        if (index is -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];
+    }
+
+    /**
+     * Computes the fragment index given an origin offset. Returns the index of
+     * the fragment that contains <code>offset</code>, or <code>-1</code>
+     * if no fragment contains <code>offset</code>.
+     * <p>
+     * If <code>extensionDirection</code> is set to <code>RIGHT</code> or
+     * <code>LEFT</code>, the next fragment in that direction is returned if
+     * there is no fragment containing <code>offset</code>. Note that if
+     * <code>offset</code> occurs before any fragment and
+     * <code>extensionDirection</code> is <code>LEFT</code>,
+     * <code>-1</code> is also returned. The same applies for an offset after
+     * the last fragment and <code>extensionDirection</code> set to
+     * <code>RIGHT</code>.
+     * </p>
+     *
+     * @param offset an origin offset
+     * @param extensionDirection the direction in which to extend the search, or
+     *        <code>NONE</code>
+     * @return the index of the fragment containing <code>offset</code>, or
+     *         <code>-1</code>
+     * @throws BadLocationException if the index is not valid on the master
+     *         document
+     */
+    private int findFragmentIndex(int offset, int extensionDirection) throws BadLocationException {
+        try {
+
+            Position[] fragments= getFragments();
+            if (fragments.length is 0)
+                return -1;
+
+            int index= fMasterDocument.computeIndexInCategory(fFragmentsCategory, offset);
+
+            if (index < fragments.length && offset is fragments[index].offset)
+                return index;
+
+            if (0 < index && index <= fragments.length && fragments[index - 1].includes(offset))
+                return index - 1;
+
+            switch (extensionDirection) {
+                case LEFT:
+                    return index - 1;
+                case RIGHT:
+                    if (index < fragments.length)
+                        return index;
+            }
+
+            return -1;
+
+        } catch (BadPositionCategoryException e) {
+            throw new IllegalStateException();
+        }
+    }
+
+    private Fragment findFragment(int offset) throws BadLocationException {
+        checkOriginOffset(offset);
+
+        int index= findFragmentIndex(offset, NONE);
+        Position[] fragments= getFragments();
+        if (index is -1) {
+            if (fragments.length > 0) {
+                Fragment last= (Fragment) fragments[fragments.length - 1];
+                if (exclusiveEnd(last) is offset)
+                    return last;
+            }
+            return null;
+        }
+        return (Fragment) fragments[index];
+    }
+
+    /**
+     * Returns the image region for <code>originRegion</code>.
+     *
+     * @param originRegion the region to get the image for
+     * @param exact if <code>true</code>, the begin and end offsets of
+     *        <code>originRegion</code> must be projected, otherwise
+     *        <code>null</code> is returned. If <code>false</code>, the
+     *        begin and end range that is not visible is simply clipped.
+     * @param takeClosestImage if <code>false</code>, <code>null</code> is
+     *        returned if <code>originRegion</code> is completely invisible.
+     *        If <code>true</code>, the zero-length region is returned that
+     *        "covers" the hidden origin region
+     * @return the image region of <code>originRegion</code>
+     * @throws BadLocationException if the region is not a valid origin region
+     */
+    private IRegion toImageRegion(IRegion originRegion, bool exact, bool takeClosestImage) throws BadLocationException {
+        if (originRegion.getLength() is 0 && !takeClosestImage) {
+            int imageOffset= toImageOffset(originRegion.getOffset());
+            return imageOffset is -1 ? null : new Region(imageOffset, 0);
+        }
+
+        Fragment[] fragments= findFragments(originRegion, exact, takeClosestImage);
+        if (fragments is null) {
+            if (takeClosestImage) {
+                // originRegion may before the first or after the last fragment
+                Position[] allFragments= getFragments();
+                if (allFragments.length > 0) {
+                    // before the first
+                    if (exclusiveEnd(originRegion) <= allFragments[0].getOffset())
+                        return new Region(0, 0);
+                    // after last
+                    Position last= allFragments[allFragments.length - 1];
+                    if (originRegion.getOffset() >= exclusiveEnd(last))
+                        return new Region(exclusiveEnd(((Fragment) last).segment), 0);
+                }
+                return new Region(0, 0);
+            }
+            return null;
+        }
+
+        int imageOffset, exclusiveImageEndOffset;
+
+        // translate start offset
+        int relative= originRegion.getOffset() - fragments[0].getOffset();
+        if (relative < 0) {
+            Assert.isTrue(!exact);
+            relative= 0;
+        }
+        imageOffset= fragments[0].segment.getOffset() + relative;
+
+        // translate end offset
+        relative= exclusiveEnd(originRegion) - fragments[1].getOffset();
+        if (relative > fragments[1].getLength()) {
+            Assert.isTrue(!exact);
+            relative= fragments[1].getLength();
+        }
+        exclusiveImageEndOffset= fragments[1].segment.getOffset() + relative;
+
+        return new Region(imageOffset, exclusiveImageEndOffset - imageOffset);
+    }
+
+    /**
+     * Returns the two fragments containing the begin and end offsets of
+     * <code>originRegion</code>.
+     *
+     * @param originRegion the region to get the fragments for
+     * @param exact if <code>true</code>, only the fragments that contain the
+     *        begin and end offsets are returned; if <code>false</code>, the
+     *        first fragment after the begin offset and the last fragment before
+     *        the end offset are returned if the offsets are not projected
+     * @param takeClosestImage if <code>true</code>, the method will return
+     *        fragments also if <code>originRegion</code> completely lies in
+     *        an unprojected region.
+     * @return the two fragments containing the begin and end offset of
+     *         <code>originRegion</code>, or <code>null</code> if these do
+     *         not exist
+     * @throws BadLocationException if the region is not a valid origin region
+     */
+    private Fragment[] findFragments(IRegion originRegion, bool exact, bool takeClosestImage) throws BadLocationException {
+        Position[] fragments= getFragments();
+        if (fragments.length is 0)
+            return null;
+
+        checkOriginRegion(originRegion);
+
+        int startFragmentIdx= findFragmentIndex(originRegion.getOffset(), exact ? NONE : RIGHT);
+        if (startFragmentIdx is -1)
+            return null;
+
+        int endFragmentIdx= findFragmentIndex(inclusiveEnd(originRegion), exact ? NONE : LEFT);
+        if (!takeClosestImage && startFragmentIdx > endFragmentIdx || endFragmentIdx is -1)
+            return null;
+
+        Fragment[] result= {(Fragment) fragments[startFragmentIdx], (Fragment) fragments[endFragmentIdx]};
+        return result;
+    }
+
+    private IRegion createOriginStartRegion(Segment image, int offsetShift) {
+        return new Region(image.fragment.getOffset() + offsetShift, image.fragment.getLength() - offsetShift);
+    }
+
+    private IRegion createOriginRegion(Segment image) {
+        return new Region(image.fragment.getOffset(), image.fragment.getLength());
+    }
+
+    private IRegion createOriginEndRegion(Segment image, int lengthReduction) {
+        return new Region(image.fragment.getOffset(), image.fragment.getLength() - lengthReduction);
+    }
+
+    private IRegion createImageStartRegion(Fragment origin, int offsetShift) {
+        int shift= offsetShift > 0 ? offsetShift : 0;
+        return new Region(origin.segment.getOffset() + shift, origin.segment.getLength() - shift);
+    }
+
+    private IRegion createImageRegion(Fragment origin) {
+        return new Region(origin.segment.getOffset(), origin.segment.getLength());
+    }
+
+    private IRegion createImageEndRegion(Fragment origin, int lengthReduction) {
+        int reduction= lengthReduction > 0 ? lengthReduction : 0;
+        return new Region(origin.segment.getOffset(), origin.segment.getLength() - reduction);
+    }
+
+    private IRegion createOriginStartRegion(Fragment origin, int offsetShift) {
+        int shift= offsetShift > 0 ? offsetShift : 0;
+        return new Region(origin.getOffset() + shift, origin.getLength() - shift);
+    }
+
+    private IRegion createOriginRegion(Fragment origin) {
+        return new Region(origin.getOffset(), origin.getLength());
+    }
+
+    private IRegion createOriginEndRegion(Fragment origin, int lengthReduction) {
+        int reduction= lengthReduction > 0 ? lengthReduction : 0;
+        return new Region(origin.getOffset(), origin.getLength() - reduction);
+    }
+
+    private IRegion getIntersectingRegion(IRegion left, IRegion right) {
+        int offset= Math.max(left.getOffset(), right.getOffset());
+        int exclusiveEndOffset= Math.min(exclusiveEnd(left), exclusiveEnd(right));
+        if (exclusiveEndOffset < offset)
+            return null;
+        return new Region(offset, exclusiveEndOffset - offset);
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocumentInformationMapping#getCoverage()
+     */
+    public IRegion getCoverage() {
+        Position[] fragments= getFragments();
+        if (fragments !is null && fragments.length > 0) {
+            Position first=fragments[0];
+            Position last= fragments[fragments.length -1];
+            return  new Region(first.offset, exclusiveEnd(last) - first.offset);
+        }
+        return new Region(0, 0);
+    }
+
+    /*
+     * @see dwtx.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 dwtx.jface.text.IDocumentInformationMapping#toOriginRegion(dwtx.jface.text.IRegion)
+     */
+    public IRegion toOriginRegion(IRegion imageRegion) throws BadLocationException {
+        int imageOffset= imageRegion.getOffset();
+        int imageLength= imageRegion.getLength();
+
+        if (imageLength is 0) {
+            if (imageOffset is 0) {
+                Position[] fragments= getFragments();
+                if (fragments.length is 0 || (fragments.length is 1 && fragments[0].getOffset() is 0 && fragments[0].getLength() is 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 dwtx.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() is 0)
+            return new Region(originStartLine, 1);
+
+        int originEndLine= fMasterDocument.getLineOfOffset(inclusiveEnd(originRegion));
+        return new Region(originStartLine, (originEndLine + 1) - originStartLine);
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocumentInformationMapping#toOriginLine(int)
+     */
+    public int toOriginLine(int imageLine) throws BadLocationException {
+        IRegion lines= toOriginLines(imageLine);
+        return (lines.getLength() > 1 ? -1 : lines.getOffset());
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocumentInformationMapping#toImageOffset(int)
+     */
+    public int toImageOffset(int originOffset) throws BadLocationException {
+        Fragment fragment= findFragment(originOffset);
+        if (fragment !is null) {
+            int relative= originOffset - fragment.offset;
+            return fragment.segment.offset + relative;
+        }
+        return -1;
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocumentInformationMappingExtension#toExactImageRegion(dwtx.jface.text.IRegion)
+     */
+    public IRegion toExactImageRegion(IRegion originRegion) throws BadLocationException {
+        return toImageRegion(originRegion, true, false);
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocumentInformationMapping#toImageRegion(dwtx.jface.text.IRegion)
+     */
+    public IRegion toImageRegion(IRegion originRegion) throws BadLocationException {
+        return toImageRegion(originRegion, false, false);
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocumentInformationMappingExtension2#toClosestImageRegion(dwtx.jface.text.IRegion)
+     * @since 3.1
+     */
+    public IRegion toClosestImageRegion(IRegion originRegion) throws BadLocationException {
+        return toImageRegion(originRegion, false, true);
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocumentInformationMapping#toImageLine(int)
+     */
+    public int toImageLine(int originLine) throws BadLocationException {
+        IRegion originRegion= fMasterDocument.getLineInformation(originLine);
+        IRegion imageRegion= toImageRegion(originRegion);
+        if (imageRegion is 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() is 0)
+            return startLine;
+
+        int endLine= fSlaveDocument.getLineOfOffset(imageRegion.getOffset() + imageRegion.getLength());
+        if (endLine !is startLine)
+            throw new IllegalStateException();
+
+        return startLine;
+    }
+
+    /*
+     * @see dwtx.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 is 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() - (exclusiveEnd(left));
+                Fragment right= (Fragment) fragments[index];
+                int rightDistance= right.getOffset() - (exclusiveEnd(originLineRegion));
+
+                if (leftDistance <= rightDistance)
+                    originLine= fMasterDocument.getLineOfOffset(left.getOffset() + Math.max(left.getLength() - 1, 0));
+                else
+                    originLine= fMasterDocument.getLineOfOffset(right.getOffset());
+
+            } else if (index is 0) {
+                Fragment right= (Fragment) fragments[index];
+                originLine= fMasterDocument.getLineOfOffset(right.getOffset());
+            } else if (index is fragments.length) {
+                Fragment left= (Fragment) fragments[index - 1];
+                originLine= fMasterDocument.getLineOfOffset(exclusiveEnd(left));
+            }
+
+            return toImageLine(originLine);
+
+        } catch (BadPositionCategoryException x) {
+        }
+
+        return -1;
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocumentInformationMappingExtension#toExactOriginRegions(dwtx.jface.text.IRegion)
+     */
+    public IRegion[] toExactOriginRegions(IRegion imageRegion) throws BadLocationException {
+
+        if (imageRegion.getLength() is 0)
+            return new IRegion[] { new Region(toOriginOffset(imageRegion.getOffset()), 0) };
+
+        int endOffset= exclusiveEnd(imageRegion);
+        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= exclusiveEnd(last);
+        IRegion lastRegion= createOriginEndRegion(last, segmentEndOffset - endOffset);
+        if (resultLength > 1) {
+            // first !is last
+            result[resultLength - 1]= lastRegion;
+        } else {
+            // merge first and last
+            IRegion intersection= getIntersectingRegion(result[0], lastRegion);
+            if (intersection is null)
+                result= new IRegion[0];
+            else
+                result[0]= intersection;
+        }
+
+        return result;
+    }
+
+    /*
+     * @see dwtx.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;
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocumentInformationMappingExtension#toExactImageRegions(dwtx.jface.text.IRegion)
+     */
+    public IRegion[] toExactImageRegions(IRegion originRegion) throws BadLocationException {
+
+        int offset= originRegion.getOffset();
+        if (originRegion.getLength() is 0) {
+            int imageOffset= toImageOffset(offset);
+            return imageOffset > -1 ? new IRegion[] { new Region(imageOffset, 0) } : null;
+        }
+
+        int endOffset= exclusiveEnd(originRegion);
+        Position[] fragments= getFragments();
+        int firstIndex= findFragmentIndex(offset, RIGHT);
+        int lastIndex= findFragmentIndex(endOffset - 1, LEFT);
+
+        if (firstIndex is -1 || firstIndex > lastIndex)
+            return null;
+
+        int resultLength= lastIndex - firstIndex + 1;
+        IRegion[] result= new IRegion[resultLength];
+
+        // first
+        result[0]= createImageStartRegion((Fragment) fragments[firstIndex], offset - fragments[firstIndex].getOffset());
+        // middles
+        for (int i= 1; i < resultLength - 1; i++)
+            result[i]= createImageRegion((Fragment) fragments[firstIndex + i]);
+        // last
+        Fragment last= (Fragment) fragments[lastIndex];
+        int fragmentEndOffset= exclusiveEnd(last);
+        IRegion lastRegion= createImageEndRegion(last, fragmentEndOffset - endOffset);
+        if (resultLength > 1) {
+            // first !is last
+            result[resultLength - 1]= lastRegion;
+        } else {
+            // merge first and last
+            IRegion intersection= getIntersectingRegion(result[0], lastRegion);
+            if (intersection is null)
+                return null;
+            result[0]= intersection;
+        }
+
+        return result;
+    }
+
+    /*
+     * @see dwtx.jface.text.IDocumentInformationMappingExtension#getExactCoverage(dwtx.jface.text.IRegion)
+     */
+    public IRegion[] getExactCoverage(IRegion originRegion) throws BadLocationException {
+
+        int originOffset= originRegion.getOffset();
+        int originLength= originRegion.getLength();
+
+        if (originLength is 0) {
+            int imageOffset= toImageOffset(originOffset);
+            return imageOffset > -1 ? new IRegion[] { new Region(originOffset, 0) } : null;
+        }
+
+        int endOffset= originOffset + originLength;
+        Position[] fragments= getFragments();
+        int firstIndex= findFragmentIndex(originOffset, RIGHT);
+        int lastIndex= findFragmentIndex(endOffset - 1, LEFT);
+
+        if (firstIndex is -1 || firstIndex > lastIndex)
+            return null;
+
+        int resultLength= lastIndex - firstIndex + 1;
+        IRegion[] result= new IRegion[resultLength];
+
+        // first
+        result[0]= createOriginStartRegion((Fragment) fragments[firstIndex], originOffset - fragments[firstIndex].getOffset());
+        // middles
+        for (int i= 1; i < resultLength - 1; i++)
+            result[i]= createOriginRegion((Fragment) fragments[firstIndex + i]);
+        // last
+        Fragment last= (Fragment) fragments[lastIndex];
+        int fragmentEndOffset= exclusiveEnd(last);
+        IRegion lastRegion= createOriginEndRegion(last, fragmentEndOffset - endOffset);
+        if (resultLength > 1) {
+            // first !is last
+            result[resultLength - 1]= lastRegion;
+        } else {
+            // merge first and last
+            IRegion intersection= getIntersectingRegion(result[0], lastRegion);
+            if (intersection is null)
+                return null;
+            result[0]= intersection;
+        }
+
+        return result;
+    }
+
+    private final void checkOriginRegion(IRegion originRegion) throws BadLocationException {
+        int offset= originRegion.getOffset();
+        int endOffset= inclusiveEnd(originRegion);
+        int max= fMasterDocument.getLength();
+        if (offset < 0 || offset > max || endOffset < 0 || endOffset > max)
+            throw new BadLocationException();
+    }
+
+    private final void checkOriginOffset(int originOffset) throws BadLocationException {
+        if (originOffset < 0 || originOffset > fMasterDocument.getLength())
+            throw new BadLocationException();
+    }
+
+    private final void checkImageOffset(int imageOffset) throws BadLocationException {
+        if (imageOffset < 0 || imageOffset > getImageLength())
+            throw new BadLocationException();
+    }
+
+    private final int exclusiveEnd(Position position) {
+        return position.offset + position.length;
+    }
+
+    private final int exclusiveEnd(IRegion region) {
+        return region.getOffset() + region.getLength();
+    }
+
+    private final int inclusiveEnd(IRegion region) {
+        int length= region.getLength();
+        if (length is 0)
+            return region.getOffset();
+        return region.getOffset() + length - 1;
+    }
+
+
+}