KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > diff > builtin > provider > HuntDiff


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.provider;
21
22 import java.util.ArrayList JavaDoc;
23 import java.util.Arrays JavaDoc;
24 import java.util.Comparator JavaDoc;
25 import java.util.List JavaDoc;
26 import org.netbeans.api.diff.Difference;
27
28
29 public class HuntDiff {
30
31     private HuntDiff() {
32     }
33
34     /**
35      * @param lines1 array of lines from the first source
36      * @param lines2 array of lines from the second source
37      * @param ignoreWhitespace true to ignore leading and trailing whitespace when computing diff, false to also find differences in whitespace
38      * @return computed diff
39      */

40     public static Difference[] diff(String JavaDoc[] lines1, String JavaDoc[] lines2, boolean ignoreWhitespace) {
41         int m = lines1.length;
42         int n = lines2.length;
43         String JavaDoc [] lines1_original = lines1;
44         String JavaDoc [] lines2_original = lines2;
45         if (ignoreWhitespace) {
46             lines1 = new String JavaDoc[lines1_original.length];
47             lines2 = new String JavaDoc[lines2_original.length];
48             for (int i = 0 ; i < lines1_original.length; i++) {
49                 lines1[i] = lines1_original[i].trim();
50             }
51             for (int i = 0 ; i < lines2_original.length; i++) {
52                 lines2[i] = lines2_original[i].trim();
53             }
54         }
55         Line[] l2s = new Line[n + 1];
56         // In l2s we have sorted lines of the second file <1, n>
57
for (int i = 1; i <= n; i++) {
58             l2s[i] = new Line(i, lines2[i - 1]);
59         }
60         Arrays.sort(l2s, 1, n+1, new Comparator JavaDoc<Line>() {
61             
62             public int compare(Line l1, Line l2) {
63                 return l1.line.compareTo(l2.line);
64             }
65
66             public boolean equals(Object JavaDoc obj) {
67                 return obj == this;
68             }
69             
70         });
71         
72         int[] equvalenceLines = new int[n+1];
73         boolean[] equivalence = new boolean[n+1];
74         for (int i = 1; i <= n; i++) {
75             Line l = l2s[i];
76             equvalenceLines[i] = l.lineNo;
77             equivalence[i] = (i == n) || !l.line.equals(l2s[i+1].line);//((Line) l2s.get(i)).line);
78
}
79         equvalenceLines[0] = 0;
80         equivalence[0] = true;
81         int[] equivalenceAssoc = new int[m + 1];
82         for (int i = 1; i <= m; i++) {
83             equivalenceAssoc[i] = findAssoc(lines1[i - 1], l2s, equivalence);
84         }
85         
86         l2s = null;
87         Candidate[] K = new Candidate[Math.min(m, n) + 2];
88         K[0] = new Candidate(0, 0, null);
89         K[1] = new Candidate(m + 1, n + 1, null);
90         int k = 0;
91         for (int i = 1; i <= m; i++) {
92             if (equivalenceAssoc[i] != 0) {
93                 k = merge(K, k, i, equvalenceLines, equivalence, equivalenceAssoc[i]);
94             }
95         }
96         int[] J = new int[m+2]; // Initialized with zeros
97

98         Candidate c = K[k];
99         while (c != null) {
100             J[c.a] = c.b;
101             c = c.c;
102         }
103         
104         List JavaDoc<Difference> differences = getDifferences(J, lines1_original, lines2_original);
105         cleanup(differences);
106         return differences.toArray(new Difference[0]);
107     }
108     
109     private static int findAssoc(String JavaDoc line1, Line[] l2s, boolean[] equivalence) {
110         int idx = binarySearch(l2s, line1, 1, l2s.length - 1);
111         if (idx < 1) {
112             return 0;
113         } else {
114             int lastGoodIdx = 0;
115             for (; idx >= 1 && l2s[idx].line.equals(line1); idx--) {
116                 if (equivalence[idx - 1]) {
117                     lastGoodIdx = idx;
118                 }
119             }
120             return lastGoodIdx;
121         }
122     }
123
124     private static int binarySearch(Line[] L, String JavaDoc key, int low, int high) {
125         while (low <= high) {
126             int mid = (low + high) >> 1;
127             String JavaDoc midVal = L[mid].line;
128             int comparison = midVal.compareTo(key);
129             if (comparison < 0) {
130                 low = mid + 1;
131             } else if (comparison > 0) {
132                 high = mid - 1;
133             } else {
134                 return mid;
135             }
136         }
137         return -(low + 1);
138     }
139     
140     private static int binarySearch(Candidate[] K, int key, int low, int high) {
141         while (low <= high) {
142             int mid = (low + high) >> 1;
143             int midVal = K[mid].b;
144             if (midVal < key) {
145                 low = mid + 1;
146             } else if (midVal > key) {
147                 high = mid - 1;
148             } else {
149                 return mid;
150             }
151         }
152         return -(low + 1);
153     }
154
155     private static int merge(Candidate[] K, int k, int i, int[] equvalenceLines,
156                              boolean[] equivalence, int p) {
157         int r = 0;
158         Candidate c = K[0];
159         do {
160             int j = equvalenceLines[p];
161             int s = binarySearch(K, j, r, k);
162             if (s >= 0) {
163                 // j was found in K[]
164
s = k + 1;
165             } else {
166                 s = -s - 2;
167                 if (s < r || s > k) s = k + 1;
168             }
169             if (s <= k) {
170                 if (K[s+1].b > j) {
171                     Candidate newc = new Candidate(i, j, K[s]);
172                     K[r] = c;
173                     r = s+1;
174                     c = newc;
175                 }
176                 if (s == k) {
177                     K[k+2] = K[k+1];
178                     k++;
179                     break;
180                 }
181             }
182             if (equivalence[p] == true) {
183                 break;
184             } else {
185                 p++;
186             }
187         } while (true);
188         K[r] = c;
189         return k;
190     }
191     
192     private static List JavaDoc<Difference> getDifferences(int[] J, String JavaDoc[] lines1, String JavaDoc[] lines2) {
193         List JavaDoc<Difference> differences = new ArrayList JavaDoc<Difference>();
194         int n = lines1.length;
195         int m = lines2.length;
196         int start1 = 1;
197         int start2 = 1;
198         do {
199             while (start1 <= n && J[start1] == start2) {
200                 start1++;
201                 start2++;
202             }
203             if (start1 > n) break;
204             if (J[start1] < start2) { // There's something extra in the first file
205
int end1 = start1 + 1;
206                 StringBuffer JavaDoc deletedText = new StringBuffer JavaDoc();
207                 deletedText.append(lines1[start1-1]).append('\n');
208                 while (end1 <= n && J[end1] < start2) {
209                     String JavaDoc line = lines1[end1-1];
210                     deletedText.append(line).append('\n');
211                     end1++;
212                 }
213                 differences.add(new Difference(Difference.DELETE, start1, end1 - 1, start2 - 1, 0, deletedText.toString(), null));
214                 start1 = end1;
215             } else { // There's something extra in the second file
216
int end2 = J[start1];
217                 StringBuffer JavaDoc addedText = new StringBuffer JavaDoc();
218                 for (int i = start2; i < end2; i++) {
219                     String JavaDoc line = lines2[i-1];
220                     addedText.append(line).append('\n');
221                 }
222                 differences.add(new Difference(Difference.ADD, (start1 - 1), 0, start2, (end2 - 1), null, addedText.toString()));
223                 start2 = end2;
224             }
225         } while (start1 <= n);
226         if (start2 <= m) { // There's something extra at the end of the second file
227
int end2 = start2 + 1;
228             StringBuilder JavaDoc addedText = new StringBuilder JavaDoc();
229             addedText.append(lines2[start2-1]).append('\n');
230             while (end2 <= m) {
231                 String JavaDoc line = lines2[end2-1];
232                 addedText.append(line).append('\n');
233                 end2++;
234             }
235             differences.add(new Difference(Difference.ADD, n, 0, start2, m, null, addedText.toString()));
236         }
237         return differences;
238     }
239     
240     private static void cleanup(List JavaDoc<Difference> diffs) {
241         Difference last = null;
242         for (int i = 0; i < diffs.size(); i++) {
243             Difference diff = diffs.get(i);
244             if (last != null) {
245                 if (diff.getType() == Difference.ADD && last.getType() == Difference.DELETE ||
246                     diff.getType() == Difference.DELETE && last.getType() == Difference.ADD) {
247
248                     Difference add;
249                     Difference del;
250                     if (Difference.ADD == diff.getType()) {
251                         add = diff;
252                         del = last;
253                     } else {
254                         add = last;
255                         del = diff;
256                     }
257                     int d1f1l1 = add.getFirstStart() - (del.getFirstEnd() - del.getFirstStart());
258                     int d2f1l1 = del.getFirstStart();
259                     if (d1f1l1 == d2f1l1) {
260                         Difference newDiff = new Difference(Difference.CHANGE,
261                             d1f1l1, del.getFirstEnd(), add.getSecondStart(), add.getSecondEnd(),
262                             del.getFirstText(), add.getSecondText());
263                         diffs.set(i - 1, newDiff);
264                         diffs.remove(i);
265                         i--;
266                         diff = newDiff;
267                     }
268                 }
269             }
270             last = diff;
271         }
272     }
273     
274     private static class Line {
275
276         public int lineNo;
277         public String JavaDoc line;
278         public int hash;
279         
280         
281         public Line(int lineNo, String JavaDoc line) {
282             this.lineNo = lineNo;
283             this.line = line;
284             this.hash = line.hashCode();
285         }
286         
287     }
288     
289     private static class Candidate {
290         
291         private int a;
292         private int b;
293         private Candidate c;
294         
295         public Candidate(int a, int b, Candidate c) {
296             this.a = a;
297             this.b = b;
298             this.c = c;
299         }
300     }
301
302 }
303
Popular Tags