KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > edu > umd > cs > findbugs > FindBugs


1 /*
2  * FindBugs - Find bugs in Java programs
3  * Copyright (C) 2003-2006 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;
21
22 import java.io.DataInputStream JavaDoc;
23 import java.io.File JavaDoc;
24 import java.io.FileFilter JavaDoc;
25 import java.io.FileInputStream JavaDoc;
26 import java.io.IOException JavaDoc;
27 import java.io.InputStream JavaDoc;
28 import java.lang.reflect.Constructor JavaDoc;
29 import java.net.URL JavaDoc;
30 import java.net.URLConnection JavaDoc;
31 import java.util.ArrayList JavaDoc;
32 import java.util.Collection JavaDoc;
33 import java.util.HashMap JavaDoc;
34 import java.util.HashSet JavaDoc;
35 import java.util.Iterator JavaDoc;
36 import java.util.LinkedList JavaDoc;
37 import java.util.List JavaDoc;
38 import java.util.Map JavaDoc;
39 import java.util.Set JavaDoc;
40 import java.util.StringTokenizer JavaDoc;
41 import java.util.zip.ZipEntry JavaDoc;
42 import java.util.zip.ZipInputStream JavaDoc;
43
44 import org.apache.bcel.Repository;
45 import org.apache.bcel.classfile.ClassFormatException;
46 import org.apache.bcel.classfile.ClassParser;
47 import org.apache.bcel.classfile.JavaClass;
48 import org.apache.bcel.util.ClassPath;
49
50 import edu.umd.cs.findbugs.annotations.SuppressWarnings;
51 import edu.umd.cs.findbugs.ba.AbstractClassMember;
52 import edu.umd.cs.findbugs.ba.AnalysisContext;
53 import edu.umd.cs.findbugs.ba.AnalysisException;
54 import edu.umd.cs.findbugs.ba.AnalysisFeatures;
55 import edu.umd.cs.findbugs.ba.ClassContext;
56 import edu.umd.cs.findbugs.ba.URLClassPath;
57 import edu.umd.cs.findbugs.ba.URLClassPathRepository;
58 import edu.umd.cs.findbugs.classfile.ClassDescriptor;
59 import edu.umd.cs.findbugs.classfile.IClassObserver;
60 import edu.umd.cs.findbugs.config.AnalysisFeatureSetting;
61 import edu.umd.cs.findbugs.config.CommandLine;
62 import edu.umd.cs.findbugs.config.UserPreferences;
63 import edu.umd.cs.findbugs.config.CommandLine.HelpRequestedException;
64 import edu.umd.cs.findbugs.filter.Filter;
65 import edu.umd.cs.findbugs.filter.FilterException;
66 import edu.umd.cs.findbugs.plan.AnalysisPass;
67 import edu.umd.cs.findbugs.plan.ExecutionPlan;
68 import edu.umd.cs.findbugs.plan.OrderingConstraintException;
69 import edu.umd.cs.findbugs.util.Archive;
70 import edu.umd.cs.findbugs.util.ClassName;
71 import edu.umd.cs.findbugs.visitclass.Constants2;
72
73 /**
74  * An instance of this class is used to apply the selected set of
75  * analyses on some collection of Java classes. It also implements the
76  * comand line interface.
77  *
78  * @author Bill Pugh
79  * @author David Hovemeyer
80  */

