KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > edu > umd > cs > findbugs > workflow > BugHistory


1 /*
2  * FindBugs - Find bugs in Java programs
3  * Copyright (C) 2003-2005, University of Maryland
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18  */

19
20 package edu.umd.cs.findbugs.workflow;
21
22 import java.io.BufferedOutputStream JavaDoc;
23 import java.io.BufferedReader JavaDoc;
24 import java.io.File JavaDoc;
25 import java.io.FileNotFoundException JavaDoc;
26 import java.io.FileOutputStream JavaDoc;
27 import java.io.FileReader JavaDoc;
28 import java.io.IOException JavaDoc;
29 import java.io.InputStreamReader JavaDoc;
30 import java.io.OutputStream JavaDoc;
31 import java.util.Collection JavaDoc;
32 import java.util.Iterator JavaDoc;
33 import java.util.LinkedHashMap JavaDoc;
34 import java.util.LinkedList JavaDoc;
35 import java.util.List JavaDoc;
36 import java.util.Set JavaDoc;
37 import java.util.TreeSet JavaDoc;
38 import java.util.Map.Entry;
39
40 import org.dom4j.DocumentException;
41
42 import edu.umd.cs.findbugs.BugInstance;
43 import edu.umd.cs.findbugs.FuzzyBugComparator;
44 import edu.umd.cs.findbugs.Project;
45 import edu.umd.cs.findbugs.SloppyBugComparator;
46 import edu.umd.cs.findbugs.SortedBugCollection;
47 import edu.umd.cs.findbugs.VersionInsensitiveBugComparator;
48 import edu.umd.cs.findbugs.WarningComparator;
49 import edu.umd.cs.findbugs.config.CommandLine;
50 import edu.umd.cs.findbugs.model.MovedClassMap;
51
52 /**
53  * Analyze bug results to find new, fixed, and retained bugs
54  * between versions of the same program. Uses VersionInsensitiveBugComparator
55  * (or FuzzyBugComparator)
56  * to determine when two BugInstances are the "same".
57  * The new BugCollection returned is a deep copy of one of the input collections
58  * (depending on the operation performed), with only a subset of the original
59  * BugInstances retained. Because it is a deep copy, it may be freely modified.
60  *
61  * @author David Hovemeyer
62  */

