jtulach@1326: /** jtulach@1326: * Back 2 Browser Bytecode Translator jtulach@1326: * Copyright (C) 2012 Jaroslav Tulach jtulach@1326: * jtulach@1326: * This program is free software: you can redistribute it and/or modify jtulach@1326: * it under the terms of the GNU General Public License as published by jtulach@1326: * the Free Software Foundation, version 2 of the License. jtulach@1326: * jtulach@1326: * This program is distributed in the hope that it will be useful, jtulach@1326: * but WITHOUT ANY WARRANTY; without even the implied warranty of jtulach@1326: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the jtulach@1326: * GNU General Public License for more details. jtulach@1326: * jtulach@1326: * You should have received a copy of the GNU General Public License jtulach@1326: * along with this program. Look for COPYING file in the top folder. jtulach@1326: * If not, see http://opensource.org/licenses/GPL-2.0. tzezula@1324: */ tzezula@1324: tzezula@1324: package org.apidesign.bck2brwsr.dew; tzezula@1324: tzezula@1324: import java.io.File; tzezula@1324: import java.io.IOException; tzezula@1325: import java.io.InputStream; tzezula@1325: import java.net.URL; tzezula@1324: import java.util.ArrayList; tzezula@1324: import java.util.Collections; tzezula@1324: import java.util.EnumSet; tzezula@1324: import java.util.Enumeration; tzezula@1324: import java.util.HashMap; tzezula@1324: import java.util.Iterator; tzezula@1324: import java.util.List; tzezula@1324: import java.util.Map; tzezula@1324: import java.util.Set; tzezula@1324: import java.util.zip.ZipEntry; tzezula@1324: import java.util.zip.ZipFile; tzezula@1324: import javax.tools.FileObject; tzezula@1324: import javax.tools.JavaFileManager; tzezula@1324: import javax.tools.JavaFileObject; tzezula@1324: import javax.tools.StandardLocation; tzezula@1324: tzezula@1324: /** tzezula@1324: * tzezula@1324: * @author Tomas Zezula tzezula@1324: */ tzezula@1324: public class ClassLoaderFileManager implements JavaFileManager { tzezula@1324: tzezula@1324: private static final Location[] READ_LOCATIONS = { tzezula@1324: StandardLocation.PLATFORM_CLASS_PATH, tzezula@1324: StandardLocation.CLASS_PATH, tzezula@1324: StandardLocation.SOURCE_PATH tzezula@1324: }; tzezula@1324: tzezula@1324: private static final Location[] WRITE_LOCATIONS = { tzezula@1324: StandardLocation.CLASS_OUTPUT, tzezula@1324: StandardLocation.SOURCE_OUTPUT tzezula@1324: }; tzezula@1324: tzezula@1324: private static final Location[] CLASS_LOADER_LOCATIONS = { tzezula@1324: StandardLocation.ANNOTATION_PROCESSOR_PATH tzezula@1324: }; tzezula@1324: tzezula@1324: private Map>> generated; tzezula@1324: tzezula@1324: tzezula@1324: ClassLoaderFileManager() { tzezula@1324: generated = new HashMap<>(); tzezula@1324: for (Location l : WRITE_LOCATIONS) { tzezula@1324: generated.put(l, new HashMap>()); tzezula@1324: } tzezula@1324: } tzezula@1324: tzezula@1324: tzezula@1324: @Override tzezula@1324: public ClassLoader getClassLoader(Location location) { tzezula@1324: if (canClassLoad(location)) { tzezula@1325: return new SafeClassLoader(getClass().getClassLoader()); tzezula@1324: } else { tzezula@1324: return null; tzezula@1324: } tzezula@1324: } tzezula@1324: tzezula@1324: @Override tzezula@1324: public Iterable list(Location location, String packageName, Set kinds, boolean recurse) throws IOException { tzezula@1324: if (canRead(location)) { tzezula@1324: final List res = new ArrayList(); tzezula@1324: for (String resource : getResources(convertFQNToResource(packageName))) { tzezula@1324: final JavaFileObject jfo = new ClassLoaderJavaFileObject(resource); tzezula@1324: if (kinds.contains(jfo.getKind())) { tzezula@1324: res.add(jfo); tzezula@1324: } tzezula@1324: } tzezula@1324: return res; tzezula@1324: } else if (canWrite(location)) { tzezula@1324: Map> folders = generated.get(location); tzezula@1324: List files = folders.get(convertFQNToResource(packageName)); tzezula@1324: if (files != null) { tzezula@1324: final List res = new ArrayList(); tzezula@1324: for (JavaFileObject file : files) { tzezula@1324: if (kinds.contains(file.getKind()) && file.getLastModified() >= 0) { tzezula@1324: res.add(file); tzezula@1324: } tzezula@1324: } tzezula@1324: return res; tzezula@1324: } tzezula@1324: } tzezula@1324: return Collections.emptyList(); tzezula@1324: } tzezula@1324: tzezula@1324: @Override tzezula@1324: public String inferBinaryName(Location location, JavaFileObject file) { tzezula@1324: return ((InferableJavaFileObject)file).infer(); tzezula@1324: } tzezula@1324: tzezula@1324: @Override tzezula@1324: public boolean isSameFile(FileObject a, FileObject b) { tzezula@1324: return a.toUri().equals(b.toUri()); tzezula@1324: } tzezula@1324: tzezula@1324: @Override tzezula@1324: public boolean handleOption(String current, Iterator remaining) { tzezula@1324: return false; tzezula@1324: } tzezula@1324: tzezula@1324: @Override tzezula@1324: public boolean hasLocation(Location location) { tzezula@1324: for (Location l : StandardLocation.values()) { tzezula@1324: if (l.equals(location)) { tzezula@1324: return true; tzezula@1324: } tzezula@1324: } tzezula@1324: return false; tzezula@1324: } tzezula@1324: tzezula@1324: @Override tzezula@1324: public JavaFileObject getJavaFileForInput(Location location, String className, JavaFileObject.Kind kind) throws IOException { tzezula@1324: if (canRead(location)) { tzezula@1324: return new ClassLoaderJavaFileObject(convertFQNToResource(className) + kind.extension); tzezula@1324: } else { tzezula@1324: throw new UnsupportedOperationException("Unsupported location for reading: " + location); //NOI18N tzezula@1324: } tzezula@1324: } tzezula@1324: tzezula@1324: @Override tzezula@1324: public JavaFileObject getJavaFileForOutput(Location location, String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException { tzezula@1324: if (canWrite(location)) { tzezula@1324: final String resource = convertFQNToResource(className) + kind.extension; tzezula@1324: final MemoryFileObject res = new MemoryFileObject(resource, null); tzezula@1324: register(location, resource, res); tzezula@1324: return res; tzezula@1324: } else { tzezula@1324: throw new UnsupportedOperationException("Unsupported location for reading: " + location); //NOI18N tzezula@1324: } tzezula@1324: } tzezula@1324: tzezula@1324: @Override tzezula@1324: public FileObject getFileForInput(Location location, String packageName, String relativeName) throws IOException { tzezula@1324: if (canRead(location)) { tzezula@1324: return new ClassLoaderJavaFileObject(convertFQNToResource(packageName) + '/' + relativeName); //NOI18N tzezula@1324: } else { tzezula@1324: throw new UnsupportedOperationException("Unsupported location for reading: " + location); //NOI18N tzezula@1324: } tzezula@1324: } tzezula@1324: tzezula@1324: @Override tzezula@1324: public FileObject getFileForOutput(Location location, String packageName, String relativeName, FileObject sibling) throws IOException { tzezula@1324: if (canWrite(location)) { tzezula@1324: final String resource = convertFQNToResource(packageName) + '/' + relativeName; //NOI18N tzezula@1324: final MemoryFileObject res = new MemoryFileObject(resource, null); tzezula@1324: register(location, resource, res); tzezula@1324: return res; tzezula@1324: } else { tzezula@1324: throw new UnsupportedOperationException("Unsupported location for reading: " + location); //NOI18N tzezula@1324: } tzezula@1324: } tzezula@1324: tzezula@1324: @Override tzezula@1324: public void flush() throws IOException { tzezula@1324: } tzezula@1324: tzezula@1324: @Override tzezula@1324: public void close() throws IOException { tzezula@1324: } tzezula@1324: tzezula@1324: @Override tzezula@1324: public int isSupportedOption(String option) { tzezula@1324: return -1; tzezula@1324: } tzezula@1324: tzezula@1324: // private List getResources(String folder) throws IOException { tzezula@1324: // final List result = new ArrayList(); tzezula@1324: // final BufferedReader in = new BufferedReader(new InputStreamReader( tzezula@1324: // this.getClass().getClassLoader().getResourceAsStream(String.format("%s/pkg-list", folder.substring(0))), //NOI18N tzezula@1324: // "UTF-8")); //NOI18N tzezula@1324: // try { tzezula@1324: // String line; tzezula@1324: // while ((line = in.readLine()) != null) { tzezula@1324: // result.add(line); tzezula@1324: // } tzezula@1324: // } finally { tzezula@1324: // in.close(); tzezula@1324: // } tzezula@1324: // return result; tzezula@1324: // } tzezula@1324: tzezula@1324: //MOCK IMPL tzezula@1324: private List getResources(String folder) throws IOException { tzezula@1324: if (classPathContent == null) { tzezula@1324: classPathContent = new HashMap<>(); tzezula@1324: // final String boot = System.getProperty("sun.boot.class.path"); //NOI18N tzezula@1324: final String cp = System.getProperty("java.class.path"); tzezula@1324: for (String entry : cp.split(File.pathSeparator)) { tzezula@1324: File f = new File (entry); tzezula@1324: if (f.canRead()) { tzezula@1324: if (f.isFile()) { tzezula@1324: ZipFile zf = new ZipFile(f); tzezula@1324: try { tzezula@1324: Enumeration entries = zf.entries(); tzezula@1324: while (entries.hasMoreElements()) { tzezula@1324: ZipEntry e = entries.nextElement(); tzezula@1324: if (e.isDirectory()) { tzezula@1324: continue; tzezula@1324: } tzezula@1324: final String name = String.format("/%s",e.getName()); tzezula@1324: final String owner = getOwner(name); tzezula@1324: List content = classPathContent.get(owner); tzezula@1324: if (content == null) { tzezula@1324: content = new ArrayList<>(); tzezula@1324: classPathContent.put(owner, content); tzezula@1324: } tzezula@1324: content.add(name); tzezula@1324: } tzezula@1324: } finally { tzezula@1324: zf.close(); tzezula@1324: } tzezula@1324: } else if (f.isDirectory()) { tzezula@1324: addFiles(f,"/", classPathContent); tzezula@1324: } tzezula@1324: } tzezula@1324: } tzezula@1324: } tzezula@1324: List content = classPathContent.get(folder); tzezula@1324: return content == null ? Collections.emptyList() : content; tzezula@1324: } tzezula@1324: tzezula@1324: private void addFiles(File folder, String path, Map> into) { tzezula@1324: for (File f : folder.listFiles()) { tzezula@1324: String fname = path + (path.length() == 1 ? "" : "/") + f.getName(); tzezula@1324: if (f.isDirectory()) { tzezula@1324: addFiles(f, fname, into); tzezula@1324: } else { tzezula@1324: List content = into.get(path); tzezula@1324: if (content == null) { tzezula@1324: content = new ArrayList<>(); tzezula@1324: classPathContent.put(path, content); tzezula@1324: } tzezula@1324: content.add(fname); tzezula@1324: } tzezula@1324: } tzezula@1324: } tzezula@1324: tzezula@1324: private Map> classPathContent; tzezula@1324: tzezula@1324: private void register(Location loc, String resource, MemoryFileObject jfo) { tzezula@1324: Map> folders = generated.get(loc); tzezula@1324: final String folder = getOwner(resource); tzezula@1324: List content = folders.get(folder); tzezula@1324: if (content == null) { tzezula@1324: content = new ArrayList<>(); tzezula@1324: folders.put(folder, content); tzezula@1324: } tzezula@1324: content.add(jfo); tzezula@1324: } tzezula@1324: tzezula@1324: private static String getOwner(String resource) { tzezula@1324: int lastSlash = resource.lastIndexOf('/'); tzezula@1324: assert lastSlash >= 0; tzezula@1324: return resource.substring(0, lastSlash); tzezula@1324: } tzezula@1324: tzezula@1324: private static boolean canRead(Location loc) { tzezula@1324: for (Location rl : READ_LOCATIONS) { tzezula@1324: if (rl.equals(loc)) { tzezula@1324: return true; tzezula@1324: } tzezula@1324: } tzezula@1324: return false; tzezula@1324: } tzezula@1324: tzezula@1324: private static boolean canWrite(Location loc) { tzezula@1324: for (Location wl : WRITE_LOCATIONS) { tzezula@1324: if (wl.equals(loc)) { tzezula@1324: return true; tzezula@1324: } tzezula@1324: } tzezula@1324: return false; tzezula@1324: } tzezula@1324: tzezula@1324: private static boolean canClassLoad(Location loc) { tzezula@1324: for (Location cll : CLASS_LOADER_LOCATIONS) { tzezula@1324: if (cll.equals(loc)) { tzezula@1324: return true; tzezula@1324: } tzezula@1324: } tzezula@1324: return false; tzezula@1324: } tzezula@1324: tzezula@1324: static String convertFQNToResource(String fqn) { tzezula@1324: return '/' + fqn.replace('.', '/'); //NOI18N tzezula@1324: } tzezula@1324: tzezula@1324: static String convertResourceToFQN(String resource) { tzezula@1324: assert resource.startsWith("/"); //NOI18N tzezula@1324: int lastSlash = resource.lastIndexOf('/'); //NOI18N tzezula@1324: int lastDot = resource.lastIndexOf('.'); //NOI18N tzezula@1324: int stop = lastSlash < lastDot ? tzezula@1324: lastDot : tzezula@1324: resource.length(); tzezula@1324: return resource.substring(1, stop).replace('/', '.'); //NOI18N tzezula@1324: } tzezula@1324: tzezula@1324: tzezula@1324: JavaFileObject createMemoryFileObject (String resourceName, JavaFileObject.Kind kind, byte[] content) { tzezula@1324: final InferableJavaFileObject jfo = new MemoryFileObject(resourceName, kind, content); tzezula@1324: return jfo; tzezula@1324: } tzezula@1324: tzezula@1324: Iterable getGeneratedFiles(JavaFileObject.Kind... kinds) { tzezula@1324: final Set ks = EnumSet.noneOf(JavaFileObject.Kind.class); tzezula@1324: Collections.addAll(ks, kinds); tzezula@1324: final List res = new ArrayList<>(); tzezula@1324: for (Map> folders : generated.values()) { tzezula@1324: for (List content : folders.values()) { tzezula@1324: for (MemoryFileObject fo : content) { tzezula@1324: if (ks.contains(fo.getKind()) && fo.getLastModified() >= 0) { tzezula@1324: res.add(fo); tzezula@1324: } tzezula@1324: } tzezula@1324: } tzezula@1324: } tzezula@1324: return res; tzezula@1324: } tzezula@1324: tzezula@1325: private static final class SafeClassLoader extends ClassLoader { tzezula@1325: private final ClassLoader delegate; tzezula@1325: tzezula@1325: SafeClassLoader(final ClassLoader delegate) { tzezula@1325: this.delegate = delegate; tzezula@1325: tzezula@1325: } tzezula@1325: tzezula@1325: @Override tzezula@1325: public URL getResource(String name) { tzezula@1325: return delegate.getResource(name); tzezula@1325: } tzezula@1325: tzezula@1325: @Override tzezula@1325: public InputStream getResourceAsStream(String name) { tzezula@1325: return delegate.getResourceAsStream(name); tzezula@1325: } tzezula@1325: tzezula@1325: @Override tzezula@1325: public Enumeration getResources(String name) throws IOException { tzezula@1325: return delegate.getResources(name); tzezula@1325: } tzezula@1325: tzezula@1325: @Override tzezula@1325: public Class loadClass(String name) throws ClassNotFoundException { tzezula@1325: return delegate.loadClass(name); tzezula@1325: } tzezula@1325: } tzezula@1325: tzezula@1324: }