View Javadoc

1   /*
2    * Copyright  2000-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.bcel.util;
18  
19  import java.io.DataInputStream;
20  import java.io.File;
21  import java.io.FileInputStream;
22  import java.io.FilenameFilter;
23  import java.io.IOException;
24  import java.io.InputStream;
25  import java.io.Serializable;
26  import java.util.ArrayList;
27  import java.util.Iterator;
28  import java.util.List;
29  import java.util.Locale;
30  import java.util.StringTokenizer;
31  import java.util.zip.ZipEntry;
32  import java.util.zip.ZipFile;
33  
34  /***
35   * Responsible for loading (class) files from the CLASSPATH. Inspired by
36   * sun.tools.ClassPath.
37   *
38   * @version $Id: ClassPath.java 386056 2006-03-15 11:31:56Z tcurdt $
39   * @author  <A HREF="mailto:m.dahm@gmx.de">M. Dahm</A>
40   */
41  public class ClassPath implements Serializable {
42  
43      public static final ClassPath SYSTEM_CLASS_PATH = new ClassPath();
44      private PathEntry[] paths;
45      private String class_path;
46  
47  
48      /***
49       * Search for classes in given path.
50       */
51      public ClassPath(String class_path) {
52          this.class_path = class_path;
53          List vec = new ArrayList();
54          for (StringTokenizer tok = new StringTokenizer(class_path, System
55                  .getProperty("path.separator")); tok.hasMoreTokens();) {
56              String path = tok.nextToken();
57              if (!path.equals("")) {
58                  File file = new File(path);
59                  try {
60                      if (file.exists()) {
61                          if (file.isDirectory()) {
62                              vec.add(new Dir(path));
63                          } else {
64                              vec.add(new Zip(new ZipFile(file)));
65                          }
66                      }
67                  } catch (IOException e) {
68                      System.err.println("CLASSPATH component " + file + ": " + e);
69                  }
70              }
71          }
72          paths = new PathEntry[vec.size()];
73          vec.toArray(paths);
74      }
75  
76  
77      /***
78       * Search for classes in CLASSPATH.
79       * @deprecated Use SYSTEM_CLASS_PATH constant
80       */
81      public ClassPath() {
82          this(getClassPath());
83      }
84  
85  
86      /*** @return used class path string
87       */
88      public String toString() {
89          return class_path;
90      }
91  
92  
93      public int hashCode() {
94          return class_path.hashCode();
95      }
96  
97  
98      public boolean equals( Object o ) {
99          if (o instanceof ClassPath) {
100             return class_path.equals(((ClassPath) o).class_path);
101         }
102         return false;
103     }
104 
105 
106     private static final void getPathComponents( String path, List list ) {
107         if (path != null) {
108             StringTokenizer tok = new StringTokenizer(path, File.pathSeparator);
109             while (tok.hasMoreTokens()) {
110                 String name = tok.nextToken();
111                 File file = new File(name);
112                 if (file.exists()) {
113                     list.add(name);
114                 }
115             }
116         }
117     }
118 
119 
120     /*** Checks for class path components in the following properties:
121      * "java.class.path", "sun.boot.class.path", "java.ext.dirs"
122      *
123      * @return class path as used by default by BCEL
124      */
125     public static final String getClassPath() {
126         String class_path = System.getProperty("java.class.path");
127         String boot_path = System.getProperty("sun.boot.class.path");
128         String ext_path = System.getProperty("java.ext.dirs");
129         List list = new ArrayList();
130         getPathComponents(class_path, list);
131         getPathComponents(boot_path, list);
132         List dirs = new ArrayList();
133         getPathComponents(ext_path, dirs);
134         for (Iterator e = dirs.iterator(); e.hasNext();) {
135             File ext_dir = new File((String) e.next());
136             String[] extensions = ext_dir.list(new FilenameFilter() {
137 
138                 public boolean accept( File dir, String name ) {
139                     name = name.toLowerCase(Locale.ENGLISH);
140                     return name.endsWith(".zip") || name.endsWith(".jar");
141                 }
142             });
143             if (extensions != null) {
144                 for (int i = 0; i < extensions.length; i++) {
145                     list.add(ext_dir.getPath() + File.separatorChar + extensions[i]);
146                 }
147             }
148         }
149         StringBuffer buf = new StringBuffer();
150         for (Iterator e = list.iterator(); e.hasNext();) {
151             buf.append((String) e.next());
152             if (e.hasNext()) {
153                 buf.append(File.pathSeparatorChar);
154             }
155         }
156         return buf.toString().intern();
157     }
158 
159 
160     /***
161      * @param name fully qualified class name, e.g. java.lang.String
162      * @return input stream for class
163      */
164     public InputStream getInputStream( String name ) throws IOException {
165         return getInputStream(name.replace('.', '/'), ".class");
166     }
167 
168 
169     /***
170      * Return stream for class or resource on CLASSPATH.
171      *
172      * @param name fully qualified file name, e.g. java/lang/String
173      * @param suffix file name ends with suff, e.g. .java
174      * @return input stream for file on class path
175      */
176     public InputStream getInputStream( String name, String suffix ) throws IOException {
177         InputStream is = null;
178         try {
179             is = getClass().getClassLoader().getResourceAsStream(name + suffix);
180         } catch (Exception e) {
181         }
182         if (is != null) {
183             return is;
184         }
185         return getClassFile(name, suffix).getInputStream();
186     }
187 
188 
189     /***
190      * @param name fully qualified file name, e.g. java/lang/String
191      * @param suffix file name ends with suff, e.g. .java
192      * @return class file for the java class
193      */
194     public ClassFile getClassFile( String name, String suffix ) throws IOException {
195         for (int i = 0; i < paths.length; i++) {
196             ClassFile cf;
197             if ((cf = paths[i].getClassFile(name, suffix)) != null) {
198                 return cf;
199             }
200         }
201         throw new IOException("Couldn't find: " + name + suffix);
202     }
203 
204 
205     /***
206      * @param name fully qualified class name, e.g. java.lang.String
207      * @return input stream for class
208      */
209     public ClassFile getClassFile( String name ) throws IOException {
210         return getClassFile(name, ".class");
211     }
212 
213 
214     /***
215      * @param name fully qualified file name, e.g. java/lang/String
216      * @param suffix file name ends with suffix, e.g. .java
217      * @return byte array for file on class path
218      */
219     public byte[] getBytes( String name, String suffix ) throws IOException {
220         DataInputStream dis = null;
221         try {
222             InputStream is = getInputStream(name, suffix);
223             if (is == null) {
224                 throw new IOException("Couldn't find: " + name + suffix);
225             }
226             dis = new DataInputStream(is);
227             byte[] bytes = new byte[is.available()];
228             dis.readFully(bytes);
229             return bytes;
230         } finally {
231             if (dis != null) {
232                 dis.close();
233             }
234         }
235     }
236 
237 
238     /***
239      * @return byte array for class
240      */
241     public byte[] getBytes( String name ) throws IOException {
242         return getBytes(name, ".class");
243     }
244 
245 
246     /***
247      * @param name name of file to search for, e.g. java/lang/String.java
248      * @return full (canonical) path for file
249      */
250     public String getPath( String name ) throws IOException {
251         int index = name.lastIndexOf('.');
252         String suffix = "";
253         if (index > 0) {
254             suffix = name.substring(index);
255             name = name.substring(0, index);
256         }
257         return getPath(name, suffix);
258     }
259 
260 
261     /***
262      * @param name name of file to search for, e.g. java/lang/String
263      * @param suffix file name suffix, e.g. .java
264      * @return full (canonical) path for file, if it exists
265      */
266     public String getPath( String name, String suffix ) throws IOException {
267         return getClassFile(name, suffix).getPath();
268     }
269 
270     private static abstract class PathEntry implements Serializable {
271 
272         abstract ClassFile getClassFile( String name, String suffix ) throws IOException;
273     }
274 
275     /*** Contains information about file/ZIP entry of the Java class.
276      */
277     public interface ClassFile {
278 
279         /*** @return input stream for class file.
280          */
281         public abstract InputStream getInputStream() throws IOException;
282 
283 
284         /*** @return canonical path to class file.
285          */
286         public abstract String getPath();
287 
288 
289         /*** @return base path of found class, i.e. class is contained relative
290          * to that path, which may either denote a directory, or zip file
291          */
292         public abstract String getBase();
293 
294 
295         /*** @return modification time of class file.
296          */
297         public abstract long getTime();
298 
299 
300         /*** @return size of class file.
301          */
302         public abstract long getSize();
303     }
304 
305     private static class Dir extends PathEntry {
306 
307         private String dir;
308 
309 
310         Dir(String d) {
311             dir = d;
312         }
313 
314 
315         ClassFile getClassFile( String name, String suffix ) throws IOException {
316             final File file = new File(dir + File.separatorChar
317                     + name.replace('.', File.separatorChar) + suffix);
318             return file.exists() ? new ClassFile() {
319 
320                 public InputStream getInputStream() throws IOException {
321                     return new FileInputStream(file);
322                 }
323 
324 
325                 public String getPath() {
326                     try {
327                         return file.getCanonicalPath();
328                     } catch (IOException e) {
329                         return null;
330                     }
331                 }
332 
333 
334                 public long getTime() {
335                     return file.lastModified();
336                 }
337 
338 
339                 public long getSize() {
340                     return file.length();
341                 }
342 
343 
344                 public String getBase() {
345                     return dir;
346                 }
347             } : null;
348         }
349 
350 
351         public String toString() {
352             return dir;
353         }
354     }
355 
356     private static class Zip extends PathEntry {
357 
358         private ZipFile zip;
359 
360 
361         Zip(ZipFile z) {
362             zip = z;
363         }
364 
365 
366         ClassFile getClassFile( String name, String suffix ) throws IOException {
367             final ZipEntry entry = zip.getEntry(name.replace('.', '/') + suffix);
368             return (entry != null) ? new ClassFile() {
369 
370                 public InputStream getInputStream() throws IOException {
371                     return zip.getInputStream(entry);
372                 }
373 
374 
375                 public String getPath() {
376                     return entry.toString();
377                 }
378 
379 
380                 public long getTime() {
381                     return entry.getTime();
382                 }
383 
384 
385                 public long getSize() {
386                     return entry.getSize();
387                 }
388 
389 
390                 public String getBase() {
391                     return zip.getName();
392                 }
393             } : null;
394         }
395     }
396 }