Reflection Java

/*
 * Copyright  2000-2004 The Apache Software Foundation
 *
 *  Licensed under the Apache License, Version 2.0 (the "License"); 
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License. 
 *
 */
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.StringTokenizer;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
/**
 * Responsible for loading (class) files from the CLASSPATH. Inspired by
 * sun.tools.ClassPath.
 * 
 * @version $Id: ClassPath.java 386056 2006-03-15 11:31:56Z tcurdt $
 * @author M. Dahm
 */
public class ClassPath implements Serializable {
  public static final ClassPath SYSTEM_CLASS_PATH = new ClassPath();
  private PathEntry[] paths;
  private String class_path;
  /**
   * Search for classes in given path.
   */
  public ClassPath(String class_path) {
    this.class_path = class_path;
    List vec = new ArrayList();
    for (StringTokenizer tok = new StringTokenizer(class_path, System.getProperty("path.separator")); tok
        .hasMoreTokens();) {
      String path = tok.nextToken();
      if (!path.equals("")) {
        File file = new File(path);
        try {
          if (file.exists()) {
            if (file.isDirectory()) {
              vec.add(new Dir(path));
            } else {
              vec.add(new Zip(new ZipFile(file)));
            }
          }
        } catch (IOException e) {
          System.err.println("CLASSPATH component " + file + ": " + e);
        }
      }
    }
    paths = new PathEntry[vec.size()];
    vec.toArray(paths);
  }
  /**
   * Search for classes in CLASSPATH.
   * 
   * @deprecated Use SYSTEM_CLASS_PATH constant
   */
  public ClassPath() {
    this(getClassPath());
  }
  /**
   * @return used class path string
   */
  public String toString() {
    return class_path;
  }
  public int hashCode() {
    return class_path.hashCode();
  }
  public boolean equals(Object o) {
    if (o instanceof ClassPath) {
      return class_path.equals(((ClassPath) o).class_path);
    }
    return false;
  }
  private static final void getPathComponents(String path, List list) {
    if (path != null) {
      StringTokenizer tok = new StringTokenizer(path, File.pathSeparator);
      while (tok.hasMoreTokens()) {
        String name = tok.nextToken();
        File file = new File(name);
        if (file.exists()) {
          list.add(name);
        }
      }
    }
  }
  /**
   * Checks for class path components in the following properties:
   * "java.class.path", "sun.boot.class.path", "java.ext.dirs"
   * 
   * @return class path as used by default by BCEL
   */
  public static final String getClassPath() {
    String class_path = System.getProperty("java.class.path");
    String boot_path = System.getProperty("sun.boot.class.path");
    String ext_path = System.getProperty("java.ext.dirs");
    List list = new ArrayList();
    getPathComponents(class_path, list);
    getPathComponents(boot_path, list);
    List dirs = new ArrayList();
    getPathComponents(ext_path, dirs);
    for (Iterator e = dirs.iterator(); e.hasNext();) {
      File ext_dir = new File((String) e.next());
      String[] extensions = ext_dir.list(new FilenameFilter() {
        public boolean accept(File dir, String name) {
          name = name.toLowerCase(Locale.ENGLISH);
          return name.endsWith(".zip") || name.endsWith(".jar");
        }
      });
      if (extensions != null) {
        for (int i = 0; i < extensions.length; i++) {
          list.add(ext_dir.getPath() + File.separatorChar + extensions[i]);
        }
      }
    }
    StringBuffer buf = new StringBuffer();
    for (Iterator e = list.iterator(); e.hasNext();) {
      buf.append((String) e.next());
      if (e.hasNext()) {
        buf.append(File.pathSeparatorChar);
      }
    }
    return buf.toString().intern();
  }
  /**
   * @param name
   *          fully qualified class name, e.g. java.lang.String
   * @return input stream for class
   */
  public InputStream getInputStream(String name) throws IOException {
    return getInputStream(name.replace('.', '/'), ".class");
  }
  /**
   * Return stream for class or resource on CLASSPATH.
   * 
   * @param name
   *          fully qualified file name, e.g. java/lang/String
   * @param suffix
   *          file name ends with suff, e.g. .java
   * @return input stream for file on class path
   */
  public InputStream getInputStream(String name, String suffix) throws IOException {
    InputStream is = null;
    try {
      is = getClass().getClassLoader().getResourceAsStream(name + suffix);
    } catch (Exception e) {
    }
    if (is != null) {
      return is;
    }
    return getClassFile(name, suffix).getInputStream();
  }
  /**
   * @param name
   *          fully qualified file name, e.g. java/lang/String
   * @param suffix
   *          file name ends with suff, e.g. .java
   * @return class file for the java class
   */
  public ClassFile getClassFile(String name, String suffix) throws IOException {
    for (int i = 0; i < paths.length; i++) {
      ClassFile cf;
      if ((cf = paths[i].getClassFile(name, suffix)) != null) {
        return cf;
      }
    }
    throw new IOException("Couldn't find: " + name + suffix);
  }
  /**
   * @param name
   *          fully qualified class name, e.g. java.lang.String
   * @return input stream for class
   */
  public ClassFile getClassFile(String name) throws IOException {
    return getClassFile(name, ".class");
  }
  /**
   * @param name
   *          fully qualified file name, e.g. java/lang/String
   * @param suffix
   *          file name ends with suffix, e.g. .java
   * @return byte array for file on class path
   */
  public byte[] getBytes(String name, String suffix) throws IOException {
    DataInputStream dis = null;
    try {
      InputStream is = getInputStream(name, suffix);
      if (is == null) {
        throw new IOException("Couldn't find: " + name + suffix);
      }
      dis = new DataInputStream(is);
      byte[] bytes = new byte[is.available()];
      dis.readFully(bytes);
      return bytes;
    } finally {
      if (dis != null) {
        dis.close();
      }
    }
  }
  /**
   * @return byte array for class
   */
  public byte[] getBytes(String name) throws IOException {
    return getBytes(name, ".class");
  }
  /**
   * @param name
   *          name of file to search for, e.g. java/lang/String.java
   * @return full (canonical) path for file
   */
  public String getPath(String name) throws IOException {
    int index = name.lastIndexOf('.');
    String suffix = "";
    if (index > 0) {
      suffix = name.substring(index);
      name = name.substring(0, index);
    }
    return getPath(name, suffix);
  }
  /**
   * @param name
   *          name of file to search for, e.g. java/lang/String
   * @param suffix
   *          file name suffix, e.g. .java
   * @return full (canonical) path for file, if it exists
   */
  public String getPath(String name, String suffix) throws IOException {
    return getClassFile(name, suffix).getPath();
  }
  private static abstract class PathEntry implements Serializable {
    abstract ClassFile getClassFile(String name, String suffix) throws IOException;
  }
  /**
   * Contains information about file/ZIP entry of the Java class.
   */
  public interface ClassFile {
    /**
     * @return input stream for class file.
     */
    public abstract InputStream getInputStream() throws IOException;
    /**
     * @return canonical path to class file.
     */
    public abstract String getPath();
    /**
     * @return base path of found class, i.e. class is contained relative to
     *         that path, which may either denote a directory, or zip file
     */
    public abstract String getBase();
    /**
     * @return modification time of class file.
     */
    public abstract long getTime();
    /**
     * @return size of class file.
     */
    public abstract long getSize();
  }
  private static class Dir extends PathEntry {
    private String dir;
    Dir(String d) {
      dir = d;
    }
    ClassFile getClassFile(String name, String suffix) throws IOException {
      final File file = new File(dir + File.separatorChar + name.replace('.', File.separatorChar)
          + suffix);
      return file.exists() ? new ClassFile() {
        public InputStream getInputStream() throws IOException {
          return new FileInputStream(file);
        }
        public String getPath() {
          try {
            return file.getCanonicalPath();
          } catch (IOException e) {
            return null;
          }
        }
        public long getTime() {
          return file.lastModified();
        }
        public long getSize() {
          return file.length();
        }
        public String getBase() {
          return dir;
        }
      } : null;
    }
    public String toString() {
      return dir;
    }
  }
  private static class Zip extends PathEntry {
    private ZipFile zip;
    Zip(ZipFile z) {
      zip = z;
    }
    ClassFile getClassFile(String name, String suffix) throws IOException {
      final ZipEntry entry = zip.getEntry(name.replace('.', '/') + suffix);
      return (entry != null) ? new ClassFile() {
        public InputStream getInputStream() throws IOException {
          return zip.getInputStream(entry);
        }
        public String getPath() {
          return entry.toString();
        }
        public long getTime() {
          return entry.getTime();
        }
        public long getSize() {
          return entry.getSize();
        }
        public String getBase() {
          return zip.getName();
        }
      } : null;
    }
  }
}