KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > diff > builtin > Patch


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;
21
22 import java.io.BufferedReader JavaDoc;
23 import java.io.PushbackReader JavaDoc;
24 import java.io.Reader JavaDoc;
25 import java.io.IOException JavaDoc;
26 import java.util.ArrayList JavaDoc;
27 import java.util.List JavaDoc;
28 import java.util.Iterator JavaDoc;
29 import java.util.regex.Pattern JavaDoc;
30 import java.util.regex.PatternSyntaxException JavaDoc;
31
32 import org.netbeans.api.diff.Difference;
33
34 import org.netbeans.modules.diff.cmdline.CmdlineDiffProvider;
35
36 /**
37  * Utility class for patch application.
38  *
39  * @author Martin Entlicher
40  */

41 public class Patch extends Reader JavaDoc {
42
43     private static final int CONTEXT_DIFF = 0;
44     private static final int NORMAL_DIFF = 1;
45     private static final int UNIFIED_DIFF = 2;
46     
47     private Difference[] diffs;
48     private PushbackReader JavaDoc source;
49     private int currDiff = 0;
50     private int line = 1;
51     private String JavaDoc newLine = null; // String, that is used to separate lines
52
private StringBuffer JavaDoc buff = new StringBuffer JavaDoc();
53     
54     /** Creates a new instance of Patch */
55     private Patch(Difference[] diffs, Reader JavaDoc source) {
56         this.diffs = diffs;
57         this.source = new PushbackReader JavaDoc(new BufferedReader JavaDoc(source), 1);
58     }
59     
60     /**
61      * Apply the patch to the source.
62      * @param diffs The differences to patch
63      * @param source The source stream
64      * @return The patched stream
65      * @throws IOException When reading from the source stread fails
66      * @throws ParseException When the source does not match the patch to be applied
67      */

68     public static Reader JavaDoc apply(Difference[] diffs, Reader JavaDoc source) {//throws IOException, ParseException {
69
return new Patch(diffs, source);
70     }
71     
72     /**
73      * Parse the differences.
74      *
75     public static Difference[] parse(Reader source) throws IOException {
76         return parseContextDiff(source);
77     }
78      */

79     
80     /**
81      * Parse the differences and corresponding file names.
82      */

83     public static FileDifferences[] parse(Reader JavaDoc source) throws IOException JavaDoc {
84         List JavaDoc<FileDifferences> fileDifferences = new ArrayList JavaDoc<FileDifferences>();
85         //int pushBackLimit = DIFFERENCE_DELIMETER.length();
86
//PushbackReader recognizedSource = new PushbackReader(source, pushBackLimit);
87
Patch.SinglePatchReader patchReader = new Patch.SinglePatchReader(source);
88         int[] diffType = new int[1];
89         String JavaDoc[] fileName = new String JavaDoc[2];
90         while (patchReader.hasNextPatch(diffType, fileName)) {
91             //System.out.println("Have a next patch of name '"+fileName[0]+"'");
92
Difference[] diffs = null;
93             switch (diffType[0]) {
94                 case CONTEXT_DIFF:
95                     diffs = parseContextDiff(patchReader);
96                     break;
97                 case UNIFIED_DIFF:
98                     diffs = parseUnifiedDiff(patchReader);
99                     break;
100                 case NORMAL_DIFF:
101                     diffs = parseNormalDiff(patchReader);
102                     break;
103             }
104             if (diffs != null) {
105                 fileDifferences.add(new FileDifferences(fileName[0], fileName[1], diffs));
106             }
107         }
108         return fileDifferences.toArray(new FileDifferences[fileDifferences.size()]);
109     }
110     
111     public int read(char[] cbuf, int off, int length) throws java.io.IOException JavaDoc {
112         if (buff.length() < length) {
113             doRetrieve(length - buff.length());
114         }
115         int ret = Math.min(buff.length(), length);
116         if (ret == 0) return -1;
117         String JavaDoc retStr = buff.substring(0, ret);
118         char[] retChars = retStr.toCharArray();
119         System.arraycopy(retChars, 0, cbuf, off, ret);
120         buff.delete(0, ret);
121         return ret;
122     }
123     
124     public void close() throws java.io.IOException JavaDoc {
125         if (currDiff < diffs.length) {
126             throw new IOException JavaDoc("There are " + (diffs.length - currDiff) + " pending hunks!");
127         }
128         source.close();
129     }
130     
131     private void doRetrieve(int length) throws IOException JavaDoc {
132         for (int size = 0; size < length; line++) {
133             if (currDiff < diffs.length &&
134                 ((Difference.ADD == diffs[currDiff].getType() &&
135                   line == (diffs[currDiff].getFirstStart() + 1)) ||
136                  (Difference.ADD != diffs[currDiff].getType() &&
137                   line == diffs[currDiff].getFirstStart()))) {
138                 if (compareText(source, diffs[currDiff].getFirstText())) {
139                     String JavaDoc text = convertNewLines(diffs[currDiff].getSecondText(), newLine);
140                     buff.append(text);
141                     currDiff++;
142                 } else {
143                     throw new IOException JavaDoc("Patch not applicable.");
144                 }
145             }
146             StringBuffer JavaDoc newLineBuffer = null;
147             if (newLine == null) {
148                 newLineBuffer = new StringBuffer JavaDoc();
149             }
150             String JavaDoc lineStr = readLine(source, newLineBuffer);
151             if (newLineBuffer != null) newLine = newLineBuffer.toString();
152             if (lineStr == null) break;
153             buff.append(lineStr);
154             buff.append(newLine);
155         }
156     }
157     
158     /** Reads a line and returns the char sequence for newline */
159     private static String JavaDoc readLine(PushbackReader JavaDoc r, StringBuffer JavaDoc nl) throws IOException JavaDoc {
160         StringBuffer JavaDoc line = new StringBuffer JavaDoc();
161         int ic = r.read();
162         if (ic == -1) return null;
163         char c = (char) ic;
164         while (c != '\n' && c != '\r') {
165             line.append(c);
166             ic = r.read();
167             if (ic == -1) break;
168             c = (char) ic;
169         }
170         if (nl != null) {
171             nl.append(c);
172         }
173         if (c == '\r') {
174             try {
175                 ic = r.read();
176                 if (ic != -1) {
177                     c = (char) ic;
178                     if (c != '\n') r.unread(c);
179                     else if (nl != null) nl.append(c);
180                 }
181             } catch (IOException JavaDoc ioex) {}
182         }
183         return line.toString();
184     }
185     
186     private static String JavaDoc convertNewLines(String JavaDoc text, String JavaDoc newLine) {
187         if (text == null) return ""; // NOI18N
188
if (newLine == null) return text;
189         StringBuffer JavaDoc newText = new StringBuffer JavaDoc();
190         for (int i = 0; i < text.length(); i++) {
191             char c = text.charAt(i);
192             if (c == '\n') newText.append(newLine);
193             else if (c == '\r') {
194                 if ((i + 1) < text.length() && text.charAt(i + 1) == '\n') {
195                     i++;
196                     newText.append(newLine);
197                 }
198             } else newText.append(c);
199         }
200         return newText.toString();
201     }
202     
203     private boolean compareText(PushbackReader JavaDoc source, String JavaDoc text) throws IOException JavaDoc {
204         if (text == null || text.length() == 0) return true;
205         text = adjustTextNL(text);
206         char[] chars = new char[text.length()];
207         int pos = 0;
208         int n;
209         String JavaDoc readStr = "";
210         do {
211             n = source.read(chars, 0, chars.length - pos);
212             if (n > 0) {
213                 pos += n;
214                 readStr = readStr + new String JavaDoc(chars, 0, n);
215             }
216             if (readStr.endsWith("\r")) {
217                 try {
218                     char c = (char) source.read();
219                     if (c != '\n') source.unread(c);
220                     else readStr += c;
221                 } catch (IOException JavaDoc ioex) {}
222             }
223             readStr = adjustTextNL(readStr);
224             pos = readStr.length();
225         } while (n > 0 && pos < chars.length);
226         readStr.getChars(0, readStr.length(), chars, 0);
227         line += numChars('\n', chars);
228         //System.out.println("Comparing text of the diff:\n'"+text+"'\nWith the read text:\n'"+readStr+"'\n");
229
//System.out.println(" EQUALS = "+readStr.equals(text));
230
return readStr.equals(text);
231     }
232     
233     /**
234      * When comparing the two texts, it's important to ignore different line endings.
235      * This method assures, that only '\n' is used as the line ending.
236      */

237     private String JavaDoc adjustTextNL(String JavaDoc text) {
238         text = org.openide.util.Utilities.replaceString(text, "\r\n", "\n");
239         text = org.openide.util.Utilities.replaceString(text, "\n\r", "\n");
240         text = org.openide.util.Utilities.replaceString(text, "\r", "\n");
241         return text;
242     }
243     
244     private static int numChars(char c, char[] chars) {
245         int n = 0;
246         for (int i = 0; i < chars.length; i++) {
247             if (chars[i] == c) n++;
248         }
249         return n;
250     }
251     
252     private static final String JavaDoc CONTEXT_MARK1B = "*** ";
253 // private static final String CONTEXT_MARK1E = " ****";
254
private static final String JavaDoc CONTEXT_MARK2B = "--- ";
255 // private static final String CONTEXT_MARK2E = " ----";
256
private static final String JavaDoc CONTEXT_MARK_DELIMETER = ",";
257     private static final String JavaDoc DIFFERENCE_DELIMETER = "***************";
258 // private static final String LINE_PREP = " ";
259
private static final String JavaDoc LINE_PREP_ADD = "+ ";
260     private static final String JavaDoc LINE_PREP_REMOVE = "- ";
261     private static final String JavaDoc LINE_PREP_CHANGE = "! ";
262     
263     private static Difference[] parseContextDiff(Reader JavaDoc in) throws IOException JavaDoc {
264         BufferedReader JavaDoc br = new BufferedReader JavaDoc(in);
265         ArrayList JavaDoc<Difference> diffs = new ArrayList JavaDoc<Difference>();
266         String JavaDoc line = null;
267         do {
268             if (line == null || !DIFFERENCE_DELIMETER.equals(line)) {
269                 do {
270                     line = br.readLine();
271                 } while (line != null && !DIFFERENCE_DELIMETER.equals(line));
272             }
273             int[] firstInterval = new int[2];
274             line = br.readLine();
275             if (line != null && line.startsWith(CONTEXT_MARK1B)) {
276                 try {
277                     readNums(line, CONTEXT_MARK1B.length(), firstInterval);
278                 } catch (NumberFormatException JavaDoc nfex) {
279                     throw new IOException JavaDoc(nfex.getLocalizedMessage());
280                 }
281             } else continue;
282             ArrayList JavaDoc<Object JavaDoc> firstChanges = new ArrayList JavaDoc<Object JavaDoc>(); // List of intervals and texts
283
line = fillChanges(firstInterval, br, CONTEXT_MARK2B, firstChanges);
284             int[] secondInterval = new int[2];
285             if (line != null && line.startsWith(CONTEXT_MARK2B)) {
286                 try {
287                     readNums(line, CONTEXT_MARK2B.length(), secondInterval);
288                 } catch (NumberFormatException JavaDoc nfex) {
289                     throw new IOException JavaDoc(nfex.getLocalizedMessage());
290                 }
291             } else continue;
292             ArrayList JavaDoc<Object JavaDoc> secondChanges = new ArrayList JavaDoc<Object JavaDoc>(); // List of intervals and texts
293
line = fillChanges(secondInterval, br, DIFFERENCE_DELIMETER, secondChanges);
294             if (changesCountInvariant(firstChanges, secondChanges) == false) {
295                 throw new IOException JavaDoc("Diff file format error. Number of new and old file changes in one hunk must be same!"); // NOI18N
296
}
297             mergeChanges(firstInterval, secondInterval, firstChanges, secondChanges, diffs);
298         } while (line != null);
299         return diffs.toArray(new Difference[diffs.size()]);
300     }
301
302
303     private static boolean changesCountInvariant(List JavaDoc<Object JavaDoc> changes1, List JavaDoc<Object JavaDoc> changes2) { // both are Union<int[],String>
304
int i1 = 0;
305         Iterator JavaDoc it = changes1.iterator();
306         while (it.hasNext()) {
307             int[] ints = (int[]) it.next();
308             if (ints[2] == 2) {
309                 i1++;
310             }
311             String JavaDoc skip = (String JavaDoc) it.next();
312         }
313
314         int i2 = 0;
315         it = changes2.iterator();
316         while (it.hasNext()) {
317             int[] ints = (int[]) it.next();
318             if (ints[2] == 2) {
319                 i2++;
320             }
321             String JavaDoc skip = (String JavaDoc) it.next();
322         }
323
324         return i1 == i2;
325     }
326
327     private static void readNums(String JavaDoc str, int off, int[] values) throws NumberFormatException JavaDoc {
328         int end = str.indexOf(CONTEXT_MARK_DELIMETER, off);
329         if (end > 0) {
330             values[0] = Integer.parseInt(str.substring(off, end).trim());
331         } else throw new NumberFormatException JavaDoc("Missing comma.");
332         off = end + 1;
333         end = str.indexOf(' ', off);
334         if (end > 0) {
335             values[1] = Integer.parseInt(str.substring(off, end).trim());
336         } else throw new NumberFormatException JavaDoc("Missing final space.");
337     }
338
339     private static String JavaDoc fillChanges(int[] interval, BufferedReader JavaDoc br,
340                                       String JavaDoc untilStartsWith, List JavaDoc<Object JavaDoc/* int[3] or String*/> changes) throws IOException JavaDoc {
341         String JavaDoc line = br.readLine();
342         for (int pos = interval[0]; pos <= interval[1]; pos++) {
343             if (line == null || line.startsWith(untilStartsWith)) break;
344             if (line.startsWith(LINE_PREP_ADD)) {
345                 int[] changeInterval = new int[3];
346                 changeInterval[0] = pos;
347                 changeInterval[2] = Difference.ADD;
348                 StringBuffer JavaDoc changeText = new StringBuffer JavaDoc();
349                 changeText.append(line.substring(LINE_PREP_ADD.length()));
350                 changeText.append('\n');
351                 do {
352                     line = br.readLine();
353                     if (line == null)
354                         break;
355                     if (line.startsWith(LINE_PREP_ADD)) {
356                         changeText.append(line.substring(LINE_PREP_ADD.length()));
357                         changeText.append('\n');
358                     } else {
359                         break;
360                     }
361                     pos++;
362                 } while (true);
363                 changeInterval[1] = pos;
364                 changes.add(changeInterval);
365                 changes.add(changeText.toString());
366             } else if (line.startsWith(LINE_PREP_REMOVE)) {
367                 int[] changeInterval = new int[3];
368                 changeInterval[0] = pos;
369                 changeInterval[2] = Difference.DELETE;
370                 StringBuffer JavaDoc changeText = new StringBuffer JavaDoc();
371                 changeText.append(line.substring(LINE_PREP_REMOVE.length()));
372                 changeText.append('\n');
373                 do {
374                     line = br.readLine();
375                     if (line == null)
376                         break;
377                     if (line.startsWith(LINE_PREP_REMOVE)) {
378                         changeText.append(line.substring(LINE_PREP_REMOVE.length()));
379                         changeText.append('\n');
380                     } else {
381                         break;
382                     }
383                     pos++;
384                 } while (true);
385                 changeInterval[1] = pos;
386                 changes.add(changeInterval);
387                 changes.add(changeText.toString());
388             } else if (line.startsWith(LINE_PREP_CHANGE)) {
389                 int[] changeInterval = new int[3];
390                 changeInterval[0] = pos;
391                 changeInterval[2] = Difference.CHANGE;
392                 StringBuffer JavaDoc changeText = new StringBuffer JavaDoc();
393                 changeText.append(line.substring(LINE_PREP_CHANGE.length()));
394                 changeText.append('\n');
395                 do {
396                     line = br.readLine();
397                     if (line == null)
398                         break;
399                     if (line.startsWith(LINE_PREP_CHANGE)) {
400                         changeText.append(line.substring(LINE_PREP_CHANGE.length()));
401                         changeText.append('\n');
402                     } else {
403                         break;
404                     }
405                     pos++;
406                 } while (true);
407                 changeInterval[1] = pos;
408                 changes.add(changeInterval);
409                 changes.add(changeText.toString());
410             } else {
411                 line = br.readLine();
412             }
413         }
414         return line;
415     }
416     
417     private static void mergeChanges(int[] firstInterval, int[] secondInterval,
418                               List JavaDoc firstChanges, List JavaDoc secondChanges, List JavaDoc<Difference> diffs) {
419
420
421         int p1, p2;
422         int n1 = firstChanges.size();
423         int n2 = secondChanges.size();
424         //System.out.println("mergeChanges(("+firstInterval[0]+", "+firstInterval[1]+"), ("+secondInterval[0]+", "+secondInterval[1]+"))");
425
//System.out.println("firstChanges.size() = "+n1);
426
//System.out.println("secondChanges.size() = "+n2);
427
int firstToSecondIntervalShift = secondInterval[0] - firstInterval[0];
428         //System.out.println("shift = "+firstToSecondIntervalShift);
429
for (p1 = p2 = 0; p1 < n1 || p2 < n2; ) {
430             boolean isAddRemove = true;
431             while (isAddRemove && p1 < n1) {
432                 int[] interval = (int[]) firstChanges.get(p1);
433                 if (p2 < n2) {
434                     int[] interval2 = (int[]) secondChanges.get(p2);
435                     if (interval[0] + firstToSecondIntervalShift > interval2[0]) {
436                         break;
437                     }
438                     // We need to set differences successively. Differences with
439
// higher line numbers must not precede differences with
440
// smaller line numbers
441
}
442                 isAddRemove = interval[2] == Difference.ADD || interval[2] == Difference.DELETE;
443                 if (isAddRemove) {
444                     if (interval[2] == Difference.ADD) {
445                         diffs.add(new Difference(interval[2], interval[0] - 1, 0,
446                                                  interval[0] + firstToSecondIntervalShift,
447                                                  interval[1] + firstToSecondIntervalShift,
448                                                  (String JavaDoc) firstChanges.get(p1 + 1), ""));
449                         firstToSecondIntervalShift += interval[1] - interval[0] + 1;
450                     } else {
451                         diffs.add(new Difference(interval[2], interval[0], interval[1],
452                                                  interval[0] + firstToSecondIntervalShift - 1, 0,
453                                                  (String JavaDoc) firstChanges.get(p1 + 1), ""));
454                         firstToSecondIntervalShift -= interval[1] - interval[0] + 1;
455                     }
456                     p1 += 2;
457                     //System.out.println("added diff = "+diffs.get(diffs.size() - 1));
458
//System.out.println("new shift = "+firstToSecondIntervalShift);
459
}
460             }
461             isAddRemove = true;
462             while (isAddRemove && p2 < n2) {
463                 int[] interval = (int[]) secondChanges.get(p2);
464                 isAddRemove = interval[2] == Difference.ADD || interval[2] == Difference.DELETE;
465                 if (isAddRemove) {
466                     if (interval[2] == Difference.ADD) {
467                         diffs.add(new Difference(interval[2],
468                                                  interval[0] - firstToSecondIntervalShift - 1, 0,
469                                                  interval[0], interval[1],
470                                                  "", (String JavaDoc) secondChanges.get(p2 + 1)));
471                         firstToSecondIntervalShift += interval[1] - interval[0] + 1;
472                     } else {
473                         diffs.add(new Difference(interval[2],
474                                                  interval[0] - firstToSecondIntervalShift,
475                                                  interval[1] - firstToSecondIntervalShift,
476                                                  interval[0] - 1, 0,
477                                                  "", (String JavaDoc) secondChanges.get(p2 + 1)));
478                         firstToSecondIntervalShift -= interval[1] - interval[0] + 1;
479                     }
480                     p2 += 2;
481                     //System.out.println("added diff = "+diffs.get(diffs.size() - 1));
482
//System.out.println("new shift = "+firstToSecondIntervalShift);
483
}
484             }
485             // Change is remaining
486
if (p1 < n1 && p2 < n2) {
487                 int[] interval1 = (int[]) firstChanges.get(p1);
488                 if (interval1[2] == Difference.CHANGE) { // double check the break above
489
int[] interval2 = (int[]) secondChanges.get(p2);
490                     diffs.add(new Difference(interval1[2], interval1[0], interval1[1],
491                                              interval2[0], interval2[1],
492                                              (String JavaDoc) firstChanges.get(p1 + 1),
493                                              (String JavaDoc) secondChanges.get(p2 + 1)));
494                     p1 += 2;
495                     p2 += 2;
496                     firstToSecondIntervalShift += interval2[1] - interval2[0] - (interval1[1] - interval1[0]);
497                     //System.out.println("added diff = "+diffs.get(diffs.size() - 1));
498
//System.out.println("new shift = "+firstToSecondIntervalShift);
499
}
500             }
501         }
502     }
503     
504     private static final String JavaDoc UNIFIED_MARK = "@@";
505     private static final String JavaDoc UNIFIED_MARK1 = "--- ";
506 // private static final String UNIFIED_MARK2 = "+++ ";
507
private static final String JavaDoc LINE_PREP_UNIF_ADD = "+";
508     private static final String JavaDoc LINE_PREP_UNIF_REMOVE = "-";
509 // private static final String LINE_PREP_UNIF_CHANGE = null;
510

511     private static Difference[] parseUnifiedDiff(Reader JavaDoc in) throws IOException JavaDoc {
512         BufferedReader JavaDoc br = new BufferedReader JavaDoc(in);
513         List JavaDoc<Difference> diffs = new ArrayList JavaDoc<Difference>();
514         String JavaDoc line = null;
515         do {
516             while (line == null || !(line.startsWith(UNIFIED_MARK) &&
517                                      line.length() > UNIFIED_MARK.length() &&
518                                      line.endsWith(UNIFIED_MARK))) {
519                 line = br.readLine();
520                 if (line == null) break;
521             }
522             if (line == null) continue;
523             int[] intervals = new int[4];
524             try {
525                 readUnifiedNums(line, UNIFIED_MARK.length(), intervals);
526             } catch (NumberFormatException JavaDoc nfex) {
527                 IOException JavaDoc ioex = new IOException JavaDoc("Can not parse: " + line);
528                 ioex.initCause(nfex);
529                 throw ioex;
530             }
531             line = fillUnidifChanges(intervals, br, diffs);
532         } while (line != null);
533         return diffs.toArray(new Difference[diffs.size()]);
534     }
535     
536     private static void readUnifiedNums(String JavaDoc str, int off, int[] values) throws NumberFormatException JavaDoc {
537         while (str.charAt(off) == ' ' || str.charAt(off) == '-') off++;
538         int end = str.indexOf(CONTEXT_MARK_DELIMETER, off);
539         if (end > 0) {
540             values[0] = Integer.parseInt(str.substring(off, end).trim());
541         } else throw new NumberFormatException JavaDoc("Missing comma.");
542         off = end + 1;
543         end = str.indexOf(' ', off);
544         if (end > 0) {
545             values[1] = Integer.parseInt(str.substring(off, end).trim());
546         } else throw new NumberFormatException JavaDoc("Missing middle space.");
547         off = end + 1;
548         while (str.charAt(off) == ' ' || str.charAt(off) == '+') off++;
549         end = str.indexOf(CONTEXT_MARK_DELIMETER, off);
550         if (end > 0) {
551             values[2] = Integer.parseInt(str.substring(off, end).trim());
552         } else throw new NumberFormatException JavaDoc("Missing second comma.");
553         off = end + 1;
554         end = str.indexOf(' ', off);
555         if (end > 0) {
556             values[3] = Integer.parseInt(str.substring(off, end).trim());
557         } else throw new NumberFormatException JavaDoc("Missing final space.");
558         values[1] += values[0] - 1;
559         values[3] += values[2] - 1;
560     }
561
562     private static String JavaDoc fillUnidifChanges(int[] interval, BufferedReader JavaDoc br,
563                                             List JavaDoc<Difference> diffs) throws IOException JavaDoc {
564         String JavaDoc line = br.readLine();
565         int pos1 = interval[0];
566         int pos2 = interval[2];
567         while (line != null && pos1 <= interval[1] && pos2 <= interval[3]) {
568             if (line.startsWith(LINE_PREP_UNIF_ADD)) {
569                 int begin = pos2;
570                 StringBuffer JavaDoc changeText = new StringBuffer JavaDoc();
571                 changeText.append(line.substring(LINE_PREP_UNIF_ADD.length()));
572                 changeText.append('\n');
573                 do {
574                     line = br.readLine();
575                     pos2++;
576                     if (line.startsWith(LINE_PREP_UNIF_ADD)) {
577                         changeText.append(line.substring(LINE_PREP_UNIF_ADD.length()));
578                         changeText.append('\n');
579                     } else {
580                         break;
581                     }
582                 } while (true);
583                 Difference diff = null;
584                 if (diffs.size() > 0) {
585                     Difference previousDiff = (Difference) diffs.get(diffs.size() - 1);
586                     if (Difference.DELETE == previousDiff.getType() && previousDiff.getFirstEnd() == (pos1 - 1)) {
587                         diff = new Difference(Difference.CHANGE,
588                             previousDiff.getFirstStart(), previousDiff.getFirstEnd(),
589                             begin, pos2 - 1, previousDiff.getFirstText(), changeText.toString());
590                         diffs.remove(diffs.size() - 1);
591                     }
592                 }
593                 if (diff == null) {
594                     diff = new Difference(Difference.ADD, pos1 - 1, 0, begin, pos2 - 1, null, changeText.toString());
595                 }
596                 diffs.add(diff);
597             } else if (line.startsWith(LINE_PREP_UNIF_REMOVE)) {
598                 int begin = pos1;
599                 StringBuffer JavaDoc changeText = new StringBuffer JavaDoc();
600                 changeText.append(line.substring(LINE_PREP_UNIF_REMOVE.length()));
601                 changeText.append('\n');
602                 do {
603                     line = br.readLine();
604                     pos1++;
605                     if (line.startsWith(LINE_PREP_UNIF_REMOVE)) {
606                         changeText.append(line.substring(LINE_PREP_UNIF_REMOVE.length()));
607                         changeText.append('\n');
608                     } else {
609                         break;
610                     }
611                 } while (true);
612                 Difference diff = null;
613                 if (diffs.size() > 0) {
614                     Difference previousDiff = (Difference) diffs.get(diffs.size() - 1);
615                     if (Difference.ADD == previousDiff.getType() && previousDiff.getSecondEnd() == (pos2 - 1)) {
616                         diff = new Difference(Difference.CHANGE, begin, pos1 - 1,
617                             previousDiff.getFirstStart(), previousDiff.getFirstEnd(),
618                             changeText.toString(), previousDiff.getFirstText());
619                         diffs.remove(diffs.size() - 1);
620                     }
621                 }
622                 if (diff == null) {
623                     diff = new Difference(Difference.DELETE, begin, pos1 - 1, pos2 - 1, 0, changeText.toString(), null);
624                 }
625                 diffs.add(diff);
626             } else {
627                 line = br.readLine();
628                 pos1++;
629                 pos2++;
630             }
631         }
632         return line;
633     }
634     
635     private static Difference[] parseNormalDiff(Reader JavaDoc in) throws IOException JavaDoc {
636         Pattern JavaDoc normRegexp;
637         try {
638             normRegexp = Pattern.compile(CmdlineDiffProvider.DIFF_REGEXP);
639         } catch (PatternSyntaxException JavaDoc rsex) {
640             normRegexp = null;
641         }
642         StringBuffer JavaDoc firstText = new StringBuffer JavaDoc();
643         StringBuffer JavaDoc secondText = new StringBuffer JavaDoc();
644         BufferedReader JavaDoc br = new BufferedReader JavaDoc(in);
645         List JavaDoc<Difference> diffs = new ArrayList JavaDoc<Difference>();
646         String JavaDoc line;
647         while ((line = br.readLine()) != null) {
648             CmdlineDiffProvider.outputLine(line, normRegexp, diffs, firstText, secondText);
649         }
650         CmdlineDiffProvider.setTextOnLastDifference(diffs, firstText, secondText);
651         return diffs.toArray(new Difference[diffs.size()]);
652     }
653     
654     /**
655      * A reader, that will not read more, than a single patch content
656      * from the supplied reader with possibly more patches.
657      */

658     private static class SinglePatchReader extends Reader JavaDoc {
659         
660         private static final int BUFF_SIZE = 512;
661         private PushbackReader JavaDoc in;
662         private char[] buffer = new char[BUFF_SIZE];
663         private int buffLength = 0;
664         private int buffPos = 0;
665         private boolean isAtEndOfPatch = false;
666         
667         public SinglePatchReader(Reader JavaDoc in) {
668             this.in = new PushbackReader JavaDoc(in, BUFF_SIZE);
669         }
670         
671         public int read(char[] values, int offset, int length) throws java.io.IOException JavaDoc {
672             //System.out.println("SinglePatchReader.read("+offset+", "+length+")");
673
int totRead = 0;
674             while (length > 0) {
675                 int buffCopyLength;
676                 if (length < buffLength) {
677                     buffCopyLength = length;
678                     length = 0;
679                 } else {
680                     if (buffLength > 0) {
681                         buffCopyLength = buffLength;
682                         length -= buffLength;
683                     } else {
684                         if (isAtEndOfPatch) {
685                             length = 0;
686                             buffCopyLength = -1;
687                         } else {
688                             buffLength = readTillEndOfPatch(buffer);
689                             buffPos = 0;
690                             if (buffLength <= 0) {
691                                 buffCopyLength = -1;
692                             } else {
693                                 buffCopyLength = Math.min(length, buffLength);
694                                 length -= buffCopyLength;
695                             }
696                         }
697                     }
698                 }
699                 if (buffCopyLength > 0) {
700                     System.arraycopy(buffer, buffPos, values, offset, buffCopyLength);
701                     offset += buffCopyLength;
702                     buffLength -= buffCopyLength;
703                     buffPos += buffCopyLength;
704                     totRead += buffCopyLength;
705                 } else {
706                     length = 0;
707                 }
708             }
709             if (totRead == 0) totRead = -1;
710             //System.out.println(" read = '"+((totRead >= 0) ? new String(values, 0, totRead) : "NOTHING")+"', totRead = "+totRead);
711
return totRead;
712         }
713         
714         private int readTillEndOfPatch(char[] buffer) throws IOException JavaDoc {
715             int length = in.read(buffer);
716             String JavaDoc input = new String JavaDoc(buffer);
717             int end = 0;
718             if (input.startsWith(FILE_INDEX) || ((end = input.indexOf("\n"+FILE_INDEX))) >= 0) {
719                 isAtEndOfPatch = true;
720             } else {
721                 end = input.lastIndexOf('\n');
722                 if (end >= 0) end++;
723             }
724             if (end >= 0 && end < length) {
725                 in.unread(buffer, end, length - end);
726                 length = end;
727             }
728             if (end == 0) length = -1;
729             return length;
730         }
731         
732         public void close() throws java.io.IOException JavaDoc {
733             // Do nothing!
734
}
735         
736         private static final String JavaDoc FILE_INDEX = "Index: "; // NOI18N
737

738         private boolean hasNextPatch(int[] diffType, String JavaDoc[] fileName) throws IOException JavaDoc {
739             isAtEndOfPatch = false; // We're prepared for the next patch
740
PushbackReader JavaDoc patchSource = in;
741             char[] buff = new char[DIFFERENCE_DELIMETER.length()];
742             int length;
743             Pattern JavaDoc normRegexp;
744             boolean contextBeginDetected = false;
745             try {
746                 normRegexp = Pattern.compile(CmdlineDiffProvider.DIFF_REGEXP);
747             } catch (PatternSyntaxException JavaDoc rsex) {
748                 normRegexp = null;
749             }
750             while ((length = patchSource.read(buff)) > 0) {
751                 String JavaDoc input = new String JavaDoc(buff, 0, length);
752                 int nl;
753                 int nln = input.indexOf('\n');
754                 int nlr = input.indexOf('\r');
755                 if (nln < 0) nl = nlr;
756                 else nl = nln;
757                 if (nl >= 0) {
758                     if (nln > 0 && nln == nlr + 1) {
759                         input = input.substring(0, nl - 1);
760                     } else {
761                         input = input.substring(0, nl);
762                     }
763                     if (nl + 1 < length) {
764                         patchSource.unread(buff, nl + 1, length - (nl + 1));
765                         length = nl + 1;
766                     }
767                 }
768                 if (input.equals(DIFFERENCE_DELIMETER)) {
769                     diffType[0] = CONTEXT_DIFF;
770                     patchSource.unread(buff, 0, length);
771                     return true;
772                 } else if (input.startsWith(UNIFIED_MARK + " ")) {
773                     diffType[0] = UNIFIED_DIFF;
774                     patchSource.unread(buff, 0, length);
775                     return true;
776                 } else if (input.startsWith(FILE_INDEX)) {
777                     StringBuffer JavaDoc name = new StringBuffer JavaDoc(input.substring(FILE_INDEX.length()));
778                     if (nl < 0) {
779                         int r;
780                         char c;
781                         while ((c = (char) (r = patchSource.read())) != '\n' && r != -1 && r != '\r') {
782                             name.append(c);
783                         }
784                     }
785                     fileName[1] = name.toString();
786                 } else if (input.startsWith(CONTEXT_MARK1B) || !contextBeginDetected && input.startsWith(UNIFIED_MARK1)) {
787                     StringBuffer JavaDoc name;
788                     if (input.startsWith(CONTEXT_MARK1B)) {
789                         contextBeginDetected = true;
790                         name = new StringBuffer JavaDoc(input.substring(CONTEXT_MARK1B.length()));
791                     } else {
792                         name = new StringBuffer JavaDoc(input.substring(UNIFIED_MARK1.length()));
793                     }
794                     String JavaDoc sname = name.toString();
795                     int spaceIndex = sname.indexOf('\t');
796                     if (spaceIndex > 0) {
797                         name = name.delete(spaceIndex, name.length());
798                     }
799                     if (nl < 0) {
800                         int r = 0;
801                         char c = 0;
802                         if (spaceIndex < 0) {
803                             while ((c = (char) (r = patchSource.read())) != '\n' && c != '\r' && c != '\t' && r != -1) {
804                                 name.append(c);
805                             }
806                         }
807                         if (c != '\n' && c != '\r' && r != -1) {
808                             while ((c = (char) (r = patchSource.read())) != '\n' && c != '\r' && r != -1) ; // Read the rest of the line
809
}
810                         if (c == '\r') {
811                             r = patchSource.read();
812                             if (r != -1) {
813                                 c = (char) r;
814                                 if (c != '\n') patchSource.unread(c);
815                             }
816                         }
817                     }
818                     fileName[0] = name.toString();
819                 } else if (normRegexp != null && normRegexp.matcher(input).matches()) {
820                     diffType[0] = NORMAL_DIFF;
821                     patchSource.unread(buff, 0, length);
822                     return true;
823                 } else { // Read the rest of the garbaged line
824
if (nl < 0) {
825                         int r;
826                         char c;
827                         while ((c = (char) (r = patchSource.read())) != '\n' && c != '\r' && r != -1) ;
828                         if (c == '\r') {
829                             r = patchSource.read();
830                             if (r != -1) {
831                                 c = (char) r;
832                                 if (c != '\n') patchSource.unread(c);
833                             }
834                         }
835                     }
836                 }
837             }
838             return false;
839         }
840         
841     }
842     
843     public static class FileDifferences extends Object JavaDoc {
844         
845         private String JavaDoc fileName;
846         private String JavaDoc indexName;
847         private Difference[] diffs;
848         
849         public FileDifferences(String JavaDoc fileName, String JavaDoc indexName, Difference[] diffs) {
850             this.fileName = fileName;
851             this.diffs = diffs;
852             this.indexName = indexName;
853         }
854
855         /**
856          * @return header filename (typically absolute path on source host) or null
857          */

858         public final String JavaDoc getFileName() {
859             return fileName;
860         }
861
862         /**
863          * @return relative Index: file name or null
864          */

865         public final String JavaDoc getIndexName() {
866             return indexName;
867         }
868
869         public final Difference[] getDifferences() {
870             return diffs;
871         }
872     }
873     
874 }
875
Popular Tags