View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   *  Unless required by applicable law or agreed to in writing, software
12   *  distributed under the License is distributed on an "AS IS" BASIS,
13   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   *  See the License for the specific language governing permissions and
15   *  limitations under the License. 
16   *
17   */
18  package org.apache.bcel.util;
19  
20  import java.io.DataInputStream;
21  import java.io.File;
22  import java.io.FileInputStream;
23  import java.io.FilenameFilter;
24  import java.io.IOException;
25  import java.io.InputStream;
26  import java.io.Serializable;
27  import java.net.MalformedURLException;
28  import java.net.URL;
29  import java.util.ArrayList;
30  import java.util.Enumeration;
31  import java.util.List;
32  import java.util.Locale;
33  import java.util.StringTokenizer;
34  import java.util.Vector;
35  import java.util.zip.ZipEntry;
36  import java.util.zip.ZipFile;
37  
38  /**
39   * Responsible for loading (class) files from the CLASSPATH. Inspired by
40   * sun.tools.ClassPath.
41   *
42   * @version $Id: ClassPath.java 1152077 2011-07-29 02:29:42Z dbrosius $
43   * @author  <A HREF="mailto:m.dahm@gmx.de">M. Dahm</A>
44   */
45  public class ClassPath implements Serializable {
46  
47      private static final long serialVersionUID = 2099441438483340671L;
48      public static final ClassPath SYSTEM_CLASS_PATH = new ClassPath();
49      private PathEntry[] paths;
50      private String class_path;
51      private ClassPath parent;
52  
53      public ClassPath(ClassPath parent, String class_path) {
54          this(class_path);
55          this.parent = parent;
56      }
57  
58      /**
59       * Search for classes in given path.
60       * 
61       * @param class_path
62       */
63      public ClassPath(String class_path) {
64          this.class_path = class_path;
65          List<PathEntry> vec = new ArrayList<PathEntry>();
66          for (StringTokenizer tok = new StringTokenizer(class_path, System
67                  .getProperty("path.separator")); tok.hasMoreTokens();) {
68              String path = tok.nextToken();
69              if (!path.equals("")) {
70                  File file = new File(path);
71                  try {
72                      if (file.exists()) {
73                          if (file.isDirectory()) {
74                              vec.add(new Dir(path));
75                          } else {
76                              vec.add(new Zip(new ZipFile(file)));
77                          }
78                      }
79                  } catch (IOException e) {
80                      System.err.println("CLASSPATH component " + file + ": " + e);
81                  }
82              }
83          }
84          paths = new PathEntry[vec.size()];
85          vec.toArray(paths);
86      }
87  
88  
89      /**
90       * Search for classes in CLASSPATH.
91       * @deprecated Use SYSTEM_CLASS_PATH constant
92       */
93      @Deprecated
94      public ClassPath() {
95          this(getClassPath());
96      }
97  
98  
99      /** @return used class path string
100      */
101     @Override
102     public String toString() {
103         if (parent != null) {
104             return parent.toString() + File.pathSeparator + class_path;
105         }
106         return class_path;
107     }
108 
109     @Override
110     public int hashCode() {
111         if (parent != null) {
112             return class_path.hashCode() + parent.hashCode();            
113         }
114         return class_path.hashCode();
115     }
116 
117 
118     @Override
119     public boolean equals( Object o ) {
120         if (o instanceof ClassPath) {
121             ClassPath cp = (ClassPath)o;
122             return class_path.equals(cp.toString());
123         }
124         return false;
125     }
126 
127 
128     private static final void getPathComponents( String path, List<String> list ) {
129         if (path != null) {
130             StringTokenizer tok = new StringTokenizer(path, File.pathSeparator);
131             while (tok.hasMoreTokens()) {
132                 String name = tok.nextToken();
133                 File file = new File(name);
134                 if (file.exists()) {
135                     list.add(name);
136                 }
137             }
138         }
139     }
140 
141 
142     /** Checks for class path components in the following properties:
143      * "java.class.path", "sun.boot.class.path", "java.ext.dirs"
144      *
145      * @return class path as used by default by BCEL
146      */
147     public static final String getClassPath() {
148         String class_path = System.getProperty("java.class.path");
149         String boot_path = System.getProperty("sun.boot.class.path");
150         String ext_path = System.getProperty("java.ext.dirs");
151         List<String> list = new ArrayList<String>();
152         getPathComponents(class_path, list);
153         getPathComponents(boot_path, list);
154         List<String> dirs = new ArrayList<String>();
155         getPathComponents(ext_path, dirs);
156         for (String d : dirs) {
157             File ext_dir = new File(d);
158             String[] extensions = ext_dir.list(new FilenameFilter() {
159 
160                 public boolean accept( File dir, String name ) {
161                     name = name.toLowerCase(Locale.ENGLISH);
162                     return name.endsWith(".zip") || name.endsWith(".jar");
163                 }
164             });
165             if (extensions != null) {
166                 for (int i = 0; i < extensions.length; i++) {
167                     list.add(ext_dir.getPath() + File.separatorChar + extensions[i]);
168                 }
169             }
170         }
171         StringBuilder buf = new StringBuilder();
172         String separator = "";
173         for (String path : list) {
174             buf.append(separator);
175             separator = File.pathSeparator;
176             buf.append(path);
177         }
178         return buf.toString().intern();
179     }
180 
181 
182     /**
183      * @param name fully qualified class name, e.g. java.lang.String
184      * @return input stream for class
185      */
186     public InputStream getInputStream( String name ) throws IOException {
187         return getInputStream(name.replace('.', '/'), ".class");
188     }
189 
190 
191     /**
192      * Return stream for class or resource on CLASSPATH.
193      *
194      * @param name fully qualified file name, e.g. java/lang/String
195      * @param suffix file name ends with suff, e.g. .java
196      * @return input stream for file on class path
197      */
198     public InputStream getInputStream( String name, String suffix ) throws IOException {
199         InputStream is = null;
200         try {
201             is = getClass().getClassLoader().getResourceAsStream(name + suffix);
202         } catch (Exception e) {
203         }
204         if (is != null) {
205             return is;
206         }
207         return getClassFile(name, suffix).getInputStream();
208     }
209 
210     /**
211      * @param name fully qualified resource name, e.g. java/lang/String.class
212      * @return InputStream supplying the resource, or null if no resource with that name.
213      */
214     public InputStream getResourceAsStream(String name) {
215         for (int i = 0; i < paths.length; i++) {
216             InputStream is;
217             if ((is = paths[i].getResourceAsStream(name)) != null) {
218                 return is;
219             }
220         }
221         return null;
222     }
223     
224     /**
225      * @param name fully qualified resource name, e.g. java/lang/String.class
226      * @return URL supplying the resource, or null if no resource with that name.
227      */
228     public URL getResource(String name) {
229         for (int i = 0; i < paths.length; i++) {
230             URL url;
231             if ((url = paths[i].getResource(name)) != null) {
232                 return url;
233             }
234         }
235         return null;
236     }
237 
238     /**
239      * @param name fully qualified resource name, e.g. java/lang/String.class
240      * @return An Enumeration of URLs supplying the resource, or an
241      * empty Enumeration if no resource with that name.
242      */
243     public Enumeration<URL> getResources(String name) {
244         Vector<URL> results = new Vector<URL>();
245         for (int i = 0; i < paths.length; i++) {
246             URL url;
247             if ((url = paths[i].getResource(name)) != null) {
248                 results.add(url);
249             }
250         }
251         return results.elements();
252     }
253 
254     /**
255      * @param name fully qualified file name, e.g. java/lang/String
256      * @param suffix file name ends with suff, e.g. .java
257      * @return class file for the java class
258      */
259     public ClassFile getClassFile( String name, String suffix ) throws IOException {
260         for (int i = 0; i < paths.length; i++) {
261             ClassFile cf = null;
262 
263             if(parent != null) {
264                 cf = parent.getClassFileInternal(name, suffix);
265             }
266             
267             if(cf == null) {
268                 cf = getClassFileInternal(name,suffix);
269             }
270             
271             if(cf != null) {
272                 return cf;
273             }
274         }
275 
276         throw new IOException("Couldn't find: " + name + suffix);
277     }
278 
279     private ClassFile getClassFileInternal(String name, String suffix) throws IOException {
280 
281       for(int i=0; i < paths.length; i++) {
282           ClassFile cf = paths[i].getClassFile(name, suffix);
283           
284           if(cf != null) {
285               return cf;
286           }
287       }
288 
289       return null;
290    }
291 
292 
293     /**
294      * @param name fully qualified class name, e.g. java.lang.String
295      * @return input stream for class
296      */
297     public ClassFile getClassFile( String name ) throws IOException {
298         return getClassFile(name, ".class");
299     }
300 
301 
302     /**
303      * @param name fully qualified file name, e.g. java/lang/String
304      * @param suffix file name ends with suffix, e.g. .java
305      * @return byte array for file on class path
306      */
307     public byte[] getBytes( String name, String suffix ) throws IOException {
308         DataInputStream dis = null;
309         try {
310             InputStream is = getInputStream(name, suffix);
311             if (is == null) {
312                 throw new IOException("Couldn't find: " + name + suffix);
313             }
314             dis = new DataInputStream(is);
315             byte[] bytes = new byte[is.available()];
316             dis.readFully(bytes);
317             return bytes;
318         } finally {
319             if (dis != null) {
320                 dis.close();
321             }
322         }
323     }
324 
325 
326     /**
327      * @return byte array for class
328      */
329     public byte[] getBytes( String name ) throws IOException {
330         return getBytes(name, ".class");
331     }
332 
333 
334     /**
335      * @param name name of file to search for, e.g. java/lang/String.java
336      * @return full (canonical) path for file
337      */
338     public String getPath( String name ) throws IOException {
339         int index = name.lastIndexOf('.');
340         String suffix = "";
341         if (index > 0) {
342             suffix = name.substring(index);
343             name = name.substring(0, index);
344         }
345         return getPath(name, suffix);
346     }
347 
348 
349     /**
350      * @param name name of file to search for, e.g. java/lang/String
351      * @param suffix file name suffix, e.g. .java
352      * @return full (canonical) path for file, if it exists
353      */
354     public String getPath( String name, String suffix ) throws IOException {
355         return getClassFile(name, suffix).getPath();
356     }
357 
358     private static abstract class PathEntry implements Serializable {
359 
360         private static final long serialVersionUID = 6828494485207666122L;
361         abstract ClassFile getClassFile( String name, String suffix ) throws IOException;
362         abstract URL getResource(String name);
363         abstract InputStream getResourceAsStream(String name);
364     }
365 
366     /** Contains information about file/ZIP entry of the Java class.
367      */
368     public interface ClassFile {
369 
370         /** @return input stream for class file.
371          */
372         public abstract InputStream getInputStream() throws IOException;
373 
374 
375         /** @return canonical path to class file.
376          */
377         public abstract String getPath();
378 
379 
380         /** @return base path of found class, i.e. class is contained relative
381          * to that path, which may either denote a directory, or zip file
382          */
383         public abstract String getBase();
384 
385 
386         /** @return modification time of class file.
387          */
388         public abstract long getTime();
389 
390 
391         /** @return size of class file.
392          */
393         public abstract long getSize();
394     }
395 
396     private static class Dir extends PathEntry {
397 
398         private static final long serialVersionUID = 4374062802142373088L;
399         private String dir;
400 
401 
402         Dir(String d) {
403             dir = d;
404         }
405 
406         @Override
407         URL getResource(String name) {
408             // Resource specification uses '/' whatever the platform
409             final File file = new File(dir + File.separatorChar + name.replace('/', File.separatorChar));
410             try {
411                 return file.exists() ? file.toURL() : null;
412             } catch (MalformedURLException e) {
413                return null;
414             }
415         }
416         
417         @Override
418         InputStream getResourceAsStream(String name) {
419             // Resource specification uses '/' whatever the platform
420             final File file = new File(dir + File.separatorChar + name.replace('/', File.separatorChar));
421             try {
422                return file.exists() ? new FileInputStream(file) : null;
423             } catch (IOException e) {
424                return null;
425             }
426         }
427 
428         @Override
429         ClassFile getClassFile( String name, String suffix ) throws IOException {
430             final File file = new File(dir + File.separatorChar
431                     + name.replace('.', File.separatorChar) + suffix);
432             return file.exists() ? new ClassFile() {
433 
434                 public InputStream getInputStream() throws IOException {
435                     return new FileInputStream(file);
436                 }
437 
438 
439                 public String getPath() {
440                     try {
441                         return file.getCanonicalPath();
442                     } catch (IOException e) {
443                         return null;
444                     }
445                 }
446 
447 
448                 public long getTime() {
449                     return file.lastModified();
450                 }
451 
452 
453                 public long getSize() {
454                     return file.length();
455                 }
456 
457 
458                 public String getBase() {
459                     return dir;
460                 }
461             } : null;
462         }
463 
464 
465         @Override
466         public String toString() {
467             return dir;
468         }
469     }
470 
471     private static class Zip extends PathEntry {
472 
473         private static final long serialVersionUID = -2210747632897905532L;
474         private ZipFile zip;
475 
476 
477         Zip(ZipFile z) {
478             zip = z;
479         }
480 
481         @Override
482         URL getResource(String name) {
483             final ZipEntry entry = zip.getEntry(name);
484             try {
485                 return (entry != null) ? new URL("jar:file:" + zip.getName() + "!/" + name) : null;
486             } catch (MalformedURLException e) {
487                 return null;
488            }
489         }
490         
491         @Override
492         InputStream getResourceAsStream(String name) {
493             final ZipEntry entry = zip.getEntry(name);
494             try {
495                 return (entry != null) ? zip.getInputStream(entry) : null;
496             } catch (IOException e) {
497                 return null;
498             }
499         }
500         	
501         @Override
502         ClassFile getClassFile( String name, String suffix ) throws IOException {
503             final ZipEntry entry = zip.getEntry(name.replace('.', '/') + suffix);
504             
505             if (entry == null)
506             	return null;
507             
508             return new ClassFile() {
509 
510                 public InputStream getInputStream() throws IOException {
511                     return zip.getInputStream(entry);
512                 }
513 
514 
515                 public String getPath() {
516                     return entry.toString();
517                 }
518 
519 
520                 public long getTime() {
521                     return entry.getTime();
522                 }
523 
524 
525                 public long getSize() {
526                     return entry.getSize();
527                 }
528 
529 
530                 public String getBase() {
531                     return zip.getName();
532                 }
533             };
534         }
535     }
536 }