KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > tools > ant > taskdefs > optional > sitraka > bytecode > ClassPathLoader


1 /*
2  * Copyright 2001-2004 The Apache Software Foundation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */

17 package org.apache.tools.ant.taskdefs.optional.sitraka.bytecode;
18
19 import java.io.BufferedInputStream JavaDoc;
20 import java.io.ByteArrayInputStream JavaDoc;
21 import java.io.ByteArrayOutputStream JavaDoc;
22 import java.io.File JavaDoc;
23 import java.io.FileInputStream JavaDoc;
24 import java.io.FilenameFilter JavaDoc;
25 import java.io.IOException JavaDoc;
26 import java.io.InputStream JavaDoc;
27 import java.util.Enumeration JavaDoc;
28 import java.util.Hashtable JavaDoc;
29 import java.util.NoSuchElementException JavaDoc;
30 import java.util.StringTokenizer JavaDoc;
31 import java.util.Vector JavaDoc;
32 import java.util.zip.ZipEntry JavaDoc;
33 import java.util.zip.ZipFile JavaDoc;
34
35 /**
36  * Core of the bytecode analyzer. It loads classes from a given classpath.
37  *
38  */

39 public class ClassPathLoader {
40
41     public static final FileLoader NULL_LOADER = new NullLoader();
42
43     /** the list of files to look for */
44     private File JavaDoc[] files;
45
46     /**
47      * create a new instance with a given classpath. It must be urls
48      * separated by the platform specific path separator.
49      * @param classPath the classpath to load all the classes from.
50      */

51     public ClassPathLoader(String JavaDoc classPath) {
52         StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(classPath, File.pathSeparator);
53         Vector JavaDoc entries = new Vector JavaDoc();
54         while (st.hasMoreTokens()) {
55             File JavaDoc file = new File JavaDoc(st.nextToken());
56             entries.addElement(file);
57         }
58         files = new File JavaDoc[entries.size()];
59         entries.copyInto(files);
60     }
61
62     /**
63      * create a new instance with a given set of urls.
64      * @param entries valid file urls (either .jar, .zip or directory)
65      */

66     public ClassPathLoader(String JavaDoc[] entries) {
67         files = new File JavaDoc[entries.length];
68         for (int i = 0; i < entries.length; i++) {
69             files[i] = new File JavaDoc(entries[i]);
70         }
71     }
72
73     /**
74      * create a new instance with a given set of urls
75      * @param entries file urls to look for classes (.jar, .zip or directory)
76      */

77     public ClassPathLoader(File JavaDoc[] entries) {
78         files = entries;
79     }
80
81     /** the interface to implement to look up for specific resources */
82     public interface FileLoader {
83         /** the file url that is looked for .class files */
84         File JavaDoc getFile();
85
86         /** return the set of classes found in the file */
87         ClassFile[] getClasses() throws IOException JavaDoc;
88     }
89
90     /**
91      * @return the set of <tt>FileLoader</tt> loaders matching the given classpath.
92      */

93     public Enumeration JavaDoc loaders() {
94         return new LoaderEnumeration();
95     }
96
97     /**
98      * return the whole set of classes in the classpath. Note that this method
99      * can be very resource demanding since it must load all bytecode from
100      * all classes in all resources in the classpath at a time.
101      * To process it in a less resource demanding way, it is maybe better to
102      * use the <tt>loaders()</tt> that will return loader one by one.
103      *
104      * @return the hashtable containing ALL classes that are found in the given
105      * classpath. Note that the first entry of a given classname will shadow
106      * classes with the same name (as a classloader does)
107      */

108     public Hashtable JavaDoc getClasses() throws IOException JavaDoc {
109         Hashtable JavaDoc map = new Hashtable JavaDoc();
110         Enumeration JavaDoc e = loaders();
111         while (e.hasMoreElements()) {
112             FileLoader loader = (FileLoader) e.nextElement();
113             System.out.println("Processing " + loader.getFile());
114             long t0 = System.currentTimeMillis();
115             ClassFile[] classes = loader.getClasses();
116             long dt = System.currentTimeMillis() - t0;
117             System.out.println("" + classes.length + " classes loaded in " + dt + "ms");
118             for (int j = 0; j < classes.length; j++) {
119                 String JavaDoc name = classes[j].getFullName();
120                 // do not allow duplicates entries to preserve 'classpath' behavior
121
// first class in wins
122
if (!map.containsKey(name)) {
123                     map.put(name, classes[j]);
124                 }
125             }
126         }
127         return map;
128     }
129
130     /** the loader enumeration that will return loaders */
131     private class LoaderEnumeration implements Enumeration JavaDoc {
132         private int index = 0;
133
134         public boolean hasMoreElements() {
135             return index < files.length;
136         }
137
138         public Object JavaDoc nextElement() {
139             if (index >= files.length) {
140                 throw new NoSuchElementException JavaDoc();
141             }
142             File JavaDoc file = files[index++];
143             if (!file.exists()) {
144                 return new NullLoader(file);
145             }
146             if (file.isDirectory()) {
147                 // it's a directory
148
return new DirectoryLoader(file);
149             } else if (file.getName().endsWith(".zip") || file.getName().endsWith(".jar")) {
150                 // it's a jar/zip file
151
return new JarLoader(file);
152             }
153             return new NullLoader(file);
154
155         }
156     }
157
158     /**
159      * useful methods to read the whole input stream in memory so that
160      * it can be accessed faster. Processing rt.jar and tools.jar from JDK 1.3.1
161      * brings time from 50s to 7s.
162      */

163     public static InputStream JavaDoc getCachedStream(InputStream JavaDoc is) throws IOException JavaDoc {
164         final InputStream JavaDoc bis = new BufferedInputStream JavaDoc(is);
165         final byte[] buffer = new byte[8192];
166         final ByteArrayOutputStream JavaDoc bos = new ByteArrayOutputStream JavaDoc(2048);
167         int n;
168         bos.reset();
169         while ((n = bis.read(buffer, 0, buffer.length)) != -1) {
170             bos.write(buffer, 0, n);
171         }
172         is.close();
173         return new ByteArrayInputStream JavaDoc(bos.toByteArray());
174     }
175 }
176
177 /** a null loader to return when the file is not valid */
178 final class NullLoader implements ClassPathLoader.FileLoader {
179     private File JavaDoc file;
180
181     NullLoader() {
182         this(null);
183     }
184
185     NullLoader(File JavaDoc file) {
186         this.file = file;
187     }
188
189     public File JavaDoc getFile() {
190         return file;
191     }
192
193     public ClassFile[] getClasses() throws IOException JavaDoc {
194         return new ClassFile[0];
195     }
196 }
197
198 /**
199  * jar loader specified in looking for classes in jar and zip
200  * @todo read the jar manifest in case there is a Class-Path
201  * entry.
202  */

