From 9a4a1f069ed03eb2013f4ef750e469c314edbdf1 Mon Sep 17 00:00:00 2001 From: Robert Sutton Date: Tue, 28 Jun 2016 13:33:14 +1000 Subject: [PATCH] add ability to set and use a DiagnosticCollector add ability to set options, such as a class path changed source version to 1.7 --- pom.xml | 4 +- src/main/java/com/javax0/jscc/Compiler.java | 168 ++++++++++-------- .../javax0/jscc/MemoryJavaFileManager.java | 46 +++-- 3 files changed, 113 insertions(+), 105 deletions(-) diff --git a/pom.xml b/pom.xml index 7798fa5..199b306 100644 --- a/pom.xml +++ b/pom.xml @@ -39,8 +39,8 @@ maven-compiler-plugin 3.0 - 1.8 - 1.8 + 1.7 + 1.7 diff --git a/src/main/java/com/javax0/jscc/Compiler.java b/src/main/java/com/javax0/jscc/Compiler.java index d157f0c..51eb52c 100644 --- a/src/main/java/com/javax0/jscc/Compiler.java +++ b/src/main/java/com/javax0/jscc/Compiler.java @@ -7,7 +7,9 @@ import java.util.List; import java.util.Map; +import javax.tools.DiagnosticCollector; import javax.tools.JavaCompiler; +import javax.tools.JavaFileObject; import javax.tools.ToolProvider; /** @@ -26,89 +28,99 @@ */ public class Compiler { - private ClassLoader classLoader = Compiler.class.getClassLoader(); - - private String compilerErrorOutput = null; - - /** - * Setting the parent class loader for the {@see ByteClassLoader} used to - * load the compiled byte code. This is needed when there are different - * class loaders in the application and the code compiling the class uses - * different class loader than the code wanting to use the compiled class. - * If no class loader is specified via this setter then the actual class - * loader will be used, which may work for most of the cases. - * - * @param classLoader - */ - public void setClassLoader(ClassLoader classLoader) { - this.classLoader = classLoader; - } + private ClassLoader classLoader = Compiler.class.getClassLoader(); - private String calculateSimpleClassName(String canonicalClassName) { - return canonicalClassName - .substring(canonicalClassName.lastIndexOf('.') + 1); - } + private String compilerErrorOutput = null; + + DiagnosticCollector diagnostics = null; + + private Iterable optionList = null; + + /** + * Setting the parent class loader for the {@see ByteClassLoader} used to + * load the compiled byte code. This is needed when there are different + * class loaders in the application and the code compiling the class uses + * different class loader than the code wanting to use the compiled class. + * If no class loader is specified via this setter then the actual class + * loader will be used, which may work for most of the cases. + * + * @param classLoader + */ + public void setClassLoader(ClassLoader classLoader) { + this.classLoader = classLoader; + } + + private String calculateSimpleClassName(String canonicalClassName) { + return canonicalClassName.substring(canonicalClassName.lastIndexOf('.') + 1); + } + + /** + * Compile the Java code provided as a string and if the compilation was + * successful then load the class. + * + * @param sourceCode + * @param canonicalClassName + * the fully qualified name of the class + * @return the loaded class or null if the compilation was not successful + * @throws Exception + * exceptions loading the class are not caught by the method + */ + public Class compile(String sourceCode, String canonicalClassName) throws Exception { + JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); + List sources = new LinkedList<>(); + String className = calculateSimpleClassName(canonicalClassName); + sources.add(new JavaSourceFromString(className, sourceCode)); - /** - * Compile the Java code provided as a string and if the compilation was - * successful then load the class. - * - * @param sourceCode - * @param canonicalClassName - * the fully qualified name of the class - * @return the loaded class or null if the compilation was not successful - * @throws Exception - * exceptions loading the class are not caught by the method - */ - public Class compile(String sourceCode, String canonicalClassName) - throws Exception { - JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); - List sources = new LinkedList<>(); - String className = calculateSimpleClassName(canonicalClassName); - sources.add(new JavaSourceFromString(className, sourceCode)); - - StringWriter sw = new StringWriter(); - MemoryJavaFileManager fm = new MemoryJavaFileManager( - compiler.getStandardFileManager(null, null, null)); - JavaCompiler.CompilationTask task = compiler.getTask(sw, fm, null, - null, null, sources); - - Boolean compilationWasSuccessful = task.call(); - if (compilationWasSuccessful) { - ByteClassLoader byteClassLoader = new ByteClassLoader(new URL[0], - classLoader, classesByteArraysMap(fm)); - - Class klass = byteClassLoader.loadClass(canonicalClassName); - byteClassLoader.close(); - return klass; - } else { - compilerErrorOutput = sw.toString(); - return null; - } + StringWriter sw = null; + if (diagnostics == null) { + sw = new StringWriter(); } + MemoryJavaFileManager fm = new MemoryJavaFileManager(compiler.getStandardFileManager(diagnostics, null, null)); + JavaCompiler.CompilationTask task = compiler.getTask(sw, fm, diagnostics, optionList, null, sources); - /** - * Get the map of class name / class byte array from the file manager. - * - * @param fileManager - * @return - */ - private Map classesByteArraysMap( - MemoryJavaFileManager fileManager) { - Map map = new HashMap<>(); - for (String name : fileManager.getClassFileObjectsMap().keySet()) { - map.put(name, fileManager.getClassFileObjectsMap().get(name) - .getByteArray()); - } - return map; + Boolean compilationWasSuccessful = task.call(); + if (compilationWasSuccessful) { + ByteClassLoader byteClassLoader = new ByteClassLoader(new URL[0], classLoader, classesByteArraysMap(fm)); + + Class klass = byteClassLoader.loadClass(canonicalClassName); + byteClassLoader.close(); + return klass; + } + if (sw != null) { + compilerErrorOutput = sw.toString(); } + return null; + + } - /** - * - * @return null if the source was compiled fine or the textual output of the - * compiler when there was some error. - */ - public String getCompilerErrorOutput() { - return compilerErrorOutput; + /** + * Get the map of class name / class byte array from the file manager. + * + * @param fileManager + * @return + */ + private Map classesByteArraysMap(MemoryJavaFileManager fileManager) { + Map map = new HashMap<>(); + for (String name : fileManager.getClassFileObjectsMap().keySet()) { + map.put(name, fileManager.getClassFileObjectsMap().get(name).getByteArray()); } + return map; + } + + /** + * + * @return null if the source was compiled fine or the textual output of the + * compiler when there was some error. + */ + public String getCompilerErrorOutput() { + return compilerErrorOutput; + } + + public void setOptionList(Iterable optionList) { + this.optionList = optionList; + } + + public void setDiagnosticCollector(DiagnosticCollector diagnostics) { + this.diagnostics = diagnostics; + } } diff --git a/src/main/java/com/javax0/jscc/MemoryJavaFileManager.java b/src/main/java/com/javax0/jscc/MemoryJavaFileManager.java index ab3dc6e..3d91996 100644 --- a/src/main/java/com/javax0/jscc/MemoryJavaFileManager.java +++ b/src/main/java/com/javax0/jscc/MemoryJavaFileManager.java @@ -13,30 +13,26 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class MemoryJavaFileManager extends - ForwardingJavaFileManager { - private static final Logger LOG = LoggerFactory - .getLogger(MemoryJavaFileManager.class); - private final Map classFilesMap; - - protected MemoryJavaFileManager(final StandardJavaFileManager fileManager) { - super(fileManager); - classFilesMap = new HashMap<>(); - } - - public Map getClassFileObjectsMap() { - return classFilesMap; - } - - @Override - public JavaFileObject getJavaFileForOutput(final Location location, - final String className, final Kind kind, final FileObject sibling) - throws IOException { - LOG.debug("getJavaFileForOutput({},{},{},{}", location, className, - kind, sibling); - MemoryFileObject classFile = new MemoryFileObject(className); - classFilesMap.put(className, classFile); - return classFile; - } +public class MemoryJavaFileManager extends ForwardingJavaFileManager { + private static final Logger LOG = LoggerFactory.getLogger(MemoryJavaFileManager.class); + private final Map classFilesMap; + + protected MemoryJavaFileManager(final StandardJavaFileManager fileManager) { + super(fileManager); + classFilesMap = new HashMap<>(); + } + + public Map getClassFileObjectsMap() { + return classFilesMap; + } + + @Override + public JavaFileObject getJavaFileForOutput(final Location location, final String className, final Kind kind, + final FileObject sibling) throws IOException { + LOG.debug("getJavaFileForOutput({},{},{},{}", location, className, kind, sibling); + MemoryFileObject classFile = new MemoryFileObject(className); + classFilesMap.put(className, classFile); + return classFile; + } }