jaroslav@468: /** jaroslav@468: * Back 2 Browser Bytecode Translator jaroslav@468: * Copyright (C) 2012 Jaroslav Tulach jaroslav@468: * jaroslav@468: * This program is free software: you can redistribute it and/or modify jaroslav@468: * it under the terms of the GNU General Public License as published by jaroslav@468: * the Free Software Foundation, version 2 of the License. jaroslav@468: * jaroslav@468: * This program is distributed in the hope that it will be useful, jaroslav@468: * but WITHOUT ANY WARRANTY; without even the implied warranty of jaroslav@468: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the jaroslav@468: * GNU General Public License for more details. jaroslav@468: * jaroslav@468: * You should have received a copy of the GNU General Public License jaroslav@468: * along with this program. Look for COPYING file in the top folder. jaroslav@468: * If not, see http://opensource.org/licenses/GPL-2.0. jaroslav@468: */ jaroslav@769: package org.apidesign.bck2brwsr.htmlpage; jaroslav@462: jaroslav@463: import java.io.ByteArrayInputStream; jaroslav@463: import java.io.ByteArrayOutputStream; jaroslav@462: import java.io.IOException; jaroslav@463: import java.io.InputStream; jaroslav@463: import java.io.OutputStream; jaroslav@463: import java.net.URI; jaroslav@463: import java.net.URISyntaxException; jaroslav@462: import java.util.ArrayList; jaroslav@463: import java.util.Arrays; jaroslav@463: import java.util.HashMap; jaroslav@462: import java.util.List; jaroslav@462: import java.util.Map; jaroslav@462: import java.util.regex.Matcher; jaroslav@462: import java.util.regex.Pattern; jaroslav@462: import javax.tools.Diagnostic; jaroslav@462: import javax.tools.DiagnosticListener; jaroslav@462: import javax.tools.FileObject; jaroslav@463: import javax.tools.ForwardingJavaFileManager; jaroslav@462: import javax.tools.JavaFileManager; jaroslav@462: import javax.tools.JavaFileObject; jaroslav@462: import javax.tools.JavaFileObject.Kind; jaroslav@463: import javax.tools.SimpleJavaFileObject; jaroslav@462: import javax.tools.StandardJavaFileManager; jaroslav@462: import javax.tools.StandardLocation; jaroslav@463: import javax.tools.ToolProvider; jaroslav@462: jaroslav@462: /** jaroslav@462: * jaroslav@462: * @author Jaroslav Tulach jaroslav@462: */ jaroslav@464: final class Compile implements DiagnosticListener { jaroslav@464: private final List> errors = new ArrayList<>(); jaroslav@464: private final Map classes; jaroslav@462: private final String pkg; jaroslav@464: private final String cls; jaroslav@579: private final String html; jaroslav@462: jaroslav@464: private Compile(String html, String code) throws IOException { jaroslav@464: this.pkg = findPkg(code); jaroslav@464: this.cls = findCls(code); jaroslav@579: this.html = html; jaroslav@464: classes = compile(html, code); jaroslav@464: } jaroslav@464: jaroslav@464: /** Performs compilation of given HTML page and associated Java code jaroslav@464: */ jaroslav@464: public static Compile create(String html, String code) throws IOException { jaroslav@464: return new Compile(html, code); jaroslav@462: } jaroslav@462: jaroslav@464: /** Checks for given class among compiled resources */ jaroslav@464: public byte[] get(String res) { jaroslav@464: return classes.get(res); jaroslav@464: } jaroslav@464: jaroslav@464: /** Obtains errors created during compilation. jaroslav@464: */ jaroslav@464: public List> getErrors() { jaroslav@464: List> err = new ArrayList<>(); jaroslav@464: for (Diagnostic diagnostic : errors) { jaroslav@464: if (diagnostic.getKind() == Diagnostic.Kind.ERROR) { jaroslav@464: err.add(diagnostic); jaroslav@464: } jaroslav@462: } jaroslav@464: return err; jaroslav@462: } jaroslav@464: jaroslav@464: private Map compile(final String html, final String code) throws IOException { jaroslav@464: StandardJavaFileManager sjfm = ToolProvider.getSystemJavaCompiler().getStandardFileManager(this, null, null); jaroslav@463: jaroslav@464: final Map class2BAOS = new HashMap<>(); jaroslav@463: jaroslav@463: JavaFileObject file = new SimpleJavaFileObject(URI.create("mem://mem"), Kind.SOURCE) { jaroslav@463: @Override jaroslav@463: public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { jaroslav@463: return code; jaroslav@463: } jaroslav@463: }; jaroslav@463: final JavaFileObject htmlFile = new SimpleJavaFileObject(URI.create("mem://mem2"), Kind.OTHER) { jaroslav@463: @Override jaroslav@463: public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { jaroslav@463: return html; jaroslav@463: } jaroslav@463: jaroslav@463: @Override jaroslav@463: public InputStream openInputStream() throws IOException { jaroslav@463: return new ByteArrayInputStream(html.getBytes()); jaroslav@463: } jaroslav@463: }; jaroslav@463: jaroslav@463: final URI scratch; jaroslav@463: try { jaroslav@463: scratch = new URI("mem://mem3"); jaroslav@463: } catch (URISyntaxException ex) { jaroslav@463: throw new IOException(ex); jaroslav@463: } jaroslav@463: jaroslav@463: JavaFileManager jfm = new ForwardingJavaFileManager(sjfm) { jaroslav@463: @Override jaroslav@463: public JavaFileObject getJavaFileForOutput(Location location, String className, Kind kind, FileObject sibling) throws IOException { jaroslav@463: if (kind == Kind.CLASS) { jaroslav@463: final ByteArrayOutputStream buffer = new ByteArrayOutputStream(); jaroslav@463: jaroslav@463: class2BAOS.put(className.replace('.', '/') + ".class", buffer); jaroslav@463: return new SimpleJavaFileObject(sibling.toUri(), kind) { jaroslav@463: @Override jaroslav@463: public OutputStream openOutputStream() throws IOException { jaroslav@463: return buffer; jaroslav@463: } jaroslav@463: }; jaroslav@463: } jaroslav@463: jaroslav@463: if (kind == Kind.SOURCE) { jaroslav@463: return new SimpleJavaFileObject(scratch/*sibling.toUri()*/, kind) { jaroslav@463: private final ByteArrayOutputStream data = new ByteArrayOutputStream(); jaroslav@463: @Override jaroslav@463: public OutputStream openOutputStream() throws IOException { jaroslav@463: return data; jaroslav@463: } jaroslav@463: jaroslav@463: @Override jaroslav@463: public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { jaroslav@463: data.close(); jaroslav@463: return new String(data.toByteArray()); jaroslav@463: } jaroslav@463: }; jaroslav@463: } jaroslav@463: jaroslav@463: throw new IllegalStateException(); jaroslav@463: } jaroslav@463: jaroslav@463: @Override jaroslav@463: public FileObject getFileForInput(Location location, String packageName, String relativeName) throws IOException { jaroslav@463: if (location == StandardLocation.SOURCE_PATH) { jaroslav@463: if (packageName.equals(pkg)) { jaroslav@463: return htmlFile; jaroslav@463: } jaroslav@463: } jaroslav@463: jaroslav@463: return null; jaroslav@463: } jhorvath@913: jhorvath@913: @Override jhorvath@913: public boolean isSameFile(FileObject a, FileObject b) { jhorvath@913: if (a == null || b == null) { jhorvath@913: throw new NullPointerException(); jhorvath@913: } jhorvath@913: if (!(a instanceof SimpleJavaFileObject)) { jhorvath@913: throw new IllegalArgumentException("Not supported: " + a); jhorvath@913: } jhorvath@913: if (!(b instanceof SimpleJavaFileObject)) { jhorvath@913: throw new IllegalArgumentException("Not supported: " + b); jhorvath@913: } jhorvath@913: return a.equals(b); jhorvath@913: } jaroslav@463: }; jaroslav@463: jaroslav@464: ToolProvider.getSystemJavaCompiler().getTask(null, jfm, this, /*XXX:*/Arrays.asList("-source", "1.7", "-target", "1.7"), null, Arrays.asList(file)).call(); jaroslav@463: jaroslav@464: Map result = new HashMap<>(); jaroslav@463: jaroslav@463: for (Map.Entry e : class2BAOS.entrySet()) { jaroslav@463: result.put(e.getKey(), e.getValue().toByteArray()); jaroslav@463: } jaroslav@463: jaroslav@463: return result; jaroslav@463: } jaroslav@462: jaroslav@462: jaroslav@462: @Override jaroslav@462: public void report(Diagnostic diagnostic) { jaroslav@462: errors.add(diagnostic); jaroslav@462: } jaroslav@462: private static String findPkg(String java) throws IOException { jaroslav@462: Pattern p = Pattern.compile("package\\p{javaWhitespace}*([\\p{Alnum}\\.]+)\\p{javaWhitespace}*;", Pattern.MULTILINE); jaroslav@462: Matcher m = p.matcher(java); jaroslav@462: if (!m.find()) { jaroslav@462: throw new IOException("Can't find package declaration in the java file"); jaroslav@462: } jaroslav@462: String pkg = m.group(1); jaroslav@462: return pkg; jaroslav@462: } jaroslav@462: private static String findCls(String java) throws IOException { jaroslav@462: Pattern p = Pattern.compile("class\\p{javaWhitespace}*([\\p{Alnum}\\.]+)\\p{javaWhitespace}", Pattern.MULTILINE); jaroslav@462: Matcher m = p.matcher(java); jaroslav@462: if (!m.find()) { jaroslav@462: throw new IOException("Can't find package declaration in the java file"); jaroslav@462: } jaroslav@462: String cls = m.group(1); jaroslav@462: return cls; jaroslav@462: } jaroslav@579: jaroslav@579: String getHtml() { jaroslav@579: String fqn = "'" + pkg + '.' + cls + "'"; jaroslav@579: return html.replace("'${fqn}'", fqn); jaroslav@579: } jaroslav@462: }