KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > edu > umd > cs > findbugs > classfile > impl > ClassPathBuilder


1 /*
2  * FindBugs - Find Bugs in Java programs
3  * Copyright (C) 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.classfile.impl;
21
22 import java.io.DataInputStream JavaDoc;
23 import java.io.File JavaDoc;
24 import java.io.FileFilter JavaDoc;
25 import java.io.IOException JavaDoc;
26 import java.io.InputStream JavaDoc;
27 import java.util.HashMap JavaDoc;
28 import java.util.HashSet JavaDoc;
29 import java.util.Iterator JavaDoc;
30 import java.util.LinkedList JavaDoc;
31 import java.util.List JavaDoc;
32 import java.util.ListIterator JavaDoc;
33 import java.util.Map JavaDoc;
34 import java.util.Set JavaDoc;
35 import java.util.StringTokenizer JavaDoc;
36 import java.util.jar.Attributes JavaDoc;
37 import java.util.jar.Manifest JavaDoc;
38
39 import edu.umd.cs.findbugs.SystemProperties;
40 import edu.umd.cs.findbugs.classfile.CheckedAnalysisException;
41 import edu.umd.cs.findbugs.classfile.ClassDescriptor;
42 import edu.umd.cs.findbugs.classfile.IClassFactory;
43 import edu.umd.cs.findbugs.classfile.IClassPath;
44 import edu.umd.cs.findbugs.classfile.IClassPathBuilder;
45 import edu.umd.cs.findbugs.classfile.IClassPathBuilderProgress;
46 import edu.umd.cs.findbugs.classfile.ICodeBase;
47 import edu.umd.cs.findbugs.classfile.ICodeBaseEntry;
48 import edu.umd.cs.findbugs.classfile.ICodeBaseIterator;
49 import edu.umd.cs.findbugs.classfile.ICodeBaseLocator;
50 import edu.umd.cs.findbugs.classfile.IErrorLogger;
51 import edu.umd.cs.findbugs.classfile.IScannableCodeBase;
52 import edu.umd.cs.findbugs.classfile.InvalidClassFileFormatException;
53 import edu.umd.cs.findbugs.classfile.ResourceNotFoundException;
54 import edu.umd.cs.findbugs.classfile.analysis.ClassInfo;
55 import edu.umd.cs.findbugs.classfile.analysis.ClassNameAndSuperclassInfo;
56 import edu.umd.cs.findbugs.classfile.engine.ClassParser;
57 import edu.umd.cs.findbugs.io.IO;
58 import edu.umd.cs.findbugs.util.Archive;
59
60 /**
61  * Implementation of IClassPathBuilder.
62  *
63  * @author David Hovemeyer
64  */

