Skip to content

Commit a919fe3

Browse files
committed
refactored to fluent API and minor refactoring for muliple sources compilation and nested class support
1 parent 049f604 commit a919fe3

9 files changed

Lines changed: 178 additions & 117 deletions

File tree

.classpath

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<classpath>
3+
<classpathentry kind="src" output="target/classes" path="src/main/java">
4+
<attributes>
5+
<attribute name="optional" value="true"/>
6+
<attribute name="maven.pomderived" value="true"/>
7+
</attributes>
8+
</classpathentry>
9+
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
10+
<attributes>
11+
<attribute name="optional" value="true"/>
12+
<attribute name="maven.pomderived" value="true"/>
13+
</attributes>
14+
</classpathentry>
15+
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8">
16+
<attributes>
17+
<attribute name="maven.pomderived" value="true"/>
18+
</attributes>
19+
</classpathentry>
20+
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
21+
<attributes>
22+
<attribute name="maven.pomderived" value="true"/>
23+
</attributes>
24+
</classpathentry>
25+
<classpathentry kind="output" path="target/classes"/>
26+
</classpath>

.project

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<projectDescription>
3+
<name>InMemoryJavaCompiler</name>
4+
<comment></comment>
5+
<projects>
6+
</projects>
7+
<buildSpec>
8+
<buildCommand>
9+
<name>org.eclipse.jdt.core.javabuilder</name>
10+
<arguments>
11+
</arguments>
12+
</buildCommand>
13+
<buildCommand>
14+
<name>org.eclipse.m2e.core.maven2Builder</name>
15+
<arguments>
16+
</arguments>
17+
</buildCommand>
18+
</buildSpec>
19+
<natures>
20+
<nature>org.eclipse.jdt.core.javanature</nature>
21+
<nature>org.eclipse.m2e.core.maven2Nature</nature>
22+
</natures>
23+
</projectDescription>
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
eclipse.preferences.version=1
2+
encoding//src/main/java=UTF-8
3+
encoding//src/test/java=UTF-8
4+
encoding/<project>=UTF-8
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
eclipse.preferences.version=1
2+
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
3+
org.eclipse.jdt.core.compiler.compliance=1.8
4+
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
5+
org.eclipse.jdt.core.compiler.source=1.8
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
activeProfiles=
2+
eclipse.preferences.version=1
3+
resolveWorkspaceProjects=true
4+
version=1

pom.xml

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,16 @@
33

44
<groupId>org.mdkt.compiler</groupId>
55
<artifactId>InMemoryJavaCompiler</artifactId>
6-
<version>1.3-SNAPSHOT</version>
6+
<version>1.3.0-SNAPSHOT</version>
77
<packaging>jar</packaging>
88
<url>https://github.com/trung/InMemoryJavaCompiler</url>
99

1010
<name>InMemoryJavaCompiler</name>
1111
<description>Small utility to compile java code in memory</description>
1212

1313
<properties>
14+
<maven.compiler.source>1.8</maven.compiler.source>
15+
<maven.compiler.target>1.8</maven.compiler.target>
1416
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
1517
<maven-compiler-plugin.version>3.2</maven-compiler-plugin.version>
1618
<nexus-staging-maven-plugin.version>1.6.3</nexus-staging-maven-plugin.version>
@@ -73,10 +75,6 @@
7375
<groupId>org.apache.maven.plugins</groupId>
7476
<artifactId>maven-compiler-plugin</artifactId>
7577
<version>${maven-compiler-plugin.version}</version>
76-
<configuration>
77-
<source>1.7</source>
78-
<target>1.7</target>
79-
</configuration>
8078
</plugin>
8179
<plugin>
8280
<groupId>org.sonatype.plugins</groupId>

src/main/java/org/mdkt/compiler/DynamicClassLoader.java

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,28 +3,25 @@
33
import java.util.HashMap;
44
import java.util.Map;
55

6-
/**
7-
* Created by trung on 5/3/15.
8-
*/
96
public class DynamicClassLoader extends ClassLoader {
107

11-
private Map<String, CompiledCode> customCompiledCode = new HashMap<>();
8+
private Map<String, CompiledCode> customCompiledCode = new HashMap<>();
129

13-
public DynamicClassLoader(ClassLoader parent) {
14-
super(parent);
15-
}
10+
public DynamicClassLoader(ClassLoader parent) {
11+
super(parent);
12+
}
1613

17-
public void addCode(CompiledCode cc) {
18-
customCompiledCode.put(cc.getName(), cc);
19-
}
14+
public void addCode(CompiledCode cc) {
15+
customCompiledCode.put(cc.getName(), cc);
16+
}
2017

21-
@Override
22-
protected Class<?> findClass(String name) throws ClassNotFoundException {
23-
CompiledCode cc = customCompiledCode.get(name);
24-
if (cc == null) {
25-
return super.findClass(name);
26-
}
27-
byte[] byteCode = cc.getByteCode();
28-
return defineClass(name, byteCode, 0, byteCode.length);
29-
}
18+
@Override
19+
protected Class<?> findClass(String name) throws ClassNotFoundException {
20+
CompiledCode cc = customCompiledCode.get(name);
21+
if (cc == null) {
22+
return super.findClass(name);
23+
}
24+
byte[] byteCode = cc.getByteCode();
25+
return defineClass(name, byteCode, 0, byteCode.length);
26+
}
3027
}
Lines changed: 54 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,81 +1,92 @@
11
package org.mdkt.compiler;
22

3-
import java.io.IOException;
43
import java.util.Collection;
54
import java.util.HashMap;
65
import java.util.Iterator;
76
import java.util.Map;
87

9-
import javax.tools.Diagnostic;
10-
import javax.tools.DiagnosticListener;
118
import javax.tools.JavaCompiler;
12-
import javax.tools.JavaFileObject;
139
import javax.tools.ToolProvider;
1410

1511
/**
16-
* Created by trung on 5/3/15.
17-
* Edited by turpid-monkey on 9/25/15, added support for multiple, dependent compile units.
12+
* Complile Java sources in-memory
1813
*/
1914
public class InMemoryJavaCompiler {
20-
JavaCompiler javac;
21-
DynamicClassLoader classLoader;
15+
private JavaCompiler javac;
16+
private DynamicClassLoader classLoader;
2217

23-
Map<String, SourceCode> clazzCode = new HashMap<String, SourceCode>();
18+
private Map<String, SourceCode> sourceCodes = new HashMap<String, SourceCode>();
2419

25-
public InMemoryJavaCompiler(ClassLoader parent) {
26-
this(ToolProvider.getSystemJavaCompiler(), parent);
20+
public static InMemoryJavaCompiler newInstance() {
21+
return new InMemoryJavaCompiler();
2722
}
2823

29-
public InMemoryJavaCompiler(JavaCompiler javac, ClassLoader parent) {
30-
this.javac = javac;
31-
this.classLoader = new DynamicClassLoader(parent);
32-
}
33-
34-
public InMemoryJavaCompiler() {
35-
this(ToolProvider.getSystemJavaCompiler(), ClassLoader
36-
.getSystemClassLoader());
24+
private InMemoryJavaCompiler() {
25+
this.javac = ToolProvider.getSystemJavaCompiler();
26+
this.classLoader = new DynamicClassLoader(ClassLoader.getSystemClassLoader());
3727
}
3828

39-
public void addSource(String className, String sourceCodeInText)
40-
throws Exception {
41-
clazzCode.put(className, new SourceCode(className, sourceCodeInText));
29+
public InMemoryJavaCompiler useParentClassLoader(ClassLoader parent) {
30+
this.classLoader = new DynamicClassLoader(parent);
31+
return this;
4232
}
4333

34+
/**
35+
* Compile all sources
36+
*
37+
* @return
38+
* @throws Exception
39+
*/
4440
public Map<String, Class<?>> compileAll() throws Exception {
45-
Collection<SourceCode> compilationUnits = clazzCode.values();
41+
if (sourceCodes.size() == 0) {
42+
throw new Exception("No source code to compile");
43+
}
44+
Collection<SourceCode> compilationUnits = sourceCodes.values();
4645
CompiledCode[] code;
47-
46+
4847
code = new CompiledCode[compilationUnits.size()];
4948
Iterator<SourceCode> iter = compilationUnits.iterator();
50-
for (int i=0; i<code.length; i++)
51-
{
49+
for (int i = 0; i < code.length; i++) {
5250
code[i] = new CompiledCode(iter.next().getClassName());
5351
}
54-
55-
ExtendedStandardJavaFileManager fileManager = new ExtendedStandardJavaFileManager(
56-
javac.getStandardFileManager(null, null, null), classLoader);
57-
JavaCompiler.CompilationTask task = javac.getTask(null, fileManager,
58-
null, null, null, compilationUnits);
52+
53+
ExtendedStandardJavaFileManager fileManager = new ExtendedStandardJavaFileManager(javac.getStandardFileManager(null, null, null), classLoader);
54+
JavaCompiler.CompilationTask task = javac.getTask(null, fileManager, null, null, null, compilationUnits);
5955
boolean result = task.call();
60-
if (!result)
56+
if (!result) {
6157
throw new RuntimeException("Unknown error during compilation.");
58+
}
59+
6260
Map<String, Class<?>> classes = new HashMap<String, Class<?>>();
63-
for (String className : clazzCode.keySet()) {
61+
for (String className : sourceCodes.keySet()) {
6462
classes.put(className, classLoader.loadClass(className));
6563
}
6664
return classes;
6765
}
6866

69-
public static Class<?> compile(String className, String sourceCodeInText)
70-
throws Exception {
71-
InMemoryJavaCompiler comp = new InMemoryJavaCompiler();
72-
comp.addSource(className, sourceCodeInText);
73-
Map<String, Class<?>> clzzes = comp.compileAll();
74-
Class<?> result = clzzes.get(className);
75-
return result;
67+
/**
68+
* Compile single source
69+
*
70+
* @param className
71+
* @param sourceCode
72+
* @return
73+
* @throws Exception
74+
*/
75+
public Class<?> compile(String className, String sourceCode) throws Exception {
76+
return addSource(className, sourceCode).compileAll().get(className);
7677
}
77-
78-
public DynamicClassLoader getClassLoader() {
79-
return classLoader;
78+
79+
/**
80+
* Add source code to the compiler
81+
*
82+
* @param className
83+
* @param sourceCode
84+
* @return
85+
* @throws Exception
86+
* @see {@link #compileAll()}
87+
*/
88+
public InMemoryJavaCompiler addSource(String className, String sourceCode) throws Exception {
89+
sourceCodes.put(className, new SourceCode(className, sourceCode));
90+
return this;
8091
}
8192
}
Lines changed: 43 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,53 @@
11
package org.mdkt.compiler;
22

3-
import java.io.StringWriter;
43
import java.util.Map;
54

65
import org.junit.Assert;
76
import org.junit.Test;
87

9-
/**
10-
* Created by trung on 5/3/15.
11-
*/
128
public class InMemoryJavaCompilerTest {
139

14-
@Test
15-
public void compile_whenTypical() throws Exception {
16-
StringBuffer sourceCode = new StringBuffer();
17-
18-
sourceCode.append("package org.mdkt;\n");
19-
sourceCode.append("public class HelloClass {\n");
20-
sourceCode.append(" public String hello() { return \"hello\"; }");
21-
sourceCode.append("}");
22-
23-
Class<?> helloClass = InMemoryJavaCompiler.compile("org.mdkt.HelloClass", sourceCode.toString());
24-
Assert.assertNotNull(helloClass);
25-
Assert.assertEquals(1, helloClass.getDeclaredMethods().length);
26-
}
27-
28-
@Test
29-
public void compile_severalFiles() throws Exception {
30-
String cls1 = "public class A{ public B b() { return new B(); }}";
31-
String cls2 = "public class B{ public String toString() { return \"B!\"; }}";
32-
33-
InMemoryJavaCompiler compiler = new InMemoryJavaCompiler();
34-
compiler.addSource("A", cls1);
35-
compiler.addSource("B", cls2);
36-
Map<String,Class<?>> compiled = compiler.compileAll();
37-
;
38-
Assert.assertNotNull(compiled.get("A"));
39-
Assert.assertNotNull(compiled.get("B"));
40-
41-
Class<?> aClass = compiled.get("A");
42-
Object a = aClass.newInstance();
43-
Assert.assertEquals("B!", aClass.getMethod("b").invoke(a).toString());
44-
}
45-
46-
@Test
47-
public void compile_filesWithInnerClasses() throws Exception {
48-
StringBuffer sourceCode = new StringBuffer();
49-
50-
sourceCode.append("package org.mdkt;\n");
51-
sourceCode.append("public class HelloClass {\n");
52-
sourceCode.append(" private static class InnerHelloWorld { int inner; }\n");
53-
sourceCode.append(" public String hello() { return \"hello\"; }");
54-
sourceCode.append("}");
55-
56-
Class<?> helloClass = InMemoryJavaCompiler.compile("org.mdkt.HelloClass", sourceCode.toString());
57-
Assert.assertNotNull(helloClass);
58-
Assert.assertEquals(1, helloClass.getDeclaredMethods().length);
59-
}
10+
@Test
11+
public void compile_WhenTypical() throws Exception {
12+
StringBuffer sourceCode = new StringBuffer();
13+
14+
sourceCode.append("package org.mdkt;\n");
15+
sourceCode.append("public class HelloClass {\n");
16+
sourceCode.append(" public String hello() { return \"hello\"; }");
17+
sourceCode.append("}");
18+
19+
Class<?> helloClass = InMemoryJavaCompiler.newInstance().compile("org.mdkt.HelloClass", sourceCode.toString());
20+
Assert.assertNotNull(helloClass);
21+
Assert.assertEquals(1, helloClass.getDeclaredMethods().length);
22+
}
23+
24+
@Test
25+
public void compile_WhenMultipleSources() throws Exception {
26+
String cls1 = "public class A{ public B b() { return new B(); }}";
27+
String cls2 = "public class B{ public String toString() { return \"B!\"; }}";
28+
29+
Map<String, Class<?>> compiled = InMemoryJavaCompiler.newInstance().addSource("A", cls1).addSource("B", cls2).compileAll();
30+
31+
Assert.assertNotNull(compiled.get("A"));
32+
Assert.assertNotNull(compiled.get("B"));
33+
34+
Class<?> aClass = compiled.get("A");
35+
Object a = aClass.newInstance();
36+
Assert.assertEquals("B!", aClass.getMethod("b").invoke(a).toString());
37+
}
38+
39+
@Test
40+
public void compile_WhenSourceContainsInnerClasses() throws Exception {
41+
StringBuffer sourceCode = new StringBuffer();
42+
43+
sourceCode.append("package org.mdkt;\n");
44+
sourceCode.append("public class HelloClass {\n");
45+
sourceCode.append(" private static class InnerHelloWorld { int inner; }\n");
46+
sourceCode.append(" public String hello() { return \"hello\"; }");
47+
sourceCode.append("}");
48+
49+
Class<?> helloClass = InMemoryJavaCompiler.newInstance().compile("org.mdkt.HelloClass", sourceCode.toString());
50+
Assert.assertNotNull(helloClass);
51+
Assert.assertEquals(1, helloClass.getDeclaredMethods().length);
52+
}
6053
}

0 commit comments

Comments
 (0)