KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > diff > builtin > visualizer > DiffViewImpl


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.modules.diff.builtin.visualizer;
21
22 import java.awt.Point JavaDoc;
23 import java.awt.Rectangle JavaDoc;
24 import java.awt.event.*;
25 import java.awt.event.ActionEvent JavaDoc;
26 import java.awt.event.FocusEvent JavaDoc;
27 import java.awt.event.FocusListener JavaDoc;
28 import java.awt.Dimension JavaDoc;
29 import java.awt.Component JavaDoc;
30 import java.awt.Color JavaDoc;
31 import java.beans.PropertyChangeEvent JavaDoc;
32 import java.beans.PropertyChangeListener JavaDoc;
33 import java.util.*;
34 import java.io.*;
35 import java.lang.reflect.InvocationTargetException JavaDoc;
36 import javax.swing.*;
37 import javax.swing.text.*;
38 import org.netbeans.api.editor.fold.FoldHierarchy;
39 import org.netbeans.api.editor.fold.FoldUtilities;
40 import org.netbeans.modules.diff.NestableDiffView;
41 import org.netbeans.modules.diff.builtin.provider.BuiltInDiffProvider;
42
43 import org.openide.actions.CopyAction;
44 import org.openide.util.actions.ActionPerformer;
45 import org.openide.util.actions.CallbackSystemAction;
46 import org.openide.util.actions.SystemAction;
47 import org.openide.util.Lookup;
48 import org.openide.ErrorManager;
49 import org.netbeans.api.diff.Difference;
50 import org.netbeans.api.diff.StreamSource;
51 import org.netbeans.spi.diff.DiffProvider;
52 import org.netbeans.spi.diff.DiffVisualizer;
53 import org.netbeans.editor.EditorUI;
54 import org.netbeans.editor.ext.ExtCaret;
55 import org.openide.text.CloneableEditorSupport;
56
57 /**
58  * Panel that shows differences between two files. The code here was originally distributed among DiffPanel and
59  * DiffComponent classes.
60  *
61  * @author Maros Sandor
62  */

63 public class DiffViewImpl extends javax.swing.JPanel JavaDoc implements org.netbeans.api.diff.DiffView, javax.swing.event.CaretListener JavaDoc, NestableDiffView {
64
65 // static final long serialVersionUID =3683458237532937983L;
66

67     public static final java.awt.Color JavaDoc COLOR_MISSING = new java.awt.Color JavaDoc(255, 160, 180);
68     public static final java.awt.Color JavaDoc COLOR_ADDED = new java.awt.Color JavaDoc(180, 255, 180);
69     public static final java.awt.Color JavaDoc COLOR_CHANGED = new java.awt.Color JavaDoc(160, 200, 255);
70     
71     private Difference[] diffs = null;
72     
73     /** The shift of differences */
74     private int[][] diffShifts;
75     
76     private java.awt.Color JavaDoc colorMissing = COLOR_MISSING;
77     private java.awt.Color JavaDoc colorAdded = COLOR_ADDED;
78     private java.awt.Color JavaDoc colorChanged = COLOR_CHANGED;
79     
80     private int currentDiffLine = -1;
81     
82     private int totalHeight = 0;
83     private int totalLines = 0;
84
85     private int horizontalScroll1ChangedValue = -1;
86     private int horizontalScroll2ChangedValue = -1;
87     
88     private LinesComponent linesComp1;
89     private LinesComponent linesComp2;
90
91     private String JavaDoc source1;
92     private String JavaDoc source2;
93     
94     private static final String JavaDoc PLAIN_TEXT_MIME = "text/plain";
95
96     private int onLayoutLine;
97     private int onLayoutLength;
98
99     public DiffViewImpl() {
100     }
101
102     private static void copyStreamsCloseAll(Writer out, Reader in) throws IOException {
103         char[] buff = new char[4096];
104         int n;
105         while ((n = in.read(buff)) > 0) {
106             out.write(buff, 0, n);
107         }
108         in.close();
109         out.close();
110     }
111     
112     public DiffViewImpl(StreamSource ss1, StreamSource ss2) throws IOException {
113         Lookup.Result<DiffVisualizer> dv = Lookup.getDefault().lookup(new Lookup.Template<DiffVisualizer>(DiffVisualizer.class));
114         for (DiffVisualizer diff: dv.allInstances()) {
115             if (diff instanceof GraphicalDiffVisualizer) {
116                 GraphicalDiffVisualizer gdv = (GraphicalDiffVisualizer) diff;
117                 colorAdded = gdv.getColorAdded();
118                 colorChanged = gdv.getColorChanged();
119                 colorMissing = gdv.getColorMissing();
120             }
121         }
122         Reader r1 = ss1.createReader();
123         Reader r2 = ss2.createReader();
124         String JavaDoc title1 = ss1.getTitle();
125         String JavaDoc title2 = ss2.getTitle();
126         String JavaDoc mimeType1 = ss1.getMIMEType();
127         String JavaDoc mimeType2 = ss2.getMIMEType();
128         if (mimeType1 == null) mimeType1 = mimeType2;
129         if (mimeType2 == null) mimeType2 = mimeType1;
130         
131         saveSources(r1, r2);
132         initComponents ();
133         setName(org.openide.util.NbBundle.getMessage(DiffViewImpl.class, "DiffComponent.title"));
134         initActions();
135         jSplitPane1.setResizeWeight(0.5);
136         putClientProperty("PersistenceType", "Never");
137         getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getMessage(DiffViewImpl.class, "ACS_DiffPanelA11yName")); // NOI18N
138
getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getMessage(DiffViewImpl.class, "ACS_DiffPanelA11yDesc")); // NOI18N
139
jEditorPane1.getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getMessage(DiffViewImpl.class, "ACS_EditorPane1A11yName")); // NOI18N
140
jEditorPane1.getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getMessage(DiffViewImpl.class, "ACS_EditorPane1A11yDescr")); // NOI18N
141
jEditorPane2.getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getMessage(DiffViewImpl.class, "ACS_EditorPane2A11yName")); // NOI18N
142
jEditorPane2.getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getMessage(DiffViewImpl.class, "ACS_EditorPane2A11yDescr")); // NOI18N
143

144         if (source1 == null) jEditorPane1.setVisible(false);
145         if (source2 == null) jEditorPane2.setVisible(false);
146
147         if (r1 != null && r2 != null) {
148             DiffProvider provider = (DiffProvider) Lookup.getDefault().lookup(DiffProvider.class);
149             if (provider == null) {
150                 provider = new BuiltInDiffProvider();
151             }
152             diffs = provider.computeDiff(new StringReader(source1), new StringReader(source2));
153         } else {
154             diffs = new Difference[0];
155         }
156         diffShifts = new int[diffs.length][2];
157
158         setSource1Title(title1);
159         setSource2Title(title2);
160         
161 // java.lang.AssertionError: BaseKit.install() incorrectly called from non-AWT thread.
162
// at org.netbeans.editor.BaseKit.install(BaseKit.java:503)
163
// at org.netbeans.modules.diff.builtin.visualizer.DiffViewImpl.setSource1(DiffViewImpl.java:642)
164
// at org.netbeans.modules.diff.builtin.visualizer.DiffViewImpl.<init>(DiffViewImpl.java:173)
165
// at org.netbeans.modules.diff.builtin.DefaultDiff.createDiff(DefaultDiff.java:112)
166
// at org.netbeans.modules.versioning.system.cvss.ui.actions.diff.DiffMainPanel$DiffPrepareTask.run(DiffMainPanel.java:634)
167

168         final String JavaDoc f1 = mimeType1;
169         final String JavaDoc f2 = mimeType2;
170         try {
171             Runnable JavaDoc awtTask = new Runnable JavaDoc() {
172                 public void run() {
173                     setMimeType1(f1);
174                     setMimeType2(f2);
175                     try {
176                         if (source1 != null) setSource1(new StringReader(source1));
177                         if (source2 != null) setSource2(new StringReader(source2));
178                     } catch (IOException ioex) {
179                         org.openide.ErrorManager.getDefault().notify(ioex);
180                     }
181                     insertEmptyLines(true);
182                     setDiffHighlight(true);
183                     insertEmptyLinesNotReported();
184
185                     Color JavaDoc borderColor = UIManager.getColor("scrollpane_border");
186                     if (borderColor == null) borderColor = UIManager.getColor("controlShadow");
187                     jScrollPane1.setBorder(BorderFactory.createMatteBorder(1, 0, 0, 0, borderColor));
188                     jScrollPane2.setBorder(BorderFactory.createMatteBorder(1, 0, 0, 0, borderColor));
189                     jSplitPane1.setBorder(BorderFactory.createMatteBorder(1, 0, 0, 0, borderColor));
190                 }
191             };
192             if (SwingUtilities.isEventDispatchThread()) {
193                 awtTask.run();
194             } else {
195                  SwingUtilities.invokeAndWait(awtTask);
196             }
197         } catch (InterruptedException JavaDoc e) {
198             ErrorManager err = ErrorManager.getDefault();
199             err.notify(e);
200         } catch (InvocationTargetException JavaDoc e) {
201             ErrorManager err = ErrorManager.getDefault();
202             err.notify(e);
203         }
204
205     }
206
207     private void saveSources(Reader r1, Reader r2) throws IOException {
208         if (r1 != null) {
209             StringWriter sw = new StringWriter();
210             copyStreamsCloseAll(sw, r1);
211             source1 = sw.toString();
212         }
213         if (r2 != null) {
214             StringWriter sw = new StringWriter();
215             copyStreamsCloseAll(sw, r2);
216             source2 = sw.toString();
217         }
218     }
219
220     public boolean requestFocusInWindow() {
221         return jEditorPane1.requestFocusInWindow();
222     }
223
224     public Component JavaDoc getComponent() {
225         return this;
226     }
227
228     public int getDifferenceCount() {
229         return diffs.length;
230     }
231
232     public boolean canSetCurrentDifference() {
233         return true;
234     }
235
236     public void setCurrentDifference(int diffNo) throws UnsupportedOperationException JavaDoc {
237
238
239         if (diffNo < -1 || diffNo >= diffs.length) throw new IllegalArgumentException JavaDoc("Illegal difference number: " + diffNo);
240
241         if (diffNo == -1) {
242             if (linesComp1 != null) {
243                 this.linesComp1.setActiveLine(-1);
244                 linesComp1.repaint();
245             }
246
247             if (linesComp2 != null) {
248                 this.linesComp2.setActiveLine(-1);
249                 linesComp2.repaint();
250             }
251         } else {
252             currentDiffLine = diffNo;
253             showCurrentLine();
254         }
255     }
256
257     public int getCurrentDifference() throws UnsupportedOperationException JavaDoc {
258         int firstVisibleLine;
259         int lastVisibleLine;
260         int candidate = currentDiffLine;
261         if (jViewport1 != null) {
262             int viewHeight = jViewport1.getViewSize().height;
263             java.awt.Point JavaDoc p1;
264             initGlobalSizes(); // The window might be resized in the mean time.
265
p1 = jViewport1.getViewPosition();
266             int HALFLINE_CEILING = 2; // compensation for rounding error and partially visible lines
267
float firstPct = ((float)p1.y / (float)viewHeight);
268             firstVisibleLine = (int) (firstPct * totalLines) + HALFLINE_CEILING;
269             float lastPct = ((float)(jViewport1.getHeight() + p1.y) / (float)viewHeight);
270             lastVisibleLine = (int) (lastPct * totalLines) - HALFLINE_CEILING;
271
272             for (int i = 0; i<diffs.length; i++) {
273                 int startLine = diffShifts[i][0] + diffs[i].getFirstStart();
274                 int endLine = diffShifts[i][0] + diffs[i].getFirstEnd(); // there no add changes in left pane
275
if (firstVisibleLine < startLine && startLine < lastVisibleLine
276                 || firstVisibleLine < endLine && endLine < lastVisibleLine) {
277                     if (i == currentDiffLine) {
278                         return currentDiffLine; // current is visible, eliminate hazards use it.
279
}
280                     candidate = i; // takes last visible, optimalized for Next>
281
}
282             }
283         }
284
285         return candidate;
286     }
287
288     public JToolBar getToolBar() {
289         return null;
290     }
291
292     private void showCurrentLine() {
293         Difference diff = diffs[currentDiffLine];
294         int line = diff.getFirstStart() + diffShifts[currentDiffLine][0];
295         if (diff.getType() == Difference.ADD) line++;
296         int lf1 = diff.getFirstEnd() - diff.getFirstStart() + 1;
297         int lf2 = diff.getSecondEnd() - diff.getSecondStart() + 1;
298         int length = Math.max(lf1, lf2);
299         setCurrentLine(line, length);
300     }
301     
302     /** This method is called from within the constructor to
303      * initialize the form.
304      * WARNING: Do NOT modify this code. The content of this method is
305      * always regenerated by the FormEditor.
306      */