65 public class ClassPathBuilder implements IClassPathBuilder {
66     private static final boolean VERBOSE = SystemProperties.getBoolean("findbugs2.builder.verbose");
67     private static final boolean DEBUG = VERBOSE || SystemProperties.getBoolean("findbugs2.builder.debug");
68     private static final boolean NO_PARSE_CLASS_NAMES =
69         SystemProperties.getBoolean("findbugs2.builder.noparseclassnames");
70
71     /**
72      * Worklist item.
73      * Represents one codebase to be processed during the
74      * classpath construction algorithm.
75      */

76     static class WorkListItem {
77         private ICodeBaseLocator codeBaseLocator;
78         private boolean isAppCodeBase;
79         private int howDiscovered;
80         
81         public String JavaDoc toString() {
82             return "WorkListItem(" + codeBaseLocator +", " + isAppCodeBase + ", " + howDiscovered +")";
83         }
84         
85         public WorkListItem(ICodeBaseLocator codeBaseLocator, boolean isApplication, int howDiscovered) {
86             this.codeBaseLocator = codeBaseLocator;
87             this.isAppCodeBase = isApplication;
88             this.howDiscovered = howDiscovered;
89         }
90         
91         public ICodeBaseLocator getCodeBaseLocator() {
92             return codeBaseLocator;
93         }
94         
95         public boolean isAppCodeBase() {
96             return isAppCodeBase;
97         }
98         
99         /**
100          * @return Returns the howDiscovered.
101          */

102         public int getHowDiscovered() {
103             return howDiscovered;
104         }
105     }
106     
107     /**
108      * A codebase discovered during classpath building.
109      */

110     static class DiscoveredCodeBase {
111         ICodeBase codeBase;
112         LinkedList JavaDoc<ICodeBaseEntry> resourceList;
113         
114         public DiscoveredCodeBase(ICodeBase codeBase) {
115             this.codeBase= codeBase;
116             this.resourceList = new LinkedList JavaDoc<ICodeBaseEntry>();
117         }
118         
119         public ICodeBase getCodeBase() {
120             return codeBase;
121         }
122         
123         public LinkedList JavaDoc<ICodeBaseEntry> getResourceList() {
124             return resourceList;
125         }
126
127         public void addCodeBaseEntry(ICodeBaseEntry entry) {
128             resourceList.add(entry);
129         }
130         
131         public ICodeBaseIterator iterator() throws InterruptedException JavaDoc {
132             if (codeBase instanceof IScannableCodeBase) {
133                 return ((IScannableCodeBase) codeBase).iterator();
134             } else {
135                 return new ICodeBaseIterator() {
136                     public boolean hasNext() throws InterruptedException JavaDoc { return false; }
137                     
138                     public ICodeBaseEntry next() throws InterruptedException JavaDoc {
139                         throw new UnsupportedOperationException JavaDoc();
140                     }
141                 };
142             }
143         }
144     }
145     
146     
147     // Fields
148
private IClassFactory classFactory;
149     private IErrorLogger errorLogger;
150     private LinkedList JavaDoc<WorkListItem> projectWorkList;
151     private LinkedList JavaDoc<DiscoveredCodeBase> discoveredCodeBaseList;
152     private Map JavaDoc<String JavaDoc, DiscoveredCodeBase> discoveredCodeBaseMap;
153     private LinkedList JavaDoc<ClassDescriptor> appClassList;
154     private boolean scanNestedArchives;
155
156     /**
157      * Constructor.
158      *
159      * @param classFactory the class factory
160      * @param errorLogger the error logger
161      */

162     ClassPathBuilder(IClassFactory classFactory, IErrorLogger errorLogger) {
163         this.classFactory = classFactory;
164         this.errorLogger = errorLogger;
165         this.projectWorkList = new LinkedList JavaDoc<WorkListItem>();
166         this.discoveredCodeBaseList = new LinkedList JavaDoc<DiscoveredCodeBase>();
167         this.discoveredCodeBaseMap = new HashMap JavaDoc<String JavaDoc, DiscoveredCodeBase>();
168         this.appClassList = new LinkedList JavaDoc<ClassDescriptor>();
169     }
170
171     /* (non-Javadoc)
172      * @see edu.umd.cs.findbugs.classfile.IClassPathBuilder#addCodeBase(edu.umd.cs.findbugs.classfile.ICodeBaseLocator, boolean)
173      */

174     public void addCodeBase(ICodeBaseLocator locator, boolean isApplication) {
175         addToWorkList(projectWorkList, new WorkListItem(locator, isApplication, ICodeBase.SPECIFIED));
176     }
177     
178     /* (non-Javadoc)
179      * @see edu.umd.cs.findbugs.classfile.IClassPathBuilder#scanNestedArchives(boolean)
180      */

181     public void scanNestedArchives(boolean scanNestedArchives) {
182         this.scanNestedArchives = scanNestedArchives;
183     }
184
185     /* (non-Javadoc)
186      * @see edu.umd.cs.findbugs.classfile.IClassPathBuilder#build(edu.umd.cs.findbugs.classfile.IClassPath, edu.umd.cs.findbugs.classfile.IClassPathBuilderProgress)
187      */

188     public void build(IClassPath classPath, IClassPathBuilderProgress progress)
189             throws CheckedAnalysisException, IOException JavaDoc, InterruptedException JavaDoc {
190         // Discover all directly and indirectly referenced codebases
191
processWorkList(classPath, projectWorkList, progress);
192         processWorkList(classPath, buildSystemCodebaseList(), progress);
193         
194         // Add all discovered codebases to the classpath
195
for (DiscoveredCodeBase discoveredCodeBase : discoveredCodeBaseList) {
196             classPath.addCodeBase(discoveredCodeBase.getCodeBase());
197         }
198         
199         Set JavaDoc<ClassDescriptor> appClassSet = new HashSet JavaDoc<ClassDescriptor>();
200         
201         // Build collection of all application classes.
202
// Also, add resource name -> codebase entry mappings for application classes.
203
for (DiscoveredCodeBase discoveredCodeBase : discoveredCodeBaseList) {
204             if (!discoveredCodeBase.getCodeBase().isApplicationCodeBase()) {
205                 continue;
206             }
207             
208         codeBaseEntryLoop:
209             for (ICodeBaseIterator i = discoveredCodeBase.iterator(); i.hasNext(); ) {
210                 ICodeBaseEntry entry = i.next();
211                 if (!ClassDescriptor.isClassResource(entry.getResourceName())) {
212                     continue;
213                 }
214                 
215                 ClassDescriptor classDescriptor = entry.getClassDescriptor();
216                 if (classDescriptor == null) throw new IllegalStateException JavaDoc();
217
218                 if (appClassSet.contains(classDescriptor)) {
219                     // An earlier entry takes precedence over this class
220
continue codeBaseEntryLoop;
221                 }
222                 appClassSet.add(classDescriptor);
223                 appClassList.add(classDescriptor);
224                 
225                 classPath.mapResourceNameToCodeBaseEntry(entry.getResourceName(), entry);
226             }
227         }
228         
229         if (DEBUG) {
230             System.out.println("Classpath:");
231             dumpCodeBaseList(classPath.appCodeBaseIterator(), "Application codebases");
232             dumpCodeBaseList(classPath.auxCodeBaseIterator(), "Auxiliary codebases");
233         }
234     }
235
236     private void dumpCodeBaseList(Iterator JavaDoc<? extends ICodeBase> i, String JavaDoc desc)
237             throws InterruptedException JavaDoc {
238         System.out.println(" " + desc + ":");
239         while (i.hasNext()) {
240             ICodeBase codeBase = i.next();
241             System.out.println(" " + codeBase.getCodeBaseLocator().toString());
242             if (codeBase.containsSourceFiles()) {
243                 System.out.println(" * contains source files");
244             }
245         }
246     }
247     
248     private LinkedList JavaDoc<WorkListItem> buildSystemCodebaseList() {
249         // This method is based on the
250
// org.apache.bcel.util.ClassPath.getClassPath()
251
// method.
252

253         LinkedList JavaDoc<WorkListItem> workList = new LinkedList JavaDoc<WorkListItem>();
254
255         // Seed worklist with system codebases.
256
// addWorkListItemsForClasspath(workList, SystemProperties.getProperty("java.class.path"));
257
addWorkListItemsForClasspath(workList, SystemProperties.getProperty("sun.boot.class.path"));
258         String JavaDoc extPath = SystemProperties.getProperty("java.ext.dirs");
259         if (extPath != null) {
260             StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(extPath, File.pathSeparator);
261             while (st.hasMoreTokens()) {
262                 String JavaDoc extDir = st.nextToken();
263                 addWorkListItemsForExtDir(workList, extDir);
264             }
265         }
266         
267         return workList;
268     }
269
270     /**
271      * Add worklist items from given system classpath.
272      *
273      * @param workList the worklist
274      * @param path a system classpath
275      */

276     private void addWorkListItemsForClasspath(LinkedList JavaDoc<WorkListItem> workList, String JavaDoc path) {
277         if (path == null) {
278             return;
279         }
280         
281         StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(path, File.pathSeparator);
282         while (st.hasMoreTokens()) {
283             String JavaDoc entry = st.nextToken();
284             if (DEBUG) {
285                 System.out.println("System classpath entry: " + entry);
286             }
287             addToWorkList(workList, new WorkListItem(
288                     classFactory.createFilesystemCodeBaseLocator(entry), false, ICodeBase.IN_SYSTEM_CLASSPATH));
289         }
290     }
291     
292     /**
293      * Add worklist items from given extensions directory.
294      *
295      * @param workList the worklist
296      * @param extDir an extensions directory
297      */

298     private void addWorkListItemsForExtDir(LinkedList JavaDoc<WorkListItem> workList, String JavaDoc extDir) {
299         File JavaDoc dir = new File JavaDoc(extDir);
300         File JavaDoc[] fileList = dir.listFiles(new FileFilter JavaDoc() {
301             /* (non-Javadoc)
302              * @see java.io.FileFilter#accept(java.io.File)
303              */

304             public boolean accept(File JavaDoc pathname) {
305                 String JavaDoc path = pathname.getParent();
306                 return Archive.isArchiveFileName(path);
307             }
308         });
309         if (fileList == null) {
310             return;
311         }
312         
313         for (File JavaDoc archive : fileList) {
314             addToWorkList(workList, new WorkListItem(
315                     classFactory.createFilesystemCodeBaseLocator(archive.getPath()), false, ICodeBase.IN_SYSTEM_CLASSPATH));
316         }
317     }
318
319     /**
320      * Process classpath worklist items.
321      * We will attempt to find all nested archives and
322      * Class-Path entries specified in Jar manifests. This should give us
323      * as good an idea as possible of all of the classes available (and
324      * which are part of the application).
325      *
326      * @param workList the worklist to process
327      * @param progress IClassPathBuilderProgress callback
328      * @throws InterruptedException
329      * @throws IOException
330      * @throws ResourceNotFoundException
331      */

332     private void processWorkList(
333             IClassPath classPath,
334             LinkedList JavaDoc<WorkListItem> workList, IClassPathBuilderProgress progress)
335             throws InterruptedException JavaDoc, IOException JavaDoc, ResourceNotFoundException {
336         // Build the classpath, scanning codebases for nested archives
337
// and referenced codebases.
338
while (!workList.isEmpty()) {
339             WorkListItem item = workList.removeFirst();
340             if (DEBUG) {
341                 System.out.println("Working: " + item.getCodeBaseLocator());
342             }
343             
344             DiscoveredCodeBase discoveredCodeBase;
345             
346             // See if we have encountered this codebase before
347
discoveredCodeBase = discoveredCodeBaseMap.get(item.getCodeBaseLocator().toString());
348             if (discoveredCodeBase != null) {
349                 // If the codebase is not an app codebase and
350
// the worklist item says that it is an app codebase,
351
// change it. Otherwise, we have nothing to do.
352
if (!discoveredCodeBase.getCodeBase().isApplicationCodeBase() && item.isAppCodeBase()) {
353                     discoveredCodeBase.getCodeBase().setApplicationCodeBase(true);
354                 }
355                 
356                 continue;
357             }
358             
359             // If we are working on an application codebase,
360
// then failing to open/scan it is a fatal error.
361
// We issue warnings about problems with aux codebases,
362
// but continue anyway.
363

364             try {
365                 // Open the codebase and add it to the classpath
366
discoveredCodeBase = new DiscoveredCodeBase(item.getCodeBaseLocator().openCodeBase());
367                 discoveredCodeBase.getCodeBase().setApplicationCodeBase(item.isAppCodeBase());
368                 discoveredCodeBase.getCodeBase().setHowDiscovered(item.getHowDiscovered());
369                 
370                 // Note that this codebase has been visited
371
discoveredCodeBaseMap.put(item.getCodeBaseLocator().toString(), discoveredCodeBase);
372                 discoveredCodeBaseList.addLast(discoveredCodeBase);
373
374                 // If it is a scannable codebase, check it for nested archives.
375
// In addition, if it is an application codebase then
376
// make a list of application classes.
377
if (discoveredCodeBase.getCodeBase() instanceof IScannableCodeBase && discoveredCodeBase.codeBase.isApplicationCodeBase()) {
378                     scanCodebase(classPath, workList, discoveredCodeBase);
379                 }
380
381                 // Check for a Jar manifest for additional aux classpath entries.
382
scanJarManifestForClassPathEntries(workList, discoveredCodeBase.getCodeBase());
383             } catch (IOException JavaDoc e) {
384                 if (item.isAppCodeBase()) {
385                     throw e;
386                 } else if (item.getHowDiscovered() == ICodeBase.SPECIFIED) {
387                     errorLogger.logError("Cannot open codebase " + item.getCodeBaseLocator(), e);
388                 }
389             } catch (ResourceNotFoundException e) {
390                 if (item.isAppCodeBase()) {
391                     throw e;
392                 } else if (item.getHowDiscovered() == ICodeBase.SPECIFIED) {
393                     errorLogger.logError("Cannot open codebase " + item.getCodeBaseLocator(), e);
394                 }
395             }
396             
397             if (item.getHowDiscovered() == ICodeBase.SPECIFIED) {
398                 progress.finishArchive();
399             }
400         }
401     }
402
403     /**
404      * Scan given codebase in order to
405      * <ul>
406      * <li> check the codebase for nested archives
407      * (adding any found to the worklist)
408      * <li> build a list of class resources found in the codebase
409      * </ul>
410      *
411      * @param workList the worklist
412      * @param discoveredCodeBase the codebase to scan
413      * @throws InterruptedException
414      */

415     private void scanCodebase(IClassPath classPath, LinkedList JavaDoc<WorkListItem> workList, DiscoveredCodeBase discoveredCodeBase)
416             throws InterruptedException JavaDoc {
417         if (DEBUG) {
418             System.out.println("Scanning " + discoveredCodeBase.getCodeBase().getCodeBaseLocator());
419         }
420         
421         IScannableCodeBase codeBase = (IScannableCodeBase) discoveredCodeBase.getCodeBase();
422         
423         ICodeBaseIterator i = codeBase.iterator();
424         while (i.hasNext()) {
425             ICodeBaseEntry entry = i.next();
426             if (VERBOSE) {
427                 System.out.println("Entry: " + entry.getResourceName());
428             }
429             
430             if (!NO_PARSE_CLASS_NAMES
431                     && codeBase.isApplicationCodeBase()
432                     && ClassDescriptor.isClassResource(entry.getResourceName())) {
433                 parseClassName(entry);
434             }
435             
436             // Note the resource exists in this codebase
437
discoveredCodeBase.addCodeBaseEntry(entry);
438
439             // If resource is a nested archive, add it to the worklist
440
if (scanNestedArchives && codeBase.isApplicationCodeBase() && Archive.isArchiveFileName(entry.getResourceName())) {
441                 if (VERBOSE) {
442                     System.out.println("Entry is an archive!");
443                 }
444                 ICodeBaseLocator nestedArchiveLocator =
445                     classFactory.createNestedArchiveCodeBaseLocator(codeBase, entry.getResourceName());
446                 addToWorkList(
447                         workList,
448                         new WorkListItem(nestedArchiveLocator, codeBase.isApplicationCodeBase(), ICodeBase.NESTED));
449             }
450         }
451     }
452
453     /**
454      * Attempt to parse data of given resource in order
455      * to divine the real name of the class contained in the
456      * resource.
457      *
458      * @param entry the resource
459      */

460     private void parseClassName(ICodeBaseEntry entry) {
461         DataInputStream JavaDoc in = null;
462         try {
463             in = new DataInputStream JavaDoc(entry.openResource());
464             ClassParser parser = new ClassParser(in, null, entry);
465             
466             ClassNameAndSuperclassInfo classInfo = new ClassNameAndSuperclassInfo();
467             parser.parse(classInfo);
468             entry.overrideResourceName(classInfo.getClassDescriptor().toResourceName());
469         } catch (IOException JavaDoc e) {
470             errorLogger.logError("Invalid class resource " + entry.getResourceName() +
471                     " in " + entry, e);
472         } catch (InvalidClassFileFormatException e) {
473             errorLogger.logError("Invalid class resource " + entry.getResourceName() +
474                     " in " + entry, e);
475         } finally {
476             IO.close(in);
477         }
478     }
479
480     /**
481      * Check a codebase for a Jar manifest to examine for Class-Path entries.
482      *
483      * @param workList the worklist
484      * @param codeBase the codebase for examine for a Jar manifest
485      * @throws IOException
486      */

487     private void scanJarManifestForClassPathEntries(LinkedList JavaDoc<WorkListItem> workList, ICodeBase codeBase)
488             throws IOException JavaDoc {
489         try {
490             // See if this codebase has a jar manifest
491
ICodeBaseEntry manifestEntry = codeBase.lookupResource("META-INF/MANIFEST.MF");
492
493             // Try to read the manifest
494
InputStream JavaDoc in = null;
495             try {
496                 in = manifestEntry.openResource();
497                 Manifest JavaDoc manifest = new Manifest JavaDoc(in);
498
499                 Attributes JavaDoc mainAttrs = manifest.getMainAttributes();
500                 String JavaDoc classPath = mainAttrs.getValue("Class-Path");
501                 if (classPath != null) {
502                     String JavaDoc[] pathList = classPath.split("\\s+");
503
504                     for (String JavaDoc path : pathList) {
505                         // Create a codebase locator for the classpath entry
506
// relative to the codebase in which we discovered the Jar
507
// manifest
508
ICodeBaseLocator relativeCodeBaseLocator =
509                             codeBase.getCodeBaseLocator().createRelativeCodeBaseLocator(path);
510                         
511                         // Codebases found in Class-Path entries are always
512
// added to the aux classpath, not the application.
513
addToWorkList(workList, new WorkListItem(relativeCodeBaseLocator, false, ICodeBase.IN_JAR_MANIFEST));
514                     }
515                 }
516             } finally {
517                 if (in != null) {
518                     IO.close(in);
519                 }
520             }
521         } catch (ResourceNotFoundException e) {
522             // Do nothing - no Jar manifest found
523
}
524         
525     }
526
527     /**
528      * Add a worklist item to the worklist.
529      * This method maintains the invariant that all of the worklist
530      * items representing application codebases appear <em>before</em>
531      * all of the worklist items representing auxiliary codebases.
532      *
533      * @param projectWorkList the worklist
534      * @param itemToAdd the worklist item to add
535      */

536     private void addToWorkList(LinkedList JavaDoc<WorkListItem> workList, WorkListItem itemToAdd) {
537         if (DEBUG) {
538             new RuntimeException JavaDoc("Adding work list item " + itemToAdd).printStackTrace(System.out);
539         }
540         if (!itemToAdd.isAppCodeBase()) {
541             // Auxiliary codebases are always added at the end
542
workList.addLast(itemToAdd);
543             return;
544         }
545         
546         // Adding an application codebase: position a ListIterator
547
// just before first auxiliary codebase (or at the end of the list
548
// if there are no auxiliary codebases)
549
ListIterator JavaDoc<WorkListItem> i = workList.listIterator();
550         while (i.hasNext()) {
551             WorkListItem listItem = i.next();
552             if (!listItem.isAppCodeBase()) {
553                 i.previous();
554                 break;
555             }
556         }
557         
558         // Add the codebase to the worklist
559
i.add(itemToAdd);
560     }
561     
562     /* (non-Javadoc)
563      * @see edu.umd.cs.findbugs.classfile.IClassPathBuilder#getAppClassList()
564      */

565     public List JavaDoc<ClassDescriptor> getAppClassList() {
566         return appClassList;
567     }
568 }
569
Popular Tags