63 @Deprecated JavaDoc
64 public class BugHistory {
65     private static final boolean DEBUG = false;
66     
67     private static class BugCollectionAndProject {
68         SortedBugCollection bugCollection;
69         Project project;
70         
71         public BugCollectionAndProject(SortedBugCollection bugCollection, Project project) {
72             this.bugCollection = bugCollection;
73             this.project = project;
74         }
75         
76         /**
77          * @return Returns the bugCollection.
78          */

79         public SortedBugCollection getBugCollection() {
80             return bugCollection;
81         }
82         
83         /**
84          * @return Returns the project.
85          */

86         public Project getProject() {
87             return project;
88         }
89     }
90
91     /**
92      * Cache of BugCollections and Projects for when we're operating in bulk mode.
93      * If the pairs of files form a chronological sequence, then we won't have to
94      * repeatedly perform I/O.
95      */

96     private static class BugCollectionAndProjectCache extends LinkedHashMap JavaDoc<String JavaDoc,BugCollectionAndProject> {
97         private static final long serialVersionUID = 1L;
98         
99         // 2 should be sufficient if the pairs are sorted
100
private static final int CACHE_SIZE = 5;
101         
102         /* (non-Javadoc)
103          * @see java.util.LinkedHashMap#removeEldestEntry(java.util.Map.Entry)
104          */

105         //@Override
106
@Override JavaDoc
107         protected boolean removeEldestEntry(Entry<String JavaDoc, BugCollectionAndProject> eldest) {
108             return size() > CACHE_SIZE;
109         }
110         
111         /**
112          * Fetch an entry, reading it if necessary.
113          *
114          * @param fileName file to get
115          * @return the BugCollectionAndProject for the file
116          * @throws IOException
117          * @throws DocumentException
118          */

119         public BugCollectionAndProject fetch(String JavaDoc fileName) throws IOException JavaDoc, DocumentException {
120             BugCollectionAndProject result = get(fileName);
121             if (result == null) {
122                 Project project = new Project();
123                 SortedBugCollection bugCollection = readCollection(fileName, project);
124                 result = new BugCollectionAndProject(bugCollection, project);
125                 put(fileName, result);
126             }
127             return result;
128         }
129     }
130     
131     /**
132      * A set operation between two bug collections.
133      */

134     public interface SetOperation {
135         /**
136          * Perform the set operation.
137          *
138          * @param result Set to put the resulting BugInstances in
139          * @param origCollection original BugCollection
140          * @param newCollection new BugCollection
141          * @return the input bug collection the results are taken from
142          */

143         public SortedBugCollection perform(Set JavaDoc<BugInstance> result,
144                 SortedBugCollection origCollection, SortedBugCollection newCollection);
145     }
146     
147     /**
148      * Get the warnings which were <em>added</em>,
149      * meaning that they were not part of the original BugCollection.
150      * The BugInstances returned are from the new BugCollection.
151      */

152     public static final SetOperation ADDED_WARNINGS = new SetOperation(){
153         public SortedBugCollection perform(Set JavaDoc<BugInstance> result,
154                 SortedBugCollection origCollection, SortedBugCollection newCollection) {
155             result.addAll(newCollection.getCollection());
156
157             // Get shared instances
158
List JavaDoc<BugInstance> inBoth = getSharedInstances(result, origCollection);
159             
160             // Remove the shared instances from the result
161
removeBugInstances(result, inBoth);
162             
163             return newCollection;
164         }
165     };
166     
167     /**
168      * Get the warnings which were <em>retained</em>,
169      * meaning that they occur in both the original and new BugCollections.
170      * The BugInstances returned are from the new BugCollection.
171      */

172     public static final SetOperation RETAINED_WARNINGS = new SetOperation(){
173         public SortedBugCollection perform(Set JavaDoc<BugInstance> result,
174                 SortedBugCollection origCollection, SortedBugCollection newCollection) {
175             result.addAll(newCollection.getCollection());
176             if (DEBUG) System.out.println(result.size() + " instances initially");
177             
178             // Get shared instances
179
List JavaDoc<BugInstance> inBoth = getSharedInstances(result, origCollection);
180             
181             // Replace instances with only those shared
182
replaceBugInstances(result, inBoth);
183             
184             if (DEBUG) System.out.println(result.size() + " after retaining new instances");
185             return newCollection;
186         }
187     };
188     
189     /**
190      * Get the warnings which were <em>removed</em>,
191      * meaning that they occur in the original BugCollection but not in
192      * the new BugCollection.
193      * The BugInstances returned are from the original BugCollection.
194      */

195     public static final SetOperation REMOVED_WARNINGS = new SetOperation(){
196         public SortedBugCollection perform(Set JavaDoc<BugInstance> result,
197                 SortedBugCollection origCollection, SortedBugCollection newCollection) {
198             result.addAll(origCollection.getCollection());
199             
200             // Get shared instances
201
List JavaDoc<BugInstance> inBoth = getSharedInstances(result, newCollection);
202             
203             // Remove shared instances
204
removeBugInstances(result, inBoth);
205             
206             return origCollection;
207         }
208     };
209     
210     private SortedBugCollection origCollection, newCollection;
211     private SortedBugCollection resultCollection;
212     private SortedBugCollection originator;
213     private WarningComparator comparator;
214     
215     /**
216      * Contructor.
217      *
218      * @param origCollection the original BugCollection
219      * @param newCollection the new BugCollection
220      */

221     public BugHistory(SortedBugCollection origCollection, SortedBugCollection newCollection) {
222         this.origCollection = origCollection;
223         this.newCollection = newCollection;
224     }
225     
226     /**
227      * Get the Comparator used to compare BugInstances from different BugCollections.
228      */

229     public WarningComparator getComparator() {
230         return comparator;
231     }
232     
233     /**
234      * @param comparator The comparator to set.
235      */

236     public void setComparator(WarningComparator comparator) {
237         this.comparator = comparator;
238     }
239
240     /**
241      * Perform a SetOperation.
242      *
243      * @param operation the SetOperation
244      * @return the BugCollection resulting from performing the SetOperation
245      */

246     public SortedBugCollection performSetOperation(SetOperation operation) {
247         // Create a result set which uses the version-insensitive/fuzzy bug comparator.
248
// This will help figure out which bug instances are the "same"
249
// between versions.
250
TreeSet JavaDoc<BugInstance> result = new TreeSet JavaDoc<BugInstance>(getComparator());
251         
252         // Perform the operation, keeping track of which input BugCollection
253
// should be cloned for metadata.
254
originator = operation.perform(result, origCollection, newCollection);
255         
256         // Clone the actual BugInstances selected by the set operation.
257
Collection JavaDoc<BugInstance> selected = new LinkedList JavaDoc<BugInstance>();
258         SortedBugCollection.cloneAll(selected, result);
259         
260         // Duplicate the collection from which the results came,
261
// in order to copy all metadata, such as analysis errors,
262
// class/method hashes, etc.
263
SortedBugCollection resultCollection = originator.duplicate();
264         
265         // Replace with just the cloned instances of the subset selected by the set operation.
266
resultCollection.clearBugInstances();
267         resultCollection.addAll(selected);
268         
269         this.resultCollection = resultCollection;
270         
271         return resultCollection;
272     }
273     
274     /**
275      * @return Returns the originator.
276      */

277     public SortedBugCollection getOriginator() {
278         return originator;
279     }
280     
281     /**
282      * @return Returns the origCollection.
283      */

284     public SortedBugCollection getOrigCollection() {
285         return origCollection;
286     }
287     
288     /**
289      * @return Returns the newCollection.
290      */

291     public SortedBugCollection getNewCollection() {
292         return newCollection;
293     }
294     
295     /**
296      * @return Returns the result.
297      */

298     public SortedBugCollection getResultCollection() {
299         return resultCollection;
300     }
301
302     public void writeResultCollection(Project origProject, Project newProject, OutputStream JavaDoc outputStream) throws IOException JavaDoc {
303         getResultCollection().writeXML(
304                 outputStream, getOriginator() == getOrigCollection() ? origProject : newProject);
305     }
306
307     /**
308      * Get instances shared between given Set and BugCollection.
309      * The Set is queried for membership, because it has a special Comparator
310      * which can match BugInstances from different versions.
311      *
312      * @param result the Set
313      * @param collection the BugCollection
314      * @return List of shared instances
315      */

316     private static List JavaDoc<BugInstance> getSharedInstances(Set JavaDoc<BugInstance> result, SortedBugCollection collection) {
317         List JavaDoc<BugInstance> inBoth = new LinkedList JavaDoc<BugInstance>();
318         for (Iterator JavaDoc<BugInstance> i = collection.iterator(); i.hasNext();) {
319             BugInstance origBugInstance = i.next();
320             if (result.contains(origBugInstance)) {
321                 inBoth.add(origBugInstance);
322             }
323         }
324         return inBoth;
325     }
326
327     /**
328      * Replace all of the BugInstances in given Set with the given Collection.
329      *
330      * @param dest the Set to replace the instances of
331      * @param source the Collection containing the instances to put in the Set
332      */

333     private static void replaceBugInstances(Set JavaDoc<BugInstance> dest, Collection JavaDoc<BugInstance> source) {
334         dest.clear();
335         dest.addAll(source);
336     }
337
338     /**
339      * Remove bug instances from Set.
340      *
341      * @param result the Set
342      * @param toRemove Collection of BugInstances to remove
343      */

344     private static void removeBugInstances(Set JavaDoc<BugInstance> result, Collection JavaDoc<BugInstance> toRemove) {
345         for (BugInstance aToRemove : toRemove) {
346             result.remove(aToRemove);
347         }
348     }
349     
350     private static final int VERSION_INSENSITIVE_COMPARATOR = 0;
351     private static final int FUZZY_COMPARATOR = 1;
352     private static final int SLOPPY_COMPARATOR = 2;
353     
354     private static class BugHistoryCommandLine extends CommandLine {
355         private int comparatorType = VERSION_INSENSITIVE_COMPARATOR;
356         private boolean count;
357         private String JavaDoc opName;
358         private SetOperation setOp;
359         private String JavaDoc listFile;
360         private String JavaDoc outputDir;
361         private boolean verbose;
362         
363         public BugHistoryCommandLine() {
364             addSwitch("-fuzzy", "use fuzzy warning matching");
365             addSwitch("-sloppy", "use sloppy warning matching");
366             addSwitch("-added", "compute added warnings");
367             addSwitch("-new", "same as \"-added\" switch");
368             addSwitch("-removed", "compute removed warnings");
369             addSwitch("-fixed", "same as \"-removed\" switch");
370             addSwitch("-retained", "compute retained warnings");
371             addSwitch("-count", "just print warning count");
372             addOption("-bulk", "file of csv xml file pairs", "bulk mode, output written to v2-OP.xml");
373             addOption("-outputDir", "output dir", "output directory for bulk mode (optional)");
374             addSwitch("-verbose", "verbose output for bulk mode");
375         }
376         
377          /* (non-Javadoc)
378          * @see edu.umd.cs.findbugs.config.CommandLine#handleOption(java.lang.String, java.lang.String)
379          */

380         //@Override
381
@Override JavaDoc
382         protected void handleOption(String JavaDoc option, String JavaDoc optionExtraPart) throws IOException JavaDoc {
383             if (option.equals("-fuzzy")) {
384                 comparatorType = FUZZY_COMPARATOR;
385             } else if (option.equals("-sloppy")) {
386                 comparatorType = SLOPPY_COMPARATOR;
387             } else if (option.equals("-added") || option.equals("-new")) {
388                 opName = option;
389                 setOp = ADDED_WARNINGS;
390             } else if (option.equals("-removed") || option.equals("-fixed")) {
391                 opName = option;
392                 setOp = REMOVED_WARNINGS;
393             } else if (option.equals("-retained")) {
394                 opName = option;
395                 setOp = RETAINED_WARNINGS;
396             } else if (option.equals("-count")) {
397                 count = true;
398             } else if (option.equals("-verbose")) {
399                 verbose = true;
400             } else {
401                 throw new IllegalArgumentException JavaDoc("Unknown option: " + option);
402             }
403         }
404         
405         /* (non-Javadoc)
406          * @see edu.umd.cs.findbugs.config.CommandLine#handleOptionWithArgument(java.lang.String, java.lang.String)
407          */

408         //@Override
409
@Override JavaDoc
410         protected void handleOptionWithArgument(String JavaDoc option, String JavaDoc argument) throws IOException JavaDoc {
411             if (option.equals("-bulk")) {
412                 listFile = argument;
413             } else if (option.equals("-outputDir")) {
414                 outputDir = argument;
415             } else {
416                 throw new IllegalArgumentException JavaDoc("Unknown option: " + option);
417             }
418         }
419         
420         /**
421          * @return Returns the comparatorType.
422          */

423         public int getComparatorType() {
424             return comparatorType;
425         }
426         
427         /**
428          * @return true if we should just output the delta
429          */

430         public boolean isCount() {
431             return count;
432         }
433         
434         /**
435          * @return Returns the opName.
436          */

437         public String JavaDoc getOpName() {
438             return opName;
439         }
440         
441         /**
442          * @return Returns the set operation to apply.
443          */

444         public SetOperation getSetOp() {
445             return setOp;
446         }
447         
448         /**
449          * @return Returns the listFile.
450          */

451         public String JavaDoc getListFile() {
452             return listFile;
453         }
454         
455         /**
456          * @return Returns the outputDir.
457          */

458         public String JavaDoc getOutputDir() {
459             return outputDir;
460         }
461         
462         /**
463          * @return Returns the verbose.
464          */

465         public boolean isVerbose() {
466             return verbose;
467         }
468
469         public void configure(BugHistory bugHistory, SortedBugCollection origCollection, SortedBugCollection newCollection) {
470             // Create comparator
471
WarningComparator comparator;
472             switch (getComparatorType()) {
473             case VERSION_INSENSITIVE_COMPARATOR:
474                 comparator = new VersionInsensitiveBugComparator();
475                 break;
476             case FUZZY_COMPARATOR:
477                 FuzzyBugComparator fuzzy = new FuzzyBugComparator();
478                 fuzzy.registerBugCollection(origCollection);
479                 fuzzy.registerBugCollection(newCollection);
480                 comparator = fuzzy;
481                 break;
482             case SLOPPY_COMPARATOR:
483                 comparator = new SloppyBugComparator();
484                 break;
485             default:
486                 throw new IllegalStateException JavaDoc();
487             }
488             
489             // Handle renamed classes
490
MovedClassMap classNameRewriter = new MovedClassMap(origCollection, newCollection).execute();
491             comparator.setClassNameRewriter(classNameRewriter);
492             
493             bugHistory.setComparator(comparator);
494         }
495         
496         public BugHistory createAndExecute(
497                 String JavaDoc origFile, String JavaDoc newFile, Project origProject, Project newProject) throws IOException JavaDoc, DocumentException {
498             SortedBugCollection origCollection = readCollection(origFile, origProject);
499             SortedBugCollection newCollection = readCollection(newFile, newProject);
500
501             return createAndExecute(origCollection, newCollection, origProject, newProject);
502         }
503         
504         public BugHistory createAndExecute(
505                 SortedBugCollection origCollection,
506                 SortedBugCollection newCollection,
507                 Project origProject,
508                 Project newProject) {
509             BugHistory bugHistory = new BugHistory(origCollection, newCollection);
510
511             configure(bugHistory, origCollection, newCollection);
512             
513             // We can ignore the return value because it will be accessible by calling getResult()
514
bugHistory.performSetOperation(getSetOp());
515
516             return bugHistory;
517         }
518         
519         public String JavaDoc getBulkOutputFileName(String JavaDoc fileName) {
520             File JavaDoc file = new File JavaDoc(fileName);
521             
522             String JavaDoc filePart = file.getName();
523             int ext = filePart.lastIndexOf('.');
524             if (ext < 0 ) {
525                 filePart = filePart + getOpName();
526             } else {
527                 filePart = filePart.substring(0, ext) + getOpName() + filePart.substring(ext);
528             }
529
530             String JavaDoc dirPart = (getOutputDir() != null) ? getOutputDir() : file.getParent();
531                     
532             File JavaDoc outputFile = new File JavaDoc(dirPart, filePart);
533             return outputFile.getPath();
534         }
535     }
536     
537     private static SortedBugCollection readCollection(String JavaDoc fileName, Project project)
538             throws IOException JavaDoc, DocumentException {
539         SortedBugCollection result = new SortedBugCollection();
540         result.readXML(fileName, project);
541         return result;
542     }
543
544     public static void main(String JavaDoc[] argv) throws Exception JavaDoc {
545         BugHistoryCommandLine commandLine = new BugHistoryCommandLine();
546         int argCount = commandLine.parse(argv);
547         
548         if (commandLine.getSetOp() == null) {
549             System.err.println("No set operation specified");
550             printUsage();
551             System.exit(1);
552         }
553
554         if (commandLine.getListFile() != null) {
555             if (argv.length != argCount) {
556                 printUsage();
557             }
558
559             runBulk(commandLine);
560         } else{
561             if (argv.length - argCount != 2) {
562                 printUsage();
563             }
564             
565             String JavaDoc origFile = argv[argCount++];
566             String JavaDoc newFile = argv[argCount++];
567             
568             runSinglePair(commandLine, origFile, newFile);
569         }
570     }
571
572     private static void runBulk(BugHistoryCommandLine commandLine) throws FileNotFoundException JavaDoc, IOException JavaDoc, DocumentException {
573         BufferedReader JavaDoc reader;
574         if (commandLine.getListFile().equals("-")) {
575             reader = new BufferedReader JavaDoc(new InputStreamReader JavaDoc(System.in));
576         } else {
577             reader = new BufferedReader JavaDoc(new FileReader JavaDoc(commandLine.getListFile()));
578         }
579         int missing = 0;
580         try {
581         BugCollectionAndProjectCache cache = new BugCollectionAndProjectCache();
582
583         
584         String JavaDoc csvRecord;
585         while ((csvRecord = reader.readLine()) != null) {
586             csvRecord = csvRecord.trim();
587             String JavaDoc[] tuple = csvRecord.split(",");
588             if (tuple.length < 2)
589                 continue;
590
591             String JavaDoc origFile = tuple[0];
592             String JavaDoc newFile = tuple[1];
593             
594             BugCollectionAndProject orig;
595             BugCollectionAndProject next;
596             
597             try {
598                 orig = cache.fetch(origFile);
599                 next = cache.fetch(newFile);
600             } catch (RuntimeException JavaDoc e) {
601                 throw e;
602             } catch (Exception JavaDoc e ) {
603                 System.err.println("Warning: error reading bug collection: " + e.toString());
604                 ++missing;
605                 continue;
606             }
607             
608             if (commandLine.isVerbose()) {
609                 System.out.print("Computing delta from " + origFile + " to " + newFile + "...");
610                 System.out.flush();
611             }
612
613             BugHistory bugHistory = commandLine.createAndExecute(
614                     orig.getBugCollection(), next.getBugCollection(), orig.getProject(), next.getProject());
615             
616             String JavaDoc outputFile = commandLine.getBulkOutputFileName(newFile);
617             if (commandLine.isVerbose()) {
618                 System.out.print("Writing " + outputFile + "...");
619                 System.out.flush();
620             }
621             
622             
623             bugHistory.writeResultCollection(orig.getProject(), next.getProject(),
624                     new BufferedOutputStream JavaDoc(new FileOutputStream JavaDoc(outputFile)));
625             if (commandLine.isVerbose()) {
626                 System.out.println("done");
627             }
628         }
629         } finally {
630             reader.close();
631         }
632         if (missing > 0) {
633             System.err.println(missing + " pairs skipped because of missing files");
634         }
635     }
636
637     private static void runSinglePair(BugHistoryCommandLine commandLine, String JavaDoc origFile, String JavaDoc newFile) throws IOException JavaDoc, DocumentException {
638         Project origProject = new Project();
639         Project newProject = new Project();
640         BugHistory bugHistory = commandLine.createAndExecute(origFile, newFile, origProject, newProject);
641         
642         if (commandLine.isCount()) {
643             System.out.println(bugHistory.getResultCollection().getCollection().size());
644         } else {
645             OutputStream JavaDoc outputStream = System.out;
646             bugHistory.writeResultCollection(origProject, newProject, outputStream);
647         }
648     }
649
650     /**
651      * Print usage and exit.
652      */

653     private static void printUsage() {
654         System.err.println("Usage: " + BugHistory.class.getName() +
655                 " [options] <operation> <old results> <new results>");
656         new BugHistoryCommandLine().printUsage(System.err);
657         System.exit(1);
658     }
659 }
660
661 // vim:ts=4
662
Popular Tags