307     // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:initComponents
308
private void initComponents() {
309         java.awt.GridBagConstraints JavaDoc gridBagConstraints;
310
311         setLayout(new java.awt.BorderLayout JavaDoc());
312
313         jSplitPane1.setDividerSize(4);
314         filePanel1.setLayout(new java.awt.GridBagLayout JavaDoc());
315
316         jEditorPane1.addCaretListener(this);
317
318         jScrollPane1.setViewportView(jEditorPane1);
319
320         gridBagConstraints = new java.awt.GridBagConstraints JavaDoc();
321         gridBagConstraints.gridx = 0;
322         gridBagConstraints.gridy = 1;
323         gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
324         gridBagConstraints.weightx = 1.0;
325         gridBagConstraints.weighty = 1.0;
326         filePanel1.add(jScrollPane1, gridBagConstraints);
327
328         fileLabel1.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
329         fileLabel1.setLabelFor(jEditorPane1);
330         gridBagConstraints = new java.awt.GridBagConstraints JavaDoc();
331         gridBagConstraints.gridx = 0;
332         gridBagConstraints.gridy = 0;
333         gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
334         gridBagConstraints.weightx = 1.0;
335         gridBagConstraints.insets = new java.awt.Insets JavaDoc(4, 4, 4, 4);
336         filePanel1.add(fileLabel1, gridBagConstraints);
337
338         jSplitPane1.setLeftComponent(filePanel1);
339
340         filePanel2.setLayout(new java.awt.GridBagLayout JavaDoc());
341
342         jEditorPane2.addCaretListener(this);
343
344         jScrollPane2.setViewportView(jEditorPane2);
345
346         gridBagConstraints = new java.awt.GridBagConstraints JavaDoc();
347         gridBagConstraints.gridx = 0;
348         gridBagConstraints.gridy = 1;
349         gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
350         gridBagConstraints.weightx = 1.0;
351         gridBagConstraints.weighty = 1.0;
352         filePanel2.add(jScrollPane2, gridBagConstraints);
353
354         fileLabel2.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
355         fileLabel2.setLabelFor(jEditorPane2);
356         gridBagConstraints = new java.awt.GridBagConstraints JavaDoc();
357         gridBagConstraints.gridx = 0;
358         gridBagConstraints.gridy = 0;
359         gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
360         gridBagConstraints.weightx = 1.0;
361         gridBagConstraints.insets = new java.awt.Insets JavaDoc(4, 4, 4, 4);
362         filePanel2.add(fileLabel2, gridBagConstraints);
363
364         jSplitPane1.setRightComponent(filePanel2);
365
366         add(jSplitPane1, java.awt.BorderLayout.CENTER);
367
368     }
369
370     // Code for dispatching events from components to event handlers.
371

372     public void caretUpdate(javax.swing.event.CaretEvent JavaDoc evt) {
373         if (evt.getSource() == jEditorPane1) {
374             DiffViewImpl.this.jEditorPane1CaretUpdate(evt);
375         }
376         else if (evt.getSource() == jEditorPane2) {
377             DiffViewImpl.this.jEditorPane2CaretUpdate(evt);
378         }
379     }// </editor-fold>//GEN-END:initComponents
380

381     private void jEditorPane1CaretUpdate(javax.swing.event.CaretEvent JavaDoc evt) {//GEN-FIRST:event_jEditorPane1CaretUpdate
382
// Add your handling code here:
383
}//GEN-LAST:event_jEditorPane1CaretUpdate
384

385     private void jEditorPane2CaretUpdate(javax.swing.event.CaretEvent JavaDoc evt) {//GEN-FIRST:event_jEditorPane2CaretUpdate
386
// Add your handling code here:
387
}//GEN-LAST:event_jEditorPane2CaretUpdate
388

389     public void setCurrentLine(int line, int diffLength) {
390         if (line > 0) showLine(line, diffLength);
391         onLayoutLine = line;
392         onLayoutLength = diffLength;
393     }
394
395     private void initActions() {
396         jEditorPane1.addFocusListener(new FocusListener JavaDoc() {
397             public void focusGained(FocusEvent JavaDoc e) {
398                 editorActivated(jEditorPane1);
399             }
400             public void focusLost(FocusEvent JavaDoc e) {
401                 editorDeactivated(jEditorPane1);
402             }
403         });
404         jEditorPane2.addFocusListener(new FocusListener JavaDoc() {
405             public void focusGained(FocusEvent JavaDoc e) {
406                 editorActivated(jEditorPane2);
407             }
408             public void focusLost(FocusEvent JavaDoc e) {
409                 editorDeactivated(jEditorPane2);
410             }
411         });
412     }
413
414     public void addNotify() {
415         super.addNotify();
416
417         jEditorPane1.putClientProperty("HighlightsLayerExcludes", "^org\\.netbeans\\.modules\\.editor\\.lib2\\.highlighting\\.CaretRowHighlighting$"); //NOI18N
418
jEditorPane2.putClientProperty("HighlightsLayerExcludes", "^org\\.netbeans\\.modules\\.editor\\.lib2\\.highlighting\\.CaretRowHighlighting$"); //NOI18N
419

420         List<Action> actions = new ArrayList<Action>(2);
421         actions.add(getActionMap().get("jumpNext")); // NOI18N
422
actions.add(getActionMap().get("jumpPrev")); // NOI18N
423
jEditorPane1.setPopupActions(actions);
424         jEditorPane2.setPopupActions(actions);
425
426         expandFolds();
427         initGlobalSizes();
428         addChangeListeners();
429     }
430
431     public void doLayout() {
432         super.doLayout();
433         setCurrentLine(onLayoutLine, onLayoutLength);
434         onLayoutLine = 0;
435     }
436
437     private Hashtable<JEditorPane, Hashtable<Object JavaDoc, Action>> kitActions;
438             /** Listener for copy action enabling */
439     private PropertyChangeListener JavaDoc copyL;
440     private PropertyChangeListener JavaDoc copyP;
441     
442     private Action getAction (String JavaDoc s, JEditorPane editor) {
443         if (kitActions == null) {
444             kitActions = new Hashtable<JEditorPane, Hashtable<Object JavaDoc, Action>>();
445         }
446         Hashtable<Object JavaDoc, Action> actions = kitActions.get(editor);
447         if (actions == null) {
448             EditorKit kit = editor.getEditorKit();
449             if (kit == null) {
450                 return null;
451             }
452             
453             Action[] a = kit.getActions ();
454             actions = new Hashtable<Object JavaDoc, Action>(a.length);
455             int k = a.length;
456             for (int i = 0; i < k; i++)
457                 actions.put (a[i].getValue (Action.NAME), a[i]);
458             kitActions.put(editor, actions);
459         }
460         return actions.get (s);
461     }
462     
463     private void editorActivated(final JEditorPane editor) {
464         final Action copy = getAction (DefaultEditorKit.copyAction, editor);
465         if (copy != null) {
466             final CallbackSystemAction sysCopy
467             = ((CallbackSystemAction) SystemAction.get (CopyAction.class));
468             final ActionPerformer perf = new ActionPerformer () {
469                 public void performAction (SystemAction action) {
470                     copy.actionPerformed (new ActionEvent JavaDoc (editor, 0, "")); // NOI18N
471
}
472             };
473             sysCopy.setActionPerformer(copy.isEnabled() ? perf : null);
474             PropertyChangeListener JavaDoc copyListener;
475             copy.addPropertyChangeListener(copyListener = new PropertyChangeListener JavaDoc() {
476                 public void propertyChange(PropertyChangeEvent JavaDoc evt) {
477                     if ("enabled".equals(evt.getPropertyName())) { // NOI18N
478
if (((Boolean JavaDoc)evt.getNewValue()).booleanValue()) {
479                             sysCopy.setActionPerformer(perf);
480                         } else if (sysCopy.getActionPerformer() == perf) {
481                             sysCopy.setActionPerformer(null);
482                         }
483                     }
484                 }
485             });
486             if (editor.equals(jEditorPane1)) copyL = copyListener;
487             else copyP = copyListener;
488         }
489     }
490     
491     private void editorDeactivated(JEditorPane editor) {
492         Action copy = getAction (DefaultEditorKit.copyAction, editor);
493         PropertyChangeListener JavaDoc copyListener;
494         if (editor.equals(jEditorPane1)) copyListener = copyL;
495         else copyListener = copyP;
496         if (copy != null) {
497             copy.removePropertyChangeListener(copyListener);
498         }
499     }
500
501     private void expandFolds() {
502         FoldHierarchy fh = FoldHierarchy.get(jEditorPane1);
503         FoldUtilities.expandAll(fh);
504         fh = FoldHierarchy.get(jEditorPane2);
505         FoldUtilities.expandAll(fh);
506     }
507
508     private void initGlobalSizes() {
509         StyledDocument doc1 = (StyledDocument) jEditorPane1.getDocument();
510         StyledDocument doc2 = (StyledDocument) jEditorPane2.getDocument();
511         int numLines1 = org.openide.text.NbDocument.findLineNumber(doc1, doc1.getEndPosition().getOffset());
512         int numLines2 = org.openide.text.NbDocument.findLineNumber(doc2, doc2.getEndPosition().getOffset());
513         int numLines = Math.max(numLines1, numLines2);
514         if (numLines < 1) numLines = 1;
515         this.totalLines = numLines;
516         int totHeight = jEditorPane1.getSize().height;
517         int value = jEditorPane2.getSize().height;
518         if (value > totHeight) totHeight = value;
519         this.totalHeight = totHeight;
520     }
521
522     private void showLine(int line, int diffLength) {
523         this.linesComp1.setActiveLine(line);
524         this.linesComp2.setActiveLine(line);
525         linesComp2.repaint();
526         linesComp1.repaint();
527         int padding = 5;
528         if (line <= 5) padding = line/2;
529         int off1, off2;
530         int ypos;
531         int viewHeight = jViewport1.getExtentSize().height;
532         java.awt.Point JavaDoc p1, p2;
533         initGlobalSizes(); // The window might be resized in the mean time.
534
p1 = jViewport1.getViewPosition();
535         p2 = jViewport2.getViewPosition();
536         ypos = (totalHeight*(line - padding - 1))/(totalLines + 1);
537
538         try {
539             off1 = org.openide.text.NbDocument.findLineOffset((StyledDocument) jEditorPane1.getDocument(), line - 1);
540             off2 = org.openide.text.NbDocument.findLineOffset((StyledDocument) jEditorPane2.getDocument(), line - 1);
541
542             jEditorPane1.setCaretPosition(off1);
543             jEditorPane2.setCaretPosition(off2);
544         } catch (IndexOutOfBoundsException JavaDoc ex) {
545             ErrorManager.getDefault().notify(ex);
546         }
547
548         if (ypos < p1.y || ypos + ((diffLength + padding)*totalHeight)/totalLines > p1.y + viewHeight) {
549             p1.y = ypos;
550             jViewport1.setViewPosition(p1); // joinScrollBar will move paired view
551
}
552     }
553
554     // NestableDiffView implementation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
555

