KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > ui > internal > texteditor > quickdiff > DocumentLineDiffer


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  *******************************************************************************/

11 package org.eclipse.ui.internal.texteditor.quickdiff;
12
13 import java.util.ArrayList JavaDoc;
14 import java.util.ConcurrentModificationException JavaDoc;
15 import java.util.Iterator JavaDoc;
16 import java.util.LinkedList JavaDoc;
17 import java.util.List JavaDoc;
18 import java.util.ListIterator JavaDoc;
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 /**
66  * Standard implementation of <code>ILineDiffer</code> as an incremental diff engine. A
67  * <code>DocumentLineDiffer</code> can be initialized to some start state. Once connected to a
68  * <code>IDocument</code> and a reference document has been set, changes reported via the
69  * <code>IDocumentListener</code> interface will be tracked and the incremental diff updated.
70  *
71  * <p>The diff state can be queried using the <code>ILineDiffer</code> interface.</p>
72  *
73  * <p>Since diff information is model information attached to a document, this class implements
74  * <code>IAnnotationModel</code> and can be attached to <code>IAnnotationModelExtension</code>s.</p>
75  *
76  * <p>This class is not supposed to be subclassed.</p>
77  *
78  * @since 3.0
79  */

80 public class DocumentLineDiffer implements ILineDiffer, IDocumentListener, IAnnotationModel, ILineDifferExtension, ILineDifferExtension2 {
81
82     /**
83      * Artificial line difference information indicating a change with an empty line as original text.
84      */

85     private static class LineChangeInfo implements ILineDiffInfo {
86
87         private static final String JavaDoc[] ORIGINAL_TEXT= new String JavaDoc[] { "\n" }; //$NON-NLS-1$
88

89         /*
90          * @see org.eclipse.jface.text.source.ILineDiffInfo#getRemovedLinesBelow()
91          */

92         public int getRemovedLinesBelow() {
93             return 0;
94         }
95
96         /*
97          * @see org.eclipse.jface.text.source.ILineDiffInfo#getRemovedLinesAbove()
98          */

99         public int getRemovedLinesAbove() {
100             return 0;
101         }
102
103         /*
104          * @see org.eclipse.jface.text.source.ILineDiffInfo#getChangeType()
105          */

106         public int getChangeType() {
107             return CHANGED;
108         }
109
110         /*
111          * @see org.eclipse.jface.text.source.ILineDiffInfo#hasChanges()
112          */

113         public boolean hasChanges() {
114             return true;
115         }
116
117         /*
118          * @see org.eclipse.jface.text.source.ILineDiffInfo#getOriginalText()
119          */

120         public String JavaDoc[] getOriginalText() {
121             return ORIGINAL_TEXT;
122         }
123     }
124
125     /** Tells whether this class is in debug mode. */
126     private static boolean DEBUG= "true".equalsIgnoreCase(Platform.getDebugOption("org.eclipse.ui.workbench.texteditor/debug/DocumentLineDiffer")); //$NON-NLS-1$//$NON-NLS-2$
127

128     /** The delay after which the initialization job is triggered. */
129     private static final int INITIALIZE_DELAY= 500;
130
131     /** Suspended state */
132     private static final int SUSPENDED= 0;
133     /** Initializing state */
134     private static final int INITIALIZING= 1;
135     /** Synchronized state */
136     private static final int SYNCHRONIZED= 2;
137
138     /** This differ's state */
139     private int fState= SUSPENDED;
140     /** Artificial line difference information indicating a change with an empty line as original text. */
141     private final ILineDiffInfo fLineChangeInfo= new LineChangeInfo();
142
143     /** The provider for the reference document. */
144     IQuickDiffReferenceProvider fReferenceProvider;
145     /** The number of clients connected to this model. */
146     private int fOpenConnections;
147     /** The current document being tracked. */
148     private IDocument fLeftDocument;
149     /**
150      * The equivalence class of the left document.
151      * @since 3.2
152      */

153     private DocumentEquivalenceClass fLeftEquivalent;
154     /** The reference document. */
155     private IDocument fRightDocument;
156     /**
157      * The equivalence class of the right document.
158      * @since 3.2
159      */

160     private DocumentEquivalenceClass fRightEquivalent;
161     /**
162      * Flag to indicate whether a change has been made to the line table and any clients should
163      * update their presentation.
164      */

165     private boolean fUpdateNeeded;
166     /** The listeners on this annotation model. */
167     private List JavaDoc fAnnotationModelListeners= new ArrayList JavaDoc();
168     /** The job currently initializing the differ, or <code>null</code> if there is none. */
169     private Job fInitializationJob;
170     /** Stores <code>DocumentEvents</code> while an initialization is going on. */
171     private List JavaDoc fStoredEvents= new ArrayList JavaDoc();
172     /**
173      * The differences between <code>fLeftDocument</code> and <code>fRightDocument</code>.
174      * This is the model we work on.
175      */

176     private List JavaDoc fDifferences= new ArrayList JavaDoc();
177     /**
178      * The differences removed in one iteration. Stored to be able to send out differentiated
179      * annotation events.
180      */

181     private List JavaDoc fRemoved= new ArrayList JavaDoc();
182     /**
183      * The differences added in one iteration. Stored to be able to send out differentiated
184      * annotation events.
185      */

186     private List JavaDoc fAdded= new ArrayList JavaDoc();
187     /**
188      * The differences changed in one iteration. Stored to be able to send out differentiated
189      * annotation events.
190      */

191     private List JavaDoc fChanged= new ArrayList JavaDoc();
192     /** The first line affected by a document event. */
193     private int fFirstLine;
194     /** The number of lines affected by a document event. */
195     private int fNLines;
196     /** The most recent range difference returned in a getLineInfo call, so it can be recyled. */
197     private RangeDifference fLastDifference;
198     /**
199      * <code>true</code> if incoming document events should be ignored,
200      * <code>false</code> if not.
201      */

202     private boolean fIgnoreDocumentEvents= true;
203     /**
204      * The listener for document rewrite sessions.
205      * @since 3.2
206      */

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 JavaDoc fThread;
219     private DocumentEvent fLastUIEvent;
220
221
222     /**
223      * Creates a new differ.
224      */

225     public DocumentLineDiffer() {
226     }
227
228     /* ILineDiffer implementation */
229
230     /*
231      * @see org.eclipse.jface.text.source.ILineDiffer#getLineInfo(int)
232      */

233     public ILineDiffInfo getLineInfo(int line) {
234
235         if (isSuspended())
236             return fLineChangeInfo;
237
238         // try cache first / speeds up linear search
239
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     /*
252      * @see org.eclipse.jface.text.source.ILineDiffer#revertLine(int)
253      */

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 JavaDoc replacement;
267         if (leftLine >= diff.leftEnd()) // restoring a deleted line?
268
replacement= ""; //$NON-NLS-1$
269
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     /*
278      * @see org.eclipse.jface.text.source.ILineDiffer#revertBlock(int)
279      */

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     /*
297      * @see org.eclipse.jface.text.source.ILineDiffer#revertSelection(int, int)
298      */

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 JavaDoc differences= fDifferences;
309         synchronized (differences) {
310             Iterator JavaDoc it= differences.iterator();
311
312             // get start
313
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             // get end / length
327
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     /*
348      * @see org.eclipse.jface.text.source.ILineDiffer#restoreAfterLine(int)
349      */

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 JavaDoc differences= fDifferences;
363         synchronized (differences) {
364             for (Iterator JavaDoc 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     /**
388      * Returns the receivers initialization state.
389      *
390      * @return <code>true</code> if we are initialized and in sync with the document.
391      */

392     private boolean isInitialized() {
393         return fState == SYNCHRONIZED;
394     }
395
396     /**
397      * Returns the receivers synchronization state.
398      *
399      * @return <code>true</code> if we are initialized and in sync with the document.
400      */

401     public synchronized boolean isSynchronized() {
402         return fState == SYNCHRONIZED;
403     }
404
405     /**
406      * Returns <code>true</code> if the differ is suspended.
407      *
408      * @return <code>true</code> if the differ is suspended
409      */

410     public synchronized boolean isSuspended() {
411         return fState == SUSPENDED;
412     }
413
414     /**
415      * Sets the reference provider for this instance. If one has been installed before, it is
416      * disposed.
417      *
418      * @param provider the new provider
419      */

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     /**
431      * Returns the reference provider currently installed, or <code>null</code> if none is installed.
432      *
433      * @return the current reference provider.
434      */

435     public IQuickDiffReferenceProvider getReferenceProvider() {
436         return fReferenceProvider;
437     }
438
439     /**
440      * (Re-)initializes the differ using the current reference and <code>DiffInitializer</code>.
441      *
442      * @since 3.2 protected for testing reasons, package visible before
443      */

444     protected synchronized void initialize() {
445         // make new incoming changes go into the queue of stored events, plus signal we can't restore.
446
fState= INITIALIZING;
447
448         if (fRightDocument == null)
449             return;
450
451         // there is no point in receiving updates before the job we get a new copy of the document for diffing
452
fIgnoreDocumentEvents= true;
453
454         if (fLeftDocument != null) {
455             fLeftDocument.removeDocumentListener(this);
456             fLeftDocument= null;
457             fLeftEquivalent= null;
458         }
459
460         // if there already is a job:
461
// return if it has not started yet, cancel it if already running
462
final Job oldJob= fInitializationJob;
463         if (oldJob != null) {
464             // don't chain up jobs if there is one waiting already.
465
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             /*
475              * This is run in a different thread. As the documents might be synchronized, never ever
476              * access the documents in a synchronized section or expect deadlocks. See
477              * https://bugs.eclipse.org/bugs/show_bug.cgi?id=44692
478              */

479             public IStatus run(IProgressMonitor monitor) {
480
481                 // 1: wait for any previous job that was canceled to avoid job flooding
482
// It will return relatively quickly as RangeDifferencer supports canceling
483
if (oldJob != null)
484                     try {
485                         oldJob.join();
486                     } catch (InterruptedException JavaDoc e) {
487                         // will not happen as no one interrupts our thread
488
Assert.isTrue(false);
489                     }
490
491
492                 // 2: get the reference document
493
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                 // Getting our own copies of the documents for offline diffing.
511
//
512
// We need to make sure that we do get all document modifications after
513
// copying the documents as we want to re-inject them later on to become consistent.
514

515                 IDocument right= fRightDocument; // fRightDocument, but not subject to change
516
IDocument actual= null; // the copy of the actual (right) document
517
IDocument reference= null; // the copy of the reference (left) document
518

519                 synchronized (DocumentLineDiffer.this) {
520                     // 4: take an early exit if the documents are not valid
521
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                     // set the reference document
531
fLeftDocument= left;
532                     // start listening to document events.
533
fIgnoreDocumentEvents= false;
534                 }
535
536                 // accessing the reference document from a different thread - reference providers need
537
// to be able to deal with this.
538
left.addDocumentListener(DocumentLineDiffer.this);
539                 
540                 // create the reference copy - note that any changes on the
541
// reference will trigger re-initialization anyway
542
reference= createCopy(left);
543                 if (reference == null)
544                     return Status.CANCEL_STATUS;
545                 
546                 // create the actual copy
547

548                 Object JavaDoc lock= null;
549                 if (right instanceof ISynchronizable)
550                     lock= ((ISynchronizable) right).getLockObject();
551                     
552                 if (lock != null) {
553                     // a) if we can, acquire locks in proper order and copy
554
// the document
555
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                     // b) cannot lock the document
565
// Now this is fun. The reference documents may be PartiallySynchronizedDocuments
566
// which will result in a deadlock if they get changed externally before we get
567
// our exclusive copies.
568
// Here's what we do: we try over and over (without synchronization) to get copies
569
// without interleaving modification. If there is a document change, we just repeat.
570
int i= 0;
571                     do {
572                         // this is an arbitrary emergency exit in case a referenced document goes nuts
573
if (i++ == 100)
574                             return new Status(IStatus.ERROR, TextEditorPlugin.PLUGIN_ID, IStatus.OK, NLSUtility.format(QuickDiffMessages.quickdiff_error_getting_document_content, new Object JavaDoc[] {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                         // access documents non synchronized:
584
// get an exclusive copy of the actual document
585
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 JavaDoc diffs= RangeDifferencer.findRanges(monitor, ref, act);
605                 // 7: Reset the model to the just gotten differences
606
// re-inject stored events to get up to date.
607
synchronized (DocumentLineDiffer.this) {
608                     if (isCanceled(monitor))
609                         return Status.CANCEL_STATUS;
610
611                     // set the new differences so we can operate on them
612
fDifferences= diffs;
613                 }
614
615                 // re-inject events accumulated in the meantime.
616
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                                 // we are back in sync with the life documents
625
fInitializationJob= null;
626                                 fState= SYNCHRONIZED;
627                                 fLastDifference= null;
628
629                                 // replace the private documents with the actual
630
leftEquivalent.setDocument(left);
631                                 rightEquivalent.setDocument(right);
632
633                                 break;
634                             }
635
636                             event= (DocumentEvent) fStoredEvents.remove(0);
637                         }
638                         
639                         // access documents non synchronized:
640
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                         // copy the event to inject it into our diff copies
649
// don't modify the original event! See https://bugs.eclipse.org/bugs/show_bug.cgi?id=134227
650
event= new DocumentEvent(copy, event.fOffset, event.fLength, event.fText);
651                         handleAboutToBeChanged(event);
652                         
653                         // inject the event into our private copy
654
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             /**
687              * Creates a copy of <code>document</code> and catches any
688              * exceptions that may occur if the document is modified concurrently.
689              * Only call this method in a synchronized block if the document is
690              * an ISynchronizable and has been locked, as document.get() is called
691              * and may result in a deadlock otherwise.
692              *
693              * @param document the document to create a copy of
694              * @return a copy of the document, or <code>null</code> if an exception was thrown
695              */

696             private IDocument createCopy(IDocument document) {
697                 Assert.isNotNull(document);
698                 // this fixes https://bugs.eclipse.org/bugs/show_bug.cgi?id=56091
699
try {
700                     return createUnprotectedCopy(document);
701                 } catch (NullPointerException JavaDoc e) {
702                 } catch (ArrayStoreException JavaDoc e) {
703                 } catch (IndexOutOfBoundsException JavaDoc e) {
704                 } catch (ConcurrentModificationException JavaDoc e) {
705                 } catch (NegativeArraySizeException JavaDoc 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