Mercurial > projects > dwt-addons
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 } |