package nebula.lang; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FilenameFilter; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.MalformedURLException; import java.net.URL; import java.util.Hashtable; import java.util.jar.JarEntry; import java.util.jar.JarFile; final class ClassResourcePath implements ResourcePath { private Class<?> thisClass; public ClassResourcePath(Class<?> c) { thisClass = c; } ClassResourcePath() { this(java.lang.Object.class); } public InputStream openResouce(String name, String ext) { name = "/" + name.replace('.', '/') + ext; return thisClass.getResourceAsStream(name); } public URL find(String name, String ext) { name = "/" + name.replace('.', '/') + ext; return thisClass.getResource(name); } public void close() { } public String toString() { return thisClass.getName(); } } final class ResourcePathList { ResourcePathList next; ResourcePath path; ResourcePathList(ResourcePath p, ResourcePathList n) { next = n; path = p; } } final class DirResourcePath implements ResourcePath { String directory; DirResourcePath(String dirName) { directory = dirName; } public InputStream openResouce(String name, String ext) { try { char sep = File.separatorChar; String filename = directory + sep + name.replace('.', sep) + ext; return new FileInputStream(filename.toString()); } catch (FileNotFoundException e) { } catch (SecurityException e) { } return null; } public URL find(String name, String ext) { char sep = File.separatorChar; String filename = directory + sep + name.replace('.', sep) + ext; File f = new File(filename); if (f.exists()) try { return f.getCanonicalFile().toURI().toURL(); } catch (MalformedURLException e) { } catch (IOException e) { } return null; } public void close() { } public String toString() { return directory; } } final class JarDirResourcePath implements ResourcePath { JarClassPath[] jars; JarDirResourcePath(String dirName) { File[] files = new File(dirName).listFiles(new FilenameFilter() { public boolean accept(File dir, String name) { name = name.toLowerCase(); return name.endsWith(".jar") || name.endsWith(".zip"); } }); if (files != null) { jars = new JarClassPath[files.length]; for (int i = 0; i < files.length; i++) jars[i] = new JarClassPath(files[i].getPath()); } } public InputStream openResouce(String name, String ext) { if (jars != null) for (int i = 0; i < jars.length; i++) { InputStream is = jars[i].openResouce(name, ext); if (is != null) return is; } return null; // not found } public URL find(String name, String ext) { if (jars != null) for (int i = 0; i < jars.length; i++) { URL url = jars[i].find(name, ext); if (url != null) return url; } return null; // not found } public void close() { if (jars != null) for (int i = 0; i < jars.length; i++) jars[i].close(); } } final class JarClassPath implements ResourcePath { JarFile jarfile; String jarfileURL; JarClassPath(String pathname) { try { jarfile = new JarFile(pathname); jarfileURL = new File(pathname).getCanonicalFile().toURI().toURL().toString(); return; } catch (IOException e) { } throw new RuntimeException(pathname); } public InputStream openResouce(String name, String ext) { try { String jarname = name.replace('.', '/') + ext; JarEntry je = jarfile.getJarEntry(jarname); if (je != null) return jarfile.getInputStream(je); else return null; // not found } catch (IOException e) { } throw new RuntimeException("broken jar file?: " + jarfile.getName()); } public URL find(String name, String ext) { String jarname = name.replace('.', '/') + ext; JarEntry je = jarfile.getJarEntry(jarname); if (je != null) try { return new URL("jar:" + jarfileURL + "!/" + jarname); } catch (MalformedURLException e) { } return null; // not found } public void close() { try { jarfile.close(); jarfile = null; } catch (IOException e) { } } public String toString() { return jarfile == null ? "<null>" : jarfile.toString(); } } public class ResourcePoolTail implements ResourcePath { protected ResourcePathList pathList; private Hashtable<String, String> packages; // should be synchronized. public ResourcePoolTail() { pathList = null; packages = new Hashtable<String, String>(); } public String toString() { StringBuilder buf = new StringBuilder(); buf.append("[class path: "); ResourcePathList list = pathList; while (list != null) { buf.append(list.path.toString()); buf.append(File.pathSeparatorChar); list = list.next; } buf.append(']'); return buf.toString(); } public synchronized ResourcePath insertResourcePath(ResourcePath cp) { pathList = new ResourcePathList(cp, pathList); return cp; } public synchronized ResourcePath appendResourcePath(ResourcePath cp) { ResourcePathList tail = new ResourcePathList(cp, null); ResourcePathList list = pathList; if (list == null) pathList = tail; else { while (list.next != null) list = list.next; list.next = tail; } return cp; } public synchronized void removeResourcePath(ResourcePath cp) { ResourcePathList list = pathList; if (list != null) if (list.path == cp) pathList = list.next; else { while (list.next != null) if (list.next.path == cp) list.next = list.next.next; else list = list.next; } cp.close(); } public ResourcePath appendSystemPath() { return appendResourcePath(new ClassResourcePath()); } public ResourcePath insertResourcePath(String pathname) { return insertResourcePath(makePathObject(pathname)); } public ResourcePath appendResourcePath(String pathname) { return appendResourcePath(makePathObject(pathname)); } private static ResourcePath makePathObject(String pathname) { String lower = pathname.toLowerCase(); if (lower.endsWith(".jar") || lower.endsWith(".zip")) return new JarClassPath(pathname); int len = pathname.length(); if (len > 2 && pathname.charAt(len - 1) == '*' && (pathname.charAt(len - 2) == '/' || pathname.charAt(len - 2) == File.separatorChar)) { String dir = pathname.substring(0, len - 2); return new JarDirResourcePath(dir); } return new DirResourcePath(pathname); } /** * You can record "System" so that java.lang.System can be quickly found * although "System" is not a package name. */ public void recordInvalidClassName(String name) { packages.put(name, name); } /** * This method does not close the output stream. */ void writeClassfile(String name, String ext, OutputStream out) { InputStream fin = openResouce(name, ext); if (fin == null) throw new RuntimeException(name); try { copyStream(fin, out); } catch (IOException e) { throw new RuntimeException(e); } finally { try { fin.close(); } catch (IOException e) { } } } /** * Opens the class file for the class specified by <code>name</code>. * * @param name * a fully-qualified class name * @return null if the file has not been found. * @throws NotFoundException * if any error is reported by ClassPath. */ public InputStream openResouce(String name, String ext) { if (packages.get(name) != null) return null; // not found ResourcePathList list = pathList; InputStream ins = null; RuntimeException error = null; while (list != null) { try { ins = list.path.openResouce(name, ext); } catch (RuntimeException e) { if (error == null) error = e; } if (ins == null) list = list.next; else return ins; } if (error != null) throw error; else return null; // not found } /** * Searches the class path to obtain the URL of the class file specified by * name. It is also used to determine whether the class file exists. * * @param name * a fully-qualified class name. * @return null if the class file could not be found. */ public URL find(String name, String ext) { if (packages.get(name) != null) return null; ResourcePathList list = pathList; URL url = null; while (list != null) { url = list.path.find(name, ext); if (url == null) list = list.next; else return url; } return null; } /** * Reads from an input stream until it reaches the end. * * @return the contents of that input stream */ public static byte[] readStream(InputStream fin) throws IOException { byte[][] bufs = new byte[8][]; int bufsize = 4096; for (int i = 0; i < 8; ++i) { bufs[i] = new byte[bufsize]; int size = 0; int len = 0; do { len = fin.read(bufs[i], size, bufsize - size); if (len >= 0) size += len; else { byte[] result = new byte[bufsize - 4096 + size]; int s = 0; for (int j = 0; j < i; ++j) { System.arraycopy(bufs[j], 0, result, s, s + 4096); s = s + s + 4096; } System.arraycopy(bufs[i], 0, result, s, size); return result; } } while (size < bufsize); bufsize *= 2; } throw new IOException("too much data"); } /** * Reads from an input stream and write to an output stream until it reaches * the end. This method does not close the streams. */ public static void copyStream(InputStream fin, OutputStream fout) throws IOException { int bufsize = 4096; for (int i = 0; i < 8; ++i) { byte[] buf = new byte[bufsize]; int size = 0; int len = 0; do { len = fin.read(buf, size, bufsize - size); if (len >= 0) size += len; else { fout.write(buf, 0, size); return; } } while (size < bufsize); fout.write(buf); bufsize *= 2; } throw new IOException("too much data"); } @Override public void close() { } }