|
1 | 1 | package org.mdkt.compiler; |
2 | 2 |
|
| 3 | + |
| 4 | +import javax.tools.Diagnostic; |
| 5 | +import javax.tools.DiagnosticCollector; |
| 6 | +import javax.tools.JavaCompiler; |
| 7 | +import javax.tools.JavaFileObject; |
| 8 | +import javax.tools.ToolProvider; |
3 | 9 | import java.util.Arrays; |
4 | 10 | import java.util.Collection; |
5 | 11 | import java.util.HashMap; |
|
8 | 14 | import java.util.Locale; |
9 | 15 | import java.util.Map; |
10 | 16 |
|
11 | | -import javax.tools.Diagnostic; |
12 | | -import javax.tools.DiagnosticCollector; |
13 | | -import javax.tools.JavaCompiler; |
14 | | -import javax.tools.JavaFileObject; |
15 | | -import javax.tools.ToolProvider; |
16 | | - |
17 | 17 | /** |
18 | 18 | * Compile Java sources in-memory |
19 | 19 | */ |
20 | 20 | public class InMemoryJavaCompiler { |
21 | | - private JavaCompiler javac; |
| 21 | + private final JavaCompiler javac; |
22 | 22 | private DynamicClassLoader classLoader; |
23 | 23 | private Iterable<String> options; |
24 | 24 | boolean ignoreWarnings = false; |
@@ -68,135 +68,112 @@ public InMemoryJavaCompiler ignoreWarnings() { |
68 | 68 | return this; |
69 | 69 | } |
70 | 70 |
|
71 | | - /** |
72 | | - * Compile all sources |
73 | | - * |
74 | | - * @return Map containing instances of all compiled classes |
75 | | - * @throws Exception |
76 | | - */ |
77 | | - public Map<String, Class<?>> compileAll() throws Exception { |
78 | | - return compile().checkNoErrors().classMap(); |
79 | | - } |
80 | | - |
81 | | - public Class<?> loadCompiledBytes(String className, byte[] compiledClasses) throws ClassNotFoundException { |
82 | | - Map<String, byte[]> map = new HashMap<>(); |
83 | | - map.put(className, compiledClasses); |
84 | | - return loadCompiledBytes(map).get(className); |
85 | | - } |
86 | | - |
87 | | - public Map<String, Class<?>> loadCompiledBytes(Map<String, byte[]> compiledClasses) throws ClassNotFoundException { |
88 | | - ClassLoader classLoader = new ClassLoader() { |
89 | | - @Override |
90 | | - protected Class<?> findClass(String name) { |
91 | | - byte[] b = compiledClasses.get(name); |
92 | | - return defineClass(name, b, 0, b.length); |
93 | | - } |
94 | | - }; |
95 | | - Map<String, Class<?>> classes = new HashMap<>(); |
96 | | - for (String className : compiledClasses.keySet()) { |
97 | | - classes.put(className, classLoader.loadClass(className)); |
98 | | - } |
99 | | - return classes; |
100 | | - } |
101 | | - |
102 | | - /** |
103 | | - * Compile all sources added until now and return {@link CompilationResult}, |
104 | | - * providing access to the compiled classes and/or errors and warnings |
105 | | - */ |
106 | | - public CompilationResult compile() throws Exception { |
107 | | - if (sourceCodes.size() == 0) { |
108 | | - throw new CompilationException("No source code to compile"); |
109 | | - } |
110 | | - Collection<SourceCode> compilationUnits = sourceCodes.values(); |
111 | | - CompiledCode[] code = new CompiledCode[compilationUnits.size()]; |
112 | | - Iterator<SourceCode> iter = compilationUnits.iterator(); |
113 | | - for (int i = 0; i < code.length; i++) { |
114 | | - code[i] = new CompiledCode(iter.next().getClassName()); |
115 | | - } |
116 | | - DiagnosticCollector<JavaFileObject> collector = new DiagnosticCollector<>(); |
117 | | -// ExtendedStandardJavaFileManager fileManager = new ExtendedStandardJavaFileManager(javac.getStandardFileManager(null, null, null), classLoader); |
118 | | - ExtendedStandardJavaFileManager fileManager = new ExtendedStandardJavaFileManager(javac.getStandardFileManager(null, null, null), classLoader, code); |
119 | | - JavaCompiler.CompilationTask task = javac.getTask(null, fileManager, collector, options, null, |
120 | | - compilationUnits); |
121 | | - task.call(); |
122 | | - boolean hasWarnings; |
123 | | - boolean hasErrors; |
124 | | - |
125 | | - { |
126 | | - boolean hasWarningsTmp = false; |
127 | | - boolean hasErrorsTmp = false; |
128 | | - for (Diagnostic<? extends JavaFileObject> d : collector.getDiagnostics()) { |
129 | | - switch (d.getKind()) { |
130 | | - case NOTE: |
131 | | - case MANDATORY_WARNING: |
132 | | - case WARNING: |
133 | | - hasWarningsTmp = true; |
134 | | - break; |
135 | | - case OTHER: |
136 | | - case ERROR: |
137 | | - default: |
138 | | - hasErrorsTmp = true; |
139 | | - break; |
140 | | - } |
141 | | - } |
142 | | - hasWarnings = hasWarningsTmp; |
143 | | - hasErrors = hasErrorsTmp; |
144 | | - } |
145 | | - |
146 | | - return new CompilationResult() { |
147 | | - |
148 | | - @Override |
149 | | - public Map<String, Class<?>> classMap() throws ClassNotFoundException { |
150 | | - Map<String, Class<?>> classes = new HashMap<String, Class<?>>(); |
151 | | - for (String className : sourceCodes.keySet()) { |
152 | | - // 由loadClass变更为findClass,优先获取新编译的同名类(如果使用loadClass则获取不到新编译的同名class) |
153 | | - // 虽然支持多次加载同名类(指不同的ClassLoader,实际上同一个ClassLoader中Java仍然不允许重复会报错) |
154 | | - // 但是还是不推荐这样做,因为这样违反了Java的基本原则,且容易出现混淆影响自己问题的处理 |
| 71 | + /** |
| 72 | + * Compile all sources |
| 73 | + * |
| 74 | + * @return Map containing instances of all compiled classes |
| 75 | + * @throws Exception |
| 76 | + */ |
| 77 | + public Map<String, Class<?>> compileAll() throws Exception { |
| 78 | + return compile().checkNoErrors().classMap(); |
| 79 | + } |
| 80 | + |
| 81 | + /** |
| 82 | + * Compile all sources added until now and return {@link CompilationResult}, |
| 83 | + * providing access to the compiled classes and/or errors and warnings |
| 84 | + */ |
| 85 | + public CompilationResult compile() throws Exception { |
| 86 | + if (sourceCodes.size() == 0) { |
| 87 | + throw new CompilationException("No source code to compile"); |
| 88 | + } |
| 89 | + Collection<SourceCode> compilationUnits = sourceCodes.values(); |
| 90 | + CompiledCode[] code = new CompiledCode[compilationUnits.size()]; |
| 91 | + Iterator<SourceCode> iter = compilationUnits.iterator(); |
| 92 | + for (int i = 0; i < code.length; i++) { |
| 93 | + code[i] = new CompiledCode(iter.next().getClassName()); |
| 94 | + } |
| 95 | + DiagnosticCollector<JavaFileObject> collector = new DiagnosticCollector<>(); |
| 96 | + ExtendedStandardJavaFileManager fileManager = new ExtendedStandardJavaFileManager(javac.getStandardFileManager(null, null, null), classLoader, code); |
| 97 | + JavaCompiler.CompilationTask task = javac.getTask(null, fileManager, collector, options, null, compilationUnits); |
| 98 | + task.call(); |
| 99 | + boolean hasWarnings; |
| 100 | + boolean hasErrors; |
| 101 | + |
| 102 | + { |
| 103 | + boolean hasWarningsTmp = false; |
| 104 | + boolean hasErrorsTmp = false; |
| 105 | + for (Diagnostic<? extends JavaFileObject> d : collector.getDiagnostics()) { |
| 106 | + switch (d.getKind()) { |
| 107 | + case NOTE: |
| 108 | + case MANDATORY_WARNING: |
| 109 | + case WARNING: |
| 110 | + hasWarningsTmp = true; |
| 111 | + break; |
| 112 | + case OTHER: |
| 113 | + case ERROR: |
| 114 | + default: |
| 115 | + hasErrorsTmp = true; |
| 116 | + break; |
| 117 | + } |
| 118 | + } |
| 119 | + hasWarnings = hasWarningsTmp; |
| 120 | + hasErrors = hasErrorsTmp; |
| 121 | + } |
| 122 | + |
| 123 | + return new CompilationResult() { |
| 124 | + |
| 125 | + @Override |
| 126 | + public Map<String, Class<?>> classMap() throws ClassNotFoundException { |
| 127 | + Map<String, Class<?>> classes = new HashMap<>(); |
| 128 | + for (String className : sourceCodes.keySet()) { |
| 129 | + // 由loadClass变更为findClass,优先获取新编译的同名类(如果使用loadClass则获取不到新编译的同名class) |
| 130 | + // 虽然支持多次加载同名类(指不同的ClassLoader,实际上同一个ClassLoader中Java仍然不允许重复会报错) |
| 131 | + // 但是还是不推荐这样做,因为这样违反了Java的基本原则,且容易出现混淆影响自己问题的处理 |
155 | 132 | // classes.put(className, classLoader.loadClass(className)); |
156 | | - classes.put(className, classLoader.findClass(className)); |
157 | | - } |
158 | | - return classes; |
159 | | - } |
160 | | - |
161 | | - @Override |
162 | | - public boolean compilationSucceeded() { |
163 | | - if (hasWarnings && !ignoreWarnings) |
164 | | - return false; |
165 | | - return !hasErrors; |
166 | | - } |
167 | | - |
168 | | - @Override |
169 | | - public boolean hasWarnings() { |
170 | | - return hasWarnings; |
171 | | - } |
172 | | - |
173 | | - @Override |
174 | | - public boolean hasErrors() { |
175 | | - return hasErrors; |
176 | | - } |
177 | | - |
178 | | - @Override |
179 | | - public List<Diagnostic<? extends JavaFileObject>> getDiagnostics() { |
180 | | - return collector.getDiagnostics(); |
181 | | - } |
182 | | - |
183 | | - @Override |
184 | | - public CompilationResult checkNoErrors() { |
185 | | - if (!compilationSucceeded()) { |
186 | | - StringBuffer exceptionMsg = new StringBuffer(); |
187 | | - exceptionMsg.append("Unable to compile the source"); |
188 | | - for (Diagnostic<? extends JavaFileObject> d : getDiagnostics()) { |
189 | | - exceptionMsg.append("\n").append("[kind=").append(d.getKind()); |
190 | | - exceptionMsg.append(", ").append("line=").append(d.getLineNumber()); |
191 | | - exceptionMsg.append(", ").append("message=").append(d.getMessage(Locale.US)).append("]"); |
192 | | - } |
193 | | - throw new CompilationException(exceptionMsg.toString()); |
194 | | - } |
195 | | - return this; |
196 | | - } |
197 | | - |
198 | | - }; |
199 | | - } |
| 133 | + classes.put(className, classLoader.findClass(className)); |
| 134 | + } |
| 135 | + return classes; |
| 136 | + } |
| 137 | + |
| 138 | + @Override |
| 139 | + public boolean compilationSucceeded() { |
| 140 | + if (hasWarnings && !ignoreWarnings) return false; |
| 141 | + return !hasErrors; |
| 142 | + } |
| 143 | + |
| 144 | + @Override |
| 145 | + public boolean hasWarnings() { |
| 146 | + return hasWarnings; |
| 147 | + } |
| 148 | + |
| 149 | + @Override |
| 150 | + public boolean hasErrors() { |
| 151 | + return hasErrors; |
| 152 | + } |
| 153 | + |
| 154 | + @Override |
| 155 | + public List<Diagnostic<? extends JavaFileObject>> getDiagnostics() { |
| 156 | + return collector.getDiagnostics(); |
| 157 | + } |
| 158 | + |
| 159 | + @Override |
| 160 | + public CompilationResult checkNoErrors() { |
| 161 | + if (!compilationSucceeded()) { |
| 162 | + StringBuilder exceptionMsg = new StringBuilder(); |
| 163 | + exceptionMsg.append("Unable to compile the source"); |
| 164 | + for (Diagnostic<? extends JavaFileObject> d : getDiagnostics()) { |
| 165 | + exceptionMsg.append("\n").append("[kind=").append(d.getKind()); |
| 166 | + exceptionMsg.append(", ").append("line=").append(d.getLineNumber()); |
| 167 | + exceptionMsg.append(", ").append("message=").append(d.getMessage(Locale.US)).append("]"); |
| 168 | + } |
| 169 | + throw new CompilationException(exceptionMsg.toString()); |
| 170 | + } |
| 171 | + return this; |
| 172 | + } |
| 173 | + |
| 174 | + }; |
| 175 | + } |
| 176 | + |
200 | 177 |
|
201 | 178 | /** |
202 | 179 | * Compile single source |
@@ -224,4 +201,15 @@ public InMemoryJavaCompiler addSource(String className, String sourceCode) throw |
224 | 201 | return this; |
225 | 202 | } |
226 | 203 |
|
| 204 | + /** |
| 205 | + * Add source code map to the compiler |
| 206 | + * |
| 207 | + * @param sourceCodeMap |
| 208 | + * @return |
| 209 | + * @throws Exception |
| 210 | + */ |
| 211 | + public InMemoryJavaCompiler addSources(Map<String, SourceCode> sourceCodeMap) throws Exception { |
| 212 | + sourceCodes.putAll(sourceCodeMap); |
| 213 | + return this; |
| 214 | + } |
227 | 215 | } |
0 commit comments