KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*
2  * Bytecode Analysis Framework
3  * Copyright (C) 2003,2004 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.FileNotFoundException JavaDoc;
24 import java.io.IOException JavaDoc;
25 import java.io.InputStream JavaDoc;
26 import java.net.JarURLConnection JavaDoc;
27 import java.net.MalformedURLException JavaDoc;
28 import java.net.URL JavaDoc;
29 import java.util.Enumeration JavaDoc;
30 import java.util.LinkedHashMap JavaDoc;
31 import java.util.LinkedList JavaDoc;
32 import java.util.List JavaDoc;
33 import java.util.Map JavaDoc;
34 import java.util.zip.ZipFile JavaDoc;
35 import java.util.zip.ZipEntry JavaDoc;
36
37 import edu.umd.cs.findbugs.SourceLineAnnotation;
38 import edu.umd.cs.findbugs.SystemProperties;
39
40 /**
41  * Class to open input streams on source files.
42  * It maintains a "source path", which is like a classpath,
43  * but for finding source files instead of class files.
44  */

45 public class SourceFinder {
46     private static final boolean DEBUG = SystemProperties.getBoolean("srcfinder.debug");
47     private static final int CACHE_SIZE = 50;
48
49     /* ----------------------------------------------------------------------
50      * Helper classes
51      * ---------------------------------------------------------------------- */

52
53     /**
54      * Cache of SourceFiles.
55      * We use this to avoid repeatedly having to read
56      * frequently accessed source files.
57      */

58     private static class Cache extends LinkedHashMap JavaDoc<String JavaDoc, SourceFile> {
59         /**
60          *
61          */

62         private static final long serialVersionUID = 1L;
63
64         @Override JavaDoc
65                  protected boolean removeEldestEntry(Map.Entry JavaDoc<String JavaDoc, SourceFile> eldest) {
66             return size() >= CACHE_SIZE;
67         }
68     }
69
70     /**
71      * A repository of source files.
72      */

73     private interface SourceRepository {
74         public boolean contains(String JavaDoc fileName);
75         
76         public boolean isPlatformDependent();
77
78         public SourceFileDataSource getDataSource(String JavaDoc fileName);
79     }
80
81     /**
82      * A directory containing source files.
83      */

84     private static class DirectorySourceRepository implements SourceRepository {
85         private String JavaDoc baseDir;
86
87         public DirectorySourceRepository(String JavaDoc baseDir) {
88             this.baseDir = baseDir;
89         }
90
91         @Override JavaDoc
92         public String JavaDoc toString() {
93             return "DirectorySourceRepository:" + baseDir;
94         }
95         public boolean contains(String JavaDoc fileName) {
96             File JavaDoc file = new File JavaDoc(getFullFileName(fileName));
97             boolean exists = file.exists();
98             if (DEBUG) System.out.println("Exists " + exists + " for " + file);
99             return exists;
100         }
101
102         public boolean isPlatformDependent() {
103             return true;
104         }
105         
106         public SourceFileDataSource getDataSource(String JavaDoc fileName) {
107             return new FileSourceFileDataSource(getFullFileName(fileName));
108         }
109
110         private String JavaDoc getFullFileName(String JavaDoc fileName) {
111             return baseDir + File.separator + fileName;
112         }
113     }
114
115      static class JarURLConnectionSourceRepository extends ZipSourceRepository {
116
117         public JarURLConnectionSourceRepository(String JavaDoc url) throws MalformedURLException JavaDoc, IOException JavaDoc {
118             super(((JarURLConnection JavaDoc) new URL JavaDoc("jar:" + url +"!/").openConnection()).getJarFile());
119
120             if (DEBUG) {
121                 System.out.println("JarURLConnectionSourceRepository entries");
122             for(Enumeration JavaDoc<? extends ZipEntry JavaDoc> e = zipFile.entries(); e.hasMoreElements(); ) {
123                 ZipEntry JavaDoc ze = e.nextElement();
124                 System.out.println(ze.getName());
125             }
126             }
127         }
128         
129     }
130     /**
131      * A zip or jar archive containing source files.
132      */

133      static class ZipSourceRepository implements SourceRepository {
134          ZipFile JavaDoc zipFile;
135
136         public ZipSourceRepository(ZipFile JavaDoc zipFile) {
137             this.zipFile = zipFile;
138         }
139
140         public boolean contains(String JavaDoc fileName) {
141             return zipFile.getEntry(fileName) != null;
142         }
143
144         public boolean isPlatformDependent() {
145             return false;
146         }
147
148         public SourceFileDataSource getDataSource(String JavaDoc fileName) {
149             return new ZipSourceFileDataSource(zipFile, fileName);
150         }
151     }
152
153     /* ----------------------------------------------------------------------
154      * Fields
155      * ---------------------------------------------------------------------- */

156
157     private List JavaDoc<SourceRepository> repositoryList;
158     private Cache cache;
159
160     /* ----------------------------------------------------------------------
161      * Public methods
162      * ---------------------------------------------------------------------- */

163
164     /**
165      * Constructor.
166      */

167     public SourceFinder() {
168         if (DEBUG) System.out.println("Debugging SourceFinder");
169         repositoryList = new LinkedList JavaDoc<SourceRepository>();
170         cache = new Cache();
171     }
172
173     /**
174      * Set the list of source directories.
175      */

176     public void setSourceBaseList(List JavaDoc<String JavaDoc> sourceBaseList) {
177         for (String JavaDoc repos : sourceBaseList) {
178             if (repos.endsWith(".zip") || repos.endsWith(".jar")) {
179                 // Zip or jar archive
180
try {
181                     if (repos.startsWith("http:") || repos.startsWith("https:") || repos.startsWith("file:"))
182                         repositoryList.add(new JarURLConnectionSourceRepository(repos));
183                     else
184                         repositoryList.add(new ZipSourceRepository(new ZipFile JavaDoc(repos)));
185                 } catch (IOException JavaDoc e) {
186                     // Ignored - we won't use this archive
187
}
188             } else {
189                 // Directory
190
repositoryList.add(new DirectorySourceRepository(repos));
191             }
192         }
193     }
194
195     /**
196      * Open an input stream on a source file in given package.
197      *
198      * @param packageName the name of the package containing the class whose source file is given
199      * @param fileName the unqualified name of the source file
200      * @return an InputStream on the source file
201      * @throws IOException if a matching source file cannot be found
202      */

203     public InputStream JavaDoc openSource(String JavaDoc packageName, String JavaDoc fileName) throws IOException JavaDoc {
204         SourceFile sourceFile = findSourceFile(packageName, fileName);
205         return sourceFile.getInputStream();
206     }
207     public InputStream JavaDoc openSource(SourceLineAnnotation source) throws IOException JavaDoc {
208         SourceFile sourceFile = findSourceFile(source);
209         return sourceFile.getInputStream();
210     }
211     public SourceFile findSourceFile(SourceLineAnnotation source) throws IOException JavaDoc {
212         if (source.isSourceFileKnown())
213             return findSourceFile(source.getPackageName(), source.getSourceFile());
214         String JavaDoc packageName = source.getPackageName();
215         String JavaDoc baseClassName = source.getClassName();
216         int i = baseClassName.lastIndexOf('.');
217         baseClassName = baseClassName.substring(i+1);
218         int j = baseClassName.indexOf("$");
219         if (j >= 0)
220             baseClassName = baseClassName.substring(0,j);
221         return findSourceFile(packageName, baseClassName + ".java");
222         
223     }
224     /**
225      * Open a source file in given package.
226      *
227      * @param packageName the name of the package containing the class whose source file is given
228      * @param fileName the unqualified name of the source file
229      * @return the source file
230      * @throws IOException if a matching source file cannot be found
231      */

232     public SourceFile findSourceFile(String JavaDoc packageName, String JavaDoc fileName) throws IOException JavaDoc {
233         // On windows the fileName specification is different between a file in a directory tree, and a
234
// file in a zip file. In a directory tree the separator used is '\', while in a zip it's '/'
235
// Therefore for each repository figure out what kind it is and use the appropriate separator.
236

237         // In all practicality, this code could just use the hardcoded '/' char, as windows can open
238
// files with this separator, but to allow for the mythical 'other' platform that uses an
239
// alternate separator, make a distinction
240

241         // Create a fully qualified source filename using the package name for both directories and zips
242
String JavaDoc platformName = packageName.replace('.', File.separatorChar) +
243                                 (packageName.length() > 0 ? File.separator : "") + fileName;
244         String JavaDoc canonicalName = packageName.replace('.', '/') +
245                                 (packageName.length() > 0 ? "/" : "") + fileName;
246
247         // Is the file in the cache already? Always cache it with the canonical name
248
SourceFile sourceFile = cache.get(canonicalName);
249         if (sourceFile != null)
250             return sourceFile;
251         
252         // Find this source file, add its data to the cache
253
if (DEBUG) System.out.println("Trying " + fileName + " in package " + packageName + "...");
254         // Query each element of the source path to find the requested source file
255
for (SourceRepository repos : repositoryList) {
256             fileName = repos.isPlatformDependent() ? platformName : canonicalName;
257             if (DEBUG) System.out.println("Looking in " + repos + " for " + fileName);
258             if (repos.contains(fileName)) {
259                 // Found it
260
sourceFile = new SourceFile(repos.getDataSource(fileName));
261                 cache.put(canonicalName, sourceFile); // always cache with canonicalName
262
return sourceFile;
263             }
264         }
265
266         throw new FileNotFoundException JavaDoc("Can't find source file " + fileName);
267     }
268 }
269
270 // vim:ts=4
271
Popular Tags