KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > edu > umd > cs > findbugs > ba > AnalysisContext


1 /*
2  * Bytecode Analysis Framework
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.ba;
21
22 import java.io.File JavaDoc;
23 import java.io.IOException JavaDoc;
24 import java.io.InputStream JavaDoc;
25 import java.util.BitSet JavaDoc;
26 import java.util.Collections JavaDoc;
27 import java.util.HashMap JavaDoc;
28 import java.util.List JavaDoc;
29 import java.util.Map JavaDoc;
30
31 import net.jcip.annotations.NotThreadSafe;
32
33 import org.apache.bcel.Repository;
34 import org.apache.bcel.classfile.JavaClass;
35
36 import edu.umd.cs.findbugs.SourceLineAnnotation;
37 import edu.umd.cs.findbugs.SystemProperties;
38 import edu.umd.cs.findbugs.annotations.NonNull;
39 import edu.umd.cs.findbugs.ba.ch.Subtypes;
40 import edu.umd.cs.findbugs.ba.interproc.PropertyDatabase;
41 import edu.umd.cs.findbugs.ba.interproc.PropertyDatabaseFormatException;
42 import edu.umd.cs.findbugs.ba.npe.ParameterNullnessPropertyDatabase;
43 import edu.umd.cs.findbugs.ba.type.FieldStoreTypeDatabase;
44 import edu.umd.cs.findbugs.classfile.ClassDescriptor;
45 import edu.umd.cs.findbugs.classfile.IAnalysisCache;
46 import edu.umd.cs.findbugs.detect.NoteAnnotationRetention;
47 import edu.umd.cs.findbugs.detect.UnreadFields;
48 import edu.umd.cs.findbugs.util.ClassName;
49 import edu.umd.cs.findbugs.util.MapCache;
50
51
52 /**
53  * A context for analysis of a complete project.
54  * This serves as the repository for whole-program information
55  * and data structures.
56  *
57  * <p>
58  * <b>NOTE</b>: this class is slated to become obsolete.
59  * New code should use the IAnalysisCache object
60  * returned by Global.getAnalysisCache() to access all analysis
61  * information (global databases, class and method analyses, etc.)
62  * </p>
63  *
64  * @author David Hovemeyer
65  * @see IAnalysisCache
66  * @see edu.umd.cs.findbugs.classfile.Global
67  */

