1 11 package org.eclipse.ui.internal.texteditor.quickdiff; 12 13 import java.util.ArrayList ; 14 import java.util.ConcurrentModificationException ; 15 import java.util.Iterator ; 16 import java.util.LinkedList ; 17 import java.util.List ; 18 import java.util.ListIterator ; 19 20 import org.eclipse.core.runtime.Assert; 21 import org.eclipse.core.runtime.CoreException; 22 import org.eclipse.core.runtime.IProgressMonitor; 23 import org.eclipse.core.runtime.IStatus; 24 import org.eclipse.core.runtime.OperationCanceledException; 25 import org.eclipse.core.runtime.Platform; 26 import org.eclipse.core.runtime.Status; 27 import org.eclipse.core.runtime.jobs.Job; 28 29 import org.eclipse.jface.text.BadLocationException; 30 import org.eclipse.jface.text.Document; 31 import org.eclipse.jface.text.DocumentEvent; 32 import org.eclipse.jface.text.DocumentRewriteSessionEvent; 33 import org.eclipse.jface.text.DocumentRewriteSessionType; 34 import org.eclipse.jface.text.IDocument; 35 import org.eclipse.jface.text.IDocumentExtension4; 36 import org.eclipse.jface.text.IDocumentListener; 37 import org.eclipse.jface.text.IDocumentRewriteSessionListener; 38 import org.eclipse.jface.text.IRegion; 39 import org.eclipse.jface.text.ISynchronizable; 40 import org.eclipse.jface.text.Position; 41 import org.eclipse.jface.text.source.Annotation; 42 import org.eclipse.jface.text.source.AnnotationModelEvent; 43 import org.eclipse.jface.text.source.IAnnotationModel; 44 import org.eclipse.jface.text.source.IAnnotationModelListener; 45 import org.eclipse.jface.text.source.IAnnotationModelListenerExtension; 46 import org.eclipse.jface.text.source.ILineDiffInfo; 47 import org.eclipse.jface.text.source.ILineDiffer; 48 import org.eclipse.jface.text.source.ILineDifferExtension; 49 import org.eclipse.jface.text.source.ILineDifferExtension2; 50 import org.eclipse.jface.text.source.ILineRange; 51 import org.eclipse.jface.text.source.LineRange; 52 53 import org.eclipse.ui.internal.texteditor.NLSUtility; 54 import org.eclipse.ui.internal.texteditor.TextEditorPlugin; 55 import org.eclipse.ui.internal.texteditor.quickdiff.compare.equivalence.DJBHashFunction; 56 import org.eclipse.ui.internal.texteditor.quickdiff.compare.equivalence.DocEquivalenceComparator; 57 import org.eclipse.ui.internal.texteditor.quickdiff.compare.equivalence.DocumentEquivalenceClass; 58 import org.eclipse.ui.internal.texteditor.quickdiff.compare.equivalence.IHashFunction; 59 import org.eclipse.ui.internal.texteditor.quickdiff.compare.rangedifferencer.IRangeComparator; 60 import org.eclipse.ui.internal.texteditor.quickdiff.compare.rangedifferencer.RangeDifference; 61 import org.eclipse.ui.internal.texteditor.quickdiff.compare.rangedifferencer.RangeDifferencer; 62 import org.eclipse.ui.progress.IProgressConstants; 63 import org.eclipse.ui.texteditor.quickdiff.IQuickDiffReferenceProvider; 64 65 80 public class DocumentLineDiffer implements ILineDiffer, IDocumentListener, IAnnotationModel, ILineDifferExtension, ILineDifferExtension2 { 81 82 85 private static class LineChangeInfo implements ILineDiffInfo { 86 87 private static final String [] ORIGINAL_TEXT= new String [] { "\n" }; 89 92 public int getRemovedLinesBelow() { 93 return 0; 94 } 95 96 99 public int getRemovedLinesAbove() { 100 return 0; 101 } 102 103 106 public int getChangeType() { 107 return CHANGED; 108 } 109 110 113 public boolean hasChanges() { 114 return true; 115 } 116 117 120 public String [] getOriginalText() { 121 return ORIGINAL_TEXT; 122 } 123 } 124 125 126 private static boolean DEBUG= "true".equalsIgnoreCase(Platform.getDebugOption("org.eclipse.ui.workbench.texteditor/debug/DocumentLineDiffer")); 128 129 private static final int INITIALIZE_DELAY= 500; 130 131 132 private static final int SUSPENDED= 0; 133 134 private static final int INITIALIZING= 1; 135 136 private static final int SYNCHRONIZED= 2; 137 138 139 private int fState= SUSPENDED; 140 141 private final ILineDiffInfo fLineChangeInfo= new LineChangeInfo(); 142 143 144 IQuickDiffReferenceProvider fReferenceProvider; 145 146 private int fOpenConnections; 147 148 private IDocument fLeftDocument; 149 153 private DocumentEquivalenceClass fLeftEquivalent; 154 155 private IDocument fRightDocument; 156 160 private DocumentEquivalenceClass fRightEquivalent; 161 165 private boolean fUpdateNeeded; 166 167 private List fAnnotationModelListeners= new ArrayList (); 168 169 private Job fInitializationJob; 170 171 private List fStoredEvents= new ArrayList (); 172 176 private List fDifferences= new ArrayList (); 177 181 private List fRemoved= new ArrayList (); 182 186 private List fAdded= new ArrayList (); 187 191 private List fChanged= new ArrayList (); 192 193 private int fFirstLine; 194 195 private int fNLines; 196 197 private RangeDifference fLastDifference; 198 202 private boolean fIgnoreDocumentEvents= true; 203 207 private final IDocumentRewriteSessionListener fSessionListener= new IDocumentRewriteSessionListener() { 208 public void documentRewriteSessionChanged(DocumentRewriteSessionEvent event) { 209 if (event.getSession().getSessionType() == DocumentRewriteSessionType.UNRESTRICTED_SMALL) 210 return; 211 if (DocumentRewriteSessionEvent.SESSION_START.equals(event.getChangeType())) 212 suspend(); 213 else if (DocumentRewriteSessionEvent.SESSION_STOP.equals(event.getChangeType())) 214 resume(); 215 } 216 }; 217 218 private Thread fThread; 219 private DocumentEvent fLastUIEvent; 220 221 222 225 public DocumentLineDiffer() { 226 } 227 228 229 230 233 public ILineDiffInfo getLineInfo(int line) { 234 235 if (isSuspended()) 236 return fLineChangeInfo; 237 238 RangeDifference last= fLastDifference; 240 if (last != null && last.rightStart() <= line && last.rightEnd() > line) 241 return new DiffRegion(last, line - last.rightStart(), fDifferences, fLeftDocument); 242 243 fLastDifference= getRangeDifferenceForRightLine(line); 244 last= fLastDifference; 245 if (last != null) 246 return new DiffRegion(last, line - last.rightStart(), fDifferences, fLeftDocument); 247 248 return null; 249 } 250 251 254 public synchronized void revertLine(int line) throws BadLocationException { 255 if (!isInitialized()) 256 throw new BadLocationException(QuickDiffMessages.quickdiff_nonsynchronized); 257 258 DiffRegion region= (DiffRegion) getLineInfo(line); 259 if (region == null || fRightDocument == null || fLeftDocument == null) 260 return; 261 262 RangeDifference diff= region.getDifference(); 263 int rOffset= fRightDocument.getLineOffset(line); 264 int rLength= fRightDocument.getLineLength(line); 265 int leftLine= diff.leftStart() + region.getOffset(); 266 String replacement; 267 if (leftLine >= diff.leftEnd()) replacement= ""; else { 270 int lOffset= fLeftDocument.getLineOffset(leftLine); 271 int lLength= fLeftDocument.getLineLength(leftLine); 272 replacement= fLeftDocument.get(lOffset, lLength); 273 } 274 fRightDocument.replace(rOffset, rLength, replacement); 275 } 276 277 280 public synchronized void revertBlock(int line) throws BadLocationException { 281 if (!isInitialized()) 282 throw new BadLocationException(QuickDiffMessages.quickdiff_nonsynchronized); 283 284 DiffRegion region= (DiffRegion) getLineInfo(line); 285 if (region == null || fRightDocument == null || fLeftDocument == null) 286 return; 287 288 RangeDifference diff= region.getDifference(); 289 int rOffset= fRightDocument.getLineOffset(diff.rightStart()); 290 int rLength= fRightDocument.getLineOffset(diff.rightEnd() - 1) + fRightDocument.getLineLength(diff.rightEnd() - 1) - rOffset; 291 int lOffset= fLeftDocument.getLineOffset(diff.leftStart()); 292 int lLength= fLeftDocument.getLineOffset(diff.leftEnd() - 1) + fLeftDocument.getLineLength(diff.leftEnd() - 1) - lOffset; 293 fRightDocument.replace(rOffset, rLength, fLeftDocument.get(lOffset, lLength)); 294 } 295 296 299 public synchronized void revertSelection(int line, int nLines) throws BadLocationException { 300 if (!isInitialized()) 301 throw new BadLocationException(QuickDiffMessages.quickdiff_nonsynchronized); 302 303 if (fRightDocument == null || fLeftDocument == null) 304 return; 305 306 int rOffset= -1, rLength= -1, lOffset= -1, lLength= -1; 307 RangeDifference diff= null; 308 final List differences= fDifferences; 309 synchronized (differences) { 310 Iterator it= differences.iterator(); 311 312 while (it.hasNext()) { 314 diff= (RangeDifference) it.next(); 315 if (line < diff.rightEnd()) { 316 rOffset= fRightDocument.getLineOffset(line); 317 int leftLine= Math.min(diff.leftStart() + line - diff.rightStart(), diff.leftEnd() - 1); 318 lOffset= fLeftDocument.getLineOffset(leftLine); 319 break; 320 } 321 } 322 323 if (rOffset == -1 || lOffset == -1) 324 return; 325 326 int to= line + nLines - 1; 328 while (it.hasNext()) { 329 diff= (RangeDifference) it.next(); 330 if (to < diff.rightEnd()) { 331 int rEndOffset= fRightDocument.getLineOffset(to) + fRightDocument.getLineLength(to); 332 rLength= rEndOffset - rOffset; 333 int leftLine= Math.min(diff.leftStart() + to - diff.rightStart(), diff.leftEnd() - 1); 334 int lEndOffset= fLeftDocument.getLineOffset(leftLine) + fLeftDocument.getLineLength(leftLine); 335 lLength= lEndOffset - lOffset; 336 break; 337 } 338 } 339 } 340 341 if (rLength == -1 || lLength == -1) 342 return; 343 344 fRightDocument.replace(rOffset, rLength, fLeftDocument.get(lOffset, lLength)); 345 } 346 347 350 public synchronized int restoreAfterLine(int line) throws BadLocationException { 351 if (!isInitialized()) 352 throw new BadLocationException(QuickDiffMessages.quickdiff_nonsynchronized); 353 354 DiffRegion region= (DiffRegion) getLineInfo(line); 355 if (region == null || fRightDocument == null || fLeftDocument == null) 356 return 0; 357 358 if (region.getRemovedLinesBelow() < 1) 359 return 0; 360 361 RangeDifference diff= null; 362 final List differences= fDifferences; 363 synchronized (differences) { 364 for (Iterator it= differences.iterator(); it.hasNext();) { 365 diff= (RangeDifference) it.next(); 366 if (line >= diff.rightStart() && line < diff.rightEnd()) { 367 if (diff.kind() == RangeDifference.NOCHANGE && it.hasNext()) 368 diff= (RangeDifference) it.next(); 369 break; 370 } 371 } 372 } 373 374 if (diff == null) 375 return 0; 376 377 int rOffset= fRightDocument.getLineOffset(diff.rightEnd()); 378 int rLength= 0; 379 int leftLine= diff.leftStart() + diff.rightLength(); 380 int lOffset= fLeftDocument.getLineOffset(leftLine); 381 int lLength= fLeftDocument.getLineOffset(diff.leftEnd() - 1) + fLeftDocument.getLineLength(diff.leftEnd() - 1) - lOffset; 382 fRightDocument.replace(rOffset, rLength, fLeftDocument.get(lOffset, lLength)); 383 384 return diff.leftLength() - diff.rightLength(); 385 } 386 387 392 private boolean isInitialized() { 393 return fState == SYNCHRONIZED; 394 } 395 396 401 public synchronized boolean isSynchronized() { 402 return fState == SYNCHRONIZED; 403 } 404 405 410 public synchronized boolean isSuspended() { 411 return fState == SUSPENDED; 412 } 413 414 420 public void setReferenceProvider(IQuickDiffReferenceProvider provider) { 421 Assert.isNotNull(provider); 422 if (provider != fReferenceProvider) { 423 if (fReferenceProvider != null) 424 fReferenceProvider.dispose(); 425 fReferenceProvider= provider; 426 initialize(); 427 } 428 } 429 430 435 public IQuickDiffReferenceProvider getReferenceProvider() { 436 return fReferenceProvider; 437 } 438 439 444 protected synchronized void initialize() { 445 fState= INITIALIZING; 447 448 if (fRightDocument == null) 449 return; 450 451 fIgnoreDocumentEvents= true; 453 454 if (fLeftDocument != null) { 455 fLeftDocument.removeDocumentListener(this); 456 fLeftDocument= null; 457 fLeftEquivalent= null; 458 } 459 460 final Job oldJob= fInitializationJob; 463 if (oldJob != null) { 464 if (oldJob.getState() == Job.WAITING) { 466 oldJob.wakeUp(INITIALIZE_DELAY); 467 return; 468 } 469 oldJob.cancel(); 470 } 471 472 fInitializationJob= new Job(QuickDiffMessages.quickdiff_initialize) { 473 474 479 public IStatus run(IProgressMonitor monitor) { 480 481 if (oldJob != null) 484 try { 485 oldJob.join(); 486 } catch (InterruptedException e) { 487 Assert.isTrue(false); 489 } 490 491 492 IQuickDiffReferenceProvider provider= fReferenceProvider; 494 final IDocument left; 495 try { 496 left= provider == null ? null : provider.getReference(monitor); 497 } catch (CoreException e) { 498 synchronized (DocumentLineDiffer.this) { 499 if (isCanceled(monitor)) 500 return Status.CANCEL_STATUS; 501 502 clearModel(); 503 fireModelChanged(); 504 return e.getStatus(); 505 } 506 } catch (OperationCanceledException e) { 507 return Status.CANCEL_STATUS; 508 } 509 510 515 IDocument right= fRightDocument; IDocument actual= null; IDocument reference= null; 519 synchronized (DocumentLineDiffer.this) { 520 if (left == null || right == null) { 522 if (isCanceled(monitor)) 523 return Status.CANCEL_STATUS; 524 525 clearModel(); 526 fireModelChanged(); 527 return Status.OK_STATUS; 528 } 529 530 fLeftDocument= left; 532 fIgnoreDocumentEvents= false; 534 } 535 536 left.addDocumentListener(DocumentLineDiffer.this); 539 540 reference= createCopy(left); 543 if (reference == null) 544 return Status.CANCEL_STATUS; 545 546 548 Object lock= null; 549 if (right instanceof ISynchronizable) 550 lock= ((ISynchronizable) right).getLockObject(); 551 552 if (lock != null) { 553 synchronized (lock) { 556 synchronized (DocumentLineDiffer.this) { 557 if (isCanceled(monitor)) 558 return Status.CANCEL_STATUS; 559 fStoredEvents.clear(); 560 actual= createUnprotectedCopy(right); 561 } 562 } 563 } else { 564 int i= 0; 571 do { 572 if (i++ == 100) 574 return new Status(IStatus.ERROR, TextEditorPlugin.PLUGIN_ID, IStatus.OK, NLSUtility.format(QuickDiffMessages.quickdiff_error_getting_document_content, new Object [] {left.getClass(), right.getClass()}), null); 575 576 synchronized (DocumentLineDiffer.this) { 577 if (isCanceled(monitor)) 578 return Status.CANCEL_STATUS; 579 580 fStoredEvents.clear(); 581 } 582 583 actual= createCopy(right); 586 587 synchronized (DocumentLineDiffer.this) { 588 if (isCanceled(monitor)) 589 return Status.CANCEL_STATUS; 590 if (fStoredEvents.size() == 0 && actual != null) 591 break; 592 } 593 } while (true); 594 } 595 596 IHashFunction hash= new DJBHashFunction(); 597 DocumentEquivalenceClass leftEquivalent= new DocumentEquivalenceClass(reference, hash); 598 fLeftEquivalent= leftEquivalent; 599 IRangeComparator ref= new DocEquivalenceComparator(leftEquivalent, null); 600 601 DocumentEquivalenceClass rightEquivalent= new DocumentEquivalenceClass(actual, hash); 602 fRightEquivalent= rightEquivalent; 603 IRangeComparator act= new DocEquivalenceComparator(rightEquivalent, null); 604 List diffs= RangeDifferencer.findRanges(monitor, ref, act); 605 synchronized (DocumentLineDiffer.this) { 608 if (isCanceled(monitor)) 609 return Status.CANCEL_STATUS; 610 611 fDifferences= diffs; 613 } 614 615 try { 617 do { 618 DocumentEvent event; 619 synchronized (DocumentLineDiffer.this) { 620 if (isCanceled(monitor)) 621 return Status.CANCEL_STATUS; 622 623 if (fStoredEvents.isEmpty()) { 624 fInitializationJob= null; 626 fState= SYNCHRONIZED; 627 fLastDifference= null; 628 629 leftEquivalent.setDocument(left); 631 rightEquivalent.setDocument(right); 632 633 break; 634 } 635 636 event= (DocumentEvent) fStoredEvents.remove(0); 637 } 638 639 IDocument copy= null; 641 if (event.fDocument == right) 642 copy= actual; 643 else if (event.fDocument == left) 644 copy= reference; 645 else 646 Assert.isTrue(false); 647 648 event= new DocumentEvent(copy, event.fOffset, event.fLength, event.fText); 651 handleAboutToBeChanged(event); 652 653 actual.replace(event.fOffset, event.fLength, event.fText); 655 656 handleChanged(event); 657 658 } while (true); 659 660 } catch (BadLocationException e) { 661 left.removeDocumentListener(DocumentLineDiffer.this); 662 clearModel(); 663 initialize(); 664 return Status.CANCEL_STATUS; 665 } 666 667 fireModelChanged(); 668 return Status.OK_STATUS; 669 } 670 671 private boolean isCanceled(IProgressMonitor monitor) { 672 return fInitializationJob != this || monitor != null && monitor.isCanceled(); 673 } 674 675 private void clearModel() { 676 synchronized (DocumentLineDiffer.this) { 677 fLeftDocument= null; 678 fLeftEquivalent= null; 679 fInitializationJob= null; 680 fStoredEvents.clear(); 681 fLastDifference= null; 682 fDifferences.clear(); 683 } 684 } 685 686 696 private IDocument createCopy(IDocument document) { 697 Assert.isNotNull(document); 698 try { 700 return createUnprotectedCopy(document); 701 } catch (NullPointerException e) { 702 } catch (ArrayStoreException e) { 703 } catch (IndexOutOfBoundsException e) { 704 } catch (ConcurrentModificationException e) { 705 } catch (NegativeArraySizeException e) { 706 } 707 return null; 708 } 709 710 private IDocument createUnprotectedCopy(IDocument document) { 711 return new Document(document.get()); 712 } 713 }; 714 715 fInitializationJob.setSystem(true); 716 fInitializationJob.setPriority(Job.DECORATE); 717 fInitializationJob.setProperty(IProgressConstants.NO_IMMEDIATE_ERROR_PROMPT_PROPERTY, Boolean.TRUE); 718 fInitializationJob.schedule(INITIALIZE_DELAY); 719 } 720 721 722 723 726 public synchronized void documentAboutToBeChanged(DocumentEvent event) { 727 if (fIgnoreDocumentEvents) 728 return; 729 730 if (event.getDocument() == fLeftDocument) { initialize(); 732 return; 733 } 734 735 if (!isInitialized()) { 737 if (fInitializationJob != null) 738 fStoredEvents.add(event); 739 return; 740 } 741 742 fLastUIEvent= event; 743 try { 744 handleAboutToBeChanged(event); 745 } catch (BadLocationException e) { 746 reinitOnError(e); 747 return; 748 } catch (NullPointerException e) { 749 reinitOnError(e); 750 return; 751 } catch (ArrayStoreException e) { 752 reinitOnError(e); 753 return; 754 } catch (IndexOutOfBoundsException e) { 755 reinitOnError(e); 756 return; 757 } catch (ConcurrentModificationException e) { 758 reinitOnError(e); 759 return; 760 } catch (NegativeArraySizeException e) { 761 reinitOnError(e); 762 return; 763 } 764 } 765 766 767 774 void handleAboutToBeChanged(DocumentEvent event) throws BadLocationException { 775 Assert.isTrue(fThread == null); 776 fThread= Thread.currentThread(); 777 778 IDocument doc= event.getDocument(); 779 DocumentEquivalenceClass rightEquivalent= fRightEquivalent; 780 781 if (doc == null || rightEquivalent == null) 782 return; 783 784 fFirstLine= doc.getLineOfOffset(event.getOffset()); fNLines= doc.getLineOfOffset(event.getOffset() + event.getLength()) - fFirstLine + 1; 787 rightEquivalent.update(event); 788 } 789 790 793 public synchronized void documentChanged(DocumentEvent event) { 794 final Thread lastCurrentThread= fThread; 795 fThread= null; 796 797 if (fIgnoreDocumentEvents) 798 return; 799 800 if (event.getDocument() == fLeftDocument) { initialize(); 805 return; 806 } 807 808 if (event != fLastUIEvent) { 809 fLastUIEvent= null; 810 return; 811 } 812 fLastUIEvent= null; 813 814 if (!isInitialized()) 815 return; 816 817 try { 818 fThread= lastCurrentThread; 819 handleChanged(event); 820 } catch (BadLocationException e) { 821 reinitOnError(e); 822 return; 823 } catch (NullPointerException e) { 824 reinitOnError(e); 825 return; 826 } catch (ArrayStoreException e) { 827 reinitOnError(e); 828 return; 829 } catch (IndexOutOfBoundsException e) { 830 reinitOnError(e); 831 return; 832 } catch (ConcurrentModificationException e) { 833 reinitOnError(e); 834 return; 835 } catch (NegativeArraySizeException e) { 836 reinitOnError(e); 837 return; 838 } 839 840 if (fUpdateNeeded) { 842 AnnotationModelEvent ame= new AnnotationModelEvent(this, false); 843 for (Iterator it= fAdded.iterator(); it.hasNext(); ) { 844 RangeDifference rd= (RangeDifference) it.next(); 845 ame.annotationAdded(rd.getDiffRegion(fDifferences, fLeftDocument)); 846 } 847 for (Iterator it= fRemoved.iterator(); it.hasNext(); ) { 848 RangeDifference rd= (RangeDifference) it.next(); 849 ame.annotationRemoved(rd.getDiffRegion(fDifferences, fLeftDocument)); 850 } 851 for (Iterator it= fChanged.iterator(); it.hasNext(); ) { 852 RangeDifference rd= (RangeDifference) it.next(); 853 ame.annotationChanged(rd.getDiffRegion(fDifferences, fLeftDocument)); 854 } 855 fireModelChanged(ame); 856 fUpdateNeeded= false; 857 } 858 } 859 860 866 private void reinitOnError(Exception e) { 867 if (DEBUG) 868 System.err.println("reinitializing quickdiff:\n" + e.getLocalizedMessage() + "\n" + e.getStackTrace()); initialize(); 870 } 871 872 878 void handleChanged(DocumentEvent event) throws BadLocationException { 879 Assert.isTrue(fThread == Thread.currentThread()); 880 fThread= null; 881 882 IDocument left= fLeftDocument; 884 DocumentEquivalenceClass leftEquivalent= fLeftEquivalent; 885 DocumentEquivalenceClass rightEquivalent= fRightEquivalent; 886 if (left == null || leftEquivalent == null || rightEquivalent == null) 887 return; 888 889 IDocument right= event.getDocument(); IDocument modified= event.getDocument(); 892 if (modified != left && modified != right) 893 Assert.isTrue(false); 894 895 boolean leftToRight= modified == left; 896 897 String insertion= event.getText(); 898 int added= insertion == null ? 1 : modified.computeNumberOfLines(insertion) + 1; 899 901 if (added > 50 || fNLines > 50) { 903 initialize(); 904 return; 905 } 906 907 int size= Math.max(fNLines, added) + 1; 908 int lineDelta= added - fNLines; 909 int lastLine= fFirstLine + fNLines - 1; 910 911 int repetitionField; 912 if (leftToRight) { 913 int originalLine= getRightLine(lastLine + 1); 914 repetitionField= searchForRepetitionField(size - 1, right, originalLine); 915 } else { 916 int originalLine= getLeftLine(lastLine + 1); 917 repetitionField= searchForRepetitionField(size - 1, left, originalLine); 918 } 919 lastLine += repetitionField; 920 921 922 final RangeDifference consistentBefore, consistentAfter; 925 if (leftToRight) { 926 consistentBefore= findConsistentRangeBeforeLeft(fFirstLine, size); 927 consistentAfter= findConsistentRangeAfterLeft(lastLine, size); 928 } else { 929 consistentBefore= findConsistentRangeBeforeRight(fFirstLine, size); 930 consistentAfter= findConsistentRangeAfterRight(lastLine, size); 931 } 932 933 int shiftBefore= 0; 936 if (consistentBefore.kind() == RangeDifference.NOCHANGE) { 937 int unchanged; 938 if (leftToRight) 939 unchanged= Math.min(fFirstLine, consistentBefore.leftEnd()) - consistentBefore.leftStart(); 940 else 941 unchanged= Math.min(fFirstLine, consistentBefore.rightEnd()) - consistentBefore.rightStart(); 942 943 shiftBefore= Math.max(0, unchanged - size); 944 } 945 946 int shiftAfter= 0; 947 if (consistentAfter.kind() == RangeDifference.NOCHANGE) { 948 int unchanged; 949 if (leftToRight) 950 unchanged= consistentAfter.leftEnd() - Math.max(lastLine + 1, consistentAfter.leftStart()); 951 else 952 unchanged= consistentAfter.rightEnd() - Math.max(lastLine + 1, consistentAfter.rightStart()); 953 954 shiftAfter= Math.max(0, unchanged - size); 955 } 956 957 int leftStartLine= consistentBefore.leftStart() + shiftBefore; 961 int leftLine= consistentAfter.leftEnd(); 962 if (leftToRight) 963 leftLine += lineDelta; 964 int leftEndLine= leftLine - shiftAfter; 965 ILineRange leftRange= new LineRange(leftStartLine, leftEndLine - leftStartLine); 966 IRangeComparator reference= new DocEquivalenceComparator(leftEquivalent, leftRange); 967 968 int rightStartLine= consistentBefore.rightStart() + shiftBefore; 970 int rightLine= consistentAfter.rightEnd(); 971 if (!leftToRight) 972 rightLine += lineDelta; 973 int rightEndLine= rightLine - shiftAfter; 974 ILineRange rightRange= new LineRange(rightStartLine, rightEndLine - rightStartLine); 975 IRangeComparator change= new DocEquivalenceComparator(rightEquivalent, rightRange); 976 977 if (leftLine - shiftAfter - leftStartLine > 50 || rightLine - shiftAfter - rightStartLine > 50) { 979 initialize(); 980 return; 981 } 982 983 987 List diffs= RangeDifferencer.findRanges(reference, change); 989 if (diffs.size() == 0) { 990 diffs.add(new RangeDifference(RangeDifference.CHANGE, 0, 0, 0, 0)); 991 } 992 993 994 for (Iterator it= diffs.iterator(); it.hasNext();) { 996 RangeDifference d= (RangeDifference) it.next(); 997 d.shiftLeft(leftStartLine); 998 d.shiftRight(rightStartLine); 999 } 1000 1001 if (shiftBefore > 0) { 1003 RangeDifference first= (RangeDifference) diffs.get(0); 1004 if (first.kind() == RangeDifference.NOCHANGE) 1005 first.extendStart(-shiftBefore); 1006 else 1007 diffs.add(0, new RangeDifference(RangeDifference.NOCHANGE, first.rightStart() - shiftBefore, shiftBefore, first.leftStart() - shiftBefore, shiftBefore)); 1008 } 1009 1010 RangeDifference last= (RangeDifference) diffs.get(diffs.size() - 1); 1011 if (shiftAfter > 0) { 1012 if (last.kind() == RangeDifference.NOCHANGE) 1013 last.extendEnd(shiftAfter); 1014 else 1015 diffs.add(new RangeDifference(RangeDifference.NOCHANGE, last.rightEnd(), shiftAfter , last.leftEnd(), shiftAfter)); 1016 } 1017 1018 synchronized (fDifferences) { 1020 final ListIterator it= fDifferences.listIterator(); 1021 Iterator newIt= diffs.iterator(); 1022 RangeDifference current; 1023 boolean changed= false; 1024 1025 1027 do { 1029 Assert.isTrue(it.hasNext()); 1030 current= (RangeDifference) it.next(); 1031 } while (current != consistentBefore); 1032 Assert.isTrue(current == consistentBefore); 1033 1034 fChanged.clear(); 1035 fRemoved.clear(); 1036 fAdded.clear(); 1037 1038 while (current != consistentAfter) { 1040 if (newIt.hasNext()) { 1041 Object o= newIt.next(); 1042 if (!current.equals(o)) { 1043 fRemoved.add(current); 1044 fAdded.add(o); 1045 changed= true; 1046 it.set(o); 1047 } 1048 } else { 1049 fRemoved.add(current); 1050 it.remove(); 1051 changed= true; 1052 } 1053 Assert.isTrue(it.hasNext()); 1054 current= (RangeDifference) it.next(); 1055 } 1056 1057 Assert.isTrue(current == consistentAfter); 1059 if (newIt.hasNext()) { 1060 Object o= newIt.next(); 1061 if (!current.equals(o)) { 1062 fRemoved.add(current); 1063 fAdded.add(o); 1064 changed= true; 1065 it.set(o); 1066 } 1067 } else { 1068 fRemoved.add(current); 1069 it.remove(); 1070 changed= true; 1071 } 1072 1073 while (newIt.hasNext()) { 1075 Object next= newIt.next(); 1076 fAdded.add(next); 1077 it.add(next); 1078 changed= true; 1079 } 1080 1081 boolean init= true; 1083 int leftShift= 0; 1084 int rightShift= 0; 1085 while (it.hasNext()) { 1086 current= (RangeDifference) it.next(); 1087 if (init) { 1088 init= false; 1089 leftShift= last.leftEnd() - current.leftStart(); 1090 rightShift= last.rightEnd() - current.rightStart(); 1091 if (leftShift != 0 || rightShift != 0) 1092 changed= true; 1093 else 1094 break; 1095 } 1096 current.shiftLeft(leftShift); 1098 current.shiftRight(rightShift); 1099 } 1100 1101 fUpdateNeeded= changed; 1102 } 1103 1104 fLastDifference= null; 1105 } 1106 1107 1114 private RangeDifference findConsistentRangeBeforeLeft(int line, int size) { 1115 RangeDifference found= null; 1116 1117 for (ListIterator it= fDifferences.listIterator(); it.hasNext();) { 1118 RangeDifference difference= (RangeDifference) it.next(); 1119 if (found == null || difference.kind() == RangeDifference.NOCHANGE 1120 && (difference.leftEnd() < line && difference.leftLength() >= size 1121 || difference.leftEnd() >= line && line - difference.leftStart() >= size)) 1122 found= difference; 1123 1124 if (difference.leftEnd() >= line) 1125 break; 1126 } 1127 1128 return found; 1129 } 1130 1131 1138 private RangeDifference findConsistentRangeAfterLeft(int line, int size) { 1139 RangeDifference found= null; 1140 1141 for (ListIterator it= fDifferences.listIterator(fDifferences.size()); it.hasPrevious();) { 1142 RangeDifference difference= (RangeDifference) it.previous(); 1143 if (found == null || difference.kind() == RangeDifference.NOCHANGE 1144 && (difference.leftStart() > line && difference.leftLength() >= size 1145 || difference.leftStart() <= line && difference.leftEnd() - line >= size)) 1146 found= difference; 1147 1148 if (difference.leftStart() <= line) 1149 break; 1150 1151 } 1152 1153 return found; 1154 } 1155 1156 1163 private RangeDifference findConsistentRangeBeforeRight(int line, int size) { 1164 RangeDifference found= null; 1165 1166 int unchanged= -1; for (ListIterator it= fDifferences.listIterator(); it.hasNext();) { 1168 RangeDifference difference= (RangeDifference) it.next(); 1169 if (found == null) 1170 found= difference; 1171 else if (difference.kind() == RangeDifference.NOCHANGE) { 1172 unchanged= Math.min(line, difference.rightEnd()) - difference.rightStart(); 1173 if (unchanged >= size) 1174 found= difference; 1175 } 1176 1177 if (difference.rightEnd() >= line) 1178 break; 1179 } 1180 1181 return found; 1182 } 1183 1184 1191 private RangeDifference findConsistentRangeAfterRight(int line, int size) { 1192 RangeDifference found= null; 1193 1194 int unchanged= -1; for (ListIterator it= fDifferences.listIterator(fDifferences.size()); it.hasPrevious();) { 1196 RangeDifference difference= (RangeDifference) it.previous(); 1197 if (found == null) 1198 found= difference; 1199 else if (difference.kind() == RangeDifference.NOCHANGE) { 1200 unchanged= difference.rightEnd() - Math.max(line + 1, difference.rightStart()); if (unchanged >= size) 1202 found= difference; 1203 } 1204 1205 if (difference.rightStart() <= line) 1206 break; 1207 } 1208 1209 return found; 1210 } 1211 1212 1221 private int searchForRepetitionField(int size, IDocument doc, int line) throws BadLocationException { 1222 1245 1246 1255 LinkedList window= new LinkedList (); 1256 int nLines= doc.getNumberOfLines(); 1257 int repetition= line - 1; 1258 int l= line; 1259 1260 while (l >= 0 && l < nLines) { 1261 IRegion r= doc.getLineInformation(l); 1262 String current= doc.get(r.getOffset(), r.getLength()); 1263 1264 if (!window.isEmpty() && window.get(0).equals(current)) { 1265 window.removeFirst(); 1267 window.addLast(current); 1268 repetition= l; 1269 } else { 1270 if (window.size() < size) 1273 window.addLast(current); 1274 else 1275 break; 1276 } 1277 1278 l++; 1279 } 1280 1281 int fieldLength= repetition - line + 1; 1282 Assert.isTrue(fieldLength >= 0); 1283 return fieldLength; 1284 } 1285 1286 1292 private int getLeftLine(int rightLine) { 1293 RangeDifference d= getRangeDifferenceForRightLine(rightLine); 1294 if (d == null) 1295 return -1; 1296 return Math.min(d.leftEnd() - 1, d.leftStart() + rightLine - d.rightStart()); 1297 } 1298 1299 1305 private int getRightLine(int leftLine) { 1306 RangeDifference d= getRangeDifferenceForLeftLine(leftLine); 1307 if (d == null) 1308 return -1; 1309 return Math.min(d.rightEnd() - 1, d.rightStart() + leftLine - d.leftStart()); 1310 } 1311 1312 1318 private RangeDifference getRangeDifferenceForLeftLine(int leftLine) { 1319 for (Iterator it= fDifferences.iterator(); it.hasNext();) { 1320 RangeDifference d= (RangeDifference) it.next(); 1321 if (leftLine >= d.leftStart() && leftLine < d.leftEnd()) { 1322 return d; 1323 } 1324 } 1325 return null; 1326 } 1327 1328 1334 private RangeDifference getRangeDifferenceForRightLine(int rightLine) { 1335 final List differences= fDifferences; 1336 synchronized (differences) { 1337 for (Iterator it= differences.iterator(); it.hasNext();) { 1338 RangeDifference d= (RangeDifference) it.next(); 1339 if (rightLine >= d.rightStart() && rightLine < d.rightEnd()) { 1340 return d; 1341 } 1342 } 1343 } 1344 return null; 1345 } 1346 1347 1350 public void addAnnotationModelListener(IAnnotationModelListener listener) { 1351 fAnnotationModelListeners.add(listener); 1352 } 1353 1354 1357 public void removeAnnotationModelListener(IAnnotationModelListener listener) { 1358 fAnnotationModelListeners.remove(listener); 1359 } 1360 1361 1364 public void connect(IDocument document) { 1365 Assert.isTrue(fRightDocument == null || fRightDocument == document); 1366 1367 ++fOpenConnections; 1368 if (fOpenConnections == 1) { 1369 fRightDocument= document; 1370 fRightDocument.addDocumentListener(this); 1371 if (document instanceof IDocumentExtension4) { 1372 IDocumentExtension4 ext= (IDocumentExtension4) document; 1373 ext.addDocumentRewriteSessionListener(fSessionListener); 1374 } 1375 initialize(); 1376 } 1377 } 1378 1379 1382 public void disconnect(IDocument document) { 1383 Assert.isTrue(fRightDocument == document); 1384 1385 --fOpenConnections; 1386 1387 if (fOpenConnections == 0) 1388 uninstall(); 1389 } 1390 1391 1394 private void uninstall() { 1395 Job job= fInitializationJob; 1396 if (job != null) 1397 job.cancel(); 1398 1399 synchronized (this) { 1400 fState= SUSPENDED; 1401 fIgnoreDocumentEvents= true; 1402 fInitializationJob= null; 1403 1404 if (fLeftDocument != null) 1405 fLeftDocument.removeDocumentListener(this); 1406 fLeftDocument= null; 1407 fLeftEquivalent= null; 1408 1409 if (fRightDocument != null) { 1410 fRightDocument.removeDocumentListener(this); 1411 if (fRightDocument instanceof IDocumentExtension4) { 1412 IDocumentExtension4 ext= (IDocumentExtension4) fRightDocument; 1413 ext.removeDocumentRewriteSessionListener(fSessionListener); 1414 } 1415 } 1416 fRightDocument= null; 1417 fRightEquivalent= null; 1418 1419 fDifferences.clear(); 1420 } 1421 1422 if (fReferenceProvider != null) { 1423 fReferenceProvider.dispose(); 1424 fReferenceProvider= null; 1425 } 1426 } 1427 1428 1431 public void addAnnotation(Annotation annotation, Position position) { 1432 throw new UnsupportedOperationException (); 1433 } 1434 1435 1438 public void removeAnnotation(Annotation annotation) { 1439 throw new UnsupportedOperationException (); 1440 } 1441 1442 1445 public Iterator getAnnotationIterator() { 1446 final List copy; 1447 List differences= fDifferences; synchronized (differences) { 1449 copy= new ArrayList (differences); 1450 } 1451 final Iterator iter= copy.iterator(); 1452 return new Iterator () { 1453 1454 public void remove() { 1455 throw new UnsupportedOperationException (); 1456 } 1457 1458 public boolean hasNext() { 1459 return iter.hasNext(); 1460 } 1461 1462 public Object next() { 1463 RangeDifference diff= (RangeDifference) iter.next(); 1464 return diff.getDiffRegion(copy, fLeftDocument); 1465 } 1466 1467 }; 1468 } 1469 1470 1473 public Position getPosition(Annotation annotation) { 1474 if (fRightDocument != null && annotation instanceof DiffRegion) { 1475 RangeDifference difference= ((DiffRegion)annotation).getDifference(); 1476 try { 1477 int offset= fRightDocument.getLineOffset(difference.rightStart()); 1478 return new Position(offset, fRightDocument.getLineOffset(difference.rightEnd() - 1) + fRightDocument.getLineLength(difference.rightEnd() - 1) - offset); 1479 } catch (BadLocationException e) { 1480 } 1482 } 1483 return null; 1484 } 1485 1486 1489 protected void fireModelChanged() { 1490 fireModelChanged(new AnnotationModelEvent(this)); 1491 } 1492 1493 1501 protected void fireModelChanged(AnnotationModelEvent event) { 1502 ArrayList v= new ArrayList (fAnnotationModelListeners); 1503 Iterator e= v.iterator(); 1504 while (e.hasNext()) { 1505 IAnnotationModelListener l= (IAnnotationModelListener)e.next(); 1506 if (l instanceof IAnnotationModelListenerExtension) 1507 ((IAnnotationModelListenerExtension)l).modelChanged(event); 1508 else 1509 l.modelChanged(this); 1510 } 1511 } 1512 1513 1516 public void suspend() { 1517 Job job= fInitializationJob; 1518 if (job != null) 1519 job.cancel(); 1520 1521 synchronized (this) { 1522 fInitializationJob= null; 1523 if (fRightDocument != null) 1524 fRightDocument.removeDocumentListener(this); 1525 if (fLeftDocument != null) 1526 fLeftDocument.removeDocumentListener(this); 1527 fLeftDocument= null; 1528 fLeftEquivalent= null; 1529 1530 fLastDifference= null; 1531 fStoredEvents.clear(); 1532 fDifferences.clear(); 1533 1534 fState= SUSPENDED; 1535 1536 fireModelChanged(); 1537 } 1538 } 1539 1540 1543 public synchronized void resume() { 1544 if (fRightDocument != null) 1545 fRightDocument.addDocumentListener(this); 1546 initialize(); 1547 } 1548} 1549 | Popular Tags |