KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > compare > internal > patch > PatchReader


1 /*******************************************************************************
2  * Copyright (c) 2006, 2007 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.compare.internal.patch;
12
13 import java.io.BufferedReader JavaDoc;
14 import java.io.IOException JavaDoc;
15 import java.text.ParseException JavaDoc;
16 import java.util.*;
17
18 import org.eclipse.core.resources.IProject;
19 import org.eclipse.core.resources.ResourcesPlugin;
20 import org.eclipse.core.runtime.*;
21 import org.eclipse.swt.SWT;
22
23 import com.ibm.icu.text.DateFormat;
24 import com.ibm.icu.text.SimpleDateFormat;
25
26 public class PatchReader {
27
28     private static final boolean DEBUG= false;
29     
30     private static final String JavaDoc DEV_NULL= "/dev/null"; //$NON-NLS-1$
31

32     static protected final String JavaDoc MARKER_TYPE= "org.eclipse.compare.rejectedPatchMarker"; //$NON-NLS-1$
33

34     // diff formats
35
// private static final int CONTEXT= 0;
36
// private static final int ED= 1;
37
// private static final int NORMAL= 2;
38
// private static final int UNIFIED= 3;
39

40     // we recognize the following date/time formats
41
private static DateFormat[] DATE_FORMATS= new DateFormat[] {
42         new SimpleDateFormat("EEE MMM dd kk:mm:ss yyyy"), //$NON-NLS-1$
43
new SimpleDateFormat("yyyy/MM/dd kk:mm:ss"), //$NON-NLS-1$
44
new SimpleDateFormat("EEE MMM dd kk:mm:ss yyyy", Locale.US) //$NON-NLS-1$
45
};
46
47     private boolean fIsWorkspacePatch;
48     private DiffProject[] fDiffProjects;
49     private FileDiff[] fDiffs;
50
51     // API for writing new multi-project patch format
52
public static final String JavaDoc MULTIPROJECTPATCH_HEADER= "### Eclipse Workspace Patch"; //$NON-NLS-1$
53

54     public static final String JavaDoc MULTIPROJECTPATCH_VERSION= "1.0"; //$NON-NLS-1$
55

56     public static final String JavaDoc MULTIPROJECTPATCH_PROJECT= "#P"; //$NON-NLS-1$
57

58     public void parse(BufferedReader JavaDoc reader) throws IOException JavaDoc {
59         List diffs= new ArrayList();
60         HashMap diffProjects= new HashMap(4);
61         String JavaDoc line= null;
62         boolean reread= false;
63         String JavaDoc diffArgs= null;
64         String JavaDoc fileName= null;
65         // no project means this is a single patch,create a placeholder project for now
66
// which will be replaced by the target selected by the user in the preview pane
67
String JavaDoc project= ""; //$NON-NLS-1$
68
fIsWorkspacePatch= false;
69
70         LineReader lr= new LineReader(reader);
71         if (!"carbon".equals(SWT.getPlatform())) //$NON-NLS-1$
72
lr.ignoreSingleCR();
73
74         // Test for our format
75
line= lr.readLine();
76         if (line != null && line.startsWith(PatchReader.MULTIPROJECTPATCH_HEADER)) {
77             fIsWorkspacePatch= true;
78         } else {
79             parse(lr, line);
80             return;
81         }
82
83         // read leading garbage
84
while (true) {
85             if (!reread)
86                 line= lr.readLine();
87             reread= false;
88             if (line == null)
89                 break;
90             if (line.length() < 4)
91                 continue; // too short
92

93             if (line.startsWith(PatchReader.MULTIPROJECTPATCH_PROJECT)) {
94                 project= line.substring(2).trim();
95                 continue;
96             }
97
98             if (line.startsWith("Index: ")) { //$NON-NLS-1$
99
fileName= line.substring(7).trim();
100                 continue;
101             }
102             if (line.startsWith("diff")) { //$NON-NLS-1$
103
diffArgs= line.substring(4).trim();
104                 continue;
105             }
106
107             if (line.startsWith("--- ")) { //$NON-NLS-1$
108
// if there is no current project or
109
// the current project doesn't equal the newly parsed project
110
// reset the current project to the newly parsed one, create a new DiffProject
111
// and add it to the array
112
DiffProject diffProject;
113                 if (!diffProjects.containsKey(project)) {
114                     IProject iproject= ResourcesPlugin.getWorkspace().getRoot().getProject(project);
115                     diffProject= new DiffProject(iproject);
116                     diffProjects.put(project, diffProject);
117                 } else {
118                     diffProject= (DiffProject) diffProjects.get(project);
119                 }
120
121                 line= readUnifiedDiff(diffs, lr, line, diffArgs, fileName, diffProject);
122                 diffArgs= fileName= null;
123                 reread= true;
124             }
125         }
126
127         lr.close();
128
129         fDiffProjects= (DiffProject[]) diffProjects.values().toArray(new DiffProject[diffProjects.size()]);
130         fDiffs = (FileDiff[]) diffs.toArray(new FileDiff[diffs.size()]);
131     }
132     
133     private String JavaDoc readUnifiedDiff(List diffs, LineReader lr, String JavaDoc line, String JavaDoc diffArgs, String JavaDoc fileName, DiffProject diffProject) throws IOException JavaDoc {
134         List newDiffs= new ArrayList();
135         String JavaDoc nextLine= readUnifiedDiff(newDiffs, lr, line, diffArgs, fileName);
136         for (Iterator iter= newDiffs.iterator(); iter.hasNext();) {
137             FileDiff diff= (FileDiff) iter.next();
138             diffProject.add(diff);
139             diffs.add(diff);
140         }
141         return nextLine;
142     }
143     
144     public void parse(LineReader lr, String JavaDoc line) throws IOException JavaDoc {
145         List diffs= new ArrayList();
146         boolean reread= false;
147         String JavaDoc diffArgs= null;
148         String JavaDoc fileName= null;
149         List headerLines = new ArrayList();
150
151         // read leading garbage
152
reread= line!=null;
153         while (true) {
154             if (!reread)
155                 line= lr.readLine();
156             reread= false;
157             if (line == null)
158                 break;
159                                 
160             // remember some infos
161
if (line.startsWith("Index: ")) { //$NON-NLS-1$
162
fileName= line.substring(7).trim();
163             } else if (line.startsWith("diff")) { //$NON-NLS-1$
164
diffArgs= line.substring(4).trim();
165             } else if (line.startsWith("--- ")) { //$NON-NLS-1$
166
line= readUnifiedDiff(diffs, lr, line, diffArgs, fileName);
167                 if (!headerLines.isEmpty())
168                     setHeader((FileDiff)diffs.get(diffs.size() - 1), headerLines);
169                 diffArgs= fileName= null;
170                 reread= true;
171             } else if (line.startsWith("*** ")) { //$NON-NLS-1$
172
line= readContextDiff(diffs, lr, line, diffArgs, fileName);
173                 if (!headerLines.isEmpty())
174                     setHeader((FileDiff)diffs.get(diffs.size() - 1), headerLines);
175                 diffArgs= fileName= null;
176                 reread= true;
177             }
178             
179             // Any lines we read here are header lines.
180
// However, if reread is set, we will add them to the header on the next pass through
181
if (!reread) {
182                 headerLines.add(line);
183             }
184         }
185         
186         lr.close();
187         
188         fDiffs = (FileDiff[]) diffs.toArray(new FileDiff[diffs.size()]);
189     }
190     
191     private void setHeader(FileDiff diff, List headerLines) {
192         String JavaDoc header = Patcher.createString(false, headerLines);
193         diff.setHeader(header);
194         headerLines.clear();
195     }
196
197     /*
198      * Returns the next line that does not belong to this diff
199      */