203 final class JarLoader implements ClassPathLoader.FileLoader {
204     private File JavaDoc file;
205
206     JarLoader(File JavaDoc file) {
207         this.file = file;
208     }
209
210     public File JavaDoc getFile() {
211         return file;
212     }
213
214     public ClassFile[] getClasses() throws IOException JavaDoc {
215         ZipFile JavaDoc zipFile = new ZipFile JavaDoc(file);
216         Vector JavaDoc v = new Vector JavaDoc();
217         Enumeration JavaDoc entries = zipFile.entries();
218         while (entries.hasMoreElements()) {
219             ZipEntry JavaDoc entry = (ZipEntry JavaDoc) entries.nextElement();
220             if (entry.getName().endsWith(".class")) {
221                 InputStream JavaDoc is = ClassPathLoader.getCachedStream(zipFile.getInputStream(entry));
222                 ClassFile classFile = new ClassFile(is);
223                 is.close();
224                 v.addElement(classFile);
225             }
226         }
227         ClassFile[] classes = new ClassFile[v.size()];
228         v.copyInto(classes);
229         return classes;
230     }
231 }
232
233 /**
234  * directory loader that will look all classes recursively
235  * @todo should discard classes which package name does not
236  * match the directory ?
237  */

238 final class DirectoryLoader implements ClassPathLoader.FileLoader {
239     private File JavaDoc directory;
240     private static final FilenameFilter JavaDoc DIRECTORY_FILTER = new DirectoryFilter();
241     private static final FilenameFilter JavaDoc CLASS_FILTER = new ClassFilter();
242
243     DirectoryLoader(File JavaDoc dir) {
244         directory = dir;
245     }
246
247     public File JavaDoc getFile() {
248         return directory;
249     }
250
251     public ClassFile[] getClasses() throws IOException JavaDoc {
252         Vector JavaDoc v = new Vector JavaDoc(127);
253         Vector JavaDoc files = listFiles(directory, CLASS_FILTER, true);
254         final int filesCount = files.size();
255         for (int i = 0; i < filesCount; i++) {
256             File JavaDoc file = (File JavaDoc) files.elementAt(i);
257             InputStream JavaDoc is = null;
258             try {
259                 is = ClassPathLoader.getCachedStream(new FileInputStream JavaDoc(file));
260                 ClassFile classFile = new ClassFile(is);
261                 is.close();
262                 is = null;
263                 v.addElement(classFile);
264             } finally {
265                 if (is != null) {
266                     try {
267                         is.close();
268                     } catch (IOException JavaDoc ignored) {
269                     }
270                 }
271             }
272         }
273         ClassFile[] classes = new ClassFile[v.size()];
274         v.copyInto(classes);
275         return classes;
276     }
277
278     /**
279      * List files that obeys to a specific filter recursively from a given base
280      * directory.
281      * @param directory the directory where to list the files from.
282      * @param filter the file filter to apply
283      * @param recurse tells whether or not the listing is recursive.
284      * @return the list of <tt>File</tt> objects that applies to the given
285      * filter.
286      */

287     public static Vector JavaDoc listFiles(File JavaDoc directory, FilenameFilter JavaDoc filter, boolean recurse) {
288         if (!directory.isDirectory()) {
289             throw new IllegalArgumentException JavaDoc(directory + " is not a directory");
290         }
291         Vector JavaDoc list = new Vector JavaDoc(512);
292         listFilesTo(list, directory, filter, recurse);
293         return list;
294     }
295
296     /**
297      * List and add files to a given list. As a convenience it sends back the
298      * instance of the list given as a parameter.
299      * @param list the list of files where the filtered files should be added
300      * @param directory the directory where to list the files from.
301      * @param filter the file filter to apply
302      * @param recurse tells whether or not the listing is recursive.
303      * @return the list instance that was passed as the <tt>list</tt> argument.
304      */

305     private static Vector JavaDoc listFilesTo(Vector JavaDoc list, File JavaDoc directory,
306                                       FilenameFilter JavaDoc filter, boolean recurse) {
307         String JavaDoc[] files = directory.list(filter);
308         for (int i = 0; i < files.length; i++) {
309             list.addElement(new File JavaDoc(directory, files[i]));
310         }
311         files = null; // we don't need it anymore
312
if (recurse) {
313             String JavaDoc[] subdirs = directory.list(DIRECTORY_FILTER);
314             for (int i = 0; i < subdirs.length; i++) {
315                 listFilesTo(list, new File JavaDoc(directory, subdirs[i]), filter, recurse);
316             }
317         }
318         return list;
319     }
320
321 }
322
323 /** Convenient filter that accepts only directory <tt>File</tt> */
324 final class DirectoryFilter implements FilenameFilter JavaDoc {
325     public boolean accept(File JavaDoc directory, String JavaDoc name) {
326         File JavaDoc pathname = new File JavaDoc(directory, name);
327         return pathname.isDirectory();
328     }
329 }
330
331 /** convenient filter to accept only .class files */
332 final class ClassFilter implements FilenameFilter JavaDoc {
333     public boolean accept(File JavaDoc dir, String JavaDoc name) {
334         return name.endsWith(".class");
335     }
336 }
337
Popular Tags