# HG changeset patch # User Jaroslav Tulach # Date 1380787402 -7200 # Node ID 217ca48c5b80f87d165fcb49d68382f05dcdd9b9 # Parent bf0b56f2dca22e8bcfca9d3457f08b71e9e2b9ab# Parent 8ae6a6c42b5f2b0bf73d932f1101dd033505a412 Bringing in Tomas's no java.io.File file manager diff -r bf0b56f2dca2 -r 217ca48c5b80 dew/src/main/java/org/apidesign/bck2brwsr/dew/BaseFileObject.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dew/src/main/java/org/apidesign/bck2brwsr/dew/BaseFileObject.java Thu Oct 03 10:03:22 2013 +0200 @@ -0,0 +1,110 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program 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, version 2 of the License. + * + * This program 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 this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ + +package org.apidesign.bck2brwsr.dew; + +import java.net.URI; +import javax.lang.model.element.Modifier; +import javax.lang.model.element.NestingKind; + +/** + * + * @author Tomas Zezula + */ +public abstract class BaseFileObject implements InferableJavaFileObject { + + protected final String path; + protected final Kind kind; + + BaseFileObject( + String path, + Kind kind) { + if (!path.startsWith("/")) { //NOI18N + throw new IllegalArgumentException(); + } + this.path = path; + this.kind = kind; + } + + + @Override + public String infer() { + return ClassLoaderFileManager.convertResourceToFQN(path); + } + + @Override + public Kind getKind() { + return kind; + } + + @Override + public boolean isNameCompatible(String simpleName, Kind kind) { + return this.kind == kind && + getSimpleName(path).equals(simpleName); + } + + @Override + public NestingKind getNestingKind() { + return null; + } + + @Override + public Modifier getAccessLevel() { + return null; + } + + @Override + public URI toUri() { + return URI.create(escape(path)); + } + + @Override + public String getName() { + return path; + } + + + + protected static String getSimpleName(String path) { + int slashIndex = path.lastIndexOf('/'); + assert slashIndex >= 0; + return (slashIndex + 1 < path.length()) ? + path.substring(slashIndex + 1) : + ""; //NOI18N + } + + protected static Kind getKind(final String path) { + final String simpleName = getSimpleName(path); + final int dotIndex = simpleName.lastIndexOf('.'); //NOI18N + final String ext = dotIndex > 0 ? + simpleName.substring(dotIndex) : + ""; + for (Kind k : Kind.values()) { + if (k.extension.equals(ext)) { + return k; + } + } + return Kind.OTHER; + } + + private String escape(String path) { + return path; + } + + +} diff -r bf0b56f2dca2 -r 217ca48c5b80 dew/src/main/java/org/apidesign/bck2brwsr/dew/ClassLoaderFileManager.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dew/src/main/java/org/apidesign/bck2brwsr/dew/ClassLoaderFileManager.java Thu Oct 03 10:03:22 2013 +0200 @@ -0,0 +1,372 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program 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, version 2 of the License. + * + * This program 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 this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ + +package org.apidesign.bck2brwsr.dew; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collections; +import java.util.EnumSet; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; +import javax.tools.FileObject; +import javax.tools.JavaFileManager; +import javax.tools.JavaFileObject; +import javax.tools.StandardLocation; + +/** + * + * @author Tomas Zezula + */ +public class ClassLoaderFileManager implements JavaFileManager { + + private static final Location[] READ_LOCATIONS = { + StandardLocation.PLATFORM_CLASS_PATH, + StandardLocation.CLASS_PATH, + StandardLocation.SOURCE_PATH + }; + + private static final Location[] WRITE_LOCATIONS = { + StandardLocation.CLASS_OUTPUT, + StandardLocation.SOURCE_OUTPUT + }; + + private static final Location[] CLASS_LOADER_LOCATIONS = { + StandardLocation.ANNOTATION_PROCESSOR_PATH + }; + + private Map>> generated; + + + ClassLoaderFileManager() { + generated = new HashMap<>(); + for (Location l : WRITE_LOCATIONS) { + generated.put(l, new HashMap>()); + } + } + + + @Override + public ClassLoader getClassLoader(Location location) { + if (canClassLoad(location)) { + return new SafeClassLoader(getClass().getClassLoader()); + } else { + return null; + } + } + + @Override + public Iterable list(Location location, String packageName, Set kinds, boolean recurse) throws IOException { + if (canRead(location)) { + final List res = new ArrayList(); + for (String resource : getResources(convertFQNToResource(packageName))) { + final JavaFileObject jfo = new ClassLoaderJavaFileObject(resource); + if (kinds.contains(jfo.getKind())) { + res.add(jfo); + } + } + return res; + } else if (canWrite(location)) { + Map> folders = generated.get(location); + List files = folders.get(convertFQNToResource(packageName)); + if (files != null) { + final List res = new ArrayList(); + for (JavaFileObject file : files) { + if (kinds.contains(file.getKind()) && file.getLastModified() >= 0) { + res.add(file); + } + } + return res; + } + } + return Collections.emptyList(); + } + + @Override + public String inferBinaryName(Location location, JavaFileObject file) { + return ((InferableJavaFileObject)file).infer(); + } + + @Override + public boolean isSameFile(FileObject a, FileObject b) { + return a.toUri().equals(b.toUri()); + } + + @Override + public boolean handleOption(String current, Iterator remaining) { + return false; + } + + @Override + public boolean hasLocation(Location location) { + for (Location l : StandardLocation.values()) { + if (l.equals(location)) { + return true; + } + } + return false; + } + + @Override + public JavaFileObject getJavaFileForInput(Location location, String className, JavaFileObject.Kind kind) throws IOException { + if (canRead(location)) { + return new ClassLoaderJavaFileObject(convertFQNToResource(className) + kind.extension); + } else { + throw new UnsupportedOperationException("Unsupported location for reading: " + location); //NOI18N + } + } + + @Override + public JavaFileObject getJavaFileForOutput(Location location, String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException { + if (canWrite(location)) { + final String resource = convertFQNToResource(className) + kind.extension; + final MemoryFileObject res = new MemoryFileObject(resource, null); + register(location, resource, res); + return res; + } else { + throw new UnsupportedOperationException("Unsupported location for reading: " + location); //NOI18N + } + } + + @Override + public FileObject getFileForInput(Location location, String packageName, String relativeName) throws IOException { + if (canRead(location)) { + return new ClassLoaderJavaFileObject(convertFQNToResource(packageName) + '/' + relativeName); //NOI18N + } else { + throw new UnsupportedOperationException("Unsupported location for reading: " + location); //NOI18N + } + } + + @Override + public FileObject getFileForOutput(Location location, String packageName, String relativeName, FileObject sibling) throws IOException { + if (canWrite(location)) { + final String resource = convertFQNToResource(packageName) + '/' + relativeName; //NOI18N + final MemoryFileObject res = new MemoryFileObject(resource, null); + register(location, resource, res); + return res; + } else { + throw new UnsupportedOperationException("Unsupported location for reading: " + location); //NOI18N + } + } + + @Override + public void flush() throws IOException { + } + + @Override + public void close() throws IOException { + } + + @Override + public int isSupportedOption(String option) { + return -1; + } + +// private List getResources(String folder) throws IOException { +// final List result = new ArrayList(); +// final BufferedReader in = new BufferedReader(new InputStreamReader( +// this.getClass().getClassLoader().getResourceAsStream(String.format("%s/pkg-list", folder.substring(0))), //NOI18N +// "UTF-8")); //NOI18N +// try { +// String line; +// while ((line = in.readLine()) != null) { +// result.add(line); +// } +// } finally { +// in.close(); +// } +// return result; +// } + + //MOCK IMPL + private List getResources(String folder) throws IOException { + if (classPathContent == null) { + classPathContent = new HashMap<>(); +// final String boot = System.getProperty("sun.boot.class.path"); //NOI18N + final String cp = System.getProperty("java.class.path"); + for (String entry : cp.split(File.pathSeparator)) { + File f = new File (entry); + if (f.canRead()) { + if (f.isFile()) { + ZipFile zf = new ZipFile(f); + try { + Enumeration entries = zf.entries(); + while (entries.hasMoreElements()) { + ZipEntry e = entries.nextElement(); + if (e.isDirectory()) { + continue; + } + final String name = String.format("/%s",e.getName()); + final String owner = getOwner(name); + List content = classPathContent.get(owner); + if (content == null) { + content = new ArrayList<>(); + classPathContent.put(owner, content); + } + content.add(name); + } + } finally { + zf.close(); + } + } else if (f.isDirectory()) { + addFiles(f,"/", classPathContent); + } + } + } + } + List content = classPathContent.get(folder); + return content == null ? Collections.emptyList() : content; + } + + private void addFiles(File folder, String path, Map> into) { + for (File f : folder.listFiles()) { + String fname = path + (path.length() == 1 ? "" : "/") + f.getName(); + if (f.isDirectory()) { + addFiles(f, fname, into); + } else { + List content = into.get(path); + if (content == null) { + content = new ArrayList<>(); + classPathContent.put(path, content); + } + content.add(fname); + } + } + } + + private Map> classPathContent; + + private void register(Location loc, String resource, MemoryFileObject jfo) { + Map> folders = generated.get(loc); + final String folder = getOwner(resource); + List content = folders.get(folder); + if (content == null) { + content = new ArrayList<>(); + folders.put(folder, content); + } + content.add(jfo); + } + + private static String getOwner(String resource) { + int lastSlash = resource.lastIndexOf('/'); + assert lastSlash >= 0; + return resource.substring(0, lastSlash); + } + + private static boolean canRead(Location loc) { + for (Location rl : READ_LOCATIONS) { + if (rl.equals(loc)) { + return true; + } + } + return false; + } + + private static boolean canWrite(Location loc) { + for (Location wl : WRITE_LOCATIONS) { + if (wl.equals(loc)) { + return true; + } + } + return false; + } + + private static boolean canClassLoad(Location loc) { + for (Location cll : CLASS_LOADER_LOCATIONS) { + if (cll.equals(loc)) { + return true; + } + } + return false; + } + + static String convertFQNToResource(String fqn) { + return '/' + fqn.replace('.', '/'); //NOI18N + } + + static String convertResourceToFQN(String resource) { + assert resource.startsWith("/"); //NOI18N + int lastSlash = resource.lastIndexOf('/'); //NOI18N + int lastDot = resource.lastIndexOf('.'); //NOI18N + int stop = lastSlash < lastDot ? + lastDot : + resource.length(); + return resource.substring(1, stop).replace('/', '.'); //NOI18N + } + + + JavaFileObject createMemoryFileObject (String resourceName, JavaFileObject.Kind kind, byte[] content) { + final InferableJavaFileObject jfo = new MemoryFileObject(resourceName, kind, content); + return jfo; + } + + Iterable getGeneratedFiles(JavaFileObject.Kind... kinds) { + final Set ks = EnumSet.noneOf(JavaFileObject.Kind.class); + Collections.addAll(ks, kinds); + final List res = new ArrayList<>(); + for (Map> folders : generated.values()) { + for (List content : folders.values()) { + for (MemoryFileObject fo : content) { + if (ks.contains(fo.getKind()) && fo.getLastModified() >= 0) { + res.add(fo); + } + } + } + } + return res; + } + + private static final class SafeClassLoader extends ClassLoader { + private final ClassLoader delegate; + + SafeClassLoader(final ClassLoader delegate) { + this.delegate = delegate; + + } + + @Override + public URL getResource(String name) { + return delegate.getResource(name); + } + + @Override + public InputStream getResourceAsStream(String name) { + return delegate.getResourceAsStream(name); + } + + @Override + public Enumeration getResources(String name) throws IOException { + return delegate.getResources(name); + } + + @Override + public Class loadClass(String name) throws ClassNotFoundException { + return delegate.loadClass(name); + } + } + +} diff -r bf0b56f2dca2 -r 217ca48c5b80 dew/src/main/java/org/apidesign/bck2brwsr/dew/ClassLoaderJavaFileObject.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dew/src/main/java/org/apidesign/bck2brwsr/dew/ClassLoaderJavaFileObject.java Thu Oct 03 10:03:22 2013 +0200 @@ -0,0 +1,91 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program 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, version 2 of the License. + * + * This program 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 this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ + +package org.apidesign.bck2brwsr.dew; + +import java.io.BufferedReader; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Reader; +import java.io.Writer; + +/** + * + * @author Tomas Zezula + */ +class ClassLoaderJavaFileObject extends BaseFileObject { + + ClassLoaderJavaFileObject(final String path) { + super(path, getKind(path)); + } + + @Override + public InputStream openInputStream() throws IOException { + final InputStream in = getClass().getClassLoader().getResourceAsStream(path.substring(1)); + if (in == null) { + throw new FileNotFoundException(path); + } + return in; + } + + @Override + public OutputStream openOutputStream() throws IOException { + throw new UnsupportedOperationException("Read Only FileObject"); //NOI18N + } + + @Override + public Reader openReader(boolean ignoreEncodingErrors) throws IOException { + return new InputStreamReader(openInputStream()); + } + + @Override + public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { + final BufferedReader in = new BufferedReader(openReader(ignoreEncodingErrors)); + try { + final StringBuilder sb = new StringBuilder(); + String line; + while ((line = in.readLine()) != null) { + sb.append(line); + sb.append('\n'); //NOI18N + } + return sb.toString(); + } finally { + in.close(); + } + } + + @Override + public Writer openWriter() throws IOException { + return new OutputStreamWriter(openOutputStream()); + } + + @Override + public long getLastModified() { + return System.currentTimeMillis(); + } + + @Override + public boolean delete() { + return false; + } + +} diff -r bf0b56f2dca2 -r 217ca48c5b80 dew/src/main/java/org/apidesign/bck2brwsr/dew/Compile.java --- a/dew/src/main/java/org/apidesign/bck2brwsr/dew/Compile.java Sat Sep 28 01:32:59 2013 +0200 +++ b/dew/src/main/java/org/apidesign/bck2brwsr/dew/Compile.java Thu Oct 03 10:03:22 2013 +0200 @@ -83,90 +83,33 @@ } private Map compile(final String html, final String code) throws IOException { - StandardJavaFileManager sjfm = ToolProvider.getSystemJavaCompiler().getStandardFileManager(this, null, null); + final ClassLoaderFileManager clfm = new ClassLoaderFileManager(); + final JavaFileObject file = clfm.createMemoryFileObject( + ClassLoaderFileManager.convertFQNToResource(pkg.isEmpty() ? cls : pkg + "." + cls) + Kind.SOURCE.extension, + Kind.SOURCE, + code.getBytes()); + final JavaFileObject htmlFile = clfm.createMemoryFileObject( + ClassLoaderFileManager.convertFQNToResource(pkg), + Kind.OTHER, + html.getBytes()); - final Map class2BAOS = new HashMap<>(); - - JavaFileObject file = new SimpleJavaFileObject(URI.create("mem://mem"), Kind.SOURCE) { - @Override - public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { - return code; - } - }; - final JavaFileObject htmlFile = new SimpleJavaFileObject(URI.create("mem://mem2"), Kind.OTHER) { - @Override - public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { - return html; - } - - @Override - public InputStream openInputStream() throws IOException { - return new ByteArrayInputStream(html.getBytes()); - } - }; - - final URI scratch; - try { - scratch = new URI("mem://mem3"); - } catch (URISyntaxException ex) { - throw new IOException(ex); - } - - JavaFileManager jfm = new ForwardingJavaFileManager(sjfm) { - @Override - public JavaFileObject getJavaFileForOutput(Location location, String className, Kind kind, FileObject sibling) throws IOException { - if (kind == Kind.CLASS) { - final ByteArrayOutputStream buffer = new ByteArrayOutputStream(); - - class2BAOS.put(className.replace('.', '/') + ".class", buffer); - return new SimpleJavaFileObject(sibling.toUri(), kind) { - @Override - public OutputStream openOutputStream() throws IOException { - return buffer; - } - }; - } - - if (kind == Kind.SOURCE) { - return new SimpleJavaFileObject(scratch/*sibling.toUri()*/, kind) { - private final ByteArrayOutputStream data = new ByteArrayOutputStream(); - @Override - public OutputStream openOutputStream() throws IOException { - return data; - } - - @Override - public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { - data.close(); - return new String(data.toByteArray()); - } - }; - } - - throw new IllegalStateException(); - } - + JavaFileManager jfm = new ForwardingJavaFileManager(clfm) { @Override public FileObject getFileForInput(Location location, String packageName, String relativeName) throws IOException { if (location == StandardLocation.SOURCE_PATH) { if (packageName.equals(pkg)) { return htmlFile; } - } - + } return null; } - }; - ToolProvider.getSystemJavaCompiler().getTask(null, jfm, this, /*XXX:*/Arrays.asList("-source", "1.7", "-target", "1.7"), null, Arrays.asList(file)).call(); - + final Boolean res = ToolProvider.getSystemJavaCompiler().getTask(null, jfm, this, /*XXX:*/Arrays.asList("-source", "1.7", "-target", "1.7"), null, Arrays.asList(file)).call(); Map result = new HashMap<>(); - - for (Map.Entry e : class2BAOS.entrySet()) { - result.put(e.getKey(), e.getValue().toByteArray()); + for (MemoryFileObject generated : clfm.getGeneratedFiles(Kind.CLASS)) { + result.put(generated.getName().substring(1), generated.getContent()); } - return result; } diff -r bf0b56f2dca2 -r 217ca48c5b80 dew/src/main/java/org/apidesign/bck2brwsr/dew/InferableJavaFileObject.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dew/src/main/java/org/apidesign/bck2brwsr/dew/InferableJavaFileObject.java Thu Oct 03 10:03:22 2013 +0200 @@ -0,0 +1,29 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program 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, version 2 of the License. + * + * This program 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 this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ + +package org.apidesign.bck2brwsr.dew; + +import javax.tools.JavaFileObject; + +/** + * + * @author Tomas Zezula + */ +interface InferableJavaFileObject extends JavaFileObject { + String infer(); +} diff -r bf0b56f2dca2 -r 217ca48c5b80 dew/src/main/java/org/apidesign/bck2brwsr/dew/MemoryFileObject.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dew/src/main/java/org/apidesign/bck2brwsr/dew/MemoryFileObject.java Thu Oct 03 10:03:22 2013 +0200 @@ -0,0 +1,137 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program 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, version 2 of the License. + * + * This program 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 this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ + +package org.apidesign.bck2brwsr.dew; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Reader; +import java.io.Writer; + +/** + * + * @author Tomas Zeuzla + */ +class MemoryFileObject extends BaseFileObject { + + private byte[] content; + private long lastModified; + + MemoryFileObject ( + String resourceName, + Kind kind, + byte[] content) { + super(resourceName, kind); + this.content = content; + this.lastModified = this.content == null ? + -1 : + System.currentTimeMillis(); + } + + MemoryFileObject ( + String resourceName, + byte[] content) { + this(resourceName, getKind(resourceName) ,content); + } + + + @Override + public InputStream openInputStream() throws IOException { + if (content == null) { + throw new IOException(); + } else { + return new ByteArrayInputStream(content); + } + } + + @Override + public OutputStream openOutputStream() throws IOException { + return new CloseStream(); + } + + @Override + public Reader openReader(boolean ignoreEncodingErrors) throws IOException { + return new InputStreamReader(openInputStream()); + } + + @Override + public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { + if (content == null) { + throw new IOException(); + } else { + return new String(content); + } + } + + @Override + public Writer openWriter() throws IOException { + return new OutputStreamWriter(openOutputStream()); + } + + @Override + public long getLastModified() { + return lastModified; + } + + @Override + public boolean delete() { + return false; + } + + byte[] getContent() { + return content; + } + + private class CloseStream extends OutputStream { + + private final ByteArrayOutputStream delegate; + + CloseStream() { + delegate = new ByteArrayOutputStream(); + } + + @Override + public void write(int b) throws IOException { + delegate.write(b); + } + + @Override + public void write(byte[] b) throws IOException { + delegate.write(b); + } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + delegate.write(b, off, len); + } + + @Override + public void close() throws IOException { + delegate.close(); + content = delegate.toByteArray(); + lastModified = System.currentTimeMillis(); + } + + } + +}