556     public void joinScrollPane(final JScrollPane pane) {
557         jScrollPane1.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER);
558         jScrollPane1.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
559         jScrollPane2.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER);
560         jScrollPane2.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
561         jScrollPane1.setWheelScrollingEnabled(false);
562         jScrollPane2.setWheelScrollingEnabled(false);
563         jScrollPane1.addMouseWheelListener(new MouseWheelListener() {
564             public void mouseWheelMoved(MouseWheelEvent e) {
565                 pane.dispatchEvent(e);
566             }
567         });
568         jScrollPane2.addMouseWheelListener(new MouseWheelListener() {
569             public void mouseWheelMoved(MouseWheelEvent e) {
570                 pane.dispatchEvent(e);
571             }
572         });
573         jEditorPane1.getCaret().setVisible(false);
574         jEditorPane2.getCaret().setVisible(false);
575
576         // map JEditorPane keystroke to JScrollPane action registered for even keystroke
577
KeyStroke[] keyStrokes = new KeyStroke[] {
578             KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN, 0),
579             KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN, 0),
580
581             KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP, 0),
582             KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP, 0),
583
584             KeyStroke.getKeyStroke(KeyEvent.VK_HOME, 0),
585             KeyStroke.getKeyStroke(KeyEvent.VK_HOME, InputEvent.CTRL_MASK),
586
587             KeyStroke.getKeyStroke(KeyEvent.VK_HOME, InputEvent.CTRL_MASK),
588             KeyStroke.getKeyStroke(KeyEvent.VK_HOME, InputEvent.CTRL_MASK),
589             
590             KeyStroke.getKeyStroke(KeyEvent.VK_END, 0),
591             KeyStroke.getKeyStroke(KeyEvent.VK_END, InputEvent.CTRL_MASK),
592
593             KeyStroke.getKeyStroke(KeyEvent.VK_END, InputEvent.CTRL_MASK),
594             KeyStroke.getKeyStroke(KeyEvent.VK_END, InputEvent.CTRL_MASK),
595
596             KeyStroke.getKeyStroke(KeyEvent.VK_END, 0),
597             KeyStroke.getKeyStroke(KeyEvent.VK_END, 0),
598
599             KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0),
600             KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0),
601
602             KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0),
603             KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0),
604
605             KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0),
606             KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0),
607
608             KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0),
609             KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0),
610         };
611
612         for (int i = 0; i<keyStrokes.length; i+=2) {
613             KeyStroke stroke = keyStrokes[i];
614             KeyStroke stroke2 = keyStrokes[i+1];
615             Object JavaDoc pane1Key = jEditorPane1.getInputMap().get(stroke);
616             Object JavaDoc pane2Key = jEditorPane2.getInputMap().get(stroke);
617             Object JavaDoc scrollKey = pane.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).get(stroke2);
618             if (scrollKey != null) {
619                 final Action scrollAction = pane.getActionMap().get(scrollKey);
620                 jEditorPane1.getActionMap().put(pane1Key, new SourceTranslatorAction(scrollAction, pane));
621                 jEditorPane2.getActionMap().put(pane2Key, new SourceTranslatorAction(scrollAction, pane));
622             } else {
623                 // System.err.println("No JScrollPane binding for " + stroke); // HOME, END
624
}
625         }
626
627     }
628
629     public int getInnerScrollableUnitIncrement(Rectangle JavaDoc visibleRect, int orientation, int direction) {
630         return jEditorPane1.getScrollableUnitIncrement(visibleRect, orientation, direction);
631     }
632
633     public int getInnerScrollableBlockIncrement(Rectangle JavaDoc visibleRect, int orientation, int direction) {
634         return jEditorPane1.getScrollableBlockIncrement(visibleRect, orientation, direction);
635     }
636     
637     public int getInnerWidth() {
638         Dimension JavaDoc d1 = jScrollPane1.getViewport().getView().getPreferredSize();
639         Dimension JavaDoc d2 = jScrollPane2.getViewport().getView().getPreferredSize();
640         int w = Math.max(d1.width, d2.width) * 2;
641         return w;
642     }
643
644     public void setInnerWidth(int width) {
645         Dimension JavaDoc dim = jScrollPane1.getViewport().getViewSize();
646         dim.width = width/2;
647         jScrollPane1.getViewport().setViewSize(dim);
648
649         dim = jScrollPane2.getViewport().getViewSize();
650         dim.width = width/2;
651         jScrollPane2.getViewport().setViewSize(dim);
652
653         jSplitPane1.setDividerLocation(0.5);
654     }
655
656     public void setHorizontalPosition(int pos) {
657         pos /= 2;
658
659         Point JavaDoc p = jScrollPane1.getViewport().getViewPosition();
660         p.x = pos;
661         jScrollPane1.getViewport().setViewPosition(p);
662
663         p = jScrollPane2.getViewport().getViewPosition();
664         p.x = pos;
665         jScrollPane2.getViewport().setViewPosition(p);
666     }
667
668     /** Return change's top y-axis position. */
669     public int getChangeY(int change) {
670         Difference diff = diffs[change];
671         int line = diff.getFirstStart() + diffShifts[change][0];
672         int padding = 5;
673         if (line <= 5) {
674             padding = line/2;
675         }
676         initGlobalSizes();
677         int ypos = (totalHeight*(line - padding - 1))/(totalLines + 1);
678         ypos += fileLabel1.getHeight();
679         return ypos;
680     }
681
682
683     private void joinScrollBars() {
684         final JScrollBar scrollBarH1 = jScrollPane1.getHorizontalScrollBar();
685         final JScrollBar scrollBarV1 = jScrollPane1.getVerticalScrollBar();
686         final JScrollBar scrollBarH2 = jScrollPane2.getHorizontalScrollBar();
687         final JScrollBar scrollBarV2 = jScrollPane2.getVerticalScrollBar();
688         scrollBarV1.getModel().addChangeListener(new javax.swing.event.ChangeListener JavaDoc() {
689             public void stateChanged(javax.swing.event.ChangeEvent JavaDoc e) {
690                 int value = scrollBarV1.getValue();
691                 int oldValue = scrollBarV2.getValue();
692                 if (oldValue != value) {
693                     scrollBarV2.setValue(value);
694                 }
695             }
696         });
697         // The vertical scroll bar must be there for mouse wheel to work correctly.
698
// However it's not necessary to be seen (but must be visible so that the wheel will work).
699
jScrollPane1.getVerticalScrollBar().setPreferredSize(new Dimension JavaDoc(0, 0));
700         scrollBarV2.getModel().addChangeListener(new javax.swing.event.ChangeListener JavaDoc() {
701             public void stateChanged(javax.swing.event.ChangeEvent JavaDoc e) {
702                 int value = scrollBarV2.getValue();
703                 int oldValue = scrollBarV1.getValue();
704                 if (oldValue != value) {
705                     scrollBarV1.setValue(value);
706                 }
707             }
708         });
709         scrollBarH1.getModel().addChangeListener(new javax.swing.event.ChangeListener JavaDoc() {
710             public void stateChanged(javax.swing.event.ChangeEvent JavaDoc e) {
711                 int value = scrollBarH1.getValue();
712                 if (value == horizontalScroll1ChangedValue) return;
713                 int max1 = scrollBarH1.getMaximum();
714                 int max2 = scrollBarH2.getMaximum();
715                 int ext1 = scrollBarH1.getModel().getExtent();
716                 int ext2 = scrollBarH2.getModel().getExtent();
717                 if (max1 == ext1) horizontalScroll2ChangedValue = 0;
718                 else horizontalScroll2ChangedValue = (value*(max2 - ext2))/(max1 - ext1);
719                 horizontalScroll1ChangedValue = -1;
720                 scrollBarH2.setValue(horizontalScroll2ChangedValue);
721             }
722         });
723         scrollBarH2.getModel().addChangeListener(new javax.swing.event.ChangeListener JavaDoc() {
724             public void stateChanged(javax.swing.event.ChangeEvent JavaDoc e) {
725                 int value = scrollBarH2.getValue();
726                 if (value == horizontalScroll2ChangedValue) return;
727                 int max1 = scrollBarH1.getMaximum();
728                 int max2 = scrollBarH2.getMaximum();
729                 int ext1 = scrollBarH1.getModel().getExtent();
730                 int ext2 = scrollBarH2.getModel().getExtent();
731                 if (max2 == ext2) horizontalScroll1ChangedValue = 0;
732                 else horizontalScroll1ChangedValue = (value*(max1 - ext1))/(max2 - ext2);
733                 horizontalScroll2ChangedValue = -1;
734                 scrollBarH1.setValue(horizontalScroll1ChangedValue);
735             }
736         });
737         jSplitPane1.setDividerLocation(0.5);
738     }
739     
740     private String JavaDoc strCharacters(char c, int num) {
741         StringBuffer JavaDoc s = new StringBuffer JavaDoc();
742         while(num-- > 0) {
743             s.append(c);
744         }
745         return s.toString();
746     }
747     
748     private void customizeEditor(JEditorPane editor) {
749         StyledDocument doc;
750         Document document = editor.getDocument();
751         try {
752             doc = (StyledDocument) editor.getDocument();
753         } catch(ClassCastException JavaDoc e) {
754             doc = new DefaultStyledDocument();
755             try {
756                 doc.insertString(0, document.getText(0, document.getLength()), null);
757             } catch (BadLocationException ble) {
758                 // leaving the document empty
759
}
760             editor.setDocument(doc);
761         }
762     }
763     
764     private void addChangeListeners() {
765         jEditorPane1.addPropertyChangeListener("font", new java.beans.PropertyChangeListener JavaDoc() {
766             public void propertyChange(java.beans.PropertyChangeEvent JavaDoc evt) {
767                 javax.swing.SwingUtilities.invokeLater(new Runnable JavaDoc() {
768                     public void run() {
769                         initGlobalSizes();
770                         linesComp1.repaint();
771                     }
772                 });
773             }
774         });
775         jEditorPane2.addPropertyChangeListener("font", new java.beans.PropertyChangeListener JavaDoc() {
776             public void propertyChange(java.beans.PropertyChangeEvent JavaDoc evt) {
777                 javax.swing.SwingUtilities.invokeLater(new Runnable JavaDoc() {
778                     public void run() {
779                         initGlobalSizes();
780                         linesComp2.repaint();
781                     }
782                 });
783             }
784         });
785     }
786
787     public void setSource1(Reader r) throws IOException {
788         EditorKit kit = jEditorPane1.getEditorKit();
789         if (kit == null) throw new IOException("Missing Editor Kit"); // NOI18N
790
Document doc = kit.createDefaultDocument();
791         if (!(doc instanceof StyledDocument)) {
792             doc = new DefaultStyledDocument(new StyleContext());
793             kit = new StyledEditorKit();
794             jEditorPane1.setEditorKit(kit);
795         }
796         try {
797             kit.read(r, doc, 0);
798         } catch (javax.swing.text.BadLocationException JavaDoc e) {
799             throw new IOException("Can not locate the beginning of the document."); // NOI18N
800
} finally {
801             r.close();
802         }
803         kit.install(jEditorPane1);
804         jEditorPane1.setDocument(doc);
805         jEditorPane1.setEditable(false);
806         customizeEditor(jEditorPane1);
807         linesComp1 = new LinesComponent(jEditorPane1);
808         jScrollPane1.setRowHeaderView(linesComp1);
809         jViewport1 = jScrollPane1.getViewport();
810     }
811     
812     public void setSource2(Reader r) throws IOException {
813         EditorKit kit = jEditorPane2.getEditorKit();
814         if (kit == null) throw new IOException("Missing Editor Kit"); // NOI18N
815
Document doc = kit.createDefaultDocument();
816         if (!(doc instanceof StyledDocument)) {
817             doc = new DefaultStyledDocument(new StyleContext());
818             kit = new StyledEditorKit();
819             jEditorPane2.setEditorKit(kit);
820         }
821         try {
822             kit.read(r, doc, 0);
823         } catch (javax.swing.text.BadLocationException JavaDoc e) {
824             throw new IOException("Can not locate the beginning of the document."); // NOI18N
825
} finally {
826             r.close();
827         }
828         kit.install(jEditorPane2);
829         jEditorPane2.setDocument(doc);
830         jEditorPane2.setEditable(false);
831         
832         customizeEditor(jEditorPane2);
833         linesComp2 = new LinesComponent(jEditorPane2);
834         jScrollPane2.setRowHeaderView(linesComp2);
835         jViewport2 = jScrollPane2.getViewport();
836         // add scrollbar listeners..
837
joinScrollBars();
838     }
839     
840     public void setSource1Title(String JavaDoc title) {
841         fileLabel1.setText(title);
842         // Set the minimum size in 'x' direction to a low value, so that the splitter can be moved to corner locations
843
fileLabel1.setMinimumSize(new Dimension JavaDoc(3, fileLabel1.getMinimumSize().height));
844     }
845     
846     public void setSource2Title(String JavaDoc title) {
847         fileLabel2.setText(title);
848         // Set the minimum size in 'x' direction to a low value, so that the splitter can be moved to corner locations
849
fileLabel2.setMinimumSize(new Dimension JavaDoc(3, fileLabel2.getMinimumSize().height));
850     }
851     
852     public void setMimeType1(String JavaDoc mime) {
853         EditorKit kit = CloneableEditorSupport.getEditorKit(mime);
854         jEditorPane1.setEditorKit(kit);
855     }
856     
857     public void setMimeType2(String JavaDoc mime) {
858         EditorKit kit = CloneableEditorSupport.getEditorKit(mime);
859         jEditorPane2.setEditorKit(kit);
860     }
861     
862     public void setDocument1(Document doc) {
863         if (doc != null) {
864             jEditorPane1.setDocument(doc);
865         }
866     }
867     
868     public void setDocument2(Document doc) {
869         if (doc != null) {
870             jEditorPane2.setDocument(doc);
871         }
872     }
873     
874     String JavaDoc getDocumentText1() {
875         return jEditorPane1.getText();
876     }
877     
878     String JavaDoc getDocumentText2() {
879         return jEditorPane2.getText();
880     }
881     
882     private void setHighlight(StyledDocument doc, int line1, int line2, java.awt.Color JavaDoc color) {
883         for(int line = line1-1; line < line2; line++) {
884             if (line < 0) continue;
885             int offset = org.openide.text.NbDocument.findLineOffset(doc, line);
886             if (offset >= 0) {
887                 Style s = doc.getLogicalStyle(offset);
888                 if (s == null) {
889                     s = doc.addStyle("diff-style("+color+"):1500", null); // NOI18N
890
}
891                 s.addAttribute(StyleConstants.ColorConstants.Background, color);
892                 doc.setLogicalStyle(offset, s);
893             }
894         }
895     }
896     
897     private void unhighlight(StyledDocument doc) {
898         int endOffset = doc.getEndPosition().getOffset();
899         int endLine = org.openide.text.NbDocument.findLineNumber(doc, endOffset);
900         Style s = doc.addStyle("diff-style(white):1500", null); // NOI18N
901
s.addAttribute(StyleConstants.ColorConstants.Background, java.awt.Color.white);
902         for(int line = 0; line <= endLine; line++) {
903             int offset = org.openide.text.NbDocument.findLineOffset(doc, line);
904             doc.setLogicalStyle(offset, s);
905         }
906     }
907     
908     public void unhighlightAll() {
909         unhighlight((StyledDocument) jEditorPane1.getDocument());
910         unhighlight((StyledDocument) jEditorPane2.getDocument());
911     }
912     
913     public void highlightRegion1(int line1, int line2, java.awt.Color JavaDoc color) {
914         StyledDocument doc = (StyledDocument) jEditorPane1.getDocument();
915         setHighlight(doc, line1, line2, color);
916     }
917     
918     public void highlightRegion2(int line1, int line2, java.awt.Color JavaDoc color) {
919         StyledDocument doc = (StyledDocument) jEditorPane2.getDocument();
920         setHighlight(doc, line1, line2, color);
921     }
922     
923     private void addEmptyLines(StyledDocument doc, int line, int numLines) {
924         int lastOffset = doc.getEndPosition().getOffset();
925         int totLines = org.openide.text.NbDocument.findLineNumber(doc, lastOffset);
926         int offset = lastOffset-1;
927         if (line <= totLines) {
928             offset = org.openide.text.NbDocument.findLineOffset(doc, line);
929         }
930         String JavaDoc insStr = strCharacters('\n', numLines);
931         try {
932             doc.insertString(offset, insStr, null);
933         } catch (BadLocationException e) {
934             org.openide.ErrorManager.getDefault().notify(e);
935         }
936     }
937     
938     public void addEmptyLines1(int line, int numLines) {
939         StyledDocument doc = (StyledDocument) jEditorPane1.getDocument();
940         addEmptyLines(doc, line, numLines);
941         linesComp1.addEmptyLines(line, numLines);
942     }
943     
944     public void addEmptyLines2(int line, int numLines) {
945         StyledDocument doc = (StyledDocument) jEditorPane2.getDocument();
946         addEmptyLines(doc, line, numLines);
947         linesComp2.addEmptyLines(line, numLines);
948     }
949     
950     
951     private javax.swing.JViewport JavaDoc jViewport1;
952     private javax.swing.JViewport JavaDoc jViewport2;
953     
954     // Variables declaration - do not modify//GEN-BEGIN:variables
955
final javax.swing.JLabel JavaDoc fileLabel1 = new javax.swing.JLabel JavaDoc();
956     final javax.swing.JLabel JavaDoc fileLabel2 = new javax.swing.JLabel JavaDoc();
957     final javax.swing.JPanel JavaDoc filePanel1 = new javax.swing.JPanel JavaDoc();
958     final javax.swing.JPanel JavaDoc filePanel2 = new javax.swing.JPanel JavaDoc();
959     final org.netbeans.modules.diff.builtin.visualizer.DEditorPane jEditorPane1 = new DEditorPane();
960     final org.netbeans.modules.diff.builtin.visualizer.DEditorPane jEditorPane2 = new DEditorPane();
961     final javax.swing.JScrollPane JavaDoc jScrollPane1 = new javax.swing.JScrollPane JavaDoc();
962     final javax.swing.JScrollPane JavaDoc jScrollPane2 = new javax.swing.JScrollPane JavaDoc();
963     final javax.swing.JSplitPane JavaDoc jSplitPane1 = new javax.swing.JSplitPane JavaDoc();
964     // End of variables declaration//GEN-END:variables
965

