001: /*******************************************************************************
002: * Copyright (c) 2000, 2006 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.jface.text.projection;
011:
012: import org.eclipse.core.runtime.Assert;
013:
014: import org.eclipse.jface.text.BadLocationException;
015: import org.eclipse.jface.text.BadPositionCategoryException;
016: import org.eclipse.jface.text.IDocument;
017: import org.eclipse.jface.text.IDocumentInformationMapping;
018: import org.eclipse.jface.text.IDocumentInformationMappingExtension;
019: import org.eclipse.jface.text.IDocumentInformationMappingExtension2;
020: import org.eclipse.jface.text.IRegion;
021: import org.eclipse.jface.text.Position;
022: import org.eclipse.jface.text.Region;
023:
024: /**
025: * Internal class. Do not use. Only public for testing purposes.
026: * <p>
027: * Implementation of {@link org.eclipse.jface.text.IDocumentInformationMapping}
028: * for the projection mapping between a master and a slave document.
029: *
030: * @since 3.0
031: */
032: public class ProjectionMapping implements IDocumentInformationMapping,
033: IDocumentInformationMappingExtension,
034: IDocumentInformationMappingExtension2, IMinimalMapping {
035:
036: private static final int LEFT = -1;
037: private static final int NONE = 0;
038: private static final int RIGHT = +1;
039:
040: /** The master document */
041: private IDocument fMasterDocument;
042: /** The position category used to manage the projection fragments inside the master document */
043: private String fFragmentsCategory;
044: /** The projection document */
045: private IDocument fSlaveDocument;
046: /** The position category to manage the projection segments inside the slave document. */
047: private String fSegmentsCategory;
048: /** Cached segments */
049: private Position[] fCachedSegments;
050: /** Cached fragments */
051: private Position[] fCachedFragments;
052:
053: /**
054: * Creates a new mapping between the given parent document and the given projection document.
055: *
056: * @param masterDocument the master document
057: * @param fragmentsCategory the position category of the parent document used to manage the projected regions
058: * @param slaveDocument the slave document
059: * @param segmentsCategory the position category of the projection document used to manage the fragments
060: */
061: public ProjectionMapping(IDocument masterDocument,
062: String fragmentsCategory, IDocument slaveDocument,
063: String segmentsCategory) {
064: fMasterDocument = masterDocument;
065: fFragmentsCategory = fragmentsCategory;
066: fSlaveDocument = slaveDocument;
067: fSegmentsCategory = segmentsCategory;
068: }
069:
070: /**
071: * Notifies this projection mapping that there was a projection change.
072: */
073: public void projectionChanged() {
074: fCachedSegments = null;
075: fCachedFragments = null;
076: }
077:
078: private Position[] getSegments() {
079: if (fCachedSegments == null) {
080: try {
081: fCachedSegments = fSlaveDocument
082: .getPositions(fSegmentsCategory);
083: } catch (BadPositionCategoryException e) {
084: return new Position[0];
085: }
086: }
087: return fCachedSegments;
088: }
089:
090: private Position[] getFragments() {
091: if (fCachedFragments == null) {
092: try {
093: fCachedFragments = fMasterDocument
094: .getPositions(fFragmentsCategory);
095: } catch (BadPositionCategoryException e) {
096: return new Position[0];
097: }
098: }
099: return fCachedFragments;
100: }
101:
102: private int findSegmentIndex(int offset)
103: throws BadLocationException {
104: Position[] segments = getSegments();
105: if (segments.length == 0) {
106: if (offset > 0)
107: throw new BadLocationException();
108: return -1;
109: }
110:
111: try {
112: int index = fSlaveDocument.computeIndexInCategory(
113: fSegmentsCategory, offset);
114: if (index == segments.length
115: && offset > exclusiveEnd(segments[index - 1]))
116: throw new BadLocationException();
117:
118: if (index < segments.length
119: && offset == segments[index].offset)
120: return index;
121:
122: if (index > 0)
123: index--;
124:
125: return index;
126:
127: } catch (BadPositionCategoryException e) {
128: throw new IllegalStateException();
129: }
130: }
131:
132: private Segment findSegment(int offset) throws BadLocationException {
133:
134: checkImageOffset(offset);
135:
136: int index = findSegmentIndex(offset);
137: if (index == -1) {
138:
139: Segment s = new Segment(0, 0);
140: Fragment f = new Fragment(0, 0);
141: s.fragment = f;
142: f.segment = s;
143: return s;
144: }
145:
146: Position[] segments = getSegments();
147: return (Segment) segments[index];
148: }
149:
150: /**
151: * Computes the fragment index given an origin offset. Returns the index of
152: * the fragment that contains <code>offset</code>, or <code>-1</code>
153: * if no fragment contains <code>offset</code>.
154: * <p>
155: * If <code>extensionDirection</code> is set to <code>RIGHT</code> or
156: * <code>LEFT</code>, the next fragment in that direction is returned if
157: * there is no fragment containing <code>offset</code>. Note that if
158: * <code>offset</code> occurs before any fragment and
159: * <code>extensionDirection</code> is <code>LEFT</code>,
160: * <code>-1</code> is also returned. The same applies for an offset after
161: * the last fragment and <code>extensionDirection</code> set to
162: * <code>RIGHT</code>.
163: * </p>
164: *
165: * @param offset an origin offset
166: * @param extensionDirection the direction in which to extend the search, or
167: * <code>NONE</code>
168: * @return the index of the fragment containing <code>offset</code>, or
169: * <code>-1</code>
170: * @throws BadLocationException if the index is not valid on the master
171: * document
172: */
173: private int findFragmentIndex(int offset, int extensionDirection)
174: throws BadLocationException {
175: try {
176:
177: Position[] fragments = getFragments();
178: if (fragments.length == 0)
179: return -1;
180:
181: int index = fMasterDocument.computeIndexInCategory(
182: fFragmentsCategory, offset);
183:
184: if (index < fragments.length
185: && offset == fragments[index].offset)
186: return index;
187:
188: if (0 < index && index <= fragments.length
189: && fragments[index - 1].includes(offset))
190: return index - 1;
191:
192: switch (extensionDirection) {
193: case LEFT:
194: return index - 1;
195: case RIGHT:
196: if (index < fragments.length)
197: return index;
198: }
199:
200: return -1;
201:
202: } catch (BadPositionCategoryException e) {
203: throw new IllegalStateException();
204: }
205: }
206:
207: private Fragment findFragment(int offset)
208: throws BadLocationException {
209: checkOriginOffset(offset);
210:
211: int index = findFragmentIndex(offset, NONE);
212: Position[] fragments = getFragments();
213: if (index == -1) {
214: if (fragments.length > 0) {
215: Fragment last = (Fragment) fragments[fragments.length - 1];
216: if (exclusiveEnd(last) == offset)
217: return last;
218: }
219: return null;
220: }
221: return (Fragment) fragments[index];
222: }
223:
224: /**
225: * Returns the image region for <code>originRegion</code>.
226: *
227: * @param originRegion the region to get the image for
228: * @param exact if <code>true</code>, the begin and end offsets of
229: * <code>originRegion</code> must be projected, otherwise
230: * <code>null</code> is returned. If <code>false</code>, the
231: * begin and end range that is not visible is simply clipped.
232: * @param takeClosestImage if <code>false</code>, <code>null</code> is
233: * returned if <code>originRegion</code> is completely invisible.
234: * If <code>true</code>, the zero-length region is returned that
235: * "covers" the hidden origin region
236: * @return the image region of <code>originRegion</code>
237: * @throws BadLocationException if the region is not a valid origin region
238: */
239: private IRegion toImageRegion(IRegion originRegion, boolean exact,
240: boolean takeClosestImage) throws BadLocationException {
241: if (originRegion.getLength() == 0 && !takeClosestImage) {
242: int imageOffset = toImageOffset(originRegion.getOffset());
243: return imageOffset == -1 ? null
244: : new Region(imageOffset, 0);
245: }
246:
247: Fragment[] fragments = findFragments(originRegion, exact,
248: takeClosestImage);
249: if (fragments == null) {
250: if (takeClosestImage) {
251: // originRegion may before the first or after the last fragment
252: Position[] allFragments = getFragments();
253: if (allFragments.length > 0) {
254: // before the first
255: if (exclusiveEnd(originRegion) <= allFragments[0]
256: .getOffset())
257: return new Region(0, 0);
258: // after last
259: Position last = allFragments[allFragments.length - 1];
260: if (originRegion.getOffset() >= exclusiveEnd(last))
261: return new Region(
262: exclusiveEnd(((Fragment) last).segment),
263: 0);
264: }
265: return new Region(0, 0);
266: }
267: return null;
268: }
269:
270: int imageOffset, exclusiveImageEndOffset;
271:
272: // translate start offset
273: int relative = originRegion.getOffset()
274: - fragments[0].getOffset();
275: if (relative < 0) {
276: Assert.isTrue(!exact);
277: relative = 0;
278: }
279: imageOffset = fragments[0].segment.getOffset() + relative;
280:
281: // translate end offset
282: relative = exclusiveEnd(originRegion)
283: - fragments[1].getOffset();
284: if (relative > fragments[1].getLength()) {
285: Assert.isTrue(!exact);
286: relative = fragments[1].getLength();
287: }
288: exclusiveImageEndOffset = fragments[1].segment.getOffset()
289: + relative;
290:
291: return new Region(imageOffset, exclusiveImageEndOffset
292: - imageOffset);
293: }
294:
295: /**
296: * Returns the two fragments containing the begin and end offsets of
297: * <code>originRegion</code>.
298: *
299: * @param originRegion the region to get the fragments for
300: * @param exact if <code>true</code>, only the fragments that contain the
301: * begin and end offsets are returned; if <code>false</code>, the
302: * first fragment after the begin offset and the last fragment before
303: * the end offset are returned if the offsets are not projected
304: * @param takeClosestImage if <code>true</code>, the method will return
305: * fragments also if <code>originRegion</code> completely lies in
306: * an unprojected region.
307: * @return the two fragments containing the begin and end offset of
308: * <code>originRegion</code>, or <code>null</code> if these do
309: * not exist
310: * @throws BadLocationException if the region is not a valid origin region
311: */
312: private Fragment[] findFragments(IRegion originRegion,
313: boolean exact, boolean takeClosestImage)
314: throws BadLocationException {
315: Position[] fragments = getFragments();
316: if (fragments.length == 0)
317: return null;
318:
319: checkOriginRegion(originRegion);
320:
321: int startFragmentIdx = findFragmentIndex(originRegion
322: .getOffset(), exact ? NONE : RIGHT);
323: if (startFragmentIdx == -1)
324: return null;
325:
326: int endFragmentIdx = findFragmentIndex(
327: inclusiveEnd(originRegion), exact ? NONE : LEFT);
328: if (!takeClosestImage && startFragmentIdx > endFragmentIdx
329: || endFragmentIdx == -1)
330: return null;
331:
332: Fragment[] result = { (Fragment) fragments[startFragmentIdx],
333: (Fragment) fragments[endFragmentIdx] };
334: return result;
335: }
336:
337: private IRegion createOriginStartRegion(Segment image,
338: int offsetShift) {
339: return new Region(image.fragment.getOffset() + offsetShift,
340: image.fragment.getLength() - offsetShift);
341: }
342:
343: private IRegion createOriginRegion(Segment image) {
344: return new Region(image.fragment.getOffset(), image.fragment
345: .getLength());
346: }
347:
348: private IRegion createOriginEndRegion(Segment image,
349: int lengthReduction) {
350: return new Region(image.fragment.getOffset(), image.fragment
351: .getLength()
352: - lengthReduction);
353: }
354:
355: private IRegion createImageStartRegion(Fragment origin,
356: int offsetShift) {
357: int shift = offsetShift > 0 ? offsetShift : 0;
358: return new Region(origin.segment.getOffset() + shift,
359: origin.segment.getLength() - shift);
360: }
361:
362: private IRegion createImageRegion(Fragment origin) {
363: return new Region(origin.segment.getOffset(), origin.segment
364: .getLength());
365: }
366:
367: private IRegion createImageEndRegion(Fragment origin,
368: int lengthReduction) {
369: int reduction = lengthReduction > 0 ? lengthReduction : 0;
370: return new Region(origin.segment.getOffset(), origin.segment
371: .getLength()
372: - reduction);
373: }
374:
375: private IRegion createOriginStartRegion(Fragment origin,
376: int offsetShift) {
377: int shift = offsetShift > 0 ? offsetShift : 0;
378: return new Region(origin.getOffset() + shift, origin
379: .getLength()
380: - shift);
381: }
382:
383: private IRegion createOriginRegion(Fragment origin) {
384: return new Region(origin.getOffset(), origin.getLength());
385: }
386:
387: private IRegion createOriginEndRegion(Fragment origin,
388: int lengthReduction) {
389: int reduction = lengthReduction > 0 ? lengthReduction : 0;
390: return new Region(origin.getOffset(), origin.getLength()
391: - reduction);
392: }
393:
394: private IRegion getIntersectingRegion(IRegion left, IRegion right) {
395: int offset = Math.max(left.getOffset(), right.getOffset());
396: int exclusiveEndOffset = Math.min(exclusiveEnd(left),
397: exclusiveEnd(right));
398: if (exclusiveEndOffset < offset)
399: return null;
400: return new Region(offset, exclusiveEndOffset - offset);
401: }
402:
403: /*
404: * @see org.eclipse.jface.text.IDocumentInformationMapping#getCoverage()
405: */
406: public IRegion getCoverage() {
407: Position[] fragments = getFragments();
408: if (fragments != null && fragments.length > 0) {
409: Position first = fragments[0];
410: Position last = fragments[fragments.length - 1];
411: return new Region(first.offset, exclusiveEnd(last)
412: - first.offset);
413: }
414: return new Region(0, 0);
415: }
416:
417: /*
418: * @see org.eclipse.jface.text.IDocumentInformationMapping#toOriginOffset(int)
419: */
420: public int toOriginOffset(int imageOffset)
421: throws BadLocationException {
422: Segment segment = findSegment(imageOffset);
423: int relative = imageOffset - segment.offset;
424: return segment.fragment.offset + relative;
425: }
426:
427: /*
428: * @see org.eclipse.jface.text.IDocumentInformationMapping#toOriginRegion(org.eclipse.jface.text.IRegion)
429: */
430: public IRegion toOriginRegion(IRegion imageRegion)
431: throws BadLocationException {
432: int imageOffset = imageRegion.getOffset();
433: int imageLength = imageRegion.getLength();
434:
435: if (imageLength == 0) {
436: if (imageOffset == 0) {
437: Position[] fragments = getFragments();
438: if (fragments.length == 0
439: || (fragments.length == 1
440: && fragments[0].getOffset() == 0 && fragments[0]
441: .getLength() == 0))
442: return new Region(0, fMasterDocument.getLength());
443: }
444: return new Region(toOriginOffset(imageOffset), 0);
445: }
446:
447: int originOffset = toOriginOffset(imageOffset);
448: int inclusiveImageEndOffset = imageOffset + imageLength - 1;
449: int inclusiveOriginEndOffset = toOriginOffset(inclusiveImageEndOffset);
450:
451: return new Region(originOffset, (inclusiveOriginEndOffset + 1)
452: - originOffset);
453: }
454:
455: /*
456: * @see org.eclipse.jface.text.IDocumentInformationMapping#toOriginLines(int)
457: */
458: public IRegion toOriginLines(int imageLine)
459: throws BadLocationException {
460: IRegion imageRegion = fSlaveDocument
461: .getLineInformation(imageLine);
462: IRegion originRegion = toOriginRegion(imageRegion);
463:
464: int originStartLine = fMasterDocument
465: .getLineOfOffset(originRegion.getOffset());
466: if (originRegion.getLength() == 0)
467: return new Region(originStartLine, 1);
468:
469: int originEndLine = fMasterDocument
470: .getLineOfOffset(inclusiveEnd(originRegion));
471: return new Region(originStartLine, (originEndLine + 1)
472: - originStartLine);
473: }
474:
475: /*
476: * @see org.eclipse.jface.text.IDocumentInformationMapping#toOriginLine(int)
477: */
478: public int toOriginLine(int imageLine) throws BadLocationException {
479: IRegion lines = toOriginLines(imageLine);
480: return (lines.getLength() > 1 ? -1 : lines.getOffset());
481: }
482:
483: /*
484: * @see org.eclipse.jface.text.IDocumentInformationMapping#toImageOffset(int)
485: */
486: public int toImageOffset(int originOffset)
487: throws BadLocationException {
488: Fragment fragment = findFragment(originOffset);
489: if (fragment != null) {
490: int relative = originOffset - fragment.offset;
491: return fragment.segment.offset + relative;
492: }
493: return -1;
494: }
495:
496: /*
497: * @see org.eclipse.jface.text.IDocumentInformationMappingExtension#toExactImageRegion(org.eclipse.jface.text.IRegion)
498: */
499: public IRegion toExactImageRegion(IRegion originRegion)
500: throws BadLocationException {
501: return toImageRegion(originRegion, true, false);
502: }
503:
504: /*
505: * @see org.eclipse.jface.text.IDocumentInformationMapping#toImageRegion(org.eclipse.jface.text.IRegion)
506: */
507: public IRegion toImageRegion(IRegion originRegion)
508: throws BadLocationException {
509: return toImageRegion(originRegion, false, false);
510: }
511:
512: /*
513: * @see org.eclipse.jface.text.IDocumentInformationMappingExtension2#toClosestImageRegion(org.eclipse.jface.text.IRegion)
514: * @since 3.1
515: */
516: public IRegion toClosestImageRegion(IRegion originRegion)
517: throws BadLocationException {
518: return toImageRegion(originRegion, false, true);
519: }
520:
521: /*
522: * @see org.eclipse.jface.text.IDocumentInformationMapping#toImageLine(int)
523: */
524: public int toImageLine(int originLine) throws BadLocationException {
525: IRegion originRegion = fMasterDocument
526: .getLineInformation(originLine);
527: IRegion imageRegion = toImageRegion(originRegion);
528: if (imageRegion == null) {
529: int imageOffset = toImageOffset(originRegion.getOffset());
530: if (imageOffset > -1)
531: imageRegion = new Region(imageOffset, 0);
532: else
533: return -1;
534: }
535:
536: int startLine = fSlaveDocument.getLineOfOffset(imageRegion
537: .getOffset());
538: if (imageRegion.getLength() == 0)
539: return startLine;
540:
541: int endLine = fSlaveDocument.getLineOfOffset(imageRegion
542: .getOffset()
543: + imageRegion.getLength());
544: if (endLine != startLine)
545: throw new IllegalStateException();
546:
547: return startLine;
548: }
549:
550: /*
551: * @see org.eclipse.jface.text.IDocumentInformationMapping#toClosestImageLine(int)
552: */
553: public int toClosestImageLine(int originLine)
554: throws BadLocationException {
555: try {
556:
557: int imageLine = toImageLine(originLine);
558: if (imageLine > -1)
559: return imageLine;
560:
561: Position[] fragments = getFragments();
562: if (fragments.length == 0)
563: return -1;
564:
565: IRegion originLineRegion = fMasterDocument
566: .getLineInformation(originLine);
567: int index = fMasterDocument.computeIndexInCategory(
568: fFragmentsCategory, originLineRegion.getOffset());
569:
570: if (0 < index && index < fragments.length) {
571: Fragment left = (Fragment) fragments[index - 1];
572: int leftDistance = originLineRegion.getOffset()
573: - (exclusiveEnd(left));
574: Fragment right = (Fragment) fragments[index];
575: int rightDistance = right.getOffset()
576: - (exclusiveEnd(originLineRegion));
577:
578: if (leftDistance <= rightDistance)
579: originLine = fMasterDocument.getLineOfOffset(left
580: .getOffset()
581: + Math.max(left.getLength() - 1, 0));
582: else
583: originLine = fMasterDocument.getLineOfOffset(right
584: .getOffset());
585:
586: } else if (index == 0) {
587: Fragment right = (Fragment) fragments[index];
588: originLine = fMasterDocument.getLineOfOffset(right
589: .getOffset());
590: } else if (index == fragments.length) {
591: Fragment left = (Fragment) fragments[index - 1];
592: originLine = fMasterDocument
593: .getLineOfOffset(exclusiveEnd(left));
594: }
595:
596: return toImageLine(originLine);
597:
598: } catch (BadPositionCategoryException x) {
599: }
600:
601: return -1;
602: }
603:
604: /*
605: * @see org.eclipse.jface.text.IDocumentInformationMappingExtension#toExactOriginRegions(org.eclipse.jface.text.IRegion)
606: */
607: public IRegion[] toExactOriginRegions(IRegion imageRegion)
608: throws BadLocationException {
609:
610: if (imageRegion.getLength() == 0)
611: return new IRegion[] { new Region(
612: toOriginOffset(imageRegion.getOffset()), 0) };
613:
614: int endOffset = exclusiveEnd(imageRegion);
615: Position[] segments = getSegments();
616: int firstIndex = findSegmentIndex(imageRegion.getOffset());
617: int lastIndex = findSegmentIndex(endOffset - 1);
618:
619: int resultLength = lastIndex - firstIndex + 1;
620: IRegion[] result = new IRegion[resultLength];
621:
622: // first
623: result[0] = createOriginStartRegion(
624: (Segment) segments[firstIndex], imageRegion.getOffset()
625: - segments[firstIndex].getOffset());
626: // middles
627: for (int i = 1; i < resultLength - 1; i++)
628: result[i] = createOriginRegion((Segment) segments[firstIndex
629: + i]);
630: // last
631: Segment last = (Segment) segments[lastIndex];
632: int segmentEndOffset = exclusiveEnd(last);
633: IRegion lastRegion = createOriginEndRegion(last,
634: segmentEndOffset - endOffset);
635: if (resultLength > 1) {
636: // first != last
637: result[resultLength - 1] = lastRegion;
638: } else {
639: // merge first and last
640: IRegion intersection = getIntersectingRegion(result[0],
641: lastRegion);
642: if (intersection == null)
643: result = new IRegion[0];
644: else
645: result[0] = intersection;
646: }
647:
648: return result;
649: }
650:
651: /*
652: * @see org.eclipse.jface.text.IDocumentInformationMappingExtension#getImageLength()
653: */
654: public int getImageLength() {
655: Position[] segments = getSegments();
656: int length = 0;
657: for (int i = 0; i < segments.length; i++)
658: length += segments[i].length;
659: return length;
660: }
661:
662: /*
663: * @see org.eclipse.jface.text.IDocumentInformationMappingExtension#toExactImageRegions(org.eclipse.jface.text.IRegion)
664: */
665: public IRegion[] toExactImageRegions(IRegion originRegion)
666: throws BadLocationException {
667:
668: int offset = originRegion.getOffset();
669: if (originRegion.getLength() == 0) {
670: int imageOffset = toImageOffset(offset);
671: return imageOffset > -1 ? new IRegion[] { new Region(
672: imageOffset, 0) } : null;
673: }
674:
675: int endOffset = exclusiveEnd(originRegion);
676: Position[] fragments = getFragments();
677: int firstIndex = findFragmentIndex(offset, RIGHT);
678: int lastIndex = findFragmentIndex(endOffset - 1, LEFT);
679:
680: if (firstIndex == -1 || firstIndex > lastIndex)
681: return null;
682:
683: int resultLength = lastIndex - firstIndex + 1;
684: IRegion[] result = new IRegion[resultLength];
685:
686: // first
687: result[0] = createImageStartRegion(
688: (Fragment) fragments[firstIndex], offset
689: - fragments[firstIndex].getOffset());
690: // middles
691: for (int i = 1; i < resultLength - 1; i++)
692: result[i] = createImageRegion((Fragment) fragments[firstIndex
693: + i]);
694: // last
695: Fragment last = (Fragment) fragments[lastIndex];
696: int fragmentEndOffset = exclusiveEnd(last);
697: IRegion lastRegion = createImageEndRegion(last,
698: fragmentEndOffset - endOffset);
699: if (resultLength > 1) {
700: // first != last
701: result[resultLength - 1] = lastRegion;
702: } else {
703: // merge first and last
704: IRegion intersection = getIntersectingRegion(result[0],
705: lastRegion);
706: if (intersection == null)
707: return null;
708: result[0] = intersection;
709: }
710:
711: return result;
712: }
713:
714: /*
715: * @see org.eclipse.jface.text.IDocumentInformationMappingExtension#getExactCoverage(org.eclipse.jface.text.IRegion)
716: */
717: public IRegion[] getExactCoverage(IRegion originRegion)
718: throws BadLocationException {
719:
720: int originOffset = originRegion.getOffset();
721: int originLength = originRegion.getLength();
722:
723: if (originLength == 0) {
724: int imageOffset = toImageOffset(originOffset);
725: return imageOffset > -1 ? new IRegion[] { new Region(
726: originOffset, 0) } : null;
727: }
728:
729: int endOffset = originOffset + originLength;
730: Position[] fragments = getFragments();
731: int firstIndex = findFragmentIndex(originOffset, RIGHT);
732: int lastIndex = findFragmentIndex(endOffset - 1, LEFT);
733:
734: if (firstIndex == -1 || firstIndex > lastIndex)
735: return null;
736:
737: int resultLength = lastIndex - firstIndex + 1;
738: IRegion[] result = new IRegion[resultLength];
739:
740: // first
741: result[0] = createOriginStartRegion(
742: (Fragment) fragments[firstIndex], originOffset
743: - fragments[firstIndex].getOffset());
744: // middles
745: for (int i = 1; i < resultLength - 1; i++)
746: result[i] = createOriginRegion((Fragment) fragments[firstIndex
747: + i]);
748: // last
749: Fragment last = (Fragment) fragments[lastIndex];
750: int fragmentEndOffset = exclusiveEnd(last);
751: IRegion lastRegion = createOriginEndRegion(last,
752: fragmentEndOffset - endOffset);
753: if (resultLength > 1) {
754: // first != last
755: result[resultLength - 1] = lastRegion;
756: } else {
757: // merge first and last
758: IRegion intersection = getIntersectingRegion(result[0],
759: lastRegion);
760: if (intersection == null)
761: return null;
762: result[0] = intersection;
763: }
764:
765: return result;
766: }
767:
768: private final void checkOriginRegion(IRegion originRegion)
769: throws BadLocationException {
770: int offset = originRegion.getOffset();
771: int endOffset = inclusiveEnd(originRegion);
772: int max = fMasterDocument.getLength();
773: if (offset < 0 || offset > max || endOffset < 0
774: || endOffset > max)
775: throw new BadLocationException();
776: }
777:
778: private final void checkOriginOffset(int originOffset)
779: throws BadLocationException {
780: if (originOffset < 0
781: || originOffset > fMasterDocument.getLength())
782: throw new BadLocationException();
783: }
784:
785: private final void checkImageOffset(int imageOffset)
786: throws BadLocationException {
787: if (imageOffset < 0 || imageOffset > getImageLength())
788: throw new BadLocationException();
789: }
790:
791: private final int exclusiveEnd(Position position) {
792: return position.offset + position.length;
793: }
794:
795: private final int exclusiveEnd(IRegion region) {
796: return region.getOffset() + region.getLength();
797: }
798:
799: private final int inclusiveEnd(IRegion region) {
800: int length = region.getLength();
801: if (length == 0)
802: return region.getOffset();
803: return region.getOffset() + length - 1;
804: }
805:
806: }
|