source: other-projects/trunk/gs3-release-maker/apache-ant-1.6.5/src/main/org/apache/tools/ant/taskdefs/optional/sitraka/bytecode/ClassPathLoader.java@ 14627

Last change on this file since 14627 was 14627, checked in by oranfry, 17 years ago

initial import of the gs3-release-maker

File size: 11.6 KB
Line 
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 */
17package org.apache.tools.ant.taskdefs.optional.sitraka.bytecode;
18
19import java.io.BufferedInputStream;
20import java.io.ByteArrayInputStream;
21import java.io.ByteArrayOutputStream;
22import java.io.File;
23import java.io.FileInputStream;
24import java.io.FilenameFilter;
25import java.io.IOException;
26import java.io.InputStream;
27import java.util.Enumeration;
28import java.util.Hashtable;
29import java.util.NoSuchElementException;
30import java.util.StringTokenizer;
31import java.util.Vector;
32import java.util.zip.ZipEntry;
33import java.util.zip.ZipFile;
34
35/**
36 * Core of the bytecode analyzer. It loads classes from a given classpath.
37 *
38 */
39public class ClassPathLoader {
40
41 public static final FileLoader NULL_LOADER = new NullLoader();
42
43 /** the list of files to look for */
44 private File[] 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 classPath) {
52 StringTokenizer st = new StringTokenizer(classPath, File.pathSeparator);
53 Vector entries = new Vector();
54 while (st.hasMoreTokens()) {
55 File file = new File(st.nextToken());
56 entries.addElement(file);
57 }
58 files = new File[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[] entries) {
67 files = new File[entries.length];
68 for (int i = 0; i < entries.length; i++) {
69 files[i] = new File(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[] 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 getFile();
85
86 /** return the set of classes found in the file */
87 ClassFile[] getClasses() throws IOException;
88 }
89
90 /**
91 * @return the set of <tt>FileLoader</tt> loaders matching the given classpath.
92 */
93 public Enumeration 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 getClasses() throws IOException {
109 Hashtable map = new Hashtable();
110 Enumeration 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 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 {
132 private int index = 0;
133
134 public boolean hasMoreElements() {
135 return index < files.length;
136 }
137
138 public Object nextElement() {
139 if (index >= files.length) {
140 throw new NoSuchElementException();
141 }
142 File 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 getCachedStream(InputStream is) throws IOException {
164 final InputStream bis = new BufferedInputStream(is);
165 final byte[] buffer = new byte[8192];
166 final ByteArrayOutputStream bos = new ByteArrayOutputStream(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(bos.toByteArray());
174 }
175}
176
177/** a null loader to return when the file is not valid */
178final class NullLoader implements ClassPathLoader.FileLoader {
179 private File file;
180
181 NullLoader() {
182 this(null);
183 }
184
185 NullLoader(File file) {
186 this.file = file;
187 }
188
189 public File getFile() {
190 return file;
191 }
192
193 public ClassFile[] getClasses() throws IOException {
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 */
203final class JarLoader implements ClassPathLoader.FileLoader {
204 private File file;
205
206 JarLoader(File file) {
207 this.file = file;
208 }
209
210 public File getFile() {
211 return file;
212 }
213
214 public ClassFile[] getClasses() throws IOException {
215 ZipFile zipFile = new ZipFile(file);
216 Vector v = new Vector();
217 Enumeration entries = zipFile.entries();
218 while (entries.hasMoreElements()) {
219 ZipEntry entry = (ZipEntry) entries.nextElement();
220 if (entry.getName().endsWith(".class")) {
221 InputStream 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 */
238final class DirectoryLoader implements ClassPathLoader.FileLoader {
239 private File directory;
240 private static final FilenameFilter DIRECTORY_FILTER = new DirectoryFilter();
241 private static final FilenameFilter CLASS_FILTER = new ClassFilter();
242
243 DirectoryLoader(File dir) {
244 directory = dir;
245 }
246
247 public File getFile() {
248 return directory;
249 }
250
251 public ClassFile[] getClasses() throws IOException {
252 Vector v = new Vector(127);
253 Vector files = listFiles(directory, CLASS_FILTER, true);
254 final int filesCount = files.size();
255 for (int i = 0; i < filesCount; i++) {
256 File file = (File) files.elementAt(i);
257 InputStream is = null;
258 try {
259 is = ClassPathLoader.getCachedStream(new FileInputStream(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 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 listFiles(File directory, FilenameFilter filter, boolean recurse) {
288 if (!directory.isDirectory()) {
289 throw new IllegalArgumentException(directory + " is not a directory");
290 }
291 Vector list = new Vector(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 listFilesTo(Vector list, File directory,
306 FilenameFilter filter, boolean recurse) {
307 String[] files = directory.list(filter);
308 for (int i = 0; i < files.length; i++) {
309 list.addElement(new File(directory, files[i]));
310 }
311 files = null; // we don't need it anymore
312 if (recurse) {
313 String[] subdirs = directory.list(DIRECTORY_FILTER);
314 for (int i = 0; i < subdirs.length; i++) {
315 listFilesTo(list, new File(directory, subdirs[i]), filter, recurse);
316 }
317 }
318 return list;
319 }
320
321}
322
323/** Convenient filter that accepts only directory <tt>File</tt> */
324final class DirectoryFilter implements FilenameFilter {
325 public boolean accept(File directory, String name) {
326 File pathname = new File(directory, name);
327 return pathname.isDirectory();
328 }
329}
330
331/** convenient filter to accept only .class files */
332final class ClassFilter implements FilenameFilter {
333 public boolean accept(File dir, String name) {
334 return name.endsWith(".class");
335 }
336}
Note: See TracBrowser for help on using the repository browser.