966     
967     
968     private void insertEmptyLines(boolean updateActionLines) {
969         int n = diffs.length;
970         //int ins1 = 0;
971
//int ins2 = 0;
972
//D.deb("insertEmptyLines():"); // NOI18N
973
for(int i = 0; i < n; i++) {
974             Difference action = diffs[i];
975             int n1 = action.getFirstStart() + diffShifts[i][0];
976             int n2 = action.getFirstEnd() + diffShifts[i][0];
977             int n3 = action.getSecondStart() + diffShifts[i][1];
978             int n4 = action.getSecondEnd() + diffShifts[i][1];
979             //System.out.println("Action = "+action);
980
//System.out.println("ins1 = "+diffShifts[i][0]+", ins2 = "+diffShifts[i][1]);
981
if (updateActionLines && i < n - 1) {
982                 diffShifts[i + 1][0] = diffShifts[i][0];
983                 diffShifts[i + 1][1] = diffShifts[i][1];
984             }
985             switch (action.getType()) {
986                 case Difference.DELETE:
987                     addEmptyLines2(n3, n2 - n1 + 1);
988                     if (updateActionLines && i < n - 1) {
989                         diffShifts[i+1][1] += n2 - n1 + 1;
990                     }
991                     //ins2 += n2 - n1 + 1;
992
break;
993                 case Difference.ADD:
994                     addEmptyLines1(n1, n4 - n3 + 1);
995                     if (updateActionLines && i < n - 1) {
996                         diffShifts[i+1][0] += n4 - n3 + 1;
997                     }
998                     //ins1 += n4 - n3 + 1;
999
break;
1000                case Difference.CHANGE:
1001                    int r1 = n2 - n1;
1002                    int r2 = n4 - n3;
1003                    if (r1 < r2) {
1004                        addEmptyLines1(n2, r2 - r1);
1005                        if (updateActionLines && i < n - 1) {
1006                            diffShifts[i+1][0] += r2 - r1;
1007                        }
1008                        //ins1 += r2 - r1;
1009
} else if (r1 > r2) {
1010                        addEmptyLines2(n4, r1 - r2);
1011                        if (updateActionLines && i < n - 1) {
1012                            diffShifts[i+1][1] += r1 - r2;
1013                        }
1014                        //ins2 += r1 - r2;
1015
}
1016                    break;
1017            }
1018        }
1019    }
1020    
1021    private void setDiffHighlight(boolean set) {
1022        int n = diffs.length;
1023        //D.deb("Num Actions = "+n); // NOI18N
1024
for(int i = 0; i < n; i++) {
1025            Difference action = diffs[i];
1026            int n1 = action.getFirstStart() + diffShifts[i][0];
1027            int n2 = action.getFirstEnd() + diffShifts[i][0];
1028            int n3 = action.getSecondStart() + diffShifts[i][1];
1029            int n4 = action.getSecondEnd() + diffShifts[i][1];
1030            //D.deb("Action: "+action.getAction()+": ("+n1+","+n2+","+n3+","+n4+")"); // NOI18N
1031
switch (action.getType()) {
1032            case Difference.DELETE:
1033                if (set) highlightRegion1(n1, n2, colorMissing);
1034                else highlightRegion1(n1, n2, java.awt.Color.white);
1035                break;
1036            case Difference.ADD:
1037                if (set) highlightRegion2(n3, n4, colorAdded);
1038                else highlightRegion2(n3, n4, java.awt.Color.white);
1039                break;
1040            case Difference.CHANGE:
1041                if (set) {
1042                    highlightRegion1(n1, n2, colorChanged);
1043                    highlightRegion2(n3, n4, colorChanged);
1044                } else {
1045                    highlightRegion1(n1, n2, java.awt.Color.white);
1046                    highlightRegion2(n3, n4, java.awt.Color.white);
1047                }
1048                break;
1049            }
1050        }
1051    }
1052        
1053    /**
1054     * We have to keep the balance of lines from the first and the second document,
1055     * so that corresponding lines will have the same line number in the document.
1056     * Because some diff providers can be set not to report changes in empty lines,
1057     * we have to use heuristics to balance the corresponding lines manually
1058     * if they do not match.
1059     * <p>
1060     * This method goes through both documents and finds unreported differences
1061     * in empty lines. Whenever it encounters an empty line with a corresponding
1062     * non-empty line, which in not inside a difference (== unreported),
1063     * it checks whether the following lines match (because there can be unreported
1064     * difference in the amount of space rather than a missing or added line).
1065     * If following lines "match", then silently add an empty line to the other
1066     * document. This added line is not highlighted, since it was not reported
1067     * as a difference.
1068     */