81 public class FindBugs implements Constants2, ExitCodes, IFindBugsEngine {
82     /* ----------------------------------------------------------------------
83      * Helper classes
84      * ---------------------------------------------------------------------- */

85
86     /**
87      * Delegating InputStream wrapper that never closes the
88      * underlying input stream.
89      */

90     private static class NoCloseInputStream extends DataInputStream JavaDoc {
91         /**
92          * Constructor.
93          * @param in the real InputStream
94          */

95         public NoCloseInputStream(InputStream JavaDoc in) {
96             super(in);
97         }
98
99         @Override JavaDoc
100         public void close() {
101         }
102     }
103
104     /**
105      * Work list item specifying a file/directory/URL containing
106      * class files to analyze.
107      */

108     private static class ArchiveWorkListItem {
109         private String JavaDoc fileName;
110         private boolean explicit;
111
112         /**
113          * Constructor.
114          *
115          * @param fileName file/directory/URL
116          * @param explicit true if this source of classes appeared explicitly
117          * in the project file, false if was found indirectly
118          * (e.g., a nested jar file in a .war file)
119          */

120         public ArchiveWorkListItem(String JavaDoc fileName, boolean explicit) {
121             this.fileName = fileName;
122             this.explicit = explicit;
123         }
124
125         /**
126          * Get the file/directory/URL.
127          */

128         public String JavaDoc getFileName() {
129             return fileName;
130         }
131
132         /**
133          * Return whether this class source appeared explicitly in
134          * the project file.
135          */

136         public boolean isExplicit() {
137             return explicit;
138         }
139     }
140
141     /**
142      * Interface for an object representing a source of class files to analyze.
143      */

144     private interface ClassProducer {
145         /**
146          * Get the next class to analyze.
147          *
148          * @return the class, or null of there are no more classes for this ClassProducer
149          * @throws IOException if an IOException occurs
150          * @throws InterruptedException if the thread is interrupted
151          */

152         public JavaClass getNextClass() throws IOException JavaDoc, InterruptedException JavaDoc;
153
154         /**
155          * Did this class producer scan any Java source files?
156          */

157         public boolean containsSourceFiles();
158
159         /**
160          * Return the latest creation/modification time of any of the class files scanned.
161          * @return the last modification time
162          */

163         public long getLastModificationTime();
164         /**
165          * Close any internal files or streams.
166          */

167         public void close();
168     }
169
170     /**
171      * ClassProducer for single class files.
172      */

173     private class SingleClassProducer implements ClassProducer {
174         private URL JavaDoc url;
175         long time = 0;
176         /**
177          * Constructor.
178          *
179          * @param url the single class file to be analyzed
180          */

181         public SingleClassProducer(URL JavaDoc url) {
182             this.url = url;
183         }
184
185         public JavaClass getNextClass() throws IOException JavaDoc, InterruptedException JavaDoc {
186             if (url == null)
187                 return null;
188             if (Thread.interrupted())
189                 throw new InterruptedException JavaDoc();
190
191             URL JavaDoc urlToParse = url;
192             url = null; // don't return it next time
193

194             // ClassScreener may veto this class.
195
if (!classScreener.matches(urlToParse.toString()))
196                 return null;
197
198             try {
199                 URLConnection JavaDoc u = urlToParse.openConnection();
200                 time = u.getLastModified();
201                 return parseFromStream(u.getInputStream(), urlToParse.toString());
202             } catch (ClassFormatException e) {
203                 throw new ClassFormatException("Invalid class file format for " +
204                         urlToParse.toString() + ": " + e.getMessage());
205             }
206         }
207
208     
209         public boolean containsSourceFiles() {
210             return false;
211         }
212
213         public void close() {
214             // Nothing to do here
215
}
216
217         /* (non-Javadoc)
218          * @see edu.umd.cs.findbugs.FindBugs.ClassProducer#getLatestTimeOfClass()
219          */

220         public long getLastModificationTime() {
221             return time;
222         }
223     }
224
225     /**
226      * ClassProducer for zip/jar archives.
227      */

228     private class ZipClassProducer implements ClassProducer {
229         private URL JavaDoc url;
230         private LinkedList JavaDoc<ArchiveWorkListItem> archiveWorkList;
231         private List JavaDoc<String JavaDoc> additionalAuxClasspathEntryList;
232         private ZipInputStream JavaDoc zipInputStream;
233         private boolean containsSourceFiles;
234         private long time = 0;
235         private long zipTime = 0;
236
237         public ZipClassProducer(URL JavaDoc url, LinkedList JavaDoc<ArchiveWorkListItem> archiveWorkList,
238                 List JavaDoc<String JavaDoc> additionalAuxClasspathEntryList)
239                 throws IOException JavaDoc {
240             this.url = url;
241             this.archiveWorkList = archiveWorkList;
242             this.additionalAuxClasspathEntryList = additionalAuxClasspathEntryList;
243             if (DEBUG) System.out.println("Opening jar/zip input stream for " + url.toString());
244             URLConnection JavaDoc u = url.openConnection();
245             this.zipTime = u.getLastModified();
246             this.zipInputStream = new ZipInputStream JavaDoc(u.getInputStream());
247             this.containsSourceFiles = false;
248         }
249
250         
251         public JavaClass getNextClass() throws IOException JavaDoc, InterruptedException JavaDoc {
252             for (;;) {
253                 if (Thread.interrupted())
254                     throw new InterruptedException JavaDoc();
255
256                 ZipEntry JavaDoc zipEntry = zipInputStream.getNextEntry();
257                 if (zipEntry == null)
258                     return null;
259
260                 try {
261                     String JavaDoc entryName = zipEntry.getName();
262
263                     // ClassScreener may veto this class.
264
if (!classScreener.matches(entryName)) {
265                         // Add archive URL to aux classpath
266
if (!additionalAuxClasspathEntryList.contains(url.toString())) {
267                             //System.out.println("Adding additional aux classpath entry: " + url.toString());
268
additionalAuxClasspathEntryList.add(url.toString());
269                         }
270                         continue;
271                     }
272
273                     String JavaDoc fileExtension = URLClassPath.getFileExtension(entryName);
274                     if (fileExtension != null) {
275                         if (fileExtension.equals(".class")) {
276                             long modTime = zipEntry.getTime();
277                             if (modTime > time) time = modTime;
278                             return parseClass(url.toString(), new NoCloseInputStream(zipInputStream), entryName);
279                         } else if (Archive.ARCHIVE_EXTENSION_SET.contains(fileExtension)) {
280                             // Add nested archive to archive work list
281
if (url.toString().indexOf("!/") < 0) {
282                             ArchiveWorkListItem nestedItem =
283                                 new ArchiveWorkListItem("jar:" + url.toString() + "!/" + entryName, false);
284                             archiveWorkList.addFirst(nestedItem);
285                             }
286                         } else if (fileExtension.equals(".java")) {
287                             containsSourceFiles = true;
288                         }
289                     }
290                 } finally {
291                     zipInputStream.closeEntry();
292                 }
293             }
294         }
295
296         public boolean containsSourceFiles() {
297             return containsSourceFiles;
298         }
299
300         public void close() {
301             if (zipInputStream != null) {
302                 try {
303                     zipInputStream.close();
304                 } catch (IOException JavaDoc ignore) {
305                     // Ignore
306
}
307             }
308         }
309         static final long millisecondsInAYear = 31556926000L;
310         /* (non-Javadoc)
311          * @see edu.umd.cs.findbugs.FindBugs.ClassProducer#getLastModificationTime()
312          */

313         public long getLastModificationTime() {
314             if (time + millisecondsInAYear > zipTime) return time;
315             return zipTime;
316         }
317     }
318
319     /**
320      * ClassProducer for directories.
321      * The directory is scanned recursively for class files.
322      */

323     private class DirectoryClassProducer implements ClassProducer {
324         private String JavaDoc dirName;
325         private List JavaDoc<String JavaDoc> additionalAuxClasspathEntryList;
326         private Iterator JavaDoc<String JavaDoc> rfsIter;
327         private boolean containsSourceFiles;
328         private long time;
329
330         public DirectoryClassProducer(String JavaDoc dirName,
331                 List JavaDoc<String JavaDoc> additionalAuxClasspathEntryList) throws InterruptedException JavaDoc {
332             this.dirName = dirName;
333             this.additionalAuxClasspathEntryList = additionalAuxClasspathEntryList;
334
335             FileFilter JavaDoc filter = new FileFilter JavaDoc() {
336                 public boolean accept(File JavaDoc file) {
337                     String JavaDoc fileName = file.getName();
338                     if (file.isDirectory() || fileName.endsWith(".class"))
339                         return true;
340                     if (fileName.endsWith(".java"))
341                         containsSourceFiles = true;
342                     return false;
343                 }
344             };
345
346             // This will throw InterruptedException if the thread is
347
// interrupted.
348
RecursiveFileSearch rfs = new RecursiveFileSearch(dirName, filter).search();
349             this.rfsIter = rfs.fileNameIterator();
350             this.containsSourceFiles = false;
351         }
352
353         public JavaClass getNextClass() throws IOException JavaDoc, InterruptedException JavaDoc {
354             String JavaDoc fileName;
355             for (;;) {
356                 if (!rfsIter.hasNext())
357                     return null;
358                 fileName = rfsIter.next();
359                 if (classScreener.matches(fileName)) {
360                     break;
361                 } else {
362                     // Add directory URL to aux classpath
363
String JavaDoc dirURL= "file:" + dirName;
364                     if (!additionalAuxClasspathEntryList.contains(dirURL)) {
365                         //System.out.println("Adding additional aux classpath entry: " + dirURL);
366
additionalAuxClasspathEntryList.add(dirURL);
367                     }
368                 }
369             }
370             try {
371                 long modTime = new File JavaDoc(fileName).lastModified();
372                 if (time < modTime) time = modTime;
373                 return parseClass(new URL JavaDoc("file:" + fileName));
374             } catch (ClassFormatException e) {
375                 throw new ClassFormatException("Invalid class file format for " +
376                         fileName + ": " + e.getMessage());
377             }
378         }
379
380         public boolean containsSourceFiles() {
381             return containsSourceFiles;
382         }
383
384         public void close() {
385             // Nothing to do here
386
}
387
388         /* (non-Javadoc)
389          * @see edu.umd.cs.findbugs.FindBugs.ClassProducer#getLastModificationTime()
390          */

391         public long getLastModificationTime() {
392             return time;
393         }
394     }
395
396     public static final AnalysisFeatureSetting[] MIN_EFFORT = new AnalysisFeatureSetting[]{
397             new AnalysisFeatureSetting(AnalysisFeatures.CONSERVE_SPACE, true),
398             new AnalysisFeatureSetting(AnalysisFeatures.ACCURATE_EXCEPTIONS, false),
399             new AnalysisFeatureSetting(AnalysisFeatures.MODEL_INSTANCEOF, false),
400             new AnalysisFeatureSetting(AnalysisFeatures.SKIP_HUGE_METHODS, true),
401             new AnalysisFeatureSetting(AnalysisFeatures.INTERATIVE_OPCODE_STACK_ANALYSIS, false),
402             new AnalysisFeatureSetting(AnalysisFeatures.TRACK_GUARANTEED_VALUE_DEREFS_IN_NULL_POINTER_ANALYSIS, false),
403             new AnalysisFeatureSetting(AnalysisFeatures.TRACK_VALUE_NUMBERS_IN_NULL_POINTER_ANALYSIS, false),
404             new AnalysisFeatureSetting(FindBugsAnalysisFeatures.INTERPROCEDURAL_ANALYSIS, false),
405             new AnalysisFeatureSetting(FindBugsAnalysisFeatures.INTERPROCEDURAL_ANALYSIS_OF_REFERENCED_CLASSES, false),
406     };
407     
408     public static final AnalysisFeatureSetting[] LESS_EFFORT = new AnalysisFeatureSetting[]{
409         new AnalysisFeatureSetting(AnalysisFeatures.CONSERVE_SPACE, false),
410         new AnalysisFeatureSetting(AnalysisFeatures.ACCURATE_EXCEPTIONS, true),
411         new AnalysisFeatureSetting(AnalysisFeatures.MODEL_INSTANCEOF, true),
412         new AnalysisFeatureSetting(AnalysisFeatures.SKIP_HUGE_METHODS, true),
413         new AnalysisFeatureSetting(AnalysisFeatures.INTERATIVE_OPCODE_STACK_ANALYSIS, true),
414         new AnalysisFeatureSetting(AnalysisFeatures.TRACK_GUARANTEED_VALUE_DEREFS_IN_NULL_POINTER_ANALYSIS, false),
415         new AnalysisFeatureSetting(AnalysisFeatures.TRACK_VALUE_NUMBERS_IN_NULL_POINTER_ANALYSIS, false),
416         new AnalysisFeatureSetting(FindBugsAnalysisFeatures.INTERPROCEDURAL_ANALYSIS, false),
417         new AnalysisFeatureSetting(FindBugsAnalysisFeatures.INTERPROCEDURAL_ANALYSIS_OF_REFERENCED_CLASSES, false),
418 };
419
420     public static final AnalysisFeatureSetting[] DEFAULT_EFFORT = new AnalysisFeatureSetting[]{
421             new AnalysisFeatureSetting(AnalysisFeatures.CONSERVE_SPACE, false),
422             new AnalysisFeatureSetting(AnalysisFeatures.ACCURATE_EXCEPTIONS, true),
423             new AnalysisFeatureSetting(AnalysisFeatures.MODEL_INSTANCEOF, true),
424             new AnalysisFeatureSetting(AnalysisFeatures.SKIP_HUGE_METHODS, true),
425             new AnalysisFeatureSetting(AnalysisFeatures.INTERATIVE_OPCODE_STACK_ANALYSIS, true),
426             new AnalysisFeatureSetting(AnalysisFeatures.TRACK_GUARANTEED_VALUE_DEREFS_IN_NULL_POINTER_ANALYSIS, true),
427             new AnalysisFeatureSetting(AnalysisFeatures.TRACK_VALUE_NUMBERS_IN_NULL_POINTER_ANALYSIS, true),
428             new AnalysisFeatureSetting(FindBugsAnalysisFeatures.INTERPROCEDURAL_ANALYSIS, true),
429             new AnalysisFeatureSetting(FindBugsAnalysisFeatures.INTERPROCEDURAL_ANALYSIS_OF_REFERENCED_CLASSES, false),
430     };
431     
432     public static final AnalysisFeatureSetting[] MORE_EFFORT = new AnalysisFeatureSetting[]{
433         new AnalysisFeatureSetting(AnalysisFeatures.CONSERVE_SPACE, false),
434         new AnalysisFeatureSetting(AnalysisFeatures.ACCURATE_EXCEPTIONS, true),
435         new AnalysisFeatureSetting(AnalysisFeatures.MODEL_INSTANCEOF, true),
436         new AnalysisFeatureSetting(AnalysisFeatures.SKIP_HUGE_METHODS, true),
437         new AnalysisFeatureSetting(AnalysisFeatures.INTERATIVE_OPCODE_STACK_ANALYSIS, true),
438         new AnalysisFeatureSetting(AnalysisFeatures.TRACK_GUARANTEED_VALUE_DEREFS_IN_NULL_POINTER_ANALYSIS, true),
439         new AnalysisFeatureSetting(AnalysisFeatures.TRACK_VALUE_NUMBERS_IN_NULL_POINTER_ANALYSIS, true),
440         new AnalysisFeatureSetting(FindBugsAnalysisFeatures.INTERPROCEDURAL_ANALYSIS, true),
441         new AnalysisFeatureSetting(FindBugsAnalysisFeatures.INTERPROCEDURAL_ANALYSIS_OF_REFERENCED_CLASSES, false),
442 };
443     public static final AnalysisFeatureSetting[] MAX_EFFORT = new AnalysisFeatureSetting[]{
444             new AnalysisFeatureSetting(AnalysisFeatures.CONSERVE_SPACE, false),
445             new AnalysisFeatureSetting(AnalysisFeatures.ACCURATE_EXCEPTIONS, true),
446             new AnalysisFeatureSetting(AnalysisFeatures.MODEL_INSTANCEOF, true),
447             new AnalysisFeatureSetting(AnalysisFeatures.SKIP_HUGE_METHODS, false),
448             new AnalysisFeatureSetting(AnalysisFeatures.INTERATIVE_OPCODE_STACK_ANALYSIS, true),
449             new AnalysisFeatureSetting(AnalysisFeatures.TRACK_GUARANTEED_VALUE_DEREFS_IN_NULL_POINTER_ANALYSIS, true),
450             new AnalysisFeatureSetting(AnalysisFeatures.TRACK_VALUE_NUMBERS_IN_NULL_POINTER_ANALYSIS, true),
451             new AnalysisFeatureSetting(FindBugsAnalysisFeatures.INTERPROCEDURAL_ANALYSIS, true),
452             new AnalysisFeatureSetting(FindBugsAnalysisFeatures.INTERPROCEDURAL_ANALYSIS_OF_REFERENCED_CLASSES, true),
453     };
454
455     public static final boolean DEBUG = SystemProperties.getBoolean("findbugs.debug");
456     public static final boolean TIMEDEBUG = SystemProperties.getBoolean("findbugs.time");
457     public static final int TIMEQUANTUM = SystemProperties.getInteger("findbugs.time.quantum", 1000);
458
459     /**
460      * FindBugs home directory.
461      */

462     private static String JavaDoc home;
463
464     /**
465      * Known URL protocols.
466      * Filename URLs that do not have an explicit protocol are
467      * assumed to be files.
468      */

469     static public final Set JavaDoc<String JavaDoc> knownURLProtocolSet = new HashSet JavaDoc<String JavaDoc>();
470     static {
471         knownURLProtocolSet.add("file");
472         knownURLProtocolSet.add("http");
473         knownURLProtocolSet.add("https");
474         knownURLProtocolSet.add("jar");
475     }
476
477     private ErrorCountingBugReporter bugReporter;
478     private boolean relaxedReportingMode;
479     private Project project;
480     private DetectorFactoryCollection detectorFactoryCollection;
481     private UserPreferences userPreferences;
482     private List JavaDoc<IClassObserver> classObserverList;
483     private ExecutionPlan executionPlan;
484     private FindBugsProgress progressCallback;
485     private IClassScreener classScreener;
486     private AnalysisContext analysisContext;
487     private String JavaDoc currentClass;
488     private Map JavaDoc<String JavaDoc,Long JavaDoc> detectorTimings;
489     private boolean useTrainingInput;
490     private boolean emitTrainingOutput;
491     private String JavaDoc trainingInputDir;
492     private String JavaDoc trainingOutputDir;
493     private AnalysisFeatureSetting[] settingList = DEFAULT_EFFORT;
494     private String JavaDoc releaseName;
495     
496     private int passCount;
497     private String JavaDoc sourceInfoFile;
498
499     /* ----------------------------------------------------------------------
500      * Public methods
501      * ---------------------------------------------------------------------- */

502     
503     /**
504      * Constructor.
505      * The setBugReporter() and setProject() methods must be called
506      * before this object is used.
507      */

508     public FindBugs() {
509         
510         this.relaxedReportingMode = false;
511
512         this.classObserverList = new LinkedList JavaDoc<IClassObserver>();
513
514         // Create a no-op progress callback.
515
this.progressCallback = new NoOpFindBugsProgress();
516
517         // Class screener
518
this.classScreener = new ClassScreener();
519     }
520
521     /**
522      * Constructor.
523      *
524      * @param bugReporter the BugReporter object that will be used to report
525      * BugInstance objects, analysis errors, class to source mapping, etc.
526      * @param project the Project indicating which files to analyze and
527      * the auxiliary classpath to use; note that the FindBugs
528      * object will create a private copy of the Project object
529      */

530     public FindBugs(BugReporter bugReporter, Project project) {
531         this();
532         
533         if (bugReporter == null)
534             throw new IllegalArgumentException JavaDoc("null bugReporter");
535         if (project == null)
536             throw new IllegalArgumentException JavaDoc("null project");
537
538         setBugReporter(bugReporter);
539         setProject(project);
540     }
541     
542     /* (non-Javadoc)
543      * @see edu.umd.cs.findbugs.IFindBugsEngine#setDetectorFactoryCollection(edu.umd.cs.findbugs.DetectorFactoryCollection)
544      */

545     public void setDetectorFactoryCollection(DetectorFactoryCollection detectorFactoryCollection) {
546         this.detectorFactoryCollection = detectorFactoryCollection;
547     }
548     
549     /* (non-Javadoc)
550      * @see edu.umd.cs.findbugs.IFindBugsEngine#getBugReporter()
551      */

552     public BugReporter getBugReporter() {
553         return bugReporter;
554     }
555     
556     /* (non-Javadoc)
557      * @see edu.umd.cs.findbugs.IFindBugsEngine#setBugReporter(edu.umd.cs.findbugs.BugReporter)
558      */

559     public void setBugReporter(BugReporter bugReporter) {
560         this.bugReporter = new ErrorCountingBugReporter(bugReporter);
561         addClassObserver(bugReporter);
562     }
563     
564     /* (non-Javadoc)
565      * @see edu.umd.cs.findbugs.IFindBugsEngine#setProject(edu.umd.cs.findbugs.Project)
566      */

567     public void setProject(Project project) {
568         this.project = project.duplicate();
569     }
570     
571     /* (non-Javadoc)
572      * @see edu.umd.cs.findbugs.IFindBugsEngine#getProject()
573      */

574     public Project getProject() {
575         return project;
576     }
577
578     /* (non-Javadoc)
579      * @see edu.umd.cs.findbugs.IFindBugsEngine#setProgressCallback(edu.umd.cs.findbugs.FindBugsProgress)
580      */

581     public void setProgressCallback(FindBugsProgress progressCallback) {
582         this.progressCallback = progressCallback;
583     }
584
585     /* (non-Javadoc)
586      * @see edu.umd.cs.findbugs.IFindBugsEngine#addFilter(java.lang.String, boolean)
587      */

588     public void addFilter(String JavaDoc filterFileName, boolean include) throws IOException JavaDoc, FilterException {
589         configureFilter(bugReporter, filterFileName, include);
590     }
591
592     /* (non-Javadoc)
593      * @see edu.umd.cs.findbugs.IFindBugsEngine#setUserPreferences(edu.umd.cs.findbugs.config.UserPreferences)
594      */

595     public void setUserPreferences(UserPreferences userPreferences) {
596         this.userPreferences = userPreferences;
597     }
598     
599     /* (non-Javadoc)
600      * @see edu.umd.cs.findbugs.IFindBugsEngine#addClassObserver(edu.umd.cs.findbugs.classfile.IClassObserver)
601      */

602     public void addClassObserver(IClassObserver classObserver) {
603         classObserverList.add(classObserver);
604     }
605
606     /* (non-Javadoc)
607      * @see edu.umd.cs.findbugs.IFindBugsEngine#setClassScreener(edu.umd.cs.findbugs.ClassScreener)
608      */

609     public void setClassScreener(IClassScreener classScreener) {
610         this.classScreener = classScreener;
611     }
612     
613     /* (non-Javadoc)
614      * @see edu.umd.cs.findbugs.IFindBugsEngine#setRelaxedReportingMode(boolean)
615      */

616     public void setRelaxedReportingMode(boolean relaxedReportingMode) {
617         this.relaxedReportingMode = relaxedReportingMode;
618     }
619     
620     /* (non-Javadoc)
621      * @see edu.umd.cs.findbugs.IFindBugsEngine#enableTrainingOutput(java.lang.String)
622      */

623     public void enableTrainingOutput(String JavaDoc trainingOutputDir) {
624         this.emitTrainingOutput = true;
625         this.trainingOutputDir = trainingOutputDir;
626     }
627     
628     /* (non-Javadoc)
629      * @see edu.umd.cs.findbugs.IFindBugsEngine#enableTrainingInput(java.lang.String)
630      */

631     public void enableTrainingInput(String JavaDoc trainingInputDir) {
632         this.useTrainingInput = true;
633         this.trainingInputDir = trainingInputDir;
634     }
635
636     /* (non-Javadoc)
637      * @see edu.umd.cs.findbugs.IFindBugsEngine#setAnalysisFeatureSettings(edu.umd.cs.findbugs.config.AnalysisFeatureSetting[])
638      */

639     public void setAnalysisFeatureSettings(AnalysisFeatureSetting[] settingList) {
640         if (settingList != null)
641             this.settingList = settingList;
642     }
643     
644     /* (non-Javadoc)
645      * @see edu.umd.cs.findbugs.IFindBugsEngine#getReleaseName()
646      */

647     public String JavaDoc getReleaseName() {
648         return releaseName;
649     }
650     
651     /* (non-Javadoc)
652      * @see edu.umd.cs.findbugs.IFindBugsEngine#setReleaseName(java.lang.String)
653      */

654     public void setReleaseName(String JavaDoc releaseName) {
655         this.releaseName = releaseName;
656     }
657     
658     /* (non-Javadoc)
659      * @see edu.umd.cs.findbugs.IFindBugsEngine#setSourceInfoFile(java.lang.String)
660      */

661     public void setSourceInfoFile(String JavaDoc sourceInfoFile) {
662         this.sourceInfoFile = sourceInfoFile;
663     }
664     
665     /* (non-Javadoc)
666      * @see edu.umd.cs.findbugs.IFindBugsEngine#execute()
667      */

668     public void execute() throws java.io.IOException JavaDoc, InterruptedException JavaDoc {
669         // Configure the analysis context
670
analysisContext = AnalysisContext.create(bugReporter);
671         // We still need to call analysisContext.initDatabases(), but not until after we have set up the repository.
672
analysisContext.setSourcePath(project.getSourceDirList());
673         if (sourceInfoFile != null) {
674             analysisContext.getSourceInfoMap().read(new FileInputStream JavaDoc(sourceInfoFile));
675         }
676         
677         // Enable/disable relaxed reporting mode
678
FindBugsAnalysisFeatures.setRelaxedMode(relaxedReportingMode);
679         
680         // Enable input/output of interprocedural property databases
681
configureTrainingDatabases(this);
682         
683         // Configure analysis features
684
configureAnalysisFeatures();
685
686         // Set the release name and timestamp(s) in the BugCollection (if we are generating one).
687
configureBugCollection(this);
688
689         // Create execution plan
690
try {
691             createExecutionPlan();
692         } catch (OrderingConstraintException e) {
693             IOException JavaDoc ioe = new IOException JavaDoc("Invalid detector ordering constraints");
694             ioe.initCause(e);
695             throw ioe;
696         }
697
698         // Clear the repository of classes
699
analysisContext.clearRepository();
700         
701         // Get list of files to analyze.
702
LinkedList JavaDoc<ArchiveWorkListItem> archiveWorkList = new LinkedList JavaDoc<ArchiveWorkListItem>();
703         for (String JavaDoc fileName : project.getFileList()) {
704             archiveWorkList.add(new ArchiveWorkListItem(fileName, true));
705         }
706
707         // Report how many archives/directories/files will be analyzed,
708
// for progress dialog in GUI
709
progressCallback.reportNumberOfArchives(archiveWorkList.size());
710
711         // Keep track of the names of all classes to be analyzed
712
List JavaDoc<String JavaDoc> repositoryClassList = new LinkedList JavaDoc<String JavaDoc>();
713
714         // set the initial repository classpath.
715
setRepositoryClassPath();
716         
717         // Record additional entries that should be added to
718
// the aux classpath. These occur when one or more classes
719
// in a directory or archive are skipped, to ensure that
720
// the skipped classes can still be referenced.
721
List JavaDoc<String JavaDoc> additionalAuxClasspathEntryList = new LinkedList JavaDoc<String JavaDoc>();
722
723         // Add all classes in analyzed archives/directories/files
724
while (!archiveWorkList.isEmpty()) {
725             ArchiveWorkListItem item = archiveWorkList.removeFirst();
726             scanArchiveOrDirectory(item, archiveWorkList, repositoryClassList,
727                 additionalAuxClasspathEntryList);
728         }
729         
730         // Add "extra" aux classpath entries needed to ensure that
731
// skipped classes can be referenced.
732
addCollectionToClasspath(additionalAuxClasspathEntryList);
733
734         // finish up initializing analysisContext
735
analysisContext.initDatabases();
736         
737         // Examine all classes for bugs.
738
// Don't examine the same class more than once.
739
// (The user might specify two jar files that contain
740
// the same class.)
741

742         if (DEBUG)
743             detectorTimings = new HashMap JavaDoc<String JavaDoc,Long JavaDoc>();
744
745         Iterator JavaDoc<AnalysisPass> i = executionPlan.passIterator();
746         if (i.hasNext()) {
747         AnalysisPass firstPass = i.next();
748         // Do this to force referenced classes to be loaded
749
Set JavaDoc<JavaClass> allReferencedClasses = analysisContext.getSubtypes().getAllClasses();
750         ArrayList JavaDoc<String JavaDoc> listOfReferencedClasses = new ArrayList JavaDoc<String JavaDoc>(allReferencedClasses.size());
751         for(JavaClass c : allReferencedClasses)
752             listOfReferencedClasses.add(c.getClassName());
753         executeAnalysisPass(firstPass, listOfReferencedClasses);
754
755         analysisContext.clearClassContextCache();
756         }
757         else if (DEBUG) System.err.println("execution plan has no passes");
758         
759         
760         // Execute each subsequent analysis pass in the execution plan
761
while (i.hasNext()) {
762             AnalysisPass analysisPass = i.next();
763             executeAnalysisPass(analysisPass, repositoryClassList);
764
765             if (false)
766                 System.out.println("Class content stats: " + analysisContext.getClassContextStats());
767             // Clear the ClassContext cache.
768
// It may contain data that should be recomputed on the next pass.
769
analysisContext.clearClassContextCache();
770         }
771
772         // Flush any queued bug reports
773
bugReporter.finish();
774
775         // Flush any queued error reports
776
bugReporter.reportQueuedErrors();
777
778         // Free up memory for reports
779
analysisContext.clearRepository();
780         
781         if (false)
782         System.out.println(analysisContext.getClassContextStats());
783     }
784
785     /* (non-Javadoc)
786      * @see edu.umd.cs.findbugs.IFindBugsEngine#getCurrentClass()
787      */

788     public String JavaDoc getCurrentClass() {
789         return currentClass;
790     }
791
792     /* (non-Javadoc)
793      * @see edu.umd.cs.findbugs.IFindBugsEngine#getBugCount()
794      */

795     public int getBugCount() {
796         return bugReporter.getBugCount();
797     }
798
799     /* (non-Javadoc)
800      * @see edu.umd.cs.findbugs.IFindBugsEngine#getErrorCount()
801      */

802     public int getErrorCount() {
803         return bugReporter.getErrorCount();
804     }
805
806     /* (non-Javadoc)
807      * @see edu.umd.cs.findbugs.IFindBugsEngine#getMissingClassCount()
808      */

809     public int getMissingClassCount() {
810         return bugReporter.getMissingClassCount();
811     }
812     
813     /* (non-Javadoc)
814      * @see edu.umd.cs.findbugs.IFindBugsEngine#emitTrainingOutput()
815      */

816     public boolean emitTrainingOutput() {
817         return emitTrainingOutput;
818     }
819     
820     /* (non-Javadoc)
821      * @see edu.umd.cs.findbugs.IFindBugsEngine#getUserPreferences()
822      */

823     public UserPreferences getUserPreferences() {
824         if (userPreferences == null)
825             userPreferences = UserPreferences.createDefaultUserPreferences();
826         return userPreferences;
827     }
828     
829     /* (non-Javadoc)
830      * @see edu.umd.cs.findbugs.IFindBugsEngine#getTrainingInputDir()
831      */

832     public String JavaDoc getTrainingInputDir() {
833         return trainingInputDir;
834     }
835     
836     /* (non-Javadoc)
837      * @see edu.umd.cs.findbugs.IFindBugsEngine#getTrainingOutputDir()
838      */

839     public String JavaDoc getTrainingOutputDir() {
840         return trainingOutputDir;
841     }
842     
843     /* (non-Javadoc)
844      * @see edu.umd.cs.findbugs.IFindBugsEngine#useTrainingInput()
845      */

846     public boolean useTrainingInput() {
847         return useTrainingInput;
848     }
849     
850     /* (non-Javadoc)
851      * @see edu.umd.cs.findbugs.IFindBugsEngine#setScanNestedArchives(boolean)
852      */

853     public void setScanNestedArchives(boolean scanNestedArchives) {
854         // Ignore this - we're not really going to try to do this
855
}
856
857     /**
858      * Set the FindBugs home directory.
859      */

860     public static void setHome(String JavaDoc home) {
861         FindBugs.home = home;
862     }
863
864     /**
865      * Get the FindBugs home directory.
866      */

867     public static String JavaDoc getHome() {
868         if (home == null) {
869             home = SystemProperties.getProperty("findbugs.home");
870             if (home == null) {
871                 System.err.println("Error: The findbugs.home property is not set!");
872             }
873         }
874         return home;
875     }
876
877     /* ----------------------------------------------------------------------
878      * Private methods
879      * ---------------------------------------------------------------------- */

880
881     /**
882      * Configure analysis features.
883      */

884     private void configureAnalysisFeatures() {
885         for (AnalysisFeatureSetting setting : settingList) {
886             setting.configure(analysisContext);
887         }
888     }
889
890     /**
891      * Configure training databases.
892      *
893      * @throws IOException
894      */

895     public static void configureTrainingDatabases(IFindBugsEngine findBugs) throws IOException JavaDoc {
896         if (findBugs.emitTrainingOutput()) {
897             String JavaDoc trainingOutputDir = findBugs.getTrainingOutputDir();
898             
899             if (!new File JavaDoc(trainingOutputDir).isDirectory())
900                 throw new IOException JavaDoc("Training output directory " + trainingOutputDir + " does not exist");
901             AnalysisContext.currentAnalysisContext().setDatabaseOutputDir(trainingOutputDir);
902             // XXX: hack
903
System.setProperty("findbugs.checkreturn.savetraining", new File JavaDoc(trainingOutputDir, "checkReturn.db").getPath());
904         }
905         if (findBugs.useTrainingInput()) {
906             String JavaDoc trainingInputDir = findBugs.getTrainingInputDir();
907             
908             if (!new File JavaDoc(trainingInputDir).isDirectory())
909                 throw new IOException JavaDoc("Training input directory " + trainingInputDir + " does not exist");
910             AnalysisContext.currentAnalysisContext().setDatabaseInputDir(trainingInputDir);
911             AnalysisContext.currentAnalysisContext().loadInterproceduralDatabases();
912             // XXX: hack
913
System.setProperty("findbugs.checkreturn.loadtraining", new File JavaDoc(trainingInputDir, "checkReturn.db").getPath());
914         }
915         else {
916             AnalysisContext.currentAnalysisContext().loadDefaultInterproceduralDatabases();
917         }
918     }
919
920     /**
921      * Create the ExecutionPlan.
922      *
923      * @throws OrderingConstraintException
924      */

925     private void createExecutionPlan() throws OrderingConstraintException {
926         executionPlan = new ExecutionPlan();
927         
928         // Only enabled detectors should be part of the execution plan
929
executionPlan.setDetectorFactoryChooser(new DetectorFactoryChooser() {
930             public boolean choose(DetectorFactory factory) {
931                 boolean enabled = isDetectorEnabled(FindBugs.this, factory);
932 // if (ExecutionPlan.DEBUG) {
933
// System.out.println(factory.getShortName() + ": enabled=" + enabled);
934
// }
935
return enabled;
936             }
937         });
938
939         // Add plugins
940
for (Iterator JavaDoc<Plugin> i = detectorFactoryCollection.pluginIterator(); i.hasNext();) {
941             Plugin plugin = i.next();
942             executionPlan.addPlugin(plugin);
943         }
944
945         // Build the plan
946
executionPlan.build();
947     }
948
949     /**
950      * Determing whether or not given DetectorFactory should be enabled.
951      *
952      * @param findBugs the IFindBugsEngine
953      * @param factory the DetectorFactory
954      * @return true if the DetectorFactory should be enabled, false otherwise
955      */

956     public static boolean isDetectorEnabled(IFindBugsEngine findBugs, DetectorFactory factory) {
957         if (!factory.getPlugin().isEnabled())
958             return false;
959         
960         if (!findBugs.getUserPreferences().isDetectorEnabled(factory))
961             return false;
962         
963         if (!factory.isEnabledForCurrentJRE())
964             return false;
965         
966         // Slow first pass detectors are usually disabled, but may be explicitly enabled
967
if (!AnalysisContext.currentAnalysisContext().getBoolProperty(FindBugsAnalysisFeatures.INTERPROCEDURAL_ANALYSIS)
968                 && factory.isDetectorClassSubtypeOf(InterproceduralFirstPassDetector.class))
969             return false;
970
971         // Training detectors are enabled if, and only if, we are emitting training output
972
boolean isTrainingDetector = factory.isDetectorClassSubtypeOf(TrainingDetector.class);
973         boolean isNonReportingDetector = factory.isDetectorClassSubtypeOf(NonReportingDetector.class);
974         if (findBugs.emitTrainingOutput()) {
975             return isTrainingDetector || isNonReportingDetector;
976         }
977         
978         if (isTrainingDetector) return false;
979             
980         return true;
981     }
982     
983     /**
984      * Based on Project settings, set the classpath to be used
985      * by the Repository when looking up classes.
986      */

987     private void setRepositoryClassPath() {
988         // Set aux classpath entries
989
addCollectionToClasspath(project.getAuxClasspathEntryList());
990         
991         // Set implicit classpath entries
992
addCollectionToClasspath(project.getImplicitClasspathEntryList());
993
994         // Add system classpath entries
995
String JavaDoc systemClassPath = ClassPath.getClassPath();
996         StringTokenizer JavaDoc tok = new StringTokenizer JavaDoc(systemClassPath, File.pathSeparator);
997         while (tok.hasMoreTokens()) {
998             String JavaDoc entry = tok.nextToken();
999             try {
1000                analysisContext.addClasspathEntry(entry);
1001            } catch (IOException JavaDoc e) {
1002                bugReporter.logError("Warning: could not add URL " +
1003                        entry + " to classpath", e);
1004            }
1005        }
1006    }
1007
1008    /**
1009     * Add all classpath entries in given Collection to the given
1010     * URLClassPathRepository. Missing entries are not fatal:
1011     * we'll log them as analysis errors, but the analysis can
1012     * continue.
1013     *
1014     * @param collection classpath entries to add
1015     */

1016    private void addCollectionToClasspath(Collection JavaDoc<String JavaDoc> collection) {
1017        for (String JavaDoc entry : collection) {
1018            try {
1019                //repository.addURL(entry);
1020
analysisContext.addClasspathEntry(entry);
1021            } catch (IOException JavaDoc e) {
1022                bugReporter.logError("Warning: could not add URL " +
1023                        entry + " to classpath", e);
1024            }
1025        }
1026    }
1027
1028    /**
1029     * Add all classes contained in given file or directory to the BCEL Repository.
1030     *
1031     * @param item work list item representing the file, which may be a jar/zip
1032     * archive, a single class file, or a directory to be recursively
1033     * searched for class files
1034     * @param archiveWorkList work list of archives to analyze: this method
1035     * may add to the work list if it finds nested archives
1036     * @param repositoryClassList a List to which all classes found in
1037     * the archive or directory are added, so we later know
1038     * which files to analyze
1039     */

1040    private void scanArchiveOrDirectory(ArchiveWorkListItem item,
1041            LinkedList JavaDoc<ArchiveWorkListItem> archiveWorkList, List JavaDoc<String JavaDoc> repositoryClassList,
1042            List JavaDoc<String JavaDoc> additionalAuxClasspathEntryList)
1043            throws IOException JavaDoc, InterruptedException JavaDoc {
1044
1045        String JavaDoc fileName = item.getFileName();
1046        ClassProducer classProducer = null;
1047
1048        try {
1049            // Create a URL for the filename.
1050
// The protocol defaults to "file" if not explicitly
1051
// specified in the filename.
1052
String JavaDoc protocol = URLClassPath.getURLProtocol(fileName);
1053            if (protocol == null) {
1054                protocol = "file";
1055                fileName = "file:" + fileName;
1056            }
1057            URL JavaDoc url = new URL JavaDoc(fileName);
1058
1059            // Figure out the file extension
1060
String JavaDoc fileExtension = null;
1061            int lastDot = fileName.lastIndexOf('.');
1062            if (lastDot >= 0) {
1063                fileExtension = fileName.substring(lastDot);
1064            }
1065
1066            // Create the ClassProducer
1067
if (fileExtension != null && URLClassPath.isArchiveExtension(fileExtension))
1068                classProducer = new ZipClassProducer(url, archiveWorkList, additionalAuxClasspathEntryList);
1069            else if (fileExtension != null && fileExtension.equals(".class"))
1070                classProducer = new SingleClassProducer(url);
1071            else if (protocol.equals("file")) {
1072                // Assume it's a directory
1073
fileName = fileName.substring("file:".length());
1074                File JavaDoc dir = new File JavaDoc(fileName);
1075                if (!dir.isDirectory())
1076                    throw new IOException JavaDoc("Path " + fileName + " is not an archive, class file, or directory");
1077                classProducer = new DirectoryClassProducer(fileName, additionalAuxClasspathEntryList);
1078            } else
1079                throw new IOException JavaDoc("URL " + fileName + " is not an archive, class file, or directory");
1080
1081            if (DEBUG || URLClassPathRepository.DEBUG) {
1082                System.out.println("Scanning " + url + " for classes");
1083            }
1084            
1085            // Load all referenced classes into the Repository
1086
for (; ;) {
1087                if (Thread.interrupted())
1088                    throw new InterruptedException JavaDoc();
1089                try {
1090                    JavaClass jclass = classProducer.getNextClass();
1091                    if (jclass == null)
1092                        break;
1093                    if (DEBUG) System.out.println("Scanned " + jclass.getClassName());
1094                    analysisContext.addApplicationClassToRepository(jclass);
1095                    repositoryClassList.add(jclass.getClassName());
1096                } catch (ClassFormatException e) {
1097                    if (DEBUG) e.printStackTrace();
1098                    bugReporter.logError("Invalid classfile format", e);
1099                }
1100            }
1101
1102            if (item.isExplicit())
1103                progressCallback.finishArchive();
1104
1105            // If the archive or directory scanned contained source files,
1106
// add it to the end of the source path.
1107
if (classProducer.containsSourceFiles())
1108                project.addSourceDir(fileName);
1109            project.addTimestamp(classProducer.getLastModificationTime());
1110
1111        } catch (IOException JavaDoc e) {
1112            // You'd think that the message for a FileNotFoundException would include
1113
// the filename, but you'd be wrong. So, we'll add it explicitly.
1114
IOException JavaDoc ioe = new IOException JavaDoc("Could not analyze " + fileName);
1115            ioe.initCause(e);
1116            throw ioe;
1117        } finally {
1118            if (classProducer != null) {
1119                classProducer.close();
1120            }
1121        }
1122    }
1123
1124    /**
1125     * Execute a single AnalysisPass.
1126     *
1127     * @param analysisPass the AnalysisPass
1128     * @param repositoryClassList list of application classes in the repository
1129     * @throws InterruptedException
1130     */

1131    private void executeAnalysisPass(AnalysisPass analysisPass, List JavaDoc<String JavaDoc> repositoryClassList) throws InterruptedException JavaDoc {
1132        // Callback for progress dialog: analysis is starting
1133
progressCallback.startAnalysis(repositoryClassList.size());
1134        
1135        int thisPass = passCount++;
1136        if (ExecutionPlan.DEBUG) {
1137            System.out.println("************* Analysis pass " + thisPass + " *************");
1138            for (Iterator JavaDoc<DetectorFactory> i = analysisPass.iterator(); i.hasNext();) {
1139                DetectorFactory factory = i.next();
1140                System.out.println("\t" + factory.getFullName());
1141            }
1142        }
1143        
1144        // Create detectors
1145
// XXX: we can only support BCEL-based detectors.
1146
Detector[] detectors = analysisPass.instantiateDetectorsInPass(bugReporter);
1147
1148        // Examine each class in the application
1149
Set JavaDoc<String JavaDoc> examinedClassSet = new HashSet JavaDoc<String JavaDoc>();
1150        for (String JavaDoc className : repositoryClassList) {
1151            if (examinedClassSet.add(className))
1152                examineClass(detectors, className);
1153        }
1154        
1155        if (DEBUG) {
1156            long total = 0;
1157            for (Long JavaDoc aLong : detectorTimings.values()) {
1158                total += aLong.longValue();
1159            }
1160            System.out.println();
1161            System.out.println("Detector Timings");
1162            for (Map.Entry JavaDoc<String JavaDoc, Long JavaDoc> entry : detectorTimings.entrySet()) {
1163                String JavaDoc detectorName = entry.getKey();
1164                long detectorTime = entry.getValue().longValue();
1165                System.out.println(detectorName + ": " + detectorTime + " ms -> (" + (detectorTime * 100.0f / (float) total) + ") %");
1166            }
1167            System.out.println();
1168            detectorTimings = new HashMap JavaDoc<String JavaDoc,Long JavaDoc>();
1169        }
1170
1171        // Callback for progress dialog: analysis finished
1172
progressCallback.finishPerClassAnalysis();
1173
1174        // Force any detectors which defer work until all classes have
1175
// been seen to do that work.
1176
this.reportFinal(detectors);
1177        
1178        AnalysisContext.currentAnalysisContext().updateDatabases(thisPass);
1179    }
1180
1181    /**
1182     * Examine a single class by invoking all of the Detectors on it.
1183     *
1184     * @param detectors the Detectors to execute on the class
1185     * @param className the fully qualified name of the class to examine
1186     */

1187    private void examineClass(Detector[] detectors, String JavaDoc className) throws InterruptedException JavaDoc {
1188        if (DEBUG) System.out.println("Examining class " + className);
1189        long entireClassAnalysisStart = 0;
1190        
1191        
1192        if (TIMEDEBUG || DEBUG) {
1193            entireClassAnalysisStart = System.currentTimeMillis();
1194        }
1195        this.currentClass = className;
1196
1197        try {
1198            JavaClass javaClass = Repository.lookupClass(className);
1199
1200            // Notify ClassObservers
1201
for (IClassObserver aClassObserver : classObserverList) {
1202                ClassDescriptor classDescriptor =
1203                    new ClassDescriptor(ClassName.toSlashedClassName(javaClass.getClassName()));
1204                aClassObserver.observeClass(classDescriptor);
1205            }
1206
1207            // Create a ClassContext for the class
1208
ClassContext classContext = analysisContext.getClassContext(javaClass);
1209
1210            // Run the Detectors
1211
for (Detector detector1 : detectors) {
1212                if (Thread.interrupted())
1213                    throw new InterruptedException JavaDoc();
1214                Detector detector = detector1;
1215                // MUSTFIX: Evaluate whether this makes a difference
1216
if (false && detector instanceof StatelessDetector) {
1217                        try {
1218                            detector = (Detector) ((StatelessDetector) detector).clone();
1219                        } catch (CloneNotSupportedException JavaDoc e) {
1220                            throw new AssertionError JavaDoc(e);
1221                        }
1222                }
1223
1224
1225                try {
1226                    long start = 0, end;
1227
1228
1229                    if (TIMEDEBUG || DEBUG) {
1230                        start = System.currentTimeMillis();
1231                        if (DEBUG) {
1232                            System.out.println(" running " + detector.getClass().getName());
1233
1234                        }
1235                    }
1236                    detector.visitClassContext(classContext);
1237
1238                    if (TIMEDEBUG || DEBUG) {
1239                        end = System.currentTimeMillis();
1240                        long delta = end - start;
1241                        entireClassAnalysisStart += delta;
1242                        if (delta > TIMEQUANTUM)
1243                            System.out.println("TIME: " + detector.getClass().getName() + " " + className + " " + delta);
1244                        if (DEBUG) {
1245                            String JavaDoc detectorName = detector.getClass().getName();
1246                            Long JavaDoc total = detectorTimings.get(detectorName);
1247                            if (total == null)
1248                                total = (Long JavaDoc)(delta);
1249                            else
1250                                total = (Long JavaDoc)(total.longValue() + delta);
1251                            detectorTimings.put(detectorName, total);
1252                        }
1253                    }
1254                } catch (AnalysisException e) {
1255                    reportRecoverableDetectorException(className, detector, e);
1256                } catch (ArrayIndexOutOfBoundsException JavaDoc e) {
1257                    reportRecoverableDetectorException(className, detector, e);
1258                } catch (ClassCastException JavaDoc e) {
1259                    reportRecoverableDetectorException(className, detector, e);
1260                }
1261            }
1262        } catch (ClassNotFoundException JavaDoc e) {
1263            // This should never happen unless there are bugs in BCEL.
1264
bugReporter.reportMissingClass(e);
1265            reportRecoverableException(className, e);
1266        } catch (ClassFormatException e) {
1267            reportRecoverableException(className, e);
1268        }
1269        catch (RuntimeException JavaDoc re) {
1270            RuntimeException JavaDoc annotatedEx;
1271            try {
1272                String JavaDoc sep = SystemProperties.getProperty("line.separator");
1273                Constructor JavaDoc<? extends RuntimeException JavaDoc> c = re.getClass().getConstructor(new Class JavaDoc[] { String JavaDoc.class });
1274                String JavaDoc msg = re.getMessage();
1275                msg = sep + "While finding bugs in class: " + className + ((msg == null) ? "" : (sep + msg));
1276                annotatedEx = c.newInstance(new Object JavaDoc[] {msg});
1277                annotatedEx.setStackTrace(re.getStackTrace());
1278            } catch (RuntimeException JavaDoc e) {
1279                throw re;
1280            } catch (Exception JavaDoc e) {
1281                throw re;
1282            }
1283            throw annotatedEx;
1284        }
1285        if (TIMEDEBUG || DEBUG) {
1286            long classSetupTime = System.currentTimeMillis() - entireClassAnalysisStart;
1287            if (classSetupTime > TIMEQUANTUM)
1288                System.out.println("TIME: setup " + className + " " + classSetupTime);
1289        }
1290        progressCallback.finishClass();
1291    }
1292
1293    private void reportRecoverableException(String JavaDoc className, Exception JavaDoc e) {
1294        if (DEBUG) {
1295            e.printStackTrace();
1296        }
1297        bugReporter.logError("Exception analyzing " + className, e);
1298    }
1299
1300    private void reportRecoverableDetectorException(String JavaDoc className, Detector detector, Exception JavaDoc e) {
1301        if (DEBUG) {
1302            e.printStackTrace();
1303        }
1304        bugReporter.logError("Exception analyzing " + className +
1305            " using detector " + detector.getClass().getName(), e);
1306    }
1307
1308    /**
1309     * Call report() on all detectors, to give them a chance to
1310     * report any accumulated bug reports.
1311     */

1312    private void reportFinal(Detector[] detectors) throws InterruptedException JavaDoc {
1313        for (Detector detector : detectors) {
1314            if (Thread.interrupted())
1315                throw new InterruptedException JavaDoc();
1316            detector.report();
1317        }
1318    }
1319
1320    /**
1321     * Parse the data for a class to create a JavaClass object.
1322     */

1323    private static JavaClass parseClass(String JavaDoc archiveName, InputStream JavaDoc in, String JavaDoc fileName)
1324            throws IOException JavaDoc {
1325        if (DEBUG) System.out.println("About to parse " + fileName + " in " + archiveName);
1326        return parseFromStream(in, fileName);
1327    }
1328
1329    /**
1330     * Parse the data for a class to create a JavaClass object.
1331     */

1332    private static JavaClass parseClass(URL JavaDoc url) throws IOException JavaDoc {
1333        if (DEBUG) System.out.println("About to parse " + url.toString());
1334        InputStream JavaDoc in = null;
1335        try {
1336            in = url.openStream();
1337            return parseFromStream(in, url.toString());
1338        } finally {
1339            if (in != null)
1340                in.close();
1341        }
1342    }
1343
1344    /**
1345     * Parse an input stream to produce a JavaClass object.
1346     * Makes sure that the input stream is closed no
1347     * matter what.
1348     */

1349    private static JavaClass parseFromStream(InputStream JavaDoc in, String JavaDoc fileName) throws IOException JavaDoc {
1350        try {
1351            return new ClassParser(in, fileName).parse();
1352        } finally {
1353            try {
1354                in.close();
1355            } catch (IOException JavaDoc ignore) {
1356                // Ignore
1357
}
1358        }
1359    }
1360    
1361
1362    /**
1363     * Process -bugCategories option.
1364     *
1365     * @param userPreferences
1366     * UserPreferences representing which Detectors are enabled
1367     * @param categories
1368     * comma-separated list of bug categories
1369     * @return Set of categories to be used
1370     */

1371    static Set JavaDoc<String JavaDoc> handleBugCategories(UserPreferences userPreferences, String JavaDoc categories) {
1372        // Parse list of bug categories
1373
Set JavaDoc<String JavaDoc> categorySet = new HashSet JavaDoc<String JavaDoc>();
1374        StringTokenizer JavaDoc tok = new StringTokenizer JavaDoc(categories, ",");
1375        while (tok.hasMoreTokens()) {
1376            categorySet.add(tok.nextToken());
1377        }
1378
1379// // Enable only those detectors that can emit those categories
1380
// // (and the ones that produce unknown bug patterns, just to be safe).
1381
// // Skip disabled detectors, though.
1382
// for (Iterator<DetectorFactory> i = DetectorFactoryCollection.instance().factoryIterator(); i.hasNext();) {
1383
// DetectorFactory factory = i.next();
1384
// if (!factory.isEnabledForCurrentJRE())
1385
// continue;
1386
// Collection<BugPattern> reported = factory.getReportedBugPatterns();
1387
// boolean enable = false;
1388
// if (reported.isEmpty()) {
1389
// // Don't know what bug patterns are produced by this detector
1390
// if (DEBUG) System.out.println("Unknown bug patterns for " + factory.getShortName());
1391
// enable = true;
1392
// } else {
1393
// for (Iterator<BugPattern> j = reported.iterator(); j.hasNext();) {
1394
// BugPattern bugPattern = j.next();
1395
// if (categorySet.contains(bugPattern.getCategory())) {
1396
// if (DEBUG)
1397
// System.out.println("MATCH ==> " + categorySet +
1398
// " -- " + bugPattern.getCategory());
1399
// enable = true;
1400
// break;
1401
// }
1402
// }
1403
// }
1404
// if (DEBUG && enable) {
1405
// System.out.println("Enabling " + factory.getShortName());
1406
// }
1407
// userPreferences.enableDetector(factory, enable);
1408
// }
1409

1410        return categorySet;
1411    }
1412
1413    /* ----------------------------------------------------------------------
1414     * main() method
1415     * ---------------------------------------------------------------------- */

1416
1417    public static void main(String JavaDoc[] argv) {
1418        try {
1419            TextUICommandLine commandLine = new TextUICommandLine();
1420            FindBugs findBugs = createEngine(commandLine, argv);
1421
1422            try {
1423                runMain(findBugs, commandLine);
1424            } catch (RuntimeException JavaDoc e) {
1425                System.err.println("Fatal exception: " + e.toString());
1426                String JavaDoc currentClass = findBugs.getCurrentClass();
1427                if (currentClass != null) {
1428                    System.err.println("\tWhile analyzing " + currentClass);
1429                }
1430                e.printStackTrace();
1431                System.err.println("Please report the failure to " + Version.SUPPORT_EMAIL);
1432                System.exit(1);
1433            }
1434
1435        } catch (java.io.IOException JavaDoc e) {
1436            // Probably a missing file
1437
if (DEBUG) {
1438                e.printStackTrace();
1439            }
1440            System.err.println("IO Error: " + e.getMessage());
1441            System.exit(1);
1442        } catch (FilterException e) {
1443            System.err.println("Filter exception: " + e.getMessage());
1444        } catch (IllegalArgumentException JavaDoc e) {
1445            // Probably an illegal command line argument
1446
System.err.println("Illegal argument: " + e.getMessage());
1447            System.exit(1);
1448        }
1449    }
1450
1451    private static FindBugs createEngine(TextUICommandLine commandLine, String JavaDoc[] argv)
1452            throws java.io.IOException JavaDoc, FilterException {
1453        
1454        FindBugs findBugs = new FindBugs();
1455        processCommandLine(commandLine, argv, findBugs);
1456        return findBugs;
1457    }
1458
1459    /**
1460     * Process the command line.
1461     *
1462     * @param commandLine the TextUICommandLine object which will parse the command line
1463     * @param argv the command line arguments
1464     * @param findBugs the IFindBugsEngine to configure
1465     * @throws IOException
1466     * @throws FilterException
1467     */

1468    public static void processCommandLine(TextUICommandLine commandLine, String JavaDoc[] argv, IFindBugsEngine findBugs) throws IOException JavaDoc, FilterException {
1469        // Expand option files in command line.
1470
// An argument beginning with "@" is treated as specifying
1471
// the name of an option file.
1472
// Each line of option files are treated as a single argument.
1473
// Blank lines and comment lines (beginning with "#")
1474
// are ignored.
1475
argv = CommandLine.expandOptionFiles(argv, true, true);
1476        
1477        int argCount = 0;
1478        try {
1479            argCount = commandLine.parse(argv);
1480        } catch (IllegalArgumentException JavaDoc e) {
1481            System.out.println(e.getMessage());
1482            showHelp(commandLine);
1483        } catch (HelpRequestedException e) {
1484            showHelp(commandLine);
1485        }
1486
1487        Project project = commandLine.getProject();
1488        for (int i = argCount; i < argv.length; ++i)
1489            project.addFile(argv[i]);
1490        commandLine.handleXArgs();
1491
1492        if (project.getFileCount() == 0) {
1493            showHelp(commandLine);
1494        }
1495        
1496        commandLine.configureEngine(findBugs);
1497    }
1498
1499    @SuppressWarnings JavaDoc("DM_EXIT")
1500    public static void showHelp(TextUICommandLine commandLine) {
1501        showSynopsis();
1502        ShowHelp.showGeneralOptions();
1503        FindBugs.showCommandLineOptions(commandLine);
1504        System.exit(1);
1505    }
1506
1507    @SuppressWarnings JavaDoc("DM_EXIT")
1508    public static void runMain(IFindBugsEngine findBugs, TextUICommandLine commandLine)
1509            throws java.io.IOException JavaDoc, RuntimeException JavaDoc {
1510        try {
1511            findBugs.execute();
1512        } catch (InterruptedException JavaDoc e) {
1513            // Not possible when running from the command line
1514
}
1515
1516        int bugCount = findBugs.getBugCount();
1517        int missingClassCount = findBugs.getMissingClassCount();
1518        int errorCount = findBugs.getErrorCount();
1519            
1520        if (!commandLine.quiet() || commandLine.setExitCode()) {
1521            if (bugCount > 0)
1522                System.err.println("Warnings generated: " + bugCount);
1523            if (missingClassCount > 0)
1524                System.err.println("Missing classes: " + missingClassCount);
1525            if (errorCount > 0)
1526                System.err.println("Analysis errors: " + errorCount);
1527        }
1528
1529        if (commandLine.setExitCode()) {
1530            int exitCode = 0;
1531            if (errorCount > 0)
1532                exitCode |= ERROR_FLAG;
1533            if (missingClassCount > 0)
1534                exitCode |= MISSING_CLASS_FLAG;
1535            if (bugCount > 0)
1536                exitCode |= BUGS_FOUND_FLAG;
1537
1538            System.exit(exitCode);
1539        }
1540    }
1541    
1542    /**
1543     * Print command line options synopses to stdout.
1544     */

1545    public static void showCommandLineOptions() {
1546        showCommandLineOptions(new TextUICommandLine());
1547    }
1548    
1549    public static void showCommandLineOptions(TextUICommandLine commandLine) {
1550        System.out.println("Command line options:");
1551        commandLine.printUsage(System.out);
1552    }
1553
1554    public static void showSynopsis() {
1555        System.out.println("Usage: findbugs [general options] -textui [command line options...] [jar/zip/class files, directories...]");
1556    }
1557    
1558    public static void configureFilter(DelegatingBugReporter bugReporter, String JavaDoc filterFileName, boolean include)
1559            throws IOException JavaDoc, FilterException {
1560        Filter JavaDoc filter = new Filter JavaDoc(filterFileName);
1561        BugReporter origBugReporter = bugReporter.getDelegate();
1562        BugReporter filterBugReporter = new FilterBugReporter(origBugReporter, filter, include);
1563        bugReporter.setDelegate(filterBugReporter);
1564    }
1565
1566    /**
1567     * Configure the BugCollection (if the BugReporter being used
1568     * is constructing one).
1569     *
1570     * @param findBugs the IFindBugsEngine
1571     */

1572    public static void configureBugCollection(IFindBugsEngine findBugs) {
1573        BugReporter realBugReporter = findBugs.getBugReporter().getRealBugReporter();
1574        
1575        if (realBugReporter instanceof BugCollectionBugReporter) {
1576            BugCollectionBugReporter bugCollectionBugReporter =
1577                (BugCollectionBugReporter) realBugReporter;
1578
1579            bugCollectionBugReporter = (BugCollectionBugReporter) realBugReporter;
1580
1581            bugCollectionBugReporter.getBugCollection().setReleaseName(findBugs.getReleaseName());
1582            
1583            Project project = findBugs.getProject();
1584            
1585            if (project.getTimestamp() != 0) {
1586                bugCollectionBugReporter.getBugCollection().setTimestamp(project.getTimestamp());
1587                bugCollectionBugReporter.getBugCollection().getProjectStats().setTimestamp(project.getTimestamp());
1588            }
1589
1590        }
1591    }
1592}
1593
1594// vim:ts=4
Popular Tags