comparison dwtx/jface/text/rules/FastPartitioner.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
comparison
equal deleted inserted replaced
128:8df1d4193877 129:eb30df5ca28b
1 /*******************************************************************************
2 * Copyright (c) 2000, 2006 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
7 *
8 * Contributors:
9 * IBM Corporation - initial API and implementation
10 * Port to the D programming language:
11 * Frank Benoit <benoit@tionex.de>
12 *******************************************************************************/
13 module dwtx.jface.text.rules.FastPartitioner;
14
15 import dwt.dwthelper.utils;
16
17
18 import java.util.ArrayList;
19 import java.util.List;
20
21 import dwtx.core.runtime.Assert;
22 import dwtx.core.runtime.Platform;
23 import dwtx.jface.text.BadLocationException;
24 import dwtx.jface.text.BadPositionCategoryException;
25 import dwtx.jface.text.DefaultPositionUpdater;
26 import dwtx.jface.text.DocumentEvent;
27 import dwtx.jface.text.DocumentRewriteSession;
28 import dwtx.jface.text.IDocument;
29 import dwtx.jface.text.IDocumentPartitioner;
30 import dwtx.jface.text.IDocumentPartitionerExtension;
31 import dwtx.jface.text.IDocumentPartitionerExtension2;
32 import dwtx.jface.text.IDocumentPartitionerExtension3;
33 import dwtx.jface.text.IRegion;
34 import dwtx.jface.text.ITypedRegion;
35 import dwtx.jface.text.Position;
36 import dwtx.jface.text.Region;
37 import dwtx.jface.text.TextUtilities;
38 import dwtx.jface.text.TypedPosition;
39 import dwtx.jface.text.TypedRegion;
40
41
42
43 /**
44 * A standard implementation of a document partitioner. It uses an
45 * {@link IPartitionTokenScanner} to scan the document and to determine the
46 * document's partitioning. The tokens returned by the scanner must return the
47 * partition type as their data. The partitioner remembers the document's
48 * partitions in the document itself rather than maintaining its own data
49 * structure.
50 * <p>
51 * To reduce array creations in {@link IDocument#getPositions(String)}, the
52 * positions get cached. The cache is cleared after updating the positions in
53 * {@link #documentChanged2(DocumentEvent)}. Subclasses need to call
54 * {@link #clearPositionCache()} after modifying the partitioner's positions.
55 * The cached positions may be accessed through {@link #getPositions()}.
56 * </p>
57 *
58 * @see IPartitionTokenScanner
59 * @since 3.1
60 */
61 public class FastPartitioner : IDocumentPartitioner, IDocumentPartitionerExtension, IDocumentPartitionerExtension2, IDocumentPartitionerExtension3 {
62
63 /**
64 * The position category this partitioner uses to store the document's partitioning information.
65 */
66 private static final String CONTENT_TYPES_CATEGORY= "__content_types_category"; //$NON-NLS-1$
67 /** The partitioner's scanner */
68 protected final IPartitionTokenScanner fScanner;
69 /** The legal content types of this partitioner */
70 protected final String[] fLegalContentTypes;
71 /** The partitioner's document */
72 protected IDocument fDocument;
73 /** The document length before a document change occurred */
74 protected int fPreviousDocumentLength;
75 /** The position updater used to for the default updating of partitions */
76 protected final DefaultPositionUpdater fPositionUpdater;
77 /** The offset at which the first changed partition starts */
78 protected int fStartOffset;
79 /** The offset at which the last changed partition ends */
80 protected int fEndOffset;
81 /**The offset at which a partition has been deleted */
82 protected int fDeleteOffset;
83 /**
84 * The position category this partitioner uses to store the document's partitioning information.
85 */
86 private final String fPositionCategory;
87 /**
88 * The active document rewrite session.
89 */
90 private DocumentRewriteSession fActiveRewriteSession;
91 /**
92 * Flag indicating whether this partitioner has been initialized.
93 */
94 private bool fIsInitialized= false;
95 /**
96 * The cached positions from our document, so we don't create a new array every time
97 * someone requests partition information.
98 */
99 private Position[] fCachedPositions= null;
100 /** Debug option for cache consistency checking. */
101 private static final bool CHECK_CACHE_CONSISTENCY= "true".equalsIgnoreCase(Platform.getDebugOption("dwtx.jface.text/debug/FastPartitioner/PositionCache")); //$NON-NLS-1$//$NON-NLS-2$;
102
103 /**
104 * Creates a new partitioner that uses the given scanner and may return
105 * partitions of the given legal content types.
106 *
107 * @param scanner the scanner this partitioner is supposed to use
108 * @param legalContentTypes the legal content types of this partitioner
109 */
110 public FastPartitioner(IPartitionTokenScanner scanner, String[] legalContentTypes) {
111 fScanner= scanner;
112 fLegalContentTypes= TextUtilities.copy(legalContentTypes);
113 fPositionCategory= CONTENT_TYPES_CATEGORY + hashCode();
114 fPositionUpdater= new DefaultPositionUpdater(fPositionCategory);
115 }
116
117 /*
118 * @see dwtx.jface.text.IDocumentPartitionerExtension2#getManagingPositionCategories()
119 */
120 public String[] getManagingPositionCategories() {
121 return new String[] { fPositionCategory };
122 }
123
124 /*
125 * @see dwtx.jface.text.IDocumentPartitioner#connect(dwtx.jface.text.IDocument)
126 */
127 public final void connect(IDocument document) {
128 connect(document, false);
129 }
130
131 /**
132 * {@inheritDoc}
133 * <p>
134 * May be extended by subclasses.
135 * </p>
136 */
137 public void connect(IDocument document, bool delayInitialization) {
138 Assert.isNotNull(document);
139 Assert.isTrue(!document.containsPositionCategory(fPositionCategory));
140
141 fDocument= document;
142 fDocument.addPositionCategory(fPositionCategory);
143
144 fIsInitialized= false;
145 if (!delayInitialization)
146 checkInitialization();
147 }
148
149 /**
150 * Calls {@link #initialize()} if the receiver is not yet initialized.
151 */
152 protected final void checkInitialization() {
153 if (!fIsInitialized)
154 initialize();
155 }
156
157 /**
158 * Performs the initial partitioning of the partitioner's document.
159 * <p>
160 * May be extended by subclasses.
161 * </p>
162 */
163 protected void initialize() {
164 fIsInitialized= true;
165 clearPositionCache();
166 fScanner.setRange(fDocument, 0, fDocument.getLength());
167
168 try {
169 IToken token= fScanner.nextToken();
170 while (!token.isEOF()) {
171
172 String contentType= getTokenContentType(token);
173
174 if (isSupportedContentType(contentType)) {
175 TypedPosition p= new TypedPosition(fScanner.getTokenOffset(), fScanner.getTokenLength(), contentType);
176 fDocument.addPosition(fPositionCategory, p);
177 }
178
179 token= fScanner.nextToken();
180 }
181 } catch (BadLocationException x) {
182 // cannot happen as offsets come from scanner
183 } catch (BadPositionCategoryException x) {
184 // cannot happen if document has been connected before
185 }
186 }
187
188 /**
189 * {@inheritDoc}
190 * <p>
191 * May be extended by subclasses.
192 * </p>
193 */
194 public void disconnect() {
195
196 Assert.isTrue(fDocument.containsPositionCategory(fPositionCategory));
197
198 try {
199 fDocument.removePositionCategory(fPositionCategory);
200 } catch (BadPositionCategoryException x) {
201 // can not happen because of Assert
202 }
203 }
204
205 /**
206 * {@inheritDoc}
207 * <p>
208 * May be extended by subclasses.
209 * </p>
210 */
211 public void documentAboutToBeChanged(DocumentEvent e) {
212 if (fIsInitialized) {
213
214 Assert.isTrue(e.getDocument() is fDocument);
215
216 fPreviousDocumentLength= e.getDocument().getLength();
217 fStartOffset= -1;
218 fEndOffset= -1;
219 fDeleteOffset= -1;
220 }
221 }
222
223 /*
224 * @see IDocumentPartitioner#documentChanged(DocumentEvent)
225 */
226 public final bool documentChanged(DocumentEvent e) {
227 if (fIsInitialized) {
228 IRegion region= documentChanged2(e);
229 return (region !is null);
230 }
231 return false;
232 }
233
234 /**
235 * Helper method for tracking the minimal region containing all partition changes.
236 * If <code>offset</code> is smaller than the remembered offset, <code>offset</code>
237 * will from now on be remembered. If <code>offset + length</code> is greater than
238 * the remembered end offset, it will be remembered from now on.
239 *
240 * @param offset the offset
241 * @param length the length
242 */
243 private void rememberRegion(int offset, int length) {
244 // remember start offset
245 if (fStartOffset is -1)
246 fStartOffset= offset;
247 else if (offset < fStartOffset)
248 fStartOffset= offset;
249
250 // remember end offset
251 int endOffset= offset + length;
252 if (fEndOffset is -1)
253 fEndOffset= endOffset;
254 else if (endOffset > fEndOffset)
255 fEndOffset= endOffset;
256 }
257
258 /**
259 * Remembers the given offset as the deletion offset.
260 *
261 * @param offset the offset
262 */
263 private void rememberDeletedOffset(int offset) {
264 fDeleteOffset= offset;
265 }
266
267 /**
268 * Creates the minimal region containing all partition changes using the
269 * remembered offset, end offset, and deletion offset.
270 *
271 * @return the minimal region containing all the partition changes
272 */
273 private IRegion createRegion() {
274 if (fDeleteOffset is -1) {
275 if (fStartOffset is -1 || fEndOffset is -1)
276 return null;
277 return new Region(fStartOffset, fEndOffset - fStartOffset);
278 } else if (fStartOffset is -1 || fEndOffset is -1) {
279 return new Region(fDeleteOffset, 0);
280 } else {
281 int offset= Math.min(fDeleteOffset, fStartOffset);
282 int endOffset= Math.max(fDeleteOffset, fEndOffset);
283 return new Region(offset, endOffset - offset);
284 }
285 }
286
287 /**
288 * {@inheritDoc}
289 * <p>
290 * May be extended by subclasses.
291 * </p>
292 */
293 public IRegion documentChanged2(DocumentEvent e) {
294
295 if (!fIsInitialized)
296 return null;
297
298 try {
299 Assert.isTrue(e.getDocument() is fDocument);
300
301 Position[] category= getPositions();
302 IRegion line= fDocument.getLineInformationOfOffset(e.getOffset());
303 int reparseStart= line.getOffset();
304 int partitionStart= -1;
305 String contentType= null;
306 int newLength= e.getText() is null ? 0 : e.getText().length();
307
308 int first= fDocument.computeIndexInCategory(fPositionCategory, reparseStart);
309 if (first > 0) {
310 TypedPosition partition= (TypedPosition) category[first - 1];
311 if (partition.includes(reparseStart)) {
312 partitionStart= partition.getOffset();
313 contentType= partition.getType();
314 if (e.getOffset() is partition.getOffset() + partition.getLength())
315 reparseStart= partitionStart;
316 -- first;
317 } else if (reparseStart is e.getOffset() && reparseStart is partition.getOffset() + partition.getLength()) {
318 partitionStart= partition.getOffset();
319 contentType= partition.getType();
320 reparseStart= partitionStart;
321 -- first;
322 } else {
323 partitionStart= partition.getOffset() + partition.getLength();
324 contentType= IDocument.DEFAULT_CONTENT_TYPE;
325 }
326 }
327
328 fPositionUpdater.update(e);
329 for (int i= first; i < category.length; i++) {
330 Position p= category[i];
331 if (p.isDeleted) {
332 rememberDeletedOffset(e.getOffset());
333 break;
334 }
335 }
336 clearPositionCache();
337 category= getPositions();
338
339 fScanner.setPartialRange(fDocument, reparseStart, fDocument.getLength() - reparseStart, contentType, partitionStart);
340
341 int behindLastScannedPosition= reparseStart;
342 IToken token= fScanner.nextToken();
343
344 while (!token.isEOF()) {
345
346 contentType= getTokenContentType(token);
347
348 if (!isSupportedContentType(contentType)) {
349 token= fScanner.nextToken();
350 continue;
351 }
352
353 int start= fScanner.getTokenOffset();
354 int length= fScanner.getTokenLength();
355
356 behindLastScannedPosition= start + length;
357 int lastScannedPosition= behindLastScannedPosition - 1;
358
359 // remove all affected positions
360 while (first < category.length) {
361 TypedPosition p= (TypedPosition) category[first];
362 if (lastScannedPosition >= p.offset + p.length ||
363 (p.overlapsWith(start, length) &&
364 (!fDocument.containsPosition(fPositionCategory, start, length) ||
365 !contentType.equals(p.getType())))) {
366
367 rememberRegion(p.offset, p.length);
368 fDocument.removePosition(fPositionCategory, p);
369 ++ first;
370
371 } else
372 break;
373 }
374
375 // if position already exists and we have scanned at least the
376 // area covered by the event, we are done
377 if (fDocument.containsPosition(fPositionCategory, start, length)) {
378 if (lastScannedPosition >= e.getOffset() + newLength)
379 return createRegion();
380 ++ first;
381 } else {
382 // insert the new type position
383 try {
384 fDocument.addPosition(fPositionCategory, new TypedPosition(start, length, contentType));
385 rememberRegion(start, length);
386 } catch (BadPositionCategoryException x) {
387 } catch (BadLocationException x) {
388 }
389 }
390
391 token= fScanner.nextToken();
392 }
393
394 first= fDocument.computeIndexInCategory(fPositionCategory, behindLastScannedPosition);
395
396 clearPositionCache();
397 category= getPositions();
398 TypedPosition p;
399 while (first < category.length) {
400 p= (TypedPosition) category[first++];
401 fDocument.removePosition(fPositionCategory, p);
402 rememberRegion(p.offset, p.length);
403 }
404
405 } catch (BadPositionCategoryException x) {
406 // should never happen on connected documents
407 } catch (BadLocationException x) {
408 } finally {
409 clearPositionCache();
410 }
411
412 return createRegion();
413 }
414
415 /**
416 * Returns the position in the partitoner's position category which is
417 * close to the given offset. This is, the position has either an offset which
418 * is the same as the given offset or an offset which is smaller than the given
419 * offset. This method profits from the knowledge that a partitioning is
420 * a ordered set of disjoint position.
421 * <p>
422 * May be extended or replaced by subclasses.
423 * </p>
424 * @param offset the offset for which to search the closest position
425 * @return the closest position in the partitioner's category
426 */
427 protected TypedPosition findClosestPosition(int offset) {
428
429 try {
430
431 int index= fDocument.computeIndexInCategory(fPositionCategory, offset);
432 Position[] category= getPositions();
433
434 if (category.length is 0)
435 return null;
436
437 if (index < category.length) {
438 if (offset is category[index].offset)
439 return (TypedPosition) category[index];
440 }
441
442 if (index > 0)
443 index--;
444
445 return (TypedPosition) category[index];
446
447 } catch (BadPositionCategoryException x) {
448 } catch (BadLocationException x) {
449 }
450
451 return null;
452 }
453
454
455 /**
456 * {@inheritDoc}
457 * <p>
458 * May be replaced or extended by subclasses.
459 * </p>
460 */
461 public String getContentType(int offset) {
462 checkInitialization();
463
464 TypedPosition p= findClosestPosition(offset);
465 if (p !is null && p.includes(offset))
466 return p.getType();
467
468 return IDocument.DEFAULT_CONTENT_TYPE;
469 }
470
471 /**
472 * {@inheritDoc}
473 * <p>
474 * May be replaced or extended by subclasses.
475 * </p>
476 */
477 public ITypedRegion getPartition(int offset) {
478 checkInitialization();
479
480 try {
481
482 Position[] category = getPositions();
483
484 if (category is null || category.length is 0)
485 return new TypedRegion(0, fDocument.getLength(), IDocument.DEFAULT_CONTENT_TYPE);
486
487 int index= fDocument.computeIndexInCategory(fPositionCategory, offset);
488
489 if (index < category.length) {
490
491 TypedPosition next= (TypedPosition) category[index];
492
493 if (offset is next.offset)
494 return new TypedRegion(next.getOffset(), next.getLength(), next.getType());
495
496 if (index is 0)
497 return new TypedRegion(0, next.offset, IDocument.DEFAULT_CONTENT_TYPE);
498
499 TypedPosition previous= (TypedPosition) category[index - 1];
500 if (previous.includes(offset))
501 return new TypedRegion(previous.getOffset(), previous.getLength(), previous.getType());
502
503 int endOffset= previous.getOffset() + previous.getLength();
504 return new TypedRegion(endOffset, next.getOffset() - endOffset, IDocument.DEFAULT_CONTENT_TYPE);
505 }
506
507 TypedPosition previous= (TypedPosition) category[category.length - 1];
508 if (previous.includes(offset))
509 return new TypedRegion(previous.getOffset(), previous.getLength(), previous.getType());
510
511 int endOffset= previous.getOffset() + previous.getLength();
512 return new TypedRegion(endOffset, fDocument.getLength() - endOffset, IDocument.DEFAULT_CONTENT_TYPE);
513
514 } catch (BadPositionCategoryException x) {
515 } catch (BadLocationException x) {
516 }
517
518 return new TypedRegion(0, fDocument.getLength(), IDocument.DEFAULT_CONTENT_TYPE);
519 }
520
521 /*
522 * @see IDocumentPartitioner#computePartitioning(int, int)
523 */
524 public final ITypedRegion[] computePartitioning(int offset, int length) {
525 return computePartitioning(offset, length, false);
526 }
527
528 /**
529 * {@inheritDoc}
530 * <p>
531 * May be replaced or extended by subclasses.
532 * </p>
533 */
534 public String[] getLegalContentTypes() {
535 return TextUtilities.copy(fLegalContentTypes);
536 }
537
538 /**
539 * Returns whether the given type is one of the legal content types.
540 * <p>
541 * May be extended by subclasses.
542 * </p>
543 *
544 * @param contentType the content type to check
545 * @return <code>true</code> if the content type is a legal content type
546 */
547 protected bool isSupportedContentType(String contentType) {
548 if (contentType !is null) {
549 for (int i= 0; i < fLegalContentTypes.length; i++) {
550 if (fLegalContentTypes[i].equals(contentType))
551 return true;
552 }
553 }
554
555 return false;
556 }
557
558 /**
559 * Returns a content type encoded in the given token. If the token's
560 * data is not <code>null</code> and a string it is assumed that
561 * it is the encoded content type.
562 * <p>
563 * May be replaced or extended by subclasses.
564 * </p>
565 *
566 * @param token the token whose content type is to be determined
567 * @return the token's content type
568 */
569 protected String getTokenContentType(IToken token) {
570 Object data= token.getData();
571 if (data instanceof String)
572 return (String) data;
573 return null;
574 }
575
576 /* zero-length partition support */
577
578 /**
579 * {@inheritDoc}
580 * <p>
581 * May be replaced or extended by subclasses.
582 * </p>
583 */
584 public String getContentType(int offset, bool preferOpenPartitions) {
585 return getPartition(offset, preferOpenPartitions).getType();
586 }
587
588 /**
589 * {@inheritDoc}
590 * <p>
591 * May be replaced or extended by subclasses.
592 * </p>
593 */
594 public ITypedRegion getPartition(int offset, bool preferOpenPartitions) {
595 ITypedRegion region= getPartition(offset);
596 if (preferOpenPartitions) {
597 if (region.getOffset() is offset && !region.getType().equals(IDocument.DEFAULT_CONTENT_TYPE)) {
598 if (offset > 0) {
599 region= getPartition(offset - 1);
600 if (region.getType().equals(IDocument.DEFAULT_CONTENT_TYPE))
601 return region;
602 }
603 return new TypedRegion(offset, 0, IDocument.DEFAULT_CONTENT_TYPE);
604 }
605 }
606 return region;
607 }
608
609 /**
610 * {@inheritDoc}
611 * <p>
612 * May be replaced or extended by subclasses.
613 * </p>
614 */
615 public ITypedRegion[] computePartitioning(int offset, int length, bool includeZeroLengthPartitions) {
616 checkInitialization();
617 List list= new ArrayList();
618
619 try {
620
621 int endOffset= offset + length;
622
623 Position[] category= getPositions();
624
625 TypedPosition previous= null, current= null;
626 int start, end, gapOffset;
627 Position gap= new Position(0);
628
629 int startIndex= getFirstIndexEndingAfterOffset(category, offset);
630 int endIndex= getFirstIndexStartingAfterOffset(category, endOffset);
631 for (int i= startIndex; i < endIndex; i++) {
632
633 current= (TypedPosition) category[i];
634
635 gapOffset= (previous !is null) ? previous.getOffset() + previous.getLength() : 0;
636 gap.setOffset(gapOffset);
637 gap.setLength(current.getOffset() - gapOffset);
638 if ((includeZeroLengthPartitions && overlapsOrTouches(gap, offset, length)) ||
639 (gap.getLength() > 0 && gap.overlapsWith(offset, length))) {
640 start= Math.max(offset, gapOffset);
641 end= Math.min(endOffset, gap.getOffset() + gap.getLength());
642 list.add(new TypedRegion(start, end - start, IDocument.DEFAULT_CONTENT_TYPE));
643 }
644
645 if (current.overlapsWith(offset, length)) {
646 start= Math.max(offset, current.getOffset());
647 end= Math.min(endOffset, current.getOffset() + current.getLength());
648 list.add(new TypedRegion(start, end - start, current.getType()));
649 }
650
651 previous= current;
652 }
653
654 if (previous !is null) {
655 gapOffset= previous.getOffset() + previous.getLength();
656 gap.setOffset(gapOffset);
657 gap.setLength(fDocument.getLength() - gapOffset);
658 if ((includeZeroLengthPartitions && overlapsOrTouches(gap, offset, length)) ||
659 (gap.getLength() > 0 && gap.overlapsWith(offset, length))) {
660 start= Math.max(offset, gapOffset);
661 end= Math.min(endOffset, fDocument.getLength());
662 list.add(new TypedRegion(start, end - start, IDocument.DEFAULT_CONTENT_TYPE));
663 }
664 }
665
666 if (list.isEmpty())
667 list.add(new TypedRegion(offset, length, IDocument.DEFAULT_CONTENT_TYPE));
668
669 } catch (BadPositionCategoryException ex) {
670 // Make sure we clear the cache
671 clearPositionCache();
672 } catch (RuntimeException ex) {
673 // Make sure we clear the cache
674 clearPositionCache();
675 throw ex;
676 }
677
678 TypedRegion[] result= new TypedRegion[list.size()];
679 list.toArray(result);
680 return result;
681 }
682
683 /**
684 * Returns <code>true</code> if the given ranges overlap with or touch each other.
685 *
686 * @param gap the first range
687 * @param offset the offset of the second range
688 * @param length the length of the second range
689 * @return <code>true</code> if the given ranges overlap with or touch each other
690 */
691 private bool overlapsOrTouches(Position gap, int offset, int length) {
692 return gap.getOffset() <= offset + length && offset <= gap.getOffset() + gap.getLength();
693 }
694
695 /**
696 * Returns the index of the first position which ends after the given offset.
697 *
698 * @param positions the positions in linear order
699 * @param offset the offset
700 * @return the index of the first position which ends after the offset
701 */
702 private int getFirstIndexEndingAfterOffset(Position[] positions, int offset) {
703 int i= -1, j= positions.length;
704 while (j - i > 1) {
705 int k= (i + j) >> 1;
706 Position p= positions[k];
707 if (p.getOffset() + p.getLength() > offset)
708 j= k;
709 else
710 i= k;
711 }
712 return j;
713 }
714
715 /**
716 * Returns the index of the first position which starts at or after the given offset.
717 *
718 * @param positions the positions in linear order
719 * @param offset the offset
720 * @return the index of the first position which starts after the offset
721 */
722 private int getFirstIndexStartingAfterOffset(Position[] positions, int offset) {
723 int i= -1, j= positions.length;
724 while (j - i > 1) {
725 int k= (i + j) >> 1;
726 Position p= positions[k];
727 if (p.getOffset() >= offset)
728 j= k;
729 else
730 i= k;
731 }
732 return j;
733 }
734
735 /*
736 * @see dwtx.jface.text.IDocumentPartitionerExtension3#startRewriteSession(dwtx.jface.text.DocumentRewriteSession)
737 */
738 public void startRewriteSession(DocumentRewriteSession session) throws IllegalStateException {
739 if (fActiveRewriteSession !is null)
740 throw new IllegalStateException();
741 fActiveRewriteSession= session;
742 }
743
744 /**
745 * {@inheritDoc}
746 * <p>
747 * May be extended by subclasses.
748 * </p>
749 */
750 public void stopRewriteSession(DocumentRewriteSession session) {
751 if (fActiveRewriteSession is session)
752 flushRewriteSession();
753 }
754
755 /**
756 * {@inheritDoc}
757 * <p>
758 * May be extended by subclasses.
759 * </p>
760 */
761 public DocumentRewriteSession getActiveRewriteSession() {
762 return fActiveRewriteSession;
763 }
764
765 /**
766 * Flushes the active rewrite session.
767 */
768 protected final void flushRewriteSession() {
769 fActiveRewriteSession= null;
770
771 // remove all position belonging to the partitioner position category
772 try {
773 fDocument.removePositionCategory(fPositionCategory);
774 } catch (BadPositionCategoryException x) {
775 }
776 fDocument.addPositionCategory(fPositionCategory);
777
778 fIsInitialized= false;
779 }
780
781 /**
782 * Clears the position cache. Needs to be called whenever the positions have
783 * been updated.
784 */
785 protected final void clearPositionCache() {
786 if (fCachedPositions !is null) {
787 fCachedPositions= null;
788 }
789 }
790
791 /**
792 * Returns the partitioners positions.
793 *
794 * @return the partitioners positions
795 * @throws BadPositionCategoryException if getting the positions from the
796 * document fails
797 */
798 protected final Position[] getPositions() throws BadPositionCategoryException {
799 if (fCachedPositions is null) {
800 fCachedPositions= fDocument.getPositions(fPositionCategory);
801 } else if (CHECK_CACHE_CONSISTENCY) {
802 Position[] positions= fDocument.getPositions(fPositionCategory);
803 int len= Math.min(positions.length, fCachedPositions.length);
804 for (int i= 0; i < len; i++) {
805 if (!positions[i].equals(fCachedPositions[i]))
806 System.err.println("FastPartitioner.getPositions(): cached position is not up to date: from document: " + toString(positions[i]) + " in cache: " + toString(fCachedPositions[i])); //$NON-NLS-1$ //$NON-NLS-2$
807 }
808 for (int i= len; i < positions.length; i++)
809 System.err.println("FastPartitioner.getPositions(): new position in document: " + toString(positions[i])); //$NON-NLS-1$
810 for (int i= len; i < fCachedPositions.length; i++)
811 System.err.println("FastPartitioner.getPositions(): stale position in cache: " + toString(fCachedPositions[i])); //$NON-NLS-1$
812 }
813 return fCachedPositions;
814 }
815
816 /**
817 * Pretty print a <code>Position</code>.
818 *
819 * @param position the position to format
820 * @return a formatted string
821 */
822 private String toString(Position position) {
823 return "P[" + position.getOffset() + "+" + position.getLength() + "]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
824 }
825 }