200     protected String JavaDoc readUnifiedDiff(List diffs, LineReader reader, String JavaDoc line, String JavaDoc args, String JavaDoc fileName) throws IOException JavaDoc {
201
202         String JavaDoc[] oldArgs= split(line.substring(4));
203
204         // read info about new file
205
line= reader.readLine();
206         if (line == null || !line.startsWith("+++ ")) //$NON-NLS-1$
207
return line;
208             
209         String JavaDoc[] newArgs= split(line.substring(4));
210     
211         FileDiff diff= new FileDiff(extractPath(oldArgs, 0, fileName), extractDate(oldArgs, 1),
212                             extractPath(newArgs, 0, fileName), extractDate(newArgs, 1));
213         diffs.add(diff);
214                    
215         int[] oldRange= new int[2];
216         int[] newRange= new int[2];
217         List lines= new ArrayList();
218
219         boolean encounteredPlus = false;
220         boolean encounteredMinus = false;
221         boolean encounteredSpace = false;
222         
223         try {
224             // read lines of hunk
225
while (true) {
226                 
227                 line= reader.readLine();
228                 if (line == null)
229                     return null;
230                     
231                 if (reader.lineContentLength(line) == 0) {
232                     //System.out.println("Warning: found empty line in hunk; ignored");
233
//lines.add(' ' + line);
234
continue;
235                 }
236                 
237                 char c= line.charAt(0);
238                 switch (c) {
239                 case '@':
240                     if (line.startsWith("@@ ")) { //$NON-NLS-1$
241
// flush old hunk
242
if (lines.size() > 0) {
243                             new Hunk(diff, oldRange, newRange, lines,encounteredPlus, encounteredMinus, encounteredSpace);
244                             lines.clear();
245                         }
246                                 
247                         // format: @@ -oldStart,oldLength +newStart,newLength @@
248
extractPair(line, '-', oldRange);
249                         extractPair(line, '+', newRange);
250                         continue;
251                     }
252                     break;
253                 case ' ':
254                     encounteredSpace = true;
255                     lines.add(line);
256                     continue;
257                 case '+':
258                     encounteredPlus = true;
259                     lines.add(line);
260                     continue;
261                 case '-':
262                     encounteredMinus = true;
263                     lines.add(line);
264                     continue;
265                 case '\\':
266                     if (line.indexOf("newline at end") > 0) { //$NON-NLS-1$
267
int lastIndex= lines.size();
268                         if (lastIndex > 0) {
269                             line= (String JavaDoc) lines.get(lastIndex-1);
270                             int end= line.length()-1;
271                             char lc= line.charAt(end);
272                             if (lc == '\n') {
273                                 end--;
274                                 if (end > 0 && line.charAt(end) == '\r')
275                                     end--;
276                             } else if (lc == '\r') {
277                                 end--;
278                             }
279                             line= line.substring(0, end+1);
280                             lines.set(lastIndex-1, line);
281                         }
282                         continue;
283                     }
284                     break;
285                 default:
286                     if (DEBUG) {
287                         int a1= c, a2= 0;
288                         if (line.length() > 1)
289                             a2= line.charAt(1);
290                         System.out.println("char: " + a1 + " " + a2); //$NON-NLS-1$ //$NON-NLS-2$
291
}
292                     break;
293                 }
294                 return line;
295             }
296         } finally {
297             if (lines.size() > 0)
298                 new Hunk(diff, oldRange, newRange, lines, encounteredPlus, encounteredMinus, encounteredSpace);
299         }
300     }
301     
302     /*
303      * Returns the next line that does not belong to this diff
304      */

305     private String JavaDoc readContextDiff(List diffs, LineReader reader, String JavaDoc line, String JavaDoc args, String JavaDoc fileName) throws IOException JavaDoc {
306         
307         String JavaDoc[] oldArgs= split(line.substring(4));
308         
309         // read info about new file
310
line= reader.readLine();
311         if (line == null || !line.startsWith("--- ")) //$NON-NLS-1$
312
return line;
313         
314         String JavaDoc[] newArgs= split(line.substring(4));
315                         
316         FileDiff diff= new FileDiff(extractPath(oldArgs, 0, fileName), extractDate(oldArgs, 1),
317                             extractPath(newArgs, 0, fileName), extractDate(newArgs, 1));
318         diffs.add(diff);
319                    
320         int[] oldRange= new int[2];
321         int[] newRange= new int[2];
322         List oldLines= new ArrayList();
323         List newLines= new ArrayList();
324         List lines= oldLines;
325         
326
327         boolean encounteredPlus = false;
328         boolean encounteredMinus = false;
329         boolean encounteredSpace = false;
330         
331         try {
332             // read lines of hunk
333
while (true) {
334                 
335                 line= reader.readLine();
336                 if (line == null)
337                     return line;
338                 
339                 int l= line.length();
340                 if (l == 0)
341                     continue;
342                 if (l > 1) {
343                     switch (line.charAt(0)) {
344                     case '*':
345                         if (line.startsWith("***************")) { // new hunk //$NON-NLS-1$
346
// flush old hunk
347
if (oldLines.size() > 0 || newLines.size() > 0) {
348                                 new Hunk(diff, oldRange, newRange, unifyLines(oldLines, newLines), encounteredPlus, encounteredMinus, encounteredSpace);
349                                 oldLines.clear();
350                                 newLines.clear();
351                             }
352                             continue;
353                         }
354                         if (line.startsWith("*** ")) { // old range //$NON-NLS-1$
355
// format: *** oldStart,oldEnd ***
356
extractPair(line, ' ', oldRange);
357                             oldRange[1]= oldRange[1]-oldRange[0]+1;
358                             lines= oldLines;
359                             continue;
360                         }
361                         break;
362                     case ' ': // context line
363
if (line.charAt(1) == ' ') {
364                             lines.add(line);
365                             continue;
366                         }
367                         break;
368                     case '+': // addition
369
if (line.charAt(1) == ' ') {
370                             encounteredPlus = true;
371                             lines.add(line);
372                             continue;
373                         }
374                         break;
375                     case '!': // change
376
if (line.charAt(1) == ' ') {
377                             encounteredSpace = true;
378                             lines.add(line);
379                             continue;
380                         }
381                         break;
382                     case '-':
383                         if (line.charAt(1) == ' ') { // deletion
384
encounteredMinus = true;
385                             lines.add(line);
386                             continue;
387                         }
388                         if (line.startsWith("--- ")) { // new range //$NON-NLS-1$
389
// format: *** newStart,newEnd ***
390
extractPair(line, ' ', newRange);
391                             newRange[1]= newRange[1]-newRange[0]+1;
392                             lines= newLines;
393                             continue;
394                         }
395                         break;
396                     default:
397                         break;
398                     }
399                 }
400                 return line;
401             }
402         } finally {
403             // flush last hunk
404
if (oldLines.size() > 0 || newLines.size() > 0)
405                 new Hunk(diff, oldRange, newRange, unifyLines(oldLines, newLines), encounteredPlus, encounteredMinus, encounteredSpace);
406         }
407     }
408     
409     /*
410      * Creates a List of lines in the unified format from
411      * two Lists of lines in the 'classic' format.
412      */

