| Index: third_party/jmake/src/org/pantsbuild/jmake/ClassPath.java
|
| diff --git a/third_party/jmake/src/org/pantsbuild/jmake/ClassPath.java b/third_party/jmake/src/org/pantsbuild/jmake/ClassPath.java
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..e6e1aebd86a31054fc802960991986414288ef01
|
| --- /dev/null
|
| +++ b/third_party/jmake/src/org/pantsbuild/jmake/ClassPath.java
|
| @@ -0,0 +1,448 @@
|
| +/* Copyright (c) 2002-2008 Sun Microsystems, Inc. All rights reserved
|
| + *
|
| + * This program is distributed under the terms of
|
| + * the GNU General Public License Version 2. See the LICENSE file
|
| + * at the top of the source tree.
|
| + */
|
| +package org.pantsbuild.jmake;
|
| +
|
| +import java.io.File;
|
| +import java.io.FilenameFilter;
|
| +import java.io.IOException;
|
| +import java.net.URL;
|
| +import java.net.URLClassLoader;
|
| +import java.util.ArrayList;
|
| +import java.util.Collection;
|
| +import java.util.LinkedHashMap;
|
| +import java.util.List;
|
| +import java.util.Locale;
|
| +import java.util.Map;
|
| +import java.util.Set;
|
| +import java.util.StringTokenizer;
|
| +import java.util.zip.ZipEntry;
|
| +import java.util.zip.ZipFile;
|
| +
|
| +/**
|
| + * An instance of this class represents a class path, on which binary classes can be looked up.
|
| + * It also provides several static methods to create and utilize several specific class paths used
|
| + * throughout jmake.
|
| + *
|
| + * @author Misha Dmitriev
|
| + * 12 October 2004
|
| + */
|
| +public class ClassPath {
|
| +
|
| + private PathEntry[] paths;
|
| + private static ClassPath projectClassPath; // Class path (currently it can contain only JARs) containing sourceless project classes.
|
| + // See also the comment to standardClassPath.
|
| + private static ClassPath standardClassPath; // Class path that the user specifies via the -classpath option. A sum of the
|
| + // standardClassPath, the projectClassPath, and the virtualPath is passed to the compiler. Each of these
|
| + // class paths are also used to look up non-project superclasses/superinterfaces of
|
| + // project classes.
|
| + private static ClassPath bootClassPath, extClassPath; // Class paths that by default are sun.boot.class.path and all JARs on
|
| + // java.ext.class.path, respectively. They are used to look up non-project
|
| + // superclasses/superinterfaces of project classes. Their values can be changed using
|
| + // setBootClassPath() and setExtDirs().
|
| + private static ClassPath virtualPath; // Class path that the user specifies via the -vpath option.
|
| + private static String compilerUserClassPath; // Class path to be passed to the compiler; equals to the sum of values of parameters of
|
| + // setClassPath() and setProjectClassPath() methods.
|
| + private static String standardClassPathStr, projectClassPathStr, bootClassPathStr, extDirsStr,
|
| + virtualPathStr;
|
| + private static Map<String,ClassInfo> classCache;
|
| +
|
| +
|
| + static {
|
| + resetOnFinish();
|
| + }
|
| +
|
| + /**
|
| + * Needed since some environments, e.g. NetBeans, can keep jmake classes in memory
|
| + * permanently. Thus unchanged class paths from previous, possibly unrelated invocations
|
| + * of jmake, may interfere with the current settings.
|
| + */
|
| + public static void resetOnFinish() {
|
| + projectClassPath = standardClassPath = bootClassPath = extClassPath = virtualPath =
|
| + null;
|
| + compilerUserClassPath = null;
|
| + standardClassPathStr = projectClassPathStr = bootClassPathStr =
|
| + extDirsStr = virtualPathStr = null;
|
| + classCache = new LinkedHashMap<String,ClassInfo>();
|
| + }
|
| +
|
| + public static void setClassPath(String value) throws PublicExceptions.InvalidCmdOptionException {
|
| + standardClassPathStr = value;
|
| + standardClassPath = new ClassPath(value, false);
|
| + }
|
| +
|
| + public static void setProjectClassPath(String value) throws PublicExceptions.InvalidCmdOptionException {
|
| + projectClassPathStr = value;
|
| + projectClassPath = new ClassPath(value, true);
|
| + }
|
| +
|
| + public static void setBootClassPath(String value) throws PublicExceptions.InvalidCmdOptionException {
|
| + bootClassPathStr = value;
|
| + bootClassPath = new ClassPath(value, false);
|
| + }
|
| +
|
| + public static void setExtDirs(String value) throws PublicExceptions.InvalidCmdOptionException {
|
| + extDirsStr = value;
|
| + // Extension class path needs special handling, since it consists of directories, which contain .jars
|
| + // So we need to find all these .jars in all these dirs and add them to extClassPathElementList
|
| + List<String> extClassPathElements = new ArrayList<String>();
|
| + for (StringTokenizer tok =
|
| + new StringTokenizer(value, File.pathSeparator); tok.hasMoreTokens();) {
|
| + File extDir = new File(tok.nextToken());
|
| + String[] extJars = extDir.list(new FilenameFilter() {
|
| +
|
| + public boolean accept(File dir, String name) {
|
| + name = name.toLowerCase(Locale.ENGLISH);
|
| + return name.endsWith(".zip") || name.endsWith(".jar");
|
| + }
|
| + });
|
| + if (extJars == null) {
|
| + continue;
|
| + }
|
| + for (int i = 0; i < extJars.length; i++) {
|
| + extClassPathElements.add(extDir + File.separator + extJars[i]);
|
| + }
|
| + }
|
| + extClassPath = new ClassPath(extClassPathElements, false);
|
| + }
|
| +
|
| + public static void setVirtualPath(String value) throws PublicExceptions.InvalidCmdOptionException {
|
| + if (value == null) {
|
| + throw new PublicExceptions.InvalidCmdOptionException("null argument");
|
| + }
|
| + StringTokenizer st = new StringTokenizer(value, File.pathSeparator);
|
| + while (st.hasMoreElements()) {
|
| + String dir = st.nextToken();
|
| + if ( ! (new File(dir)).isDirectory()) {
|
| + throw new PublicExceptions.InvalidCmdOptionException("Virtual path must contain only directories." +
|
| + " Entry " + dir + " is not a directory.");
|
| + }
|
| + }
|
| + virtualPathStr = value;
|
| + virtualPath = new ClassPath(value, false);
|
| + }
|
| +
|
| + public static void initializeAllClassPaths() {
|
| + // First set the compiler class path value
|
| + if (standardClassPathStr == null && projectClassPathStr == null) {
|
| + compilerUserClassPath = ".";
|
| + } else if (standardClassPathStr == null) {
|
| + compilerUserClassPath = projectClassPathStr;
|
| + } else if (projectClassPathStr == null) {
|
| + compilerUserClassPath = standardClassPathStr;
|
| + } else {
|
| + compilerUserClassPath =
|
| + standardClassPathStr + File.pathSeparator + projectClassPathStr;
|
| + }
|
| +
|
| + if (virtualPathStr != null) {
|
| + compilerUserClassPath += File.pathSeparator + virtualPathStr;
|
| + }
|
| +
|
| + if (standardClassPathStr == null) {
|
| + try {
|
| + String tmp = ".";
|
| + if (virtualPathStr != null) {
|
| + tmp += File.pathSeparator + virtualPathStr;
|
| + }
|
| + standardClassPath = new ClassPath(tmp, false);
|
| + } catch (PublicExceptions.InvalidCmdOptionException ex) { /* Should not happen */ }
|
| + }
|
| + if (projectClassPathStr == null) {
|
| + projectClassPath = new ClassPath();
|
| + }
|
| +
|
| + // Create the core class path as a combination of sun.boot.class.path and java.ext.dirs contents
|
| + if (bootClassPathStr == null) {
|
| + try {
|
| + bootClassPath =
|
| + new ClassPath(System.getProperty("sun.boot.class.path"), false);
|
| + } catch (PublicExceptions.InvalidCmdOptionException ex) { /* Shouldn't happen */ }
|
| + // bootClassPathStr should remain null, so that nothing that the user didn't specify is passed to the compiler
|
| + }
|
| +
|
| + if (extDirsStr == null) {
|
| + try {
|
| + setExtDirs(System.getProperty("java.ext.dirs"));
|
| + } catch (PublicExceptions.InvalidCmdOptionException ex) { /* Shouldn't happen */ }
|
| + // extDirsStr should remain null, so that nothing that the user didn't specify is passed to the compiler
|
| + extDirsStr = null;
|
| + }
|
| + }
|
| +
|
| + /** Never returns null - if classpath wasn't set explicitly, returns "." */
|
| + public static String getCompilerUserClassPath() {
|
| + return compilerUserClassPath;
|
| + }
|
| +
|
| + /** Will return null if boot class path wasn't explicitly specified */
|
| + public static String getCompilerBootClassPath() {
|
| + return bootClassPathStr;
|
| + }
|
| +
|
| + /** Will return null if extdirs weren't explicitly specified */
|
| + public static String getCompilerExtDirs() {
|
| + return extDirsStr;
|
| + }
|
| +
|
| + /** Will return null if virtualPath wasn't explicitly specified */
|
| + public static String getVirtualPath() {
|
| + return virtualPathStr;
|
| + }
|
| +
|
| + /**
|
| + * For the given class return the list of all of its superclasses (excluding Object), that can be loaded from
|
| + * projectClassPath or standardClassPath, plus the first superclass that can be loaded from coreClassPath.
|
| + * The latter is an optimization based on the assumption that core classes never change, or rather the programmer
|
| + * will recompile everything when they switch to a new JDK version. The optimization prevents us from wasting time
|
| + * repeatedly loading the same sets of core classes.
|
| + */
|
| + public static void getSuperclasses(String className,
|
| + Collection<String> res, PCDManager pcdm) {
|
| + int iterNo = 0;
|
| + while (!"java/lang/Object".equals(className)) {
|
| + ClassInfo ci = getClassInfoForName(className, pcdm);
|
| + if (ci == null) {
|
| + return;
|
| + }
|
| + if (iterNo++ > 0) {
|
| + res.add(ci.name);
|
| + }
|
| + className = ci.superName;
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Add to the given set the names of all interfaces implemented by the given class, that can be loaded from
|
| + * projectClassPath or standardClassPath, plus the first interface on each branch that can be loaded from
|
| + * coreClassPath. It's the same optimization as in getSuperclasses().
|
| + */
|
| + public static void addAllImplementedInterfaceNames(String className,
|
| + Set<String> intfSet, PCDManager pcdm) {
|
| + if ("java/lang/Object".equals(className)) {
|
| + return;
|
| + }
|
| + ClassInfo ci = getClassInfoForName(className, pcdm);
|
| + if (ci == null) {
|
| + return;
|
| + }
|
| + String[] interfaces = ci.interfaces;
|
| + if (interfaces != null) {
|
| + for (int i = 0; i < interfaces.length; i++) {
|
| + intfSet.add(interfaces[i]);
|
| + addAllImplementedInterfaceNames(interfaces[i], intfSet, pcdm);
|
| + }
|
| + }
|
| +
|
| + String superName = ci.superName;
|
| + if (superName != null) {
|
| + addAllImplementedInterfaceNames(superName, intfSet, pcdm);
|
| + }
|
| + }
|
| +
|
| + public static String[] getProjectJars() {
|
| + if (projectClassPath == null || projectClassPath.isEmpty()) {
|
| + return null;
|
| + }
|
| + PathEntry paths[] = projectClassPath.paths;
|
| + String[] ret = new String[paths.length];
|
| + for (int i = 0; i < paths.length; i++) {
|
| + ret[i] = paths[i].toString();
|
| + }
|
| + return ret;
|
| + }
|
| +
|
| + public static ClassInfo getClassInfoForName(String className, PCDManager pcdm) {
|
| + ClassInfo info = classCache.get(className);
|
| + if (info != null) {
|
| + return info;
|
| + }
|
| +
|
| + byte buf[] = bootClassPath.getBytesForClass(className);
|
| + if (buf == null) {
|
| + buf = extClassPath.getBytesForClass(className);
|
| + }
|
| + if (buf == null) {
|
| + buf = standardClassPath.getBytesForClass(className);
|
| + }
|
| + if (buf == null) {
|
| + buf = projectClassPath.getBytesForClass(className);
|
| + }
|
| + if (buf == null) {
|
| + return null;
|
| + }
|
| +
|
| + info = new ClassInfo(buf, pcdm, className);
|
| + classCache.put(className, info);
|
| + return info;
|
| + }
|
| +
|
| + /** Returns the class loader that would load classes from the given class path. */
|
| + public static ClassLoader getClassLoaderForPath(String classPath) throws Exception {
|
| + boolean isWindows = System.getProperty("os.name").startsWith("Win");
|
| + ClassPath cp = new ClassPath(classPath, false);
|
| + PathEntry[] paths = cp.paths;
|
| + URL[] urls = new URL[paths.length];
|
| + for (int i = 0; i < paths.length; i++) {
|
| + String dirOrJar = paths[i].toString();
|
| + if (!(dirOrJar.startsWith("file://") || dirOrJar.startsWith("http://"))) {
|
| + // On Windows, if I have path specified as "file://c:\...", (i.e. with the drive name) URLClassLoader works
|
| + // unbelievably slow. However, if an additional slash is added, like : "file:///c:\...", the speed becomes
|
| + // normal. To me it looks like a bug, but, anyway, I am taking measure here.
|
| + if (isWindows && dirOrJar.charAt(1) == ':') {
|
| + dirOrJar = "/" + dirOrJar;
|
| + }
|
| + dirOrJar = new File(dirOrJar).toURI().toString();
|
| + }
|
| + if (!(dirOrJar.endsWith(".jar") || dirOrJar.endsWith(".zip") || dirOrJar.endsWith(File.separator))) {
|
| + dirOrJar += File.separator; // So that URLClassLoader correctly handles it as a directory
|
| + }
|
| + urls[i] = new URL(dirOrJar);
|
| + }
|
| +
|
| + return new URLClassLoader(urls);
|
| + //} catch (java.net.MalformedURLException e) {
|
| +
|
| + //}
|
| + }
|
| +
|
| +
|
| + // ------------------------------------ Private implementation --------------------------------------------
|
| + private ClassPath() {
|
| + paths = new PathEntry[0];
|
| + }
|
| +
|
| + private ClassPath(String classPath, boolean isJarOnly) throws PublicExceptions.InvalidCmdOptionException {
|
| + if (classPath == null) {
|
| + throw new PublicExceptions.InvalidCmdOptionException("null argument");
|
| + }
|
| + List<String> vec = new ArrayList<String>();
|
| +
|
| + for (StringTokenizer tok =
|
| + new StringTokenizer(classPath, File.pathSeparator); tok.hasMoreTokens();) {
|
| + String path = tok.nextToken();
|
| + vec.add(path);
|
| + }
|
| + init(vec, isJarOnly);
|
| + }
|
| +
|
| + private ClassPath(List<String> pathEntries, boolean isJarOnly) throws PublicExceptions.InvalidCmdOptionException {
|
| + init(pathEntries, isJarOnly);
|
| + }
|
| +
|
| + private void init(List<String> pathEntries, boolean isJarOnly) throws PublicExceptions.InvalidCmdOptionException {
|
| + if (pathEntries == null) {
|
| + throw new PublicExceptions.InvalidCmdOptionException("null argument");
|
| + }
|
| + List<PathEntry> vec = new ArrayList<PathEntry>(pathEntries.size());
|
| + for (int i = 0; i < pathEntries.size(); i++) {
|
| + String path = pathEntries.get(i);
|
| + if (!path.equals("")) {
|
| + File file = new File(path);
|
| + try {
|
| + if (file.exists() && file.canRead()) {
|
| + if (file.isDirectory()) {
|
| + if (isJarOnly) {
|
| + throw new PublicExceptions.InvalidCmdOptionException("directories are not allowed on this class path: " + path);
|
| + }
|
| + vec.add(new Dir(file));
|
| + } else {
|
| + vec.add(new Zip(new ZipFile(file)));
|
| + }
|
| + } else if (isJarOnly) {
|
| + throw new IOException("file does not exist");
|
| + }
|
| + } catch (IOException e) {
|
| + if (isJarOnly) {
|
| + throw new PublicExceptions.InvalidCmdOptionException("error initializing class path component " + path + ": " + e.getMessage());
|
| + }
|
| + }
|
| + }
|
| + }
|
| +
|
| + paths = new PathEntry[vec.size()];
|
| + vec.toArray(paths);
|
| + }
|
| +
|
| + private boolean isEmpty() {
|
| + return paths.length == 0;
|
| + }
|
| +
|
| + private byte[] getBytesForClass(String className) {
|
| + String fileName = className + ".class";
|
| + for (int i = 0; i < paths.length; i++) {
|
| + byte buf[] = paths[i].getBytesForClassFile(fileName);
|
| + if (buf != null) {
|
| + return buf;
|
| + }
|
| + }
|
| + return null;
|
| + }
|
| +
|
| + public String toString() {
|
| + if (paths == null) {
|
| + return "NULL";
|
| + }
|
| + StringBuilder res = new StringBuilder();
|
| + for (int i = 0; i < paths.length; i++) {
|
| + res.append(paths[i].toString());
|
| + }
|
| + return res.toString();
|
| + }
|
| +
|
| +
|
| + // ------------------------------------ Private helper classes --------------------------------------------
|
| + private static abstract class PathEntry {
|
| +
|
| + abstract byte[] getBytesForClassFile(String fileName);
|
| +
|
| + public abstract String toString();
|
| + }
|
| +
|
| + private static class Dir extends PathEntry {
|
| +
|
| + private String dir;
|
| +
|
| + Dir(File f) throws IOException {
|
| + dir = f.getCanonicalPath();
|
| + }
|
| +
|
| + byte[] getBytesForClassFile(String fileName) {
|
| + File file = new File(dir + File.separatorChar + fileName);
|
| + if (file.exists()) {
|
| + return Utils.readFileIntoBuffer(file);
|
| + } else {
|
| + return null;
|
| + }
|
| + }
|
| +
|
| + public String toString() {
|
| + return dir;
|
| + }
|
| + }
|
| +
|
| + private static class Zip extends PathEntry {
|
| +
|
| + private ZipFile zip;
|
| +
|
| + Zip(ZipFile z) {
|
| + zip = z;
|
| + }
|
| +
|
| + byte[] getBytesForClassFile(String fileName) {
|
| + ZipEntry entry = zip.getEntry(fileName);
|
| + if (entry != null) {
|
| + return Utils.readZipEntryIntoBuffer(zip, entry);
|
| + } else {
|
| + return null;
|
| + }
|
| + }
|
| +
|
| + public String toString() {
|
| + return zip.getName();
|
| + }
|
| + }
|
| +}
|
|
|