68 @NotThreadSafe
69 public abstract class AnalysisContext {
70     public static final boolean DEBUG = SystemProperties.getBoolean("findbugs.analysiscontext.debug");
71     
72     public static final String JavaDoc DEFAULT_NONNULL_PARAM_DATABASE_FILENAME = "nonnullParam.db";
73     public static final String JavaDoc DEFAULT_CHECK_FOR_NULL_PARAM_DATABASE_FILENAME = "checkForNullParam.db";
74     public static final String JavaDoc DEFAULT_NULL_RETURN_VALUE_ANNOTATION_DATABASE = "nonnullReturn.db";
75     public static final String JavaDoc UNCONDITIONAL_DEREF_DB_FILENAME = "unconditionalDeref.db";
76     public static final String JavaDoc UNCONDITIONAL_DEREF_DB_RESOURCE = "jdkBaseUnconditionalDeref.db";
77     public static final String JavaDoc DEFAULT_NULL_RETURN_VALUE_DB_FILENAME = "mayReturnNull.db";
78
79     private static InheritableThreadLocal JavaDoc<AnalysisContext> currentAnalysisContext
80         = new InheritableThreadLocal JavaDoc<AnalysisContext>();
81     
82     private static InheritableThreadLocal JavaDoc<XFactory> currentXFactory
83     = new InheritableThreadLocal JavaDoc<XFactory>() {
84         @Override JavaDoc
85         public XFactory initialValue() {
86             return new XFactory();
87         }
88     };
89     
90     public abstract NullnessAnnotationDatabase getNullnessAnnotationDatabase();
91     public abstract CheckReturnAnnotationDatabase getCheckReturnAnnotationDatabase();
92     public abstract AnnotationRetentionDatabase getAnnotationRetentionDatabase();
93     public abstract JCIPAnnotationDatabase getJCIPAnnotationDatabase();
94     
95     /** save the original SyntheticRepository so we may
96      * obtain JavaClass objects which we can reuse.
97      * (A URLClassPathRepository gets closed after analysis.) */

98     private static final org.apache.bcel.util.Repository originalRepository =
99         Repository.getRepository(); // BCEL SyntheticRepository
100

101     /**
102      * Default maximum number of ClassContext objects to cache.
103      * FIXME: need to evaluate this parameter. Need to keep stats about accesses.
104      */

105     private static final int DEFAULT_CACHE_SIZE = 3;
106
107     
108     // Instance fields
109
private BitSet JavaDoc boolPropertySet;
110     private Map JavaDoc<Object JavaDoc,Object JavaDoc> analysisLocals;
111     private String JavaDoc databaseInputDir;
112     private String JavaDoc databaseOutputDir;
113     
114     protected AnalysisContext() {
115         this.boolPropertySet = new BitSet JavaDoc();
116         this.analysisLocals = Collections.synchronizedMap(new HashMap JavaDoc<Object JavaDoc,Object JavaDoc>());
117     }
118
119     /**
120      * Create a new AnalysisContext.
121      *
122      * @param lookupFailureCallback the RepositoryLookupFailureCallback that
123      * the AnalysisContext should use to report errors
124      * @return a new AnalysisContext
125      */

126     public static AnalysisContext create(RepositoryLookupFailureCallback lookupFailureCallback) {
127         AnalysisContext analysisContext = new LegacyAnalysisContext(lookupFailureCallback);
128         setCurrentAnalysisContext(analysisContext);
129         return analysisContext;
130     }
131     
132     /** Instantiate the CheckReturnAnnotationDatabase.
133      * Do this after the repository has been set up.
134      */

135     public abstract void initDatabases();
136
137     
138     /**
139      * After a pass has been completed, allow the analysis context to update information.
140      * @param pass -- the first pass is pass 0
141      */

142     public abstract void updateDatabases(int pass);
143     /**
144      * Get the AnalysisContext associated with this thread
145      */

146     static public AnalysisContext currentAnalysisContext() {
147         return currentAnalysisContext.get();
148     }
149
150     static public XFactory currentXFactory() {
151         return currentXFactory.get();
152     }
153     
154     UnreadFields unreadFields;
155     public UnreadFields getUnreadFields() {
156         if (unreadFields == null) throw new IllegalStateException JavaDoc("UnreadFields detector not set");
157         return unreadFields;
158     }
159     public void setUnreadFields(@NonNull UnreadFields unreadFields) {
160         if (this.unreadFields != null) throw new IllegalStateException JavaDoc("UnreadFields detector already set");
161         this.unreadFields = unreadFields;
162     }
163     /**
164      * file a ClassNotFoundException with the lookupFailureCallback
165      * @see #getLookupFailureCallback()
166      */

167     static public void reportMissingClass(ClassNotFoundException JavaDoc e) {
168         AnalysisContext currentAnalysisContext2 = currentAnalysisContext();
169         if (currentAnalysisContext2 == null) return;
170         if (currentAnalysisContext2.missingClassWarningsSuppressed) return;
171         RepositoryLookupFailureCallback lookupFailureCallback = currentAnalysisContext2.getLookupFailureCallback();
172         if (lookupFailureCallback != null) lookupFailureCallback.reportMissingClass(e);
173     }
174     
175     /**
176      * Report an error
177      */

178     static public void logError(String JavaDoc msg, Exception JavaDoc e) {
179         AnalysisContext currentAnalysisContext2 = currentAnalysisContext();
180         if (currentAnalysisContext2 == null) return;
181         RepositoryLookupFailureCallback lookupFailureCallback = currentAnalysisContext2.getLookupFailureCallback();
182         if (lookupFailureCallback != null) lookupFailureCallback.logError(msg, e);
183     }
184     /**
185      * Report an error
186      */

187     static public void logError(String JavaDoc msg) {
188         AnalysisContext currentAnalysisContext2 = currentAnalysisContext();
189         if (currentAnalysisContext2 == null) return;
190         RepositoryLookupFailureCallback lookupFailureCallback = currentAnalysisContext2.getLookupFailureCallback();
191         if (lookupFailureCallback != null) lookupFailureCallback.logError(msg);
192     }
193     
194     boolean missingClassWarningsSuppressed = false;
195     
196     public boolean setMissingClassWarningsSuppressed(boolean value) {
197         boolean oldValue = missingClassWarningsSuppressed;
198         missingClassWarningsSuppressed = value;
199         return oldValue;
200     }
201     /**
202      * Get the lookup failure callback.
203      */

204     public abstract RepositoryLookupFailureCallback getLookupFailureCallback();
205
206     /**
207      * Set the source path.
208      */

209     public final void setSourcePath(List JavaDoc<String JavaDoc> sourcePath) {
210         getSourceFinder().setSourceBaseList(sourcePath);
211     }
212
213     /**
214      * Get the SourceFinder, for finding source files.
215      */

216     public abstract SourceFinder getSourceFinder();
217
218     /**
219      * Get the Subtypes database.
220      *
221      * @return the Subtypes database
222      */

223     public abstract Subtypes getSubtypes();
224     
225     /**
226      * Clear the BCEL Repository in preparation for analysis.
227      */

228     public abstract void clearRepository();
229     
230     /**
231      * Clear the ClassContext cache.
232      * This should be done between analysis passes.
233      */

234     public abstract void clearClassContextCache();
235     
236     /**
237      * Add an entry to the Repository's classpath.
238      *
239      * @param url the classpath entry URL
240      * @throws IOException
241      */

242     public abstract void addClasspathEntry(String JavaDoc url) throws IOException JavaDoc;
243
244     /**
245      * Add an application class to the repository.
246      *
247      * @param appClass the application class
248      */

249     public abstract void addApplicationClassToRepository(JavaClass appClass);
250     
251     /**
252      * Return whether or not the given class is an application class.
253      *
254      * @param cls the class to lookup
255      * @return true if the class is an application class, false if not
256      * an application class or if the class cannot be located
257      */

258     public boolean isApplicationClass(JavaClass cls) {
259         return getSubtypes().isApplicationClass(cls);
260     }
261     
262     /**
263      * Return whether or not the given class is an application class.
264      *
265      * @param className name of a class
266      * @return true if the class is an application class, false if not
267      * an application class or if the class cannot be located
268      */

269     public boolean isApplicationClass(String JavaDoc className) {
270         try {
271             JavaClass javaClass = lookupClass(className);
272             return isApplicationClass(javaClass);
273         } catch (ClassNotFoundException JavaDoc e) {
274             AnalysisContext.reportMissingClass(e);
275             return false;
276         }
277     }
278
279     /**
280      * Lookup a class.
281      * <em>Use this method instead of Repository.lookupClass().</em>
282      *
283      * @param className the name of the class
284      * @return the JavaClass representing the class
285      * @throws ClassNotFoundException (but not really)
286      */

287     public abstract JavaClass lookupClass(@NonNull String JavaDoc className) throws ClassNotFoundException JavaDoc;
288
289     /**
290      * Lookup a class.
291      * <em>Use this method instead of Repository.lookupClass().</em>
292      *
293      * @param classDescriptor descriptor specifying the class to look up
294      * @return the class
295      * @throws ClassNotFoundException if the class can't be found
296      */

297     public JavaClass lookupClass(@NonNull ClassDescriptor classDescriptor) throws ClassNotFoundException JavaDoc {
298         return lookupClass(classDescriptor.toDottedClassName());
299     }
300     
301     /**
302      * This is equivalent to Repository.lookupClass() or this.lookupClass(),
303      * except it uses the original Repository instead of the current one.
304      *
305      * This can be important because URLClassPathRepository objects are
306      * closed after an analysis, so JavaClass objects obtained from them
307      * are no good on subsequent runs.
308      *
309      * @param className the name of the class
310      * @return the JavaClass representing the class
311      * @throws ClassNotFoundException
312      */

313     public static JavaClass lookupSystemClass(@NonNull String JavaDoc className) throws ClassNotFoundException JavaDoc {
314         // TODO: eventually we should move to our own thread-safe repository implementation
315
if (className == null) throw new IllegalArgumentException JavaDoc("className is null");
316         if (originalRepository == null) throw new IllegalStateException JavaDoc("originalRepository is null");
317
318         JavaClass clazz = originalRepository.findClass(className);
319         return (clazz==null ? originalRepository.loadClass(className) : clazz);
320     }
321
322     /**
323      * Lookup a class's source file
324      *
325      * @param className the name of the class
326      * @return the source file for the class, or SourceLineAnnotation.UNKNOWN_SOURCE_FILE if unable to determine
327      */

328     public final String JavaDoc lookupSourceFile(@NonNull String JavaDoc className) {
329         if (className == null)
330             throw new IllegalArgumentException JavaDoc("className is null");
331         try {
332             JavaClass jc = this.lookupClass(className);
333             String JavaDoc name = jc.getSourceFileName();
334             if (name == null) {
335                 System.out.println("No sourcefile for " + className);
336                 return SourceLineAnnotation.UNKNOWN_SOURCE_FILE;
337             }
338             return name;
339         } catch (ClassNotFoundException JavaDoc cnfe) {
340           return SourceLineAnnotation.UNKNOWN_SOURCE_FILE;
341         }
342     }
343
344     /**
345      * Get the ClassContext for a class.
346      *
347      * @param javaClass the class
348      * @return the ClassContext for that class
349      */

350     public abstract ClassContext getClassContext(JavaClass javaClass);
351     
352     /**
353      * Get stats about hit rate for ClassContext cache.
354      *
355      * @return stats about hit rate for ClassContext cache
356      */

357     public abstract String JavaDoc getClassContextStats();
358     
359     /**
360      * If possible, load interprocedural property databases.
361      */

362     public final void loadInterproceduralDatabases() {
363         loadPropertyDatabase(
364                 getFieldStoreTypeDatabase(),
365                 FieldStoreTypeDatabase.DEFAULT_FILENAME,
366                 "field store type database");
367         loadPropertyDatabase(
368                 getUnconditionalDerefParamDatabase(),
369                 UNCONDITIONAL_DEREF_DB_FILENAME,
370                 "unconditional param deref database");
371     }
372     
373     /**
374      * If possible, load default (built-in) interprocedural property databases.
375      * These are the databases for things like Java core APIs that
376      * unconditional dereference parameters.
377      */

378     public final void loadDefaultInterproceduralDatabases() {
379         loadPropertyDatabaseFromResource(
380                 getUnconditionalDerefParamDatabase(),
381                 UNCONDITIONAL_DEREF_DB_RESOURCE,
382                 "unconditional param deref database");
383     }
384
385     /**
386      * Set a boolean property.
387      *
388      * @param prop the property to set
389      * @param value the value of the property
390      */

391     public final void setBoolProperty(int prop, boolean value) {
392         boolPropertySet.set(prop, value);
393     }
394
395     /**
396      * Get a boolean property.
397      *
398      * @param prop the property
399      * @return value of the property; defaults to false if the property
400      * has not had a value assigned explicitly
401      */

402     public final boolean getBoolProperty(int prop) {
403         return boolPropertySet.get(prop);
404     }
405     
406     /**
407      * Get the SourceInfoMap.
408      */

409     public abstract SourceInfoMap getSourceInfoMap();
410     
411     /**
412      * Set the interprocedural database input directory.
413      *
414      * @param databaseInputDir the interprocedural database input directory
415      */

416     public final void setDatabaseInputDir(String JavaDoc databaseInputDir) {
417         if (DEBUG) System.out.println("Setting database input directory: " + databaseInputDir);
418         this.databaseInputDir = databaseInputDir;
419     }
420     
421     /**
422      * Get the interprocedural database input directory.
423      *
424      * @return the interprocedural database input directory
425      */

426     public final String JavaDoc getDatabaseInputDir() {
427         return databaseInputDir;
428     }
429
430     /**
431      * Set the interprocedural database output directory.
432      *
433      * @param databaseOutputDir the interprocedural database output directory
434      */

435     public final void setDatabaseOutputDir(String JavaDoc databaseOutputDir) {
436         if (DEBUG) System.out.println("Setting database output directory: " + databaseOutputDir);
437         this.databaseOutputDir = databaseOutputDir;
438     }
439     
440     /**
441      * Get the interprocedural database output directory.
442      *
443      * @return the interprocedural database output directory
444      */

445     public final String JavaDoc getDatabaseOutputDir() {
446         return databaseOutputDir;
447     }
448     
449     /**
450      * Get the property database recording the types of values stored
451      * into fields.
452      *
453      * @return the database, or null if there is no database available
454      */

455     public abstract FieldStoreTypeDatabase getFieldStoreTypeDatabase();
456
457     /**
458      * Get the property database recording which methods unconditionally
459      * dereference parameters.
460      *
461      * @return the database, or null if there is no database available
462      */

463     public abstract ParameterNullnessPropertyDatabase getUnconditionalDerefParamDatabase();
464
465     /**
466      * Load an interprocedural property database.
467      *
468      * @param <DatabaseType> actual type of the database
469      * @param <KeyType> type of key (e.g., method or field)
470      * @param <Property> type of properties stored in the database
471      * @param database the empty database object
472      * @param fileName file to load database from
473      * @param description description of the database (for diagnostics)
474      * @return the database object, or null if the database couldn't be loaded
475      */

476     public<
477         DatabaseType extends PropertyDatabase<KeyType,Property>,
478         KeyType extends ClassMember,
479         Property
480         > DatabaseType loadPropertyDatabase(
481             DatabaseType database,
482             String JavaDoc fileName,
483             String JavaDoc description) {
484         try {
485             File JavaDoc dbFile = new File JavaDoc(getDatabaseInputDir(), fileName);
486             if (DEBUG) System.out.println("Loading " + description + " from " + dbFile.getPath() + "...");
487             
488             database.readFromFile(dbFile.getPath());
489             return database;
490         } catch (IOException JavaDoc e) {
491             getLookupFailureCallback().logError("Error loading " + description, e);
492         } catch (PropertyDatabaseFormatException e) {
493             getLookupFailureCallback().logError("Invalid " + description, e);
494         }
495         
496         return null;
497     }
498
499     /**
500      * Load an interprocedural property database.
501      *
502      * @param <DatabaseType> actual type of the database
503      * @param <KeyType> type of key (e.g., method or field)
504      * @param <Property> type of properties stored in the database
505      * @param database the empty database object
506      * @param resourceName name of resource to load the database from
507      * @param description description of the database (for diagnostics)
508      * @return the database object, or null if the database couldn't be loaded
509      */

510     public<
511         DatabaseType extends PropertyDatabase<KeyType,Property>,
512         KeyType extends ClassMember,
513         Property
514         > DatabaseType loadPropertyDatabaseFromResource(
515             DatabaseType database,
516             String JavaDoc resourceName,
517             String JavaDoc description) {
518         try {
519             if (DEBUG) System.out.println("Loading default " + description + " from "
520                     + resourceName + " @ "
521              + PropertyDatabase.class.getResource(resourceName) + " ... ");
522             InputStream JavaDoc in = PropertyDatabase.class.getResourceAsStream(resourceName);
523             database.read(in);
524             return database;
525         } catch (IOException JavaDoc e) {
526             getLookupFailureCallback().logError("Error loading " + description, e);
527         } catch (PropertyDatabaseFormatException e) {
528             getLookupFailureCallback().logError("Invalid " + description, e);
529         }
530         
531         return null;
532     }
533
534     
535     /**
536      * Write an interprocedural property database.
537      *
538      * @param <DatabaseType> actual type of the database
539      * @param <KeyType> type of key (e.g., method or field)
540      * @param <Property> type of properties stored in the database
541      * @param database the database
542      * @param fileName name of database file
543      * @param description description of the database
544      */

545     public<
546         DatabaseType extends PropertyDatabase<KeyType,Property>,
547         KeyType extends ClassMember,
548         Property
549         > void storePropertyDatabase(DatabaseType database, String JavaDoc fileName, String JavaDoc description) {
550
551         try {
552             File JavaDoc dbFile = new File JavaDoc(getDatabaseOutputDir(), fileName);
553             if (DEBUG) System.out.println("Writing " + description + " to " + dbFile.getPath() + "...");
554             database.writeToFile(dbFile.getPath());
555         } catch (IOException JavaDoc e) {
556             getLookupFailureCallback().logError("Error writing " + description, e);
557         }
558     }
559     
560     
561     /* (non-Javadoc)
562      * @see edu.umd.cs.findbugs.ba.AnalysisContext#getAnalyisLocals()
563      */

564     public final Map JavaDoc<Object JavaDoc, Object JavaDoc> getAnalysisLocals() {
565         return analysisLocals;
566     }
567     
568     public abstract InnerClassAccessMap getInnerClassAccessMap();
569     
570     /**
571      * Set the current analysis context for this thread.
572      *
573      * @param analysisContext the current analysis context for this thread
574      */

575     public static void setCurrentAnalysisContext(AnalysisContext analysisContext) {
576         currentAnalysisContext.set(analysisContext);
577     }
578     
579 }
580
581 // vim:ts=4
Popular Tags