413     private List unifyLines(List oldLines, List newLines) {
414         List result= new ArrayList();
415
416         String JavaDoc[] ol= (String JavaDoc[]) oldLines.toArray(new String JavaDoc[oldLines.size()]);
417         String JavaDoc[] nl= (String JavaDoc[]) newLines.toArray(new String JavaDoc[newLines.size()]);
418         
419         int oi= 0, ni= 0;
420         
421         while (true) {
422             
423             char oc= 0;
424             String JavaDoc o= null;
425             if (oi < ol.length) {
426                 o= ol[oi];
427                 oc= o.charAt(0);
428             }
429             
430             char nc= 0;
431             String JavaDoc n= null;
432             if (ni < nl.length) {
433                 n= nl[ni];
434                 nc= n.charAt(0);
435             }
436             
437             // EOF
438
if (oc == 0 && nc == 0)
439                 break;
440                 
441             // deletion in old
442
if (oc == '-') {
443                 do {
444                     result.add('-' + o.substring(2));
445                     oi++;
446                     if (oi >= ol.length)
447                         break;
448                     o= ol[oi];
449                 } while (o.charAt(0) == '-');
450                 continue;
451             }
452             
453             // addition in new
454
if (nc == '+') {
455                 do {
456                     result.add('+' + n.substring(2));
457                     ni++;
458                     if (ni >= nl.length)
459                         break;
460                     n= nl[ni];
461                 } while (n.charAt(0) == '+');
462                 continue;
463             }
464             
465             // differing lines on both sides
466
if (oc == '!' && nc == '!') {
467                 // remove old
468
do {
469                     result.add('-' + o.substring(2));
470                     oi++;
471                     if (oi >= ol.length)
472                         break;
473                     o= ol[oi];
474                 } while (o.charAt(0) == '!');
475                 
476                 // add new
477
do {
478                     result.add('+' + n.substring(2));
479                     ni++;
480                     if (ni >= nl.length)
481                         break;
482                     n= nl[ni];
483                 } while (n.charAt(0) == '!');
484                 
485                 continue;
486             }
487             
488             // context lines
489
if (oc == ' ' && nc == ' ') {
490                 do {
491                     Assert.isTrue(o.equals(n), "non matching context lines"); //$NON-NLS-1$
492
result.add(' ' + o.substring(2));
493                     oi++;
494                     ni++;
495                     if (oi >= ol.length || ni >= nl.length)
496                         break;
497                     o= ol[oi];
498                     n= nl[ni];
499                 } while (o.charAt(0) == ' ' && n.charAt(0) == ' ');
500                 continue;
501             }
502             
503             if (oc == ' ') {
504                 do {
505                     result.add(' ' + o.substring(2));
506                     oi++;
507                     if (oi >= ol.length)
508                         break;
509                     o= ol[oi];
510                 } while (o.charAt(0) == ' ');
511                 continue;
512             }
513
514             if (nc == ' ') {
515                 do {
516                     result.add(' ' + n.substring(2));
517                     ni++;
518                     if (ni >= nl.length)
519                         break;
520                     n= nl[ni];
521                 } while (n.charAt(0) == ' ');
522                 continue;
523             }
524             
525             Assert.isTrue(false, "unexpected char <" + oc + "> <" + nc + ">"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
526
}
527         
528         return result;
529     }
530     
531     /*
532      * @return the parsed time/date in milliseconds or -1 on error
533      */

534     private long extractDate(String JavaDoc[] args, int n) {
535         if (n < args.length) {
536             String JavaDoc line= args[n];
537             for (int i= 0; i < DATE_FORMATS.length; i++) {
538                 DATE_FORMATS[i].setLenient(true);
539                 try {
540                     Date date= DATE_FORMATS[i].parse(line);
541                     return date.getTime();
542                 } catch (ParseException JavaDoc ex) {
543                     // silently ignored
544
}
545             }
546             // System.err.println("can't parse date: <" + line + ">");
547
}
548         return -1;
549     }
550     
551     /*
552      * Returns null if file name is "/dev/null".
553      */

554     private IPath extractPath(String JavaDoc[] args, int n, String JavaDoc path2) {
555         if (n < args.length) {
556             String JavaDoc path= args[n];
557             if (DEV_NULL.equals(path))
558                 return null;
559             int pos= path.lastIndexOf(':');
560             if (pos >= 0)
561                 path= path.substring(0, pos);
562             if (path2 != null && !path2.equals(path)) {
563                 if (DEBUG) System.out.println("path mismatch: " + path2); //$NON-NLS-1$
564
path= path2;
565             }
566             return new Path(path);
567         }
568         return null;
569     }
570     
571     /*
572      * Tries to extract two integers separated by a comma.
573      * The parsing of the line starts at the position after
574      * the first occurrence of the given character start an ends
575      * at the first blank (or the end of the line).
576      * If only a single number is found this is assumed to be the start of a one line range.
577      * If an error occurs the range -1,-1 is returned.
578      */

579     private void extractPair(String JavaDoc line, char start, int[] pair) {
580         pair[0]= pair[1]= -1;
581         int startPos= line.indexOf(start);
582         if (startPos < 0) {
583             if (DEBUG) System.out.println("parsing error in extractPair: couldn't find \'" + start + "\'"); //$NON-NLS-1$ //$NON-NLS-2$
584
return;
585         }
586         line= line.substring(startPos+1);
587         int endPos= line.indexOf(' ');
588         if (endPos < 0) {
589             if (DEBUG) System.out.println("parsing error in extractPair: couldn't find end blank"); //$NON-NLS-1$
590
return;
591         }
592         line= line.substring(0, endPos);
593         int comma= line.indexOf(',');
594         if (comma >= 0) {
595             pair[0]= Integer.parseInt(line.substring(0, comma));
596             pair[1]= Integer.parseInt(line.substring(comma+1));
597         } else { // abbreviated form for one line patch
598
pair[0]= Integer.parseInt(line);
599             pair[1]= 1;
600         }
601     }
602     
603     
604     /*
605      * Breaks the given string into tab separated substrings.
606      * Leading and trailing whitespace is removed from each token.
607      */

608     private String JavaDoc[] split(String JavaDoc line) {
609         List l= new ArrayList();
610         StringTokenizer st= new StringTokenizer(line, "\t"); //$NON-NLS-1$
611
while (st.hasMoreElements()) {
612             String JavaDoc token= st.nextToken().trim();
613             if (token.length() > 0)
614                 l.add(token);
615         }
616         return (String JavaDoc[]) l.toArray(new String JavaDoc[l.size()]);
617     }
618
619     public boolean isWorkspacePatch() {
620         return fIsWorkspacePatch;
621     }
622
623     public DiffProject[] getDiffProjects() {
624         return fDiffProjects;
625     }
626
627     public FileDiff[] getDiffs() {
628         return fDiffs;
629     }
630     
631     public FileDiff[] getAdjustedDiffs() {
632         if (!isWorkspacePatch() || fDiffs.length == 0)
633             return fDiffs;
634         List result = new ArrayList();
635         for (int i = 0; i < fDiffs.length; i++) {
636             FileDiff diff = fDiffs[i];
637             result.add(diff.asRelativeDiff());
638         }
639         return (FileDiff[]) result.toArray(new FileDiff[result.size()]);
640     }
641 }
642
Popular Tags