1 cananian 1.10.2.8  // Loader.java, created Fri Jul 31  4:33:28 1998 by cananian
  2 cananian 1.5       // Copyright (C) 1998 C. Scott Ananian <cananian@alumni.princeton.edu>
  3 cananian 1.5       // Licensed under the terms of the GNU GPL; see COPYING for details.
  4 cananian 1.1       package harpoon.ClassFile;
  5 cananian 1.1       
  6 cananian 1.1       import java.io.File;
  7 cananian 1.1       import java.io.InputStream;
  8 cananian 1.1       import java.io.FileInputStream;
  9 cananian 1.10.2.13 import java.io.Serializable;
 10 cananian 1.10.2.7  import java.util.ArrayList;
 11 cananian 1.10.2.7  import java.util.HashSet;
 12 cananian 1.10.2.7  import java.util.Iterator;
 13 cananian 1.10.2.7  import java.util.List;
 14 cananian 1.10.2.7  import java.util.Set;
 15 cananian 1.1       import java.util.zip.ZipFile;
 16 cananian 1.1       import java.util.zip.ZipEntry;
 17 cananian 1.1       
 18 cananian 1.10.2.13 import java.io.BufferedInputStream;
 19 cananian 1.1       import java.io.IOException;
 20 cananian 1.1       import java.io.FileNotFoundException;
 21 cananian 1.1       
 22 cananian 1.10.2.11 import harpoon.Util.ArrayIterator;
 23 cananian 1.16      import net.cscott.jutil.CombineIterator;
 24 cananian 1.16      import net.cscott.jutil.Default;
 25 cananian 1.10.2.11 import harpoon.Util.EnumerationIterator;
 26 cananian 1.16      import net.cscott.jutil.FilterIterator;
 27 cananian 1.10.2.2  import harpoon.Util.Util;
 28 cananian 1.15      
 29 cananian 1.16      import net.cscott.jutil.UnmodifiableIterator;
 30 cananian 1.15      
 31 cananian 1.6       /** 
 32 cananian 1.10.2.3   * Class file loader.
 33 cananian 1.10.2.3   * Looks through CLASSPATH to find resources.  Understands .jar and .zip
 34 cananian 1.10.2.3   * files.  Platform-independent (hopefully).
 35 cananian 1.2        *
 36 cananian 1.6        * @author  C. Scott Ananian <cananian@alumni.princeton.edu>
 37 cananian 1.17       * @version $Id: Loader.java,v 1.17 2004/02/08 03:21:10 cananian Exp $
 38 cananian 1.1        */
 39 cananian 1.9       public abstract class Loader {
 40 cananian 1.10.2.2    static abstract class ClasspathElement {
 41 cananian 1.10.2.3      /** Open a stream to read the given resource, or return 
 42 cananian 1.10.2.3       *  <code>null</code> if resource cannot be found. */
 43 cananian 1.10.2.2      abstract InputStream getResourceAsStream(String resourcename);
 44 cananian 1.10.2.11     /** Iterate over all classes in the given package. */
 45 cananian 1.10.2.11     abstract Iterator listPackage(String packagename);
 46 cananian 1.10.2.2    }
 47 cananian 1.10.2.2    /** A .zip or .jar file in the CLASSPATH. */
 48 cananian 1.10.2.2    static class ZipFileElement extends ClasspathElement {
 49 cananian 1.10.2.2      ZipFile zf;
 50 cananian 1.10.2.2      ZipFileElement(ZipFile zf) { this.zf = zf; }
 51 cananian 1.10.2.14     public String toString() { return zf.getName(); }
 52 cananian 1.10.2.2      InputStream getResourceAsStream(String name) {
 53 cananian 1.10.2.16       name = name.replace('\\','/'); // work around bug in windows java ports
 54 cananian 1.10.2.2        try { // look for name in zipfile, return null if something goes wrong.
 55 cananian 1.10.2.2          ZipEntry ze = zf.getEntry(name);
 56 cananian 1.10.2.2          return (ze==null)?null:zf.getInputStream(ze);
 57 cananian 1.10.2.10       } catch (UnsatisfiedLinkError e) {
 58 cananian 1.10.2.10         System.err.println("UNSATISFIED LINK ERROR: "+name);
 59 cananian 1.10.2.10         return null;
 60 cananian 1.10.2.2        } catch (IOException e) { return null; }
 61 cananian 1.10.2.2      }
 62 cananian 1.10.2.11     Iterator listPackage(final String pathname) {
 63 cananian 1.10.2.11       // look for directory name first
 64 cananian 1.10.2.11       final String filesep   = System.getProperty("file.separator");
 65 cananian 1.10.2.11       /* not all .JAR files have entries for directories, unfortunately.
 66 cananian 1.10.2.11       ZipEntry ze = zf.getEntry(pathname);
 67 cananian 1.10.2.11       if (ze==null) return Default.nullIterator;
 68 cananian 1.10.2.11       */
 69 cananian 1.10.2.11       return new FilterIterator(new EnumerationIterator(zf.entries()),
 70 cananian 1.10.2.11                                 new FilterIterator.Filter() {
 71 cananian 1.10.2.11         public boolean isElement(Object o) { ZipEntry zze=(ZipEntry) o;
 72 cananian 1.10.2.11         String name = zze.getName();
 73 cananian 1.10.2.11         return (!zze.isDirectory()) && name.startsWith(pathname) &&
 74 cananian 1.10.2.11           name.lastIndexOf(filesep)==(pathname.length()-1);
 75 cananian 1.10.2.11         }
 76 cananian 1.10.2.11         public Object map(Object o) {
 77 cananian 1.10.2.11           return ((ZipEntry)o).getName();
 78 cananian 1.7               }
 79 cananian 1.10.2.11       });
 80 cananian 1.10.2.11     }
 81 cananian 1.10.2.2      /** Close the zipfile when this object is garbage-collected. */
 82 cananian 1.10.2.2      protected void finalize() throws Throwable {
 83 cananian 1.10.2.6          // yes, it is possible to finalize an uninitialized object.
 84 cananian 1.10.2.6          try { if (zf!=null) zf.close(); } finally { super.finalize(); }
 85 cananian 1.10.2.2      }
 86 cananian 1.10.2.2    }
 87 cananian 1.10.2.2    /** A regular path string in the CLASSPATH. */
 88 cananian 1.10.2.2    static class PathElement extends ClasspathElement {
 89 cananian 1.10.2.2      String path;
 90 cananian 1.10.2.2      PathElement(String path) { this.path = path; }
 91 cananian 1.10.2.14     public String toString() { return path; }
 92 cananian 1.10.2.2      InputStream getResourceAsStream(String name) {
 93 cananian 1.10.2.2        try { // try to open the file, starting from path.
 94 cananian 1.10.2.2          File f = new File(path, name);
 95 cananian 1.10.2.5          return new FileInputStream(f);
 96 cananian 1.10.2.2        } catch (FileNotFoundException e) {
 97 cananian 1.10.2.2          return null; // if anything goes wrong, return null.
 98 cananian 1.7             }
 99 cananian 1.10.2.2      }
100 cananian 1.10.2.11     Iterator listPackage(final String pathname) {
101 cananian 1.10.2.11       File f = new File(path,pathname);
102 cananian 1.10.2.11       if (!f.exists() || !f.isDirectory()) return Default.nullIterator;
103 cananian 1.10.2.11       return new FilterIterator(new ArrayIterator(f.list()),
104 cananian 1.10.2.11                                 new FilterIterator.Filter() {
105 cananian 1.10.2.11         public Object map(Object o) { return pathname + ((String)o); }
106 cananian 1.10.2.11       });
107 cananian 1.10.2.11     }
108 cananian 1.10.2.2    }
109 cananian 1.10.2.2  
110 cananian 1.10.2.2    /** Static vector of ClasspathElements corresponding to CLASSPATH entries. */
111 cananian 1.10.2.7    static final List classpathList = new ArrayList();
112 cananian 1.10.2.2    static { // initialize classpathVector.
113 cananian 1.10.2.7      Set duplicates = new HashSet(); // don't add duplicates.
114 cananian 1.10.2.7      for (Iterator it = classpaths(); it.hasNext(); ) {
115 cananian 1.10.2.7        String path = (String) it.next();
116 cananian 1.10.2.7        if (duplicates.contains(path)) continue; // skip duplicate.
117 cananian 1.10.2.7        else duplicates.add(path);
118 cananian 1.10.2.2        if (path.toLowerCase().endsWith(".zip") ||
119 cananian 1.10.2.2            path.toLowerCase().endsWith(".jar"))
120 cananian 1.10.2.2          try {
121 cananian 1.10.2.7            classpathList.add(new ZipFileElement(new ZipFile(path)));
122 cananian 1.10.2.2          } catch (IOException ex) { /* skip this zip file, then. */ }
123 cananian 1.10.2.2        else
124 cananian 1.10.2.7          classpathList.add(new PathElement(path));
125 cananian 1.10.2.2      }
126 cananian 1.10.2.7      ((ArrayList) classpathList).trimToSize(); // save memory.
127 cananian 1.7         }
128 cananian 1.10.2.2  
129 cananian 1.10.2.7    /** Iterate over the components of the system CLASSPATH. 
130 cananian 1.10.2.3     *  Each element is a <code>String</code> naming one segment of the
131 cananian 1.10.2.3     *  CLASSPATH. */
132 cananian 1.10.2.7    public static final Iterator classpaths() {
133 cananian 1.10.2.4      final String pathsep = System.getProperty("path.separator");
134 cananian 1.10.2.9      String classpath = null;
135 cananian 1.10.2.9  
136 cananian 1.10.2.9      // allow overriding classpath.
137 cananian 1.10.2.9      /*if (classpath==null) classpath = System.getenv("HCLASSPATH");*/
138 cananian 1.10.2.9      if (classpath==null) classpath = System.getProperty("harpoon.class.path");
139 cananian 1.10.2.9      if (classpath==null) classpath = System.getProperty("java.class.path");
140 cananian 1.13.2.1      assert classpath!=null;
141 cananian 1.7       
142 cananian 1.7           // For convenience, make sure classpath begins with and ends with pathsep.
143 cananian 1.7           if (!classpath.startsWith(pathsep)) classpath = pathsep + classpath;
144 cananian 1.7           if (!classpath.endsWith(pathsep)) classpath = classpath + pathsep;
145 cananian 1.7           final String cp = classpath;
146 cananian 1.7       
147 cananian 1.10.2.7      return new UnmodifiableIterator() {
148 cananian 1.7             int i=0;
149 cananian 1.10.2.7        public boolean hasNext() { 
150 cananian 1.7               return (cp.length() > (i+pathsep.length()));
151 cananian 1.7             }
152 cananian 1.10.2.7        public Object next() {
153 cananian 1.7               i+=pathsep.length(); // cp begins with pathsep.
154 cananian 1.7               String path = cp.substring(i, cp.indexOf(pathsep, i));
155 cananian 1.7               i+=path.length(); // skip over path.
156 cananian 1.7               return path;
157 cananian 1.7             }
158 cananian 1.7           };
159 cananian 1.7         }
160 cananian 1.7       
161 cananian 1.10.2.3    /** Translate a class name into a corresponding resource name. 
162 cananian 1.10.2.3     * @param classname The class name to translate.
163 cananian 1.10.2.3     */
164 cananian 1.1         public static String classToResource(String classname) {
165 cananian 1.13.2.1      assert classname.indexOf('/')==-1; // should have '.' separators.
166 cananian 1.1           String filesep   = System.getProperty("file.separator");
167 cananian 1.1           // Swap all '.' for '/' & append ".class"
168 cananian 1.1           return classname.replace('.', filesep.charAt(0)) + ".class";
169 cananian 1.1         }
170 cananian 1.1       
171 cananian 1.10.2.3    /** Open an <code>InputStream</code> on a resource found somewhere 
172 cananian 1.10.2.3     *  in the CLASSPATH.
173 cananian 1.1          * @param name The filename of the resource to locate.
174 cananian 1.1          */
175 cananian 1.1         public static InputStream getResourceAsStream(String name) {
176 cananian 1.17          for (Object cpeO : classpathList) {
177 cananian 1.17            ClasspathElement cpe = (ClasspathElement) cpeO;
178 cananian 1.10.2.2        InputStream is = cpe.getResourceAsStream(name);
179 cananian 1.10.2.14       if (is!=null) {
180 cananian 1.10.2.14         //System.err.println("[LOADING "+new File(cpe.toString(),name)+"]");
181 cananian 1.10.2.14         return is; // return stream if found.
182 cananian 1.10.2.14       }
183 cananian 1.1           }
184 cananian 1.1           // Couldn't find resource.
185 cananian 1.1           return null;
186 cananian 1.1         }
187 cananian 1.1       
188 cananian 1.10.2.11   /** Returns an iterator of Strings naming the available classes in
189 cananian 1.10.2.11    *  the given package which are on the classpath. */
190 cananian 1.10.2.11   public static Iterator listClasses(String packagename) {
191 cananian 1.10.2.11     final String filesep = System.getProperty("file.separator");
192 cananian 1.10.2.12     final String pathname = (packagename.length()==0)?"":
193 cananian 1.10.2.12       (packagename.replace('.',filesep.charAt(0))+filesep);
194 cananian 1.10.2.11     FilterIterator.Filter cpe2sl = new FilterIterator.Filter() {
195 cananian 1.10.2.11       public Object map(Object o) {
196 cananian 1.10.2.12         return ((ClasspathElement) o).listPackage(pathname);
197 cananian 1.10.2.11       }
198 cananian 1.10.2.11     };
199 cananian 1.10.2.11     FilterIterator.Filter sl2cl = new FilterIterator.Filter() {
200 cananian 1.10.2.11       private Set nodups = new HashSet();
201 cananian 1.10.2.11       public boolean isElement(Object o) {
202 cananian 1.10.2.11         return ((String)o).toLowerCase().endsWith(".class") &&
203 cananian 1.10.2.11                !nodups.contains(o);
204 cananian 1.10.2.11       }
205 cananian 1.10.2.11       public Object map(Object o) {
206 cananian 1.10.2.11         String name = (String) o; nodups.add(o);
207 cananian 1.10.2.11         return name.substring(0,name.length()-6)
208 cananian 1.10.2.11                    .replace(filesep.charAt(0),'.');
209 cananian 1.1             }
210 cananian 1.10.2.11     };
211 cananian 1.10.2.11     return new FilterIterator(new CombineIterator(
212 cananian 1.10.2.11            new FilterIterator(classpathList.iterator(), cpe2sl)), sl2cl);
213 cananian 1.1         }
214 cananian 1.1       
215 cananian 1.10.2.13   /** System-linker: the class names resolved by this linker are always
216 cananian 1.10.2.13    *  immutable and identical to those on disk.
217 cananian 1.10.2.13    */
218 cananian 1.10.2.13   public static final Linker systemLinker = new SystemLinker();
219 cananian 1.10.2.13   private static class SystemLinker extends Linker implements Serializable {
220 cananian 1.10.2.13     protected final HClass forDescriptor0(String descriptor) 
221 cananian 1.10.2.13       throws NoSuchClassException {
222 cananian 1.13.2.1        assert descriptor.startsWith("L") && descriptor.endsWith(";");
223 cananian 1.10.2.13       // classname in descriptor is '/' delimited.
224 cananian 1.10.2.13       String className = descriptor.substring(1, descriptor.indexOf(';'));
225 cananian 1.10.2.13       className = className.replace('/','.'); // make proper class name.
226 cananian 1.10.2.13       InputStream is = 
227 cananian 1.10.2.13           Loader.getResourceAsStream(Loader.classToResource(className));
228 cananian 1.10.2.13       if (is == null) throw new NoSuchClassException(className);
229 cananian 1.10.2.13       // OK, go ahead and load this.
230 cananian 1.10.2.13       try {
231 cananian 1.10.2.13         return /*ImplGNU*/ImplMagic.forStream(this, new BufferedInputStream(is));
232 cananian 1.10.2.13       } catch (java.lang.ClassFormatError e) {
233 cananian 1.10.2.13         throw new NoSuchClassException(className+" ["+e.toString()+"]");
234 cananian 1.10.2.13       } catch (java.io.IOException e) {
235 cananian 1.10.2.13         throw new NoSuchClassException(className);
236 cananian 1.10.2.13       } finally {
237 cananian 1.10.2.13         try { is.close(); } catch(java.io.IOException e) { }
238 cananian 1.10.2.13       }
239 cananian 1.10.2.13     }
240 cananian 1.10.2.13     /* Serializable interface: system linker is unique. */
241 cananian 1.10.2.13     public Object writeReplace() { return new Stub(); }
242 cananian 1.10.2.13     private static final class Stub implements Serializable {
243 cananian 1.10.2.15       public Object readResolve() {
244 cananian 1.10.2.15         return Loader.replaceWithRelinker ?
245 cananian 1.10.2.15           Loader.systemRelinker : Loader.systemLinker;
246 cananian 1.10.2.15       }
247 cananian 1.1           }
248 cananian 1.1         }
249 cananian 1.10.2.15   // allow the user to reserialize a systemLinker (and associated classes)
250 cananian 1.10.2.15   // as a *relinker*...  evil evil evil evil. [CSA 4-apr-2000]
251 cananian 1.10.2.15   final static boolean replaceWithRelinker =
252 cananian 1.10.2.15     System.getProperty("harpoon.relinker.hack", "no")
253 cananian 1.10.2.15     .equalsIgnoreCase("yes");
254 cananian 1.10.2.15   final static Linker systemRelinker = new Relinker(systemLinker);
255 cananian 1.10.2.15 
256 cananian 1.10.2.13   /** System code factory: this code factory will return bytecode
257 cananian 1.10.2.13    *  representations for classes loaded via the system linker. */
258 cananian 1.10.2.13   public static final HCodeFactory systemCodeFactory =
259 cananian 1.10.2.13     /*ImplGNU*/ImplMagic.codeFactory;
260 cananian 1.1       }
261 cananian 1.10.2.2  // set emacs indentation style.
262 cananian 1.10.2.2  // Local Variables:
263 cananian 1.10.2.2  // c-basic-offset:2
264 cananian 1.12      // End: