From 9a4a1f069ed03eb2013f4ef750e469c314edbdf1 Mon Sep 17 00:00:00 2001
From: Robert Sutton <rlsutton1@optusnet.com.au>
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 @@
 				<artifactId>maven-compiler-plugin</artifactId>
 				<version>3.0</version>
 				<configuration>
-					<source>1.8</source>
-					<target>1.8</target>
+					<source>1.7</source>
+					<target>1.7</target>
 				</configuration>
 			</plugin>
 			<plugin>
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<JavaFileObject> diagnostics = null;
+
+    private Iterable<String> 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<JavaSourceFromString> 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<JavaSourceFromString> 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<String, byte[]> classesByteArraysMap(
-			MemoryJavaFileManager fileManager) {
-		Map<String, byte[]> 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<String, byte[]> classesByteArraysMap(MemoryJavaFileManager fileManager) {
+	Map<String, byte[]> 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<String> optionList) {
+	this.optionList = optionList;
+    }
+
+    public void setDiagnosticCollector(DiagnosticCollector<JavaFileObject> 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<StandardJavaFileManager> {
-	private static final Logger LOG = LoggerFactory
-			.getLogger(MemoryJavaFileManager.class);
-	private final Map<String, MemoryFileObject> classFilesMap;
-
-	protected MemoryJavaFileManager(final StandardJavaFileManager fileManager) {
-		super(fileManager);
-		classFilesMap = new HashMap<>();
-	}
-
-	public Map<String, MemoryFileObject> 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<StandardJavaFileManager> {
+    private static final Logger LOG = LoggerFactory.getLogger(MemoryJavaFileManager.class);
+    private final Map<String, MemoryFileObject> classFilesMap;
+
+    protected MemoryJavaFileManager(final StandardJavaFileManager fileManager) {
+	super(fileManager);
+	classFilesMap = new HashMap<>();
+    }
+
+    public Map<String, MemoryFileObject> 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;
+    }
 
 }
