1 /*
   2  * reserved comment block
   3  * DO NOT REMOVE OR ALTER!
   4  */
   5 package com.sun.org.apache.bcel.internal.util;
   6 
   7 /* ====================================================================
   8  * The Apache Software License, Version 1.1
   9  *
  10  * Copyright (c) 2001 The Apache Software Foundation.  All rights
  11  * reserved.
  12  *
  13  * Redistribution and use in source and binary forms, with or without
  14  * modification, are permitted provided that the following conditions
  15  * are met:
  16  *
  17  * 1. Redistributions of source code must retain the above copyright
  18  *    notice, this list of conditions and the following disclaimer.
  19  *
  20  * 2. Redistributions in binary form must reproduce the above copyright
  21  *    notice, this list of conditions and the following disclaimer in
  22  *    the documentation and/or other materials provided with the
  23  *    distribution.
  24  *
  25  * 3. The end-user documentation included with the redistribution,
  26  *    if any, must include the following acknowledgment:
  27  *       "This product includes software developed by the
  28  *        Apache Software Foundation (http://www.apache.org/)."
  29  *    Alternately, this acknowledgment may appear in the software itself,
  30  *    if and wherever such third-party acknowledgments normally appear.
  31  *
  32  * 4. The names "Apache" and "Apache Software Foundation" and
  33  *    "Apache BCEL" must not be used to endorse or promote products
  34  *    derived from this software without prior written permission. For
  35  *    written permission, please contact apache@apache.org.
  36  *
  37  * 5. Products derived from this software may not be called "Apache",
  38  *    "Apache BCEL", nor may "Apache" appear in their name, without
  39  *    prior written permission of the Apache Software Foundation.
  40  *
  41  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  42  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  43  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  44  * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  45  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  46  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  47  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  48  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  49  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  50  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  51  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  52  * SUCH DAMAGE.
  53  * ====================================================================
  54  *
  55  * This software consists of voluntary contributions made by many
  56  * individuals on behalf of the Apache Software Foundation.  For more
  57  * information on the Apache Software Foundation, please see
  58  * <http://www.apache.org/>.
  59  */
  60 
  61 import java.util.*;
  62 import java.util.zip.*;
  63 import java.io.*;
  64 
  65 /**
  66  * Responsible for loading (class) files from the CLASSPATH. Inspired by
  67  * sun.tools.ClassPath.
  68  *
  69  * @version $Id: ClassPath.java,v 1.4 2007-07-19 04:34:52 ofung Exp $
  70  * @author  <A HREF="mailto:markus.dahm@berlin.de">M. Dahm</A>
  71  */
  72 public class ClassPath implements Serializable {
  73   public static final ClassPath SYSTEM_CLASS_PATH = new ClassPath();
  74 
  75   private PathEntry[] paths;
  76   private String      class_path;
  77 
  78   /**
  79    * Search for classes in given path.
  80    */
  81   public ClassPath(String class_path) {
  82     this.class_path = class_path;
  83 
  84     ArrayList vec = new ArrayList();
  85 
  86     for(StringTokenizer tok=new StringTokenizer(class_path,
  87                             SecuritySupport.getSystemProperty("path.separator"));
  88         tok.hasMoreTokens();)
  89     {
  90       String path = tok.nextToken();
  91 
  92       if(!path.equals("")) {
  93         File file = new File(path);
  94 
  95         try {
  96           if(SecuritySupport.getFileExists(file)) {
  97             if(file.isDirectory())
  98               vec.add(new Dir(path));
  99             else
 100               vec.add(new Zip(new ZipFile(file)));
 101           }
 102         } catch(IOException e) {
 103           System.err.println("CLASSPATH component " + file + ": " + e);
 104         }
 105       }
 106     }
 107 
 108     paths = new PathEntry[vec.size()];
 109     vec.toArray(paths);
 110   }
 111 
 112   /**
 113    * Search for classes in CLASSPATH.
 114    * @deprecated Use SYSTEM_CLASS_PATH constant
 115    */
 116   public ClassPath() {
 117     // this(getClassPath());
 118     this("");
 119   }
 120 
 121   /** @return used class path string
 122    */
 123   public String toString() {
 124     return class_path;
 125   }
 126 
 127   public int hashCode() {
 128     return class_path.hashCode();
 129   }
 130 
 131   public boolean equals(Object o) {
 132     if(o instanceof ClassPath) {
 133       return class_path.equals(((ClassPath)o).class_path);
 134     }
 135 
 136     return false;
 137   }
 138 
 139   private static final void getPathComponents(String path, ArrayList list) {
 140     if(path != null) {
 141       StringTokenizer tok = new StringTokenizer(path, File.pathSeparator);
 142 
 143       while(tok.hasMoreTokens()) {
 144         String name = tok.nextToken();
 145         File   file = new File(name);
 146 
 147         if(SecuritySupport.getFileExists(file)) {
 148           list.add(name);
 149         }
 150       }
 151     }
 152   }
 153 
 154   /** Checks for class path components in the following properties:
 155    * "java.class.path", "sun.boot.class.path", "java.ext.dirs"
 156    *
 157    * @return class path as used by default by BCEL
 158    */
 159   public static final String getClassPath() {
 160 
 161     String class_path, boot_path, ext_path;
 162 
 163     try {
 164       class_path = SecuritySupport.getSystemProperty("java.class.path");
 165       boot_path  = SecuritySupport.getSystemProperty("sun.boot.class.path");
 166       ext_path   = SecuritySupport.getSystemProperty("java.ext.dirs");
 167     }
 168     catch (SecurityException e) {
 169         return "";
 170     }
 171 
 172     ArrayList list = new ArrayList();
 173 
 174     getPathComponents(class_path, list);
 175     getPathComponents(boot_path, list);
 176 
 177     ArrayList dirs = new ArrayList();
 178     getPathComponents(ext_path, dirs);
 179 
 180     for(Iterator e = dirs.iterator(); e.hasNext(); ) {
 181       File ext_dir = new File((String)e.next());
 182       String[] extensions = SecuritySupport.getFileList(ext_dir, new FilenameFilter() {
 183         public boolean accept(File dir, String name) {
 184           name = name.toLowerCase();
 185           return name.endsWith(".zip") || name.endsWith(".jar");
 186         }
 187       });
 188 
 189       if(extensions != null)
 190         for(int i=0; i < extensions.length; i++)
 191           list.add(ext_path + File.separatorChar + extensions[i]);
 192     }
 193 
 194     StringBuffer buf = new StringBuffer();
 195 
 196     for(Iterator e = list.iterator(); e.hasNext(); ) {
 197       buf.append((String)e.next());
 198 
 199       if(e.hasNext())
 200         buf.append(File.pathSeparatorChar);
 201     }
 202 
 203     return buf.toString().intern();
 204   }
 205 
 206   /**
 207    * @param name fully qualified class name, e.g. java.lang.String
 208    * @return input stream for class
 209    */
 210   public InputStream getInputStream(String name) throws IOException {
 211     return getInputStream(name, ".class");
 212   }
 213 
 214   /**
 215    * Return stream for class or resource on CLASSPATH.
 216    *
 217    * @param name fully qualified file name, e.g. java/lang/String
 218    * @param suffix file name ends with suff, e.g. .java
 219    * @return input stream for file on class path
 220    */
 221   public InputStream getInputStream(String name, String suffix) throws IOException {
 222     InputStream is = null;
 223 
 224     try {
 225       is = getClass().getClassLoader().getResourceAsStream(name + suffix);
 226     } catch(Exception e) { }
 227 
 228     if(is != null)
 229       return is;
 230 
 231     return getClassFile(name, suffix).getInputStream();
 232   }
 233 
 234   /**
 235    * @param name fully qualified file name, e.g. java/lang/String
 236    * @param suffix file name ends with suff, e.g. .java
 237    * @return class file for the java class
 238    */
 239   public ClassFile getClassFile(String name, String suffix) throws IOException {
 240     for(int i=0; i < paths.length; i++) {
 241       ClassFile cf;
 242 
 243       if((cf = paths[i].getClassFile(name, suffix)) != null)
 244         return cf;
 245     }
 246 
 247     throw new IOException("Couldn't find: " + name + suffix);
 248   }
 249 
 250   /**
 251    * @param name fully qualified class name, e.g. java.lang.String
 252    * @return input stream for class
 253    */
 254   public ClassFile getClassFile(String name) throws IOException {
 255     return getClassFile(name, ".class");
 256   }
 257 
 258   /**
 259    * @param name fully qualified file name, e.g. java/lang/String
 260    * @param suffix file name ends with suffix, e.g. .java
 261    * @return byte array for file on class path
 262    */
 263   public byte[] getBytes(String name, String suffix) throws IOException {
 264     InputStream is = getInputStream(name, suffix);
 265 
 266     if(is == null)
 267       throw new IOException("Couldn't find: " + name + suffix);
 268 
 269     DataInputStream dis   = new DataInputStream(is);
 270     byte[]          bytes = new byte[is.available()];
 271     dis.readFully(bytes);
 272     dis.close(); is.close();
 273 
 274     return bytes;
 275   }
 276 
 277   /**
 278    * @return byte array for class
 279    */
 280   public byte[] getBytes(String name) throws IOException {
 281     return getBytes(name, ".class");
 282   }
 283 
 284   /**
 285    * @param name name of file to search for, e.g. java/lang/String.java
 286    * @return full (canonical) path for file
 287    */
 288   public String getPath(String name) throws IOException {
 289     int    index  = name.lastIndexOf('.');
 290     String suffix = "";
 291 
 292     if(index > 0) {
 293       suffix = name.substring(index);
 294       name   = name.substring(0, index);
 295     }
 296 
 297     return getPath(name, suffix);
 298   }
 299 
 300   /**
 301    * @param name name of file to search for, e.g. java/lang/String
 302    * @param suffix file name suffix, e.g. .java
 303    * @return full (canonical) path for file, if it exists
 304    */
 305   public String getPath(String name, String suffix) throws IOException {
 306     return getClassFile(name, suffix).getPath();
 307   }
 308 
 309   private static abstract class PathEntry implements Serializable {
 310     abstract ClassFile getClassFile(String name, String suffix) throws IOException;
 311   }
 312 
 313   /** Contains information about file/ZIP entry of the Java class.
 314    */
 315   public interface ClassFile {
 316     /** @return input stream for class file.
 317      */
 318     public abstract InputStream getInputStream() throws IOException;
 319 
 320     /** @return canonical path to class file.
 321      */
 322     public abstract String getPath();
 323 
 324     /** @return base path of found class, i.e. class is contained relative
 325      * to that path, which may either denote a directory, or zip file
 326      */
 327     public abstract String getBase();
 328 
 329     /** @return modification time of class file.
 330      */
 331     public abstract long getTime();
 332 
 333     /** @return size of class file.
 334      */
 335     public abstract long getSize();
 336   }
 337 
 338   private static class Dir extends PathEntry {
 339     private String dir;
 340 
 341     Dir(String d) { dir = d; }
 342 
 343     ClassFile getClassFile(String name, String suffix) throws IOException {
 344       final File file = new File(dir + File.separatorChar +
 345                                  name.replace('.', File.separatorChar) + suffix);
 346 
 347       return SecuritySupport.getFileExists(file)? new ClassFile() {
 348         public InputStream getInputStream() throws IOException { return new FileInputStream(file); }
 349 
 350         public String      getPath()        { try {
 351           return file.getCanonicalPath();
 352         } catch(IOException e) { return null; }
 353 
 354         }
 355         public long        getTime()        { return file.lastModified(); }
 356         public long        getSize()        { return file.length(); }
 357         public String getBase() {  return dir;  }
 358 
 359       } : null;
 360     }
 361 
 362     public String toString() { return dir; }
 363   }
 364 
 365   private static class Zip extends PathEntry {
 366     private ZipFile zip;
 367 
 368     Zip(ZipFile z) { zip = z; }
 369 
 370     ClassFile getClassFile(String name, String suffix) throws IOException {
 371       final ZipEntry entry = zip.getEntry(name.replace('.', '/') + suffix);
 372 
 373       return (entry != null)? new ClassFile() {
 374         public InputStream getInputStream() throws IOException { return zip.getInputStream(entry); }
 375         public String      getPath()        { return entry.toString(); }
 376         public long        getTime()        { return entry.getTime(); }
 377         public long        getSize()       { return entry.getSize(); }
 378         public String getBase() {
 379           return zip.getName();
 380         }
 381       } : null;
 382     }
 383   }
 384 }