package com.kreative.paint.material; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; import java.net.URLStreamHandler; import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.jar.JarEntry; import java.util.jar.JarInputStream; public class JarLoader extends ClassLoader { private final MaterialLoader loader; private final Map<String,MaterialResource> knownJars; private final Map<String,byte[]> knownData; private final Map<String,Class<?>> knownClasses; public JarLoader(MaterialLoader loader) { this.loader = loader; this.knownJars = new LinkedHashMap<String,MaterialResource>(); this.knownData = new HashMap<String,byte[]>(); this.knownClasses = new HashMap<String,Class<?>>(); } public List<Class<?>> listClasses() { if (knownJars.isEmpty()) loadResources(); List<Class<?>> classes = new ArrayList<Class<?>>(); for (String path : knownJars.keySet()) { if (path.endsWith(".class") && !path.contains("$")) { String cname = path.substring(0, path.length() - 6).replace('/', '.'); try { classes.add(loadClass(cname)); } catch (Throwable e) { e.printStackTrace(); } } } return classes; } public <T> List<Class<? extends T>> listClasses(Class<T> superclass) { if (knownJars.isEmpty()) loadResources(); List<Class<? extends T>> classes = new ArrayList<Class<? extends T>>(); for (String path : knownJars.keySet()) { if (path.endsWith(".class") && !path.contains("$")) { String cname = path.substring(0, path.length() - 6).replace('/', '.'); try { Class<?> c = loadClass(cname); if (superclass.isAssignableFrom(c)) { classes.add(c.asSubclass(superclass)); } } catch (Throwable e) { e.printStackTrace(); } } } return classes; } @Override protected Class<?> findClass(String cname) throws ClassNotFoundException { String path = cname.replace('.', '/') + ".class"; if (knownJars.isEmpty()) loadResources(); if (knownJars.containsKey(path)) { try { return getClass(path, cname); } catch (IOException e) {} } throw new ClassNotFoundException(); } @Override protected URL findResource(String name) { if (knownJars.isEmpty()) loadResources(); return (knownJars.containsKey(name)) ? getDataURL(name) : null; } @Override protected Enumeration<URL> findResources(String name) { if (knownJars.isEmpty()) loadResources(); List<URL> urls = new ArrayList<URL>(); if (knownJars.containsKey(name)) urls.add(getDataURL(name)); return Collections.enumeration(urls); } private Class<?> getClass(String path, String cname) throws IOException { if (knownClasses.containsKey(path)) return knownClasses.get(path); byte[] data = getData(path); Class<?> c = defineClass(cname, data, 0, data.length); knownClasses.put(path, c); return c; } private URL getDataURL(String name) { URLStreamHandler ush = new InternalURLStreamHandler(); try { return new URL(null, "material-jarloader:///" + name, ush); } catch (MalformedURLException e) { throw new RuntimeException(e); } } private class InternalURLStreamHandler extends URLStreamHandler { @Override protected URLConnection openConnection(URL url) { return new InternalURLConnection(url); } } private class InternalURLConnection extends URLConnection { public InternalURLConnection(URL url) { super(url); } @Override public void connect() throws IOException {} @Override public InputStream getInputStream() throws IOException { String name = getURL().getPath().substring(1); byte[] data = getData(name); return new ByteArrayInputStream(data); } } private byte[] getData(String name) throws IOException { if (knownData.containsKey(name)) return knownData.get(name); MaterialResource r = knownJars.get(name); if (r != null) { JarInputStream in = new JarInputStream(r.getInputStream()); for (JarEntry e = in.getNextJarEntry(); e != null; e = in.getNextJarEntry()) { if (!e.isDirectory() && e.getName().equals(name)) { if (e.getSize() <= Integer.MAX_VALUE) { byte[] data = new byte[(int)e.getSize()]; in.read(data, 0, data.length); in.close(); knownData.put(name, data); return data; } else { in.close(); throw new IOException("Too big: " + name); } } } in.close(); } throw new IOException("Not found: " + name); } private void loadResources() { for (MaterialResource r : loader.listResources()) { if (r.isFormat("jar", false)) { try { JarInputStream in = new JarInputStream(r.getInputStream()); for (JarEntry e = in.getNextJarEntry(); e != null; e = in.getNextJarEntry()) { if (!e.isDirectory()) { knownJars.put(e.getName(), r); } } in.close(); } catch (IOException e) { System.err.println("Warning: Failed to load jar " + r.getResourceName() + "."); e.printStackTrace(); } } } } }