1 11 package org.eclipse.jface.text.rules; 12 13 14 import java.util.ArrayList ; 15 import java.util.List ; 16 17 import org.eclipse.core.runtime.Assert; 18 import org.eclipse.core.runtime.Platform; 19 20 import org.eclipse.jface.text.BadLocationException; 21 import org.eclipse.jface.text.BadPositionCategoryException; 22 import org.eclipse.jface.text.DefaultPositionUpdater; 23 import org.eclipse.jface.text.DocumentEvent; 24 import org.eclipse.jface.text.DocumentRewriteSession; 25 import org.eclipse.jface.text.IDocument; 26 import org.eclipse.jface.text.IDocumentPartitioner; 27 import org.eclipse.jface.text.IDocumentPartitionerExtension; 28 import org.eclipse.jface.text.IDocumentPartitionerExtension2; 29 import org.eclipse.jface.text.IDocumentPartitionerExtension3; 30 import org.eclipse.jface.text.IRegion; 31 import org.eclipse.jface.text.ITypedRegion; 32 import org.eclipse.jface.text.Position; 33 import org.eclipse.jface.text.Region; 34 import org.eclipse.jface.text.TextUtilities; 35 import org.eclipse.jface.text.TypedPosition; 36 import org.eclipse.jface.text.TypedRegion; 37 38 39 40 58 public class FastPartitioner implements IDocumentPartitioner, IDocumentPartitionerExtension, IDocumentPartitionerExtension2, IDocumentPartitionerExtension3 { 59 60 63 private static final String CONTENT_TYPES_CATEGORY= "__content_types_category"; 65 protected final IPartitionTokenScanner fScanner; 66 67 protected final String [] fLegalContentTypes; 68 69 protected IDocument fDocument; 70 71 protected int fPreviousDocumentLength; 72 73 protected final DefaultPositionUpdater fPositionUpdater; 74 75 protected int fStartOffset; 76 77 protected int fEndOffset; 78 79 protected int fDeleteOffset; 80 83 private final String fPositionCategory; 84 87 private DocumentRewriteSession fActiveRewriteSession; 88 91 private boolean fIsInitialized= false; 92 96 private Position[] fCachedPositions= null; 97 98 private static final boolean CHECK_CACHE_CONSISTENCY= "true".equalsIgnoreCase(Platform.getDebugOption("org.eclipse.jface.text/debug/FastPartitioner/PositionCache")); 100 107 public FastPartitioner(IPartitionTokenScanner scanner, String [] legalContentTypes) { 108 fScanner= scanner; 109 fLegalContentTypes= TextUtilities.copy(legalContentTypes); 110 fPositionCategory= CONTENT_TYPES_CATEGORY + hashCode(); 111 fPositionUpdater= new DefaultPositionUpdater(fPositionCategory); 112 } 113 114 117 public String [] getManagingPositionCategories() { 118 return new String [] { fPositionCategory }; 119 } 120 121 124 public final void connect(IDocument document) { 125 connect(document, false); 126 } 127 128 134 public void connect(IDocument document, boolean delayInitialization) { 135 Assert.isNotNull(document); 136 Assert.isTrue(!document.containsPositionCategory(fPositionCategory)); 137 138 fDocument= document; 139 fDocument.addPositionCategory(fPositionCategory); 140 141 fIsInitialized= false; 142 if (!delayInitialization) 143 checkInitialization(); 144 } 145 146 149 protected final void checkInitialization() { 150 if (!fIsInitialized) 151 initialize(); 152 } 153 154 160 protected void initialize() { 161 fIsInitialized= true; 162 clearPositionCache(); 163 fScanner.setRange(fDocument, 0, fDocument.getLength()); 164 165 try { 166 IToken token= fScanner.nextToken(); 167 while (!token.isEOF()) { 168 169 String contentType= getTokenContentType(token); 170 171 if (isSupportedContentType(contentType)) { 172 TypedPosition p= new TypedPosition(fScanner.getTokenOffset(), fScanner.getTokenLength(), contentType); 173 fDocument.addPosition(fPositionCategory, p); 174 } 175 176 token= fScanner.nextToken(); 177 } 178 } catch (BadLocationException x) { 179 } catch (BadPositionCategoryException x) { 181 } 183 } 184 185 191 public void disconnect() { 192 193 Assert.isTrue(fDocument.containsPositionCategory(fPositionCategory)); 194 195 try { 196 fDocument.removePositionCategory(fPositionCategory); 197 } catch (BadPositionCategoryException x) { 198 } 200 } 201 202 208 public void documentAboutToBeChanged(DocumentEvent e) { 209 if (fIsInitialized) { 210 211 Assert.isTrue(e.getDocument() == fDocument); 212 213 fPreviousDocumentLength= e.getDocument().getLength(); 214 fStartOffset= -1; 215 fEndOffset= -1; 216 fDeleteOffset= -1; 217 } 218 } 219 220 223 public final boolean documentChanged(DocumentEvent e) { 224 if (fIsInitialized) { 225 IRegion region= documentChanged2(e); 226 return (region != null); 227 } 228 return false; 229 } 230 231 240 private void rememberRegion(int offset, int length) { 241 if (fStartOffset == -1) 243 fStartOffset= offset; 244 else if (offset < fStartOffset) 245 fStartOffset= offset; 246 247 int endOffset= offset + length; 249 if (fEndOffset == -1) 250 fEndOffset= endOffset; 251 else if (endOffset > fEndOffset) 252 fEndOffset= endOffset; 253 } 254 255 260 private void rememberDeletedOffset(int offset) { 261 fDeleteOffset= offset; 262 } 263 264 270 private IRegion createRegion() { 271 if (fDeleteOffset == -1) { 272 if (fStartOffset == -1 || fEndOffset == -1) 273 return null; 274 return new Region(fStartOffset, fEndOffset - fStartOffset); 275 } else if (fStartOffset == -1 || fEndOffset == -1) { 276 return new Region(fDeleteOffset, 0); 277 } else { 278 int offset= Math.min(fDeleteOffset, fStartOffset); 279 int endOffset= Math.max(fDeleteOffset, fEndOffset); 280 return new Region(offset, endOffset - offset); 281 } 282 } 283 284 290 public IRegion documentChanged2(DocumentEvent e) { 291 292 if (!fIsInitialized) 293 return null; 294 295 try { 296 Assert.isTrue(e.getDocument() == fDocument); 297 298 Position[] category= getPositions(); 299 IRegion line= fDocument.getLineInformationOfOffset(e.getOffset()); 300 int reparseStart= line.getOffset(); 301 int partitionStart= -1; 302 String contentType= null; 303 int newLength= e.getText() == null ? 0 : e.getText().length(); 304 305 int first= fDocument.computeIndexInCategory(fPositionCategory, reparseStart); 306 if (first > 0) { 307 TypedPosition partition= (TypedPosition) category[first - 1]; 308 if (partition.includes(reparseStart)) { 309 partitionStart= partition.getOffset(); 310 contentType= partition.getType(); 311 if (e.getOffset() == partition.getOffset() + partition.getLength()) 312 reparseStart= partitionStart; 313 -- first; 314 } else if (reparseStart == e.getOffset() && reparseStart == partition.getOffset() + partition.getLength()) { 315 partitionStart= partition.getOffset(); 316 contentType= partition.getType(); 317 reparseStart= partitionStart; 318 -- first; 319 } else { 320 partitionStart= partition.getOffset() + partition.getLength(); 321 contentType= IDocument.DEFAULT_CONTENT_TYPE; 322 } 323 } 324 325 fPositionUpdater.update(e); 326 for (int i= first; i < category.length; i++) { 327 Position p= category[i]; 328 if (p.isDeleted) { 329 rememberDeletedOffset(e.getOffset()); 330 break; 331 } 332 } 333 clearPositionCache(); 334 category= getPositions(); 335 336 fScanner.setPartialRange(fDocument, reparseStart, fDocument.getLength() - reparseStart, contentType, partitionStart); 337 338 int behindLastScannedPosition= reparseStart; 339 IToken token= fScanner.nextToken(); 340 341 while (!token.isEOF()) { 342 343 contentType= getTokenContentType(token); 344 345 if (!isSupportedContentType(contentType)) { 346 token= fScanner.nextToken(); 347 continue; 348 } 349 350 int start= fScanner.getTokenOffset(); 351 int length= fScanner.getTokenLength(); 352 353 behindLastScannedPosition= start + length; 354 int lastScannedPosition= behindLastScannedPosition - 1; 355 356 while (first < category.length) { 358 TypedPosition p= (TypedPosition) category[first]; 359 if (lastScannedPosition >= p.offset + p.length || 360 (p.overlapsWith(start, length) && 361 (!fDocument.containsPosition(fPositionCategory, start, length) || 362 !contentType.equals(p.getType())))) { 363 364 rememberRegion(p.offset, p.length); 365 fDocument.removePosition(fPositionCategory, p); 366 ++ first; 367 368 } else 369 break; 370 } 371 372 if (fDocument.containsPosition(fPositionCategory, start, length)) { 375 if (lastScannedPosition >= e.getOffset() + newLength) 376 return createRegion(); 377 ++ first; 378 } else { 379 try { 381 fDocument.addPosition(fPositionCategory, new TypedPosition(start, length, contentType)); 382 rememberRegion(start, length); 383 } catch (BadPositionCategoryException x) { 384 } catch (BadLocationException x) { 385 } 386 } 387 388 token= fScanner.nextToken(); 389 } 390 391 first= fDocument.computeIndexInCategory(fPositionCategory, behindLastScannedPosition); 392 393 clearPositionCache(); 394 category= getPositions(); 395 TypedPosition p; 396 while (first < category.length) { 397 p= (TypedPosition) category[first++]; 398 fDocument.removePosition(fPositionCategory, p); 399 rememberRegion(p.offset, p.length); 400 } 401 402 } catch (BadPositionCategoryException x) { 403 } catch (BadLocationException x) { 405 } finally { 406 clearPositionCache(); 407 } 408 409 return createRegion(); 410 } 411 412 424 protected TypedPosition findClosestPosition(int offset) { 425 426 try { 427 428 int index= fDocument.computeIndexInCategory(fPositionCategory, offset); 429 Position[] category= getPositions(); 430 431 if (category.length == 0) 432 return null; 433 434 if (index < category.length) { 435 if (offset == category[index].offset) 436 return (TypedPosition) category[index]; 437 } 438 439 if (index > 0) 440 index--; 441 442 return (TypedPosition) category[index]; 443 444 } catch (BadPositionCategoryException x) { 445 } catch (BadLocationException x) { 446 } 447 448 return null; 449 } 450 451 452 458 public String getContentType(int offset) { 459 checkInitialization(); 460 461 TypedPosition p= findClosestPosition(offset); 462 if (p != null && p.includes(offset)) 463 return p.getType(); 464 465 return IDocument.DEFAULT_CONTENT_TYPE; 466 } 467 468 474 public ITypedRegion getPartition(int offset) { 475 checkInitialization(); 476 477 try { 478 479 Position[] category = getPositions(); 480 481 if (category == null || category.length == 0) 482 return new TypedRegion(0, fDocument.getLength(), IDocument.DEFAULT_CONTENT_TYPE); 483 484 int index= fDocument.computeIndexInCategory(fPositionCategory, offset); 485 486 if (index < category.length) { 487 488 TypedPosition next= (TypedPosition) category[index]; 489 490 if (offset == next.offset) 491 return new TypedRegion(next.getOffset(), next.getLength(), next.getType()); 492 493 if (index == 0) 494 return new TypedRegion(0, next.offset, IDocument.DEFAULT_CONTENT_TYPE); 495 496 TypedPosition previous= (TypedPosition) category[index - 1]; 497 if (previous.includes(offset)) 498 return new TypedRegion(previous.getOffset(), previous.getLength(), previous.getType()); 499 500 int endOffset= previous.getOffset() + previous.getLength(); 501 return new TypedRegion(endOffset, next.getOffset() - endOffset, IDocument.DEFAULT_CONTENT_TYPE); 502 } 503 504 TypedPosition previous= (TypedPosition) category[category.length - 1]; 505 if (previous.includes(offset)) 506 return new TypedRegion(previous.getOffset(), previous.getLength(), previous.getType()); 507 508 int endOffset= previous.getOffset() + previous.getLength(); 509 return new TypedRegion(endOffset, fDocument.getLength() - endOffset, IDocument.DEFAULT_CONTENT_TYPE); 510 511 } catch (BadPositionCategoryException x) { 512 } catch (BadLocationException x) { 513 } 514 515 return new TypedRegion(0, fDocument.getLength(), IDocument.DEFAULT_CONTENT_TYPE); 516 } 517 518 521 public final ITypedRegion[] computePartitioning(int offset, int length) { 522 return computePartitioning(offset, length, false); 523 } 524 525 531 public String [] getLegalContentTypes() { 532 return TextUtilities.copy(fLegalContentTypes); 533 } 534 535 544 protected boolean isSupportedContentType(String contentType) { 545 if (contentType != null) { 546 for (int i= 0; i < fLegalContentTypes.length; i++) { 547 if (fLegalContentTypes[i].equals(contentType)) 548 return true; 549 } 550 } 551 552 return false; 553 } 554 555 566 protected String getTokenContentType(IToken token) { 567 Object data= token.getData(); 568 if (data instanceof String ) 569 return (String ) data; 570 return null; 571 } 572 573 574 575 581 public String getContentType(int offset, boolean preferOpenPartitions) { 582 return getPartition(offset, preferOpenPartitions).getType(); 583 } 584 585 591 public ITypedRegion getPartition(int offset, boolean preferOpenPartitions) { 592 ITypedRegion region= getPartition(offset); 593 if (preferOpenPartitions) { 594 if (region.getOffset() == offset && !region.getType().equals(IDocument.DEFAULT_CONTENT_TYPE)) { 595 if (offset > 0) { 596 region= getPartition(offset - 1); 597 if (region.getType().equals(IDocument.DEFAULT_CONTENT_TYPE)) 598 return region; 599 } 600 return new TypedRegion(offset, 0, IDocument.DEFAULT_CONTENT_TYPE); 601 } 602 } 603 return region; 604 } 605 606 612 public ITypedRegion[] computePartitioning(int offset, int length, boolean includeZeroLengthPartitions) { 613 checkInitialization(); 614 List list= new ArrayList (); 615 616 try { 617 618 int endOffset= offset + length; 619 620 Position[] category= getPositions(); 621 622 TypedPosition previous= null, current= null; 623 int start, end, gapOffset; 624 Position gap= new Position(0); 625 626 int startIndex= getFirstIndexEndingAfterOffset(category, offset); 627 int endIndex= getFirstIndexStartingAfterOffset(category, endOffset); 628 for (int i= startIndex; i < endIndex; i++) { 629 630 current= (TypedPosition) category[i]; 631 632 gapOffset= (previous != null) ? previous.getOffset() + previous.getLength() : 0; 633 gap.setOffset(gapOffset); 634 gap.setLength(current.getOffset() - gapOffset); 635 if ((includeZeroLengthPartitions && overlapsOrTouches(gap, offset, length)) || 636 (gap.getLength() > 0 && gap.overlapsWith(offset, length))) { 637 start= Math.max(offset, gapOffset); 638 end= Math.min(endOffset, gap.getOffset() + gap.getLength()); 639 list.add(new TypedRegion(start, end - start, IDocument.DEFAULT_CONTENT_TYPE)); 640 } 641 642 if (current.overlapsWith(offset, length)) { 643 start= Math.max(offset, current.getOffset()); 644 end= Math.min(endOffset, current.getOffset() + current.getLength()); 645 list.add(new TypedRegion(start, end - start, current.getType())); 646 } 647 648 previous= current; 649 } 650 651 if (previous != null) { 652 gapOffset= previous.getOffset() + previous.getLength(); 653 gap.setOffset(gapOffset); 654 gap.setLength(fDocument.getLength() - gapOffset); 655 if ((includeZeroLengthPartitions && overlapsOrTouches(gap, offset, length)) || 656 (gap.getLength() > 0 && gap.overlapsWith(offset, length))) { 657 start= Math.max(offset, gapOffset); 658 end= Math.min(endOffset, fDocument.getLength()); 659 list.add(new TypedRegion(start, end - start, IDocument.DEFAULT_CONTENT_TYPE)); 660 } 661 } 662 663 if (list.isEmpty()) 664 list.add(new TypedRegion(offset, length, IDocument.DEFAULT_CONTENT_TYPE)); 665 666 } catch (BadPositionCategoryException ex) { 667 clearPositionCache(); 669 } catch (RuntimeException ex) { 670 clearPositionCache(); 672 throw ex; 673 } 674 675 TypedRegion[] result= new TypedRegion[list.size()]; 676 list.toArray(result); 677 return result; 678 } 679 680 688 private boolean overlapsOrTouches(Position gap, int offset, int length) { 689 return gap.getOffset() <= offset + length && offset <= gap.getOffset() + gap.getLength(); 690 } 691 692 699 private int getFirstIndexEndingAfterOffset(Position[] positions, int offset) { 700 int i= -1, j= positions.length; 701 while (j - i > 1) { 702 int k= (i + j) >> 1; 703 Position p= positions[k]; 704 if (p.getOffset() + p.getLength() > offset) 705 j= k; 706 else 707 i= k; 708 } 709 return j; 710 } 711 712 719 private int getFirstIndexStartingAfterOffset(Position[] positions, int offset) { 720 int i= -1, j= positions.length; 721 while (j - i > 1) { 722 int k= (i + j) >> 1; 723 Position p= positions[k]; 724 if (p.getOffset() >= offset) 725 j= k; 726 else 727 i= k; 728 } 729 return j; 730 } 731 732 735 public void startRewriteSession(DocumentRewriteSession session) throws IllegalStateException { 736 if (fActiveRewriteSession != null) 737 throw new IllegalStateException (); 738 fActiveRewriteSession= session; 739 } 740 741 747 public void stopRewriteSession(DocumentRewriteSession session) { 748 if (fActiveRewriteSession == session) 749 flushRewriteSession(); 750 } 751 752 758 public DocumentRewriteSession getActiveRewriteSession() { 759 return fActiveRewriteSession; 760 } 761 762 765 protected final void flushRewriteSession() { 766 fActiveRewriteSession= null; 767 768 try { 770 fDocument.removePositionCategory(fPositionCategory); 771 } catch (BadPositionCategoryException x) { 772 } 773 fDocument.addPositionCategory(fPositionCategory); 774 775 fIsInitialized= false; 776 } 777 778 782 protected final void clearPositionCache() { 783 if (fCachedPositions != null) { 784 fCachedPositions= null; 785 } 786 } 787 788 795 protected final Position[] getPositions() throws BadPositionCategoryException { 796 if (fCachedPositions == null) { 797 fCachedPositions= fDocument.getPositions(fPositionCategory); 798 } else if (CHECK_CACHE_CONSISTENCY) { 799 Position[] positions= fDocument.getPositions(fPositionCategory); 800 int len= Math.min(positions.length, fCachedPositions.length); 801 for (int i= 0; i < len; i++) { 802 if (!positions[i].equals(fCachedPositions[i])) 803 System.err.println("FastPartitioner.getPositions(): cached position is not up to date: from document: " + toString(positions[i]) + " in cache: " + toString(fCachedPositions[i])); } 805 for (int i= len; i < positions.length; i++) 806 System.err.println("FastPartitioner.getPositions(): new position in document: " + toString(positions[i])); for (int i= len; i < fCachedPositions.length; i++) 808 System.err.println("FastPartitioner.getPositions(): stale position in cache: " + toString(fCachedPositions[i])); } 810 return fCachedPositions; 811 } 812 813 819 private String toString(Position position) { 820 return "P[" + position.getOffset() + "+" + position.getLength() + "]"; } 822 } 823 | Popular Tags |