/* gnu.classpath.tools.FileSystemClassLoader Copyright (C) 2004 Free Software Foundation, Inc. This file is part of GNU Classpath. GNU Classpath is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. GNU Classpath is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Classpath; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. Linking this library statically or dynamically with other modules is making a combined work based on this library. Thus, the terms and conditions of the GNU General Public License cover the whole combination. As a special exception, the copyright holders of this library give you permission to link this library with independent modules to produce an executable, regardless of the license terms of these independent modules, and to copy and distribute the resulting executable under terms of your choice, provided that you also meet, for each linked independent module, the terms and conditions of the license of that module. An independent module is a module which is not derived from or based on this library. If you modify this library, you may extend this exception to your version of the library, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ package gnu.classpath.tools; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.InputStream; import java.io.IOException; import java.io.StreamTokenizer; import java.io.StringReader; import java.net.MalformedURLException; import java.net.URL; import java.util.LinkedList; import java.util.List; import java.util.ArrayList; import java.util.StringTokenizer; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.jar.Manifest; import java.util.jar.Attributes; /** * A ClassLoader implementation which looks for classes * on the local filesystem given a standard search path. */ public class FileSystemClassLoader extends ClassLoader { private File[] pathComponents; /** * Initialize the class loader with a normal path string. The path * string should contain path components separated by {@link * File.pathSeparator}. Each path component should either denote a * directory or a .jar or .zip file. */ public FileSystemClassLoader(String path) { List components = new ArrayList(); for (StringTokenizer st = new StringTokenizer(path, File.pathSeparator); st.hasMoreTokens(); ) { File pathComponent = new File(st.nextToken()); components.add(pathComponent); if (pathComponent.exists() && !pathComponent.isDirectory()) { List subComponents = tryGetJarFileClassPathComponents(pathComponent); if (null != subComponents) { components.addAll(subComponents); } } } File[] componentArray = new File[components.size()]; this.pathComponents = (File[])components.toArray(componentArray); } /** * Initialize the class loader with an array of path * components. Each path component should either denote a * directory or a .jar or .zip file. */ public FileSystemClassLoader(File[] pathComponents) { this.pathComponents = pathComponents; for (int i = 0; i < pathComponents.length; ++i) { if (!pathComponents[i].exists()) { System.err.println("WARNING: Path component '" + pathComponents[i] + "' not found."); } } } public Class loadClass(String name) throws ClassNotFoundException { return super.loadClass(name); } public Class findClass(String name) throws ClassNotFoundException { byte[] b = loadClassData(name); return defineClass(name, b, 0, b.length); } public URL findResource(String name) { StreamInfo streamInfo = getResourceStream(name); if (null == streamInfo) { return super.findResource(name); } else { try { return streamInfo.getURL(); } catch (MalformedURLException e) { System.err.println("WARNING: In FileSystemClassLoader: could not derive URL from file or jar entry: " + e.toString()); return null; } } } private byte[] readFromStream(InputStream in, long size) throws IOException { byte[] result = new byte[(int)size]; int nread = 0; int offset = 0; while (offset < size && (nread = in.read(result, offset, (int)(size - offset))) >= 0) { offset += nread; } in.close(); return result; } private byte[] readFromStream(StreamInfo streamInfo) throws IOException { InputStream in = streamInfo.openStream(); long size = streamInfo.getSize(); byte[] result = new byte[(int)size]; int nread = 0; int offset = 0; while (offset < size && (nread = in.read(result, offset, (int)(size - offset))) >= 0) { offset += nread; } in.close(); return result; } private static interface StreamInfo { public InputStream openStream() throws IOException; public long getSize(); public URL getURL() throws MalformedURLException; } private static class FileStreamInfo implements StreamInfo { File file; FileStreamInfo(File file) { this.file = file; } public InputStream openStream() throws IOException { return new FileInputStream(file); } public long getSize() { return file.length(); } public URL getURL() throws MalformedURLException { return file.toURL(); } } private static class JarStreamInfo implements StreamInfo { private File file; private JarFile jarFile; private JarEntry jarEntry; JarStreamInfo(File file, JarFile jarFile, JarEntry jarEntry) { this.file = file; this.jarFile = jarFile; this.jarEntry = jarEntry; } public InputStream openStream() throws IOException { return jarFile.getInputStream(jarEntry); } public long getSize() { return jarEntry.getSize(); } public URL getURL() throws MalformedURLException { String urlString = "jar:" + file.toURL() + "!/" + jarEntry.getName(); return new URL(urlString); } } private StreamInfo getResourceStream(String path) { for (int i = 0; i < pathComponents.length; ++i) { try { File parent = pathComponents[i]; if (parent.isDirectory()) { File file = new File(parent, path); if (file.exists()) { return new FileStreamInfo(file); } } else { JarFile jarFile = new JarFile(parent, false, JarFile.OPEN_READ); JarEntry jarEntry = jarFile.getJarEntry(path); if (null != jarEntry) { return new JarStreamInfo(parent, jarFile, jarEntry); } } } catch (IOException ignore) { } } return null; } private byte[] loadClassData(String className) throws ClassNotFoundException { String classFileName = className.replace('.', File.separatorChar) + ".class"; StreamInfo streamInfo = getResourceStream(classFileName); try { if (null != streamInfo) { return readFromStream(streamInfo); } } catch (IOException ignore) { } throw new ClassNotFoundException(className); } private static List tryGetJarFileClassPathComponents(File file) { try { JarFile jarFile = new JarFile(file, false, JarFile.OPEN_READ); Manifest manifest = jarFile.getManifest(); if (null != manifest) { Attributes mainAttributes = manifest.getMainAttributes(); if (null != mainAttributes) { String classPath = mainAttributes.getValue(Attributes.Name.CLASS_PATH); if (null != classPath) { List result = new LinkedList(); StreamTokenizer tokenizer = new StreamTokenizer(new StringReader(classPath)); tokenizer.resetSyntax(); tokenizer.wordChars(0, Integer.MAX_VALUE); tokenizer.whitespaceChars(9, 9); // tab tokenizer.whitespaceChars(10, 10); // lf tokenizer.whitespaceChars(13, 13); // cr tokenizer.whitespaceChars(32, 32); // space tokenizer.quoteChar('"'); int token; while ((token = tokenizer.nextToken()) != StreamTokenizer.TT_EOF) { if (StreamTokenizer.TT_WORD == token) { result.add(new File(file.getParentFile(), tokenizer.sval)); } } return result; } } } } catch (IOException ignore) { } return null; } }