1069    private void insertEmptyLinesNotReported() {
1070        String JavaDoc docText1 = getDocumentText1();
1071        String JavaDoc docText2 = getDocumentText2();
1072        int[] begin1 = { 0 };
1073        int[] end1 = { -1 };
1074        int[] begin2 = { 0 };
1075        int[] end2 = { -1 };
1076        int n1 = docText1.length();
1077        int n2 = docText2.length();
1078        int lineNumber = 1;
1079        int diffIndex = 0;
1080        do {
1081            int lastBegin1 = begin1[0];
1082            int lastBegin2 = begin2[0];
1083            String JavaDoc line1 = readLine(begin1, end1, docText1);
1084            String JavaDoc line2 = readLine(begin2, end2, docText2);
1085            if (line1.length() == 0 && line2.length() > 0) {
1086                //System.out.println("Detected empty line LEFT "+lineNumber);
1087
diffIndex = findDiffForLine(lineNumber, diffIndex, diffs, diffShifts);
1088                if (diffIndex >= diffs.length || !isLineInDiff(lineNumber, diffs[diffIndex], diffShifts[diffIndex])) {
1089                    boolean addMissingLine;
1090                    if (line2.trim().length() == 0) {
1091                        int emptyLines1 = numEmptyLines(begin1[0], docText1, (diffIndex < diffs.length) ? diffs[diffIndex].getFirstStart() : -1);
1092                        int emptyLines2 = numEmptyLines(begin2[0], docText2, (diffIndex < diffs.length) ? diffs[diffIndex].getSecondStart() : -1);
1093                        addMissingLine = emptyLines1 > emptyLines2;
1094                        //System.out.println("emptyLines1 = "+emptyLines1+", emptyLines2 = "+emptyLines2);
1095
} else {
1096                        addMissingLine = true;
1097                    }
1098                    if (addMissingLine) {
1099                        addEmptyLines2(lineNumber - 1, 1);
1100                        //highlightRegion2(lineNumber, lineNumber, colorAdded);
1101
shiftDiffs(false, lineNumber);
1102                        begin2[0] = lastBegin2;
1103                        end2[0] = lastBegin2 - 1;
1104                    }
1105                }
1106            } else if (line2.length() == 0 && line1.length() > 0) {
1107                //System.out.println("Detected empty line RIGHT "+lineNumber);
1108
diffIndex = findDiffForLine(lineNumber, diffIndex, diffs, diffShifts);
1109                if (diffIndex >= diffs.length || !isLineInDiff(lineNumber, diffs[diffIndex], diffShifts[diffIndex])) {
1110                    boolean addMissingLine;
1111                    if (line1.trim().length() == 0) {
1112                        int emptyLines1 = numEmptyLines(begin1[0], docText1, (diffIndex < diffs.length) ? diffs[diffIndex].getFirstStart() : -1);
1113                        int emptyLines2 = numEmptyLines(begin2[0], docText2, (diffIndex < diffs.length) ? diffs[diffIndex].getSecondStart() : -1);
1114                        addMissingLine = emptyLines2 > emptyLines1;
1115                        //System.out.println("emptyLines1 = "+emptyLines1+", emptyLines2 = "+emptyLines2);
1116
} else {
1117                        addMissingLine = true;
1118                    }
1119                    if (addMissingLine) {
1120                        addEmptyLines1(lineNumber - 1, 1);
1121                        //highlightRegion1(lineNumber, lineNumber, colorMissing);
1122
shiftDiffs(true, lineNumber);
1123                        begin1[0] = lastBegin1;
1124                        end1[0] = lastBegin1 - 1;
1125                    }
1126                }
1127            }
1128            lineNumber++;
1129        } while (begin1[0] < n1 && begin2[0] < n2);
1130    }
1131    
1132    /**
1133     * Shift the differences by one in the first or the second document from the given line.
1134     * @param inFirstDoc True to shift differences the first document, false for the second.
1135     * @param fromLine The starting line. Shift all differences after this line.
1136     */

1137    private void shiftDiffs(boolean inFirstDoc, int fromLine) {
1138        int n = diffs.length;
1139        for(int i = 0; i < n; i++) {
1140            Difference action = diffs[i];
1141            if (inFirstDoc) {
1142                if (action.getFirstStart() + diffShifts[i][0] >= fromLine) {
1143                    diffShifts[i][0]++;
1144                }
1145            } else {
1146                if (action.getSecondStart() + diffShifts[i][1] >= fromLine) {
1147                    diffShifts[i][1]++;
1148                }
1149            }
1150        }
1151    }
1152
1153    private static int numEmptyLines(int beginLine, String JavaDoc text, int endLine) {
1154        if (endLine >= 0 && endLine <= beginLine) return 0;
1155        int numLines = 0;
1156        int[] begin = { beginLine };
1157        int[] end = { 0 };
1158        do {
1159            String JavaDoc line = readLine(begin, end, text);
1160            if (line.trim().length() > 0) break;
1161            numLines++;
1162        } while ((endLine < 0 || beginLine + numLines < endLine) && begin[0] < text.length());
1163        return numLines;
1164    }
1165    
1166    /**
1167     * Find the first diff, that is on or below the given line number.
1168     * @return The index of the desired difference in the supplied array or
1169     * a value, that is bigger then the array size if such a diff does
1170     * not exist.
1171     */

1172    private static int findDiffForLine(int lineNumber, int diffIndex, Difference[] diffs, int[][] diffShifts) {
1173        while (diffIndex < diffs.length) {
1174            if ((diffs[diffIndex].getFirstEnd() + diffShifts[diffIndex][0]) >= lineNumber ||
1175                (diffs[diffIndex].getSecondEnd() + diffShifts[diffIndex][1]) >= lineNumber) break;
1176            diffIndex++;
1177        }
1178        return diffIndex;
1179    }
1180    
1181    /**
1182     * Find out whether the line lies in the difference.
1183     * @param lineNumber The number of the line.
1184     * @param diff The difference
1185     * @param diffShifts The shifts of the difference in the current document
1186     * @return true if the line lies in the difference, false if does not.
1187     */

1188    private static boolean isLineInDiff(int lineNumber, Difference diff, int[] diffShifts) {
1189        int l1 = diff.getFirstStart() + diffShifts[0];
1190        int l2 = diff.getFirstEnd() + diffShifts[0];
1191        int l3 = diff.getSecondStart() + diffShifts[1];
1192        int l4 = diff.getSecondEnd() + diffShifts[1];
1193        return (l1 <= lineNumber && ((l2 >= l1) ? (l2 >= lineNumber) : false)) ||
1194               (l3 <= lineNumber && ((l4 >= l3) ? (l4 >= lineNumber) : false));
1195    }
1196
1197    /**
1198     * Read one line from the given text, from the given position. It returns
1199     * the end position of this line and the beginning of the next one.
1200     * @param begin Contains just one value - IN: the beginning of the line to read.
1201     * OUT: the start of the next line.
1202     * @param end Contains just one value - OUT: the end of the line.
1203     * @param text The text to read.
1204     * @return The line.
1205     */

1206    private static String JavaDoc readLine(int[] begin, int[] end, String JavaDoc text) {
1207        int n = text.length();
1208        for (int i = begin[0]; i < n; i++) {
1209            char c = text.charAt(i);
1210            if (c == '\n' || c == '\r') {
1211                end[0] = i;
1212                break;
1213            }
1214        }
1215        if (end[0] < begin[0]) end[0] = n;
1216        String JavaDoc line = text.substring(begin[0], end[0]);
1217        begin[0] = end[0] + 1;
1218        if (begin[0] < n && text.charAt(end[0]) == '\r' && text.charAt(begin[0]) == '\n') begin[0]++;
1219        return line;
1220    }
1221    
1222}
1223
Popular Tags