KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > jbet > ClassInfoLoader


1 /*
2  * JBET - Java Binary Enhancement Tool
3  * Copyright (c) 2003 Networks Associates Technology, Inc.
4  *
5  * This software was developed under DARPA/SPAWAR contract
6  * N66001-00-C-8602 "SPMA" as part of the
7  * DARPA OASIS research program.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  * notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  * notice, this list of conditions and the following disclaimer in the
16  * documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  */

30
31 package jbet;
32 import java.util.*;
33 import java.util.zip.*;
34 import java.io.*;
35 import java.lang.*;
36
37 /**
38  * Finds classes and loads them into ClassInfo objects,
39  * using the CLASSPATH variable.
40  *
41  * Main program Jbet constructs an instance of this
42  * class and then calls parseClassPath(pathEnvironment).
43  * Lexer then calls getClass() to load all classes.
44  * Jbet then calls getClasses() to find the classes to write out.
45  *
46  * This class translates from Java class names
47  * ('.'-separated components not ending in '.class') to file names,
48  * and maintains a table of loaded classes.
49  *
50  * @author Larry D'Anna
51  * @version 0.1
52  * @since JDK 1.1.8
53  */

54
55 public class ClassInfoLoader implements ClassFinder {
56     Vector classPath;
57     private Hashtable classes;
58     private Hashtable files;
59
60     public static char colon = java.io.File.pathSeparatorChar;
61     public static char slash = java.io.File.separatorChar;
62
63     public static String JavaDoc JbetLogFacility = "classloader";
64
65     /** directory returns a reader for a directory.
66      * @param name input directory name.
67      * @return an anonymous class implementing ClassPathElement where
68      * get(filename) returns a FileInputStream for directory/filename,
69      * or null if the file doesn't exist in the directory
70      * put(filename) method returns a FileOutputStream
71      * for directory/filename
72      */

73     
74     public static class DirectoryCPE extends ClassPathElement {
75     String JavaDoc prefix;
76     String JavaDoc dirname;
77     public DirectoryCPE (String JavaDoc dirname) {
78         this.prefix = "";
79         this.dirname = dirname;
80     }
81     public DirectoryCPE (String JavaDoc prefix, String JavaDoc dirname) {
82         this.prefix = prefix;
83         this.dirname = dirname;
84     }
85     public InputStream get(String JavaDoc filename) throws IOException {
86         try {
87         File f = new File(dirname + slash + filename);
88         if (f.exists())
89             return new FileInputStream(f);
90         else
91             return null;
92         } catch (FileNotFoundException e) {
93         return null;
94         }
95     }
96     public OutputStream put(String JavaDoc filename) throws IOException {
97         return put (new File(dirname), filename);
98     }
99     private OutputStream put(File f, String JavaDoc filename) throws IOException {
100         if (filename.startsWith("./"))
101         filename = filename.substring(2);
102         if (filename.startsWith("/")) throw new IOException
103         ("requested an absolute path from a DirectoryCPE");
104         int pos = filename.lastIndexOf( '/' );
105         if (pos == -1) {
106         return new FileOutputStream(f + "/" + filename);
107         }
108         File d = new File (f + "/" + filename.substring(0,pos));
109         if (!d.exists()) {
110         d.mkdirs();
111         }
112         return put(d, filename.substring(pos+1));
113     }
114     public Iterator classes() {
115         File dirfile = new File(dirname);
116         final File[] list = dirfile.listFiles();
117         Vector out = new Vector();
118         for (int i = 0; i < list.length; i++) {
119         if (! list[i].isFile()) continue;
120         String JavaDoc filename = list[i].getName();
121         if (! filename.endsWith(".class")) continue;
122         filename = filename.substring (0, filename.lastIndexOf ('.'));
123         out.addElement(prefix + filename);
124         }
125         return out.iterator();
126     }
127     public boolean subdir_exists(String JavaDoc filename) {
128         File f = new File (dirname + slash + filename);
129         return f.exists() && f.isDirectory();
130     }
131     public boolean class_exists(String JavaDoc filename) {
132         File f = new File (dirname + slash + filename + ".class");
133         return f.exists();
134     }
135     public String JavaDoc toString() {
136         return dirname;
137     }
138     public Iterator subdirs() {
139         File dirfile = new File(dirname);
140         final File[] list = dirfile.listFiles();
141         Vector out = new Vector();
142         for (int i = 0; i < list.length; i++) {
143         if (list[i].isDirectory())
144             out.add(list[i].getName());
145         }
146         return out.iterator();
147     }
148     public ClassPathElement subdir (String JavaDoc subdirname) throws IOException {
149         return new DirectoryCPE (prefix + subdirname + "/",
150                   dirname + "/" + subdirname);
151     }
152     public boolean supports_subdir() { return true; }
153     }
154     
155
156     public static ClassPathElement directory(String JavaDoc name) {
157     final String JavaDoc s = ((name.length()!=0)
158               && (name.charAt(name.length()-1) == slash))
159         ? name.substring(0, name.length()-1)
160         : name;
161     return new DirectoryCPE(s);
162     }
163
164     /** zipfile returns a reader for a ZIP file.
165      * @param zfname input file pathname.
166      * @return an anonymous class implementing ClassPathElement where
167      * get(filename) returns a FileInputStream for directory/filename,
168      * or null if the file doesn't exist in the directory
169      * put(filename) method returns a FileOutputStream
170      * for directory/filename
171      */

172     
173     public static class ZipFileCPE extends ClassPathElement {
174     ZipFile zf;
175     String JavaDoc zfname;
176     public ZipFileCPE(String JavaDoc zfname) throws IOException {
177         zf = new ZipFile(zfname);
178         this.zfname = zfname;
179     }
180     public InputStream get(String JavaDoc filename) throws IOException {
181         ZipEntry ze = zf.getEntry(filename);
182         if (ze == null) return null;
183         return zf.getInputStream(ze);
184     }
185     public OutputStream put(String JavaDoc filename) throws IOException {
186         throw new RuntimeException JavaDoc
187         ("Writing to zip files not supported");
188     }
189     public Iterator classes() {
190         //return Util.enum2iter(zf.entries());
191
Vector out = new Vector();
192         for (Enumeration e = zf.entries(); e.hasMoreElements(); ) {
193         ZipEntry entry = (ZipEntry) e.nextElement();
194         if (entry.isDirectory()) continue;
195         String JavaDoc s = entry.getName();
196         if (! s.endsWith(".class")) continue;
197         s = s.substring (0, s.lastIndexOf ('.'));
198         out.addElement(s);
199         }
200         return out.iterator();
201     }
202     public Iterator subdirs() {
203         return null;
204     }
205     public boolean class_exists(String JavaDoc name) {
206         return zf.getEntry(name + ".class") != null;
207     }
208     public boolean subdir_exists(String JavaDoc name) {
209         return false;
210     }
211     public String JavaDoc toString() {
212         return zfname;
213     }
214
215     public ClassPathElement subdir (String JavaDoc dirname) throws IOException {
216         throw new
217         IllegalStateException JavaDoc ("don't put zipfiles on the command path");
218     }
219     public boolean supports_subdir() { return false; }
220     }
221     
222     public static ClassPathElement zipfile(String JavaDoc zfname) throws IOException {
223     return new ZipFileCPE(zfname);
224     }
225     
226     static ClassPathElement resolveCPElement (String JavaDoc s) throws IOException {
227     ClassPathElement cpe;
228     if (( s.endsWith(".jar") || s.endsWith(".zip")) )
229         cpe = zipfile(s);
230     else
231         cpe = directory(s);
232     return cpe;
233     }
234
235
236     /** getCPElement returns a classPathElement for the i'th element
237      * in the classPath. If that element is a string, replace it
238      * with a ClassPathElement for the directory or ZIP file.
239      * @param i the index
240      * @return a reader for the class path component
241      */

242     ClassPathElement getCPElement(int i) throws IOException {
243     Object JavaDoc o = classPath.elementAt(i);
244     if (o instanceof ClassPathElement)
245         return (ClassPathElement) o;
246     String JavaDoc s = (String JavaDoc) o;
247     ClassPathElement cpe = resolveCPElement (s);
248     classPath.setElementAt(cpe, i);
249     return cpe;
250     }
251
252     /**
253      * Access the program's CLASSPATH environment variable, split the
254      * string in CLASSPATH into separate paths, and saves the
255      * separate paths in a Vector.
256      *
257      * @param cpString input class path, colon separated elements.
258      */

259     public void parseClassPath(String JavaDoc cpString) {
260     parseClassPath(cpString, true);
261     }
262
263     /** Variant that allows control of where to insert.
264      *
265      * @param cpString input class path
266      * @param insertAtFront if true, makes vector in reverse order
267      * @return A Vector of paths.
268      */

269     public void parseClassPath(String JavaDoc cpString, boolean insertAtFront) {
270     for (int i = 0; i < cpString.length();) {
271         int io = cpString.indexOf(colon, i);
272         String JavaDoc s;
273         if (io == -1) {
274         s = cpString.substring(i);
275         i = cpString.length();
276         } else {
277         s = cpString.substring(i, io);
278         i = io + 1;
279         }
280         if (insertAtFront)
281         classPath.insertElementAt(s, 0);
282         else
283         classPath.addElement(s);
284     }
285     }
286
287     /** clear the classPath.
288      */

289     public void clearPath() {
290     classPath.removeAllElements();
291     }
292
293     /** Constructor.
294      */

295     public ClassInfoLoader() {
296     classPath = new Vector();
297     classes = new Hashtable();
298     files = new Hashtable();
299     }
300
301     /** Return an enumeration of all classes
302      */

303     public Enumeration getClasses() {
304     final Enumeration e1f = classes.elements();
305     final Enumeration e2f = files.elements();
306     return new Enumeration() {
307         Enumeration e1 = e1f, e2 = e2f;
308         public boolean hasMoreElements() {
309             return (e1 != null && e1.hasMoreElements()) ||
310             (e2 != null && e2.hasMoreElements());
311         }
312         public Object JavaDoc nextElement() {
313             if (e1 != null) {
314             if (e1.hasMoreElements())
315                 return e1.nextElement();
316             else
317                 e1 = null;
318             }
319             if (e2 != null)
320             return e2.nextElement();
321             throw new IllegalStateException JavaDoc("can't get here");
322         }
323         };
324     }
325
326
327     public ClassInfo findClass(String JavaDoc name) throws ClassFileException {
328     return getClass(name);
329     }
330
331     
332
333     /**
334      * Returns a ClassInfo for the class name specified. The
335      * ClassInfoLoader caches ClassInfo objects and returns the ClassInfo
336      * from the cache if possible, and reads it from disk (caching it)
337      * otherwise.
338      */

339
340     public ClassInfo lookupClass(String JavaDoc name) {
341     if (name.startsWith("@")) {
342         String JavaDoc filename = name.substring(1, name.length());
343         return (ClassInfo) files.get(filename);
344     } else {
345         return (ClassInfo) classes.get(name);
346     }
347     }
348
349     public ClassInfo getClass(String JavaDoc name) throws ClassFileException {
350     ClassInfo cr = lookupClass(name);
351     if (cr != null)
352         return cr;
353     cr = loadClass(name);
354     putClass(name, cr);
355     return cr;
356     }
357
358     public ClassInfo getCopy(String JavaDoc name) throws ClassFileException {
359     ClassInfo cr = lookupClass(name);
360     if (cr != null)
361         return new ClassInfo(cr);
362     else
363         return loadClass(name);
364     }
365
366     public ClassFinder getCopyFinder = new ClassFinder() {
367         public ClassInfo findClass(String JavaDoc name) throws ClassFileException {
368         return getCopy(name);
369         }
370     };
371
372     /** Get info on a method. Throw an exception if it's not there.
373      * @param cname class name
374      * @param name method name
375      * @param type method descriptor
376      * @return a MethodInfo object for the method.
377      */

378     public MethodInfo getMethod(String JavaDoc cname, String JavaDoc name, Descriptor type) throws ClassFileException, ElementNotFoundException
379     {
380     ClassInfo cr = getClass(cname);
381     MethodInfo ret = null;
382     for (int i = 0; i < cr.methods.size(); i++) {
383         MethodInfo mi = (MethodInfo) cr.methods.elementAt(i);
384         if (mi.name.equals(name) &&
385         (type==null || type.equals(mi.descriptor)))
386         if (ret == null)
387             ret = mi;
388         else
389             throw new ElementNotFoundException
390             (cname, name, type, "Can not disambiguate");
391     }
392     if (ret == null) {
393         throw new ElementNotFoundException (cname, name, type);
394     }
395     return ret;
396     }
397
398     /** Get info on a field. Throw an exception if it's not there.
399      * @param cname class name
400      * @param name field name
401      * @return a FieldInfo object for the field.
402      */

403     public FieldInfo getField(String JavaDoc cname, String JavaDoc name) throws ClassFileException, ElementNotFoundException
404     {
405     ClassInfo cr = getClass(cname);
406     return cr.getField(name);
407     }
408
409     /** Get info on a classs, method or field. Return appropriate info.
410      * Throw an exception if it's not there.
411      * @param cname class name.
412      * @param name field or method name.
413      * @param d optional method descriptor
414      * @return either a ClassInfo, MethodInfo or a FieldInfo
415      */

416     public Object JavaDoc getThing(String JavaDoc cname, String JavaDoc name, Descriptor d) throws ClassFileException, ElementNotFoundException {
417     if (name == null) return getClass(cname);
418     if (d == null) {
419         try {
420         return getField(cname, name);
421         } catch (ElementNotFoundException e) {
422         return getMethod(cname, name, null);
423         }
424     } else
425         return getMethod(cname, name, d);
426     }
427
428     /** remove a class from the loader. This is so it can be renamed. */
429     public void removeClass (ClassInfo cr) {
430     Object JavaDoc z = classes.get (cr.name());
431     if (z == cr)
432         classes.remove (cr.name());
433     }
434
435     /** Put a class.
436      * @param cr ClassInfo of the class.
437      */

438     public void putClass(ClassInfo cr) {
439     classes.put( cr.name(), cr );
440     }
441
442     public void putClass(String JavaDoc name, ClassInfo cr) {
443     if (name.startsWith("@"))
444         files.put(name.substring(1), cr);
445     else
446         classes.put(name, cr);
447     }
448
449     /** Test if a class exists on the classpath.
450      * @param name name of the class.
451      * @return true if class can be found.
452      */

453     public boolean exists (String JavaDoc name) throws ClassFileException {
454     if (lookupClass(name) != null)
455         return true;
456     try {
457         if (name.startsWith("@")) {
458         File f = new File(name.substring(1));
459         return f.exists();
460         } else {
461         for (int i = 0; i < classPath.size(); i++)
462             if (getCPElement(i).class_exists(name)) return true;
463         return false;
464         }
465     } catch (IOException e) {
466         throw new ClassFileException("IOException caught "
467                      + name, e);
468     }
469     }
470
471     /**
472      * Create a new ClassInfo object to represent the named class. Searches
473      * the CLASSPATH to find the class.
474      *
475      * @param name The name of the class to find, without the .class suffix.
476      * @return a ClassInfo representing the class.
477      */

478     private ClassInfo loadClass(String JavaDoc name) throws ClassFileException {
479     ClassInfo cr;
480     try {
481         if (name.startsWith("@")) {
482         String JavaDoc filename = name.substring(1, name.length());
483         String JavaDoc dirname = Util.basename(filename);
484         InputStream in = new FileInputStream(filename);
485         cr = new ClassInfo(in);
486         String JavaDoc pakname = Util.basename(cr.name());
487         if ( name.endsWith(".class") && dirname.endsWith(pakname) ) {
488             cr.classPathElement = directory
489             (dirname.substring(0, dirname.length()-pakname.length()));
490             cr.filename = null;
491         } else {
492             cr.classPathElement = directory(dirname);
493             cr.filename = Util.unqualify(filename);
494         }
495         return cr;
496         } else {
497         for (int i = 0; i < classPath.size(); i++) {
498             ClassPathElement cpe;
499             try {
500             cpe = getCPElement(i);
501             } catch (IOException e) {
502             continue;
503             //ignore it if a zip file doesn't exist or something
504
}
505             InputStream in = cpe.get(name + ".class");
506             if (in == null) continue;
507             return new ClassInfo(in, cpe);
508         }
509         throw new ClassFileNotFoundException(name + ".class");
510         }
511     } catch (IOException e) {
512         throw new ClassFileException("IOException caught trying to read "
513                      + name, e);
514     }
515     }
516
517     public void findPackageClasses (String JavaDoc packname, Collection out, boolean sense)
518     throws ClassFileException {
519     String JavaDoc prefix;
520     if (packname.equals(""))
521         prefix = "";
522     else
523         prefix = packname + "/";
524     try {
525         HashSet outset = new HashSet();
526         for (Enumeration e = classes.elements(); e.hasMoreElements();) {
527         ClassInfo cr = (ClassInfo) e.nextElement();
528         if (cr.name().startsWith(prefix)) {
529             outset.add(cr.name());
530         }
531         }
532         for (int i = 0; i < classPath.size(); i++) {
533         Iterator j = null;
534         ClassPathElement cpe;
535         try {
536             cpe = getCPElement(i);
537         } catch (IOException e) {
538             continue;
539         }
540         if (!packname.equals("") && cpe.supports_subdir()) {
541             if (!cpe.subdir_exists(packname)) continue;
542             j = cpe.subdir(packname).allclasses();
543         } else
544             j = cpe.allclasses();
545         while (j.hasNext()) {
546             String JavaDoc name = (String JavaDoc) j.next();
547             if (name.startsWith(prefix)) {
548             outset.add(name);
549             }
550         }
551         }
552         if (sense)
553         out.addAll(outset);
554         else
555         out.removeAll(outset);
556     } catch (IOException e) {
557         throw new ClassFileException("IOException caught ", e);
558     }
559     }
560
561
562     /** Print the class path
563      * @param out stream to print on.
564      */

565     void print(LineWriter out) {
566     for (int i = 0; i < classPath.size(); i++)
567         out.println( classPath.elementAt(i).toString() );
568     }
569
570     public String JavaDoc getClassPath () {
571     StringBuffer JavaDoc S = new StringBuffer JavaDoc();
572
573     for (int i = 0; i < classPath.size(); i++) {
574         if (S.length() > 0)
575         S.append (":");
576         S.append (classPath.elementAt(i).toString());
577     }
578
579     return S.toString();
580     }
581 }
582
Popular Tags