1
2
3
4
5
6 package dev.metaschema.databind.codegen;
7
8 import java.io.IOException;
9 import java.io.StringWriter;
10 import java.nio.file.Path;
11 import java.util.LinkedHashSet;
12 import java.util.LinkedList;
13 import java.util.List;
14 import java.util.Set;
15 import java.util.stream.Collectors;
16
17 import javax.tools.DiagnosticCollector;
18 import javax.tools.JavaCompiler;
19 import javax.tools.JavaFileObject;
20 import javax.tools.StandardJavaFileManager;
21 import javax.tools.ToolProvider;
22
23 import dev.metaschema.core.util.CollectionUtil;
24 import edu.umd.cs.findbugs.annotations.NonNull;
25 import edu.umd.cs.findbugs.annotations.Nullable;
26
27
28
29
30
31
32
33
34
35 public class JavaCompilerSupport {
36 @Nullable
37 private Logger logger;
38 @NonNull
39 private final Path classDir;
40 @NonNull
41 private final Set<String> classPath = new LinkedHashSet<>();
42 @NonNull
43 private final Set<String> modulePath = new LinkedHashSet<>();
44 @NonNull
45 private final Set<String> rootModuleNames = new LinkedHashSet<>();
46
47
48
49
50
51
52
53 public JavaCompilerSupport(@NonNull Path classDir) {
54 this.classDir = classDir;
55 }
56
57
58
59
60
61
62 public Set<String> getClassPath() {
63 return classPath;
64 }
65
66
67
68
69
70
71 public Set<String> getModulePath() {
72 return modulePath;
73 }
74
75
76
77
78
79
80 public Set<String> getRootModuleNames() {
81 return rootModuleNames;
82 }
83
84
85
86
87
88
89
90 public void addToClassPath(@NonNull String entry) {
91 classPath.add(entry);
92 }
93
94
95
96
97
98
99
100 public void addToModulePath(@NonNull String entry) {
101 modulePath.add(entry);
102 }
103
104
105
106
107
108
109
110 public void addRootModule(@NonNull String entry) {
111 rootModuleNames.add(entry);
112 }
113
114
115
116
117
118
119
120 public void setLogger(@NonNull Logger logger) {
121 this.logger = logger;
122 }
123
124
125
126
127
128
129 @NonNull
130 protected List<String> generateCompilerOptions() {
131 List<String> options = new LinkedList<>();
132 options.add("-d");
133 options.add(classDir.toString());
134
135 if (!classPath.isEmpty()) {
136 options.add("-classpath");
137 options.add(classPath.stream()
138 .collect(Collectors.joining(":")));
139 }
140
141 if (!modulePath.isEmpty()) {
142 options.add("-p");
143 options.add(modulePath.stream()
144 .collect(Collectors.joining(":")));
145 }
146
147 return options;
148 }
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163 public CompilationResult compile(@NonNull List<Path> classFiles) throws IOException {
164 DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<>();
165
166 JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
167
168 List<JavaFileObject> compilationUnits;
169 try (StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, null, null)) {
170
171 compilationUnits = classFiles.stream()
172 .map(fileManager::getJavaFileObjects)
173 .map(CollectionUtil::toList)
174 .flatMap(List::stream)
175 .collect(Collectors.toUnmodifiableList());
176
177 List<String> options = generateCompilerOptions();
178
179 Logger logger = this.logger;
180 if (logger != null && logger.isDebugEnabled()) {
181 logger.debug(String.format("Using options: %s", options));
182 }
183
184 boolean result;
185 try (StringWriter writer = new StringWriter()) {
186 JavaCompiler.CompilationTask task = compiler.getTask(
187 writer,
188 fileManager,
189 diagnostics,
190 options,
191 null,
192 compilationUnits);
193 task.addModules(rootModuleNames);
194
195 result = task.call();
196 writer.flush();
197 String output = writer.toString();
198 if (!output.isBlank() && logger != null && logger.isInfoEnabled()) {
199 logger.info(String.format("compiler output: %s", writer.toString()));
200 }
201 }
202 return new CompilationResult(result, diagnostics);
203 }
204 }
205
206
207
208
209 public static final class CompilationResult {
210 private final boolean successful;
211 @NonNull
212 private final DiagnosticCollector<JavaFileObject> diagnostics;
213
214 private CompilationResult(boolean successful, @NonNull DiagnosticCollector<JavaFileObject> diagnostics) {
215 this.successful = successful;
216 this.diagnostics = diagnostics;
217 }
218
219
220
221
222
223
224 public boolean isSuccessful() {
225 return successful;
226 }
227
228
229
230
231
232
233 public DiagnosticCollector<?> getDiagnostics() {
234 return diagnostics;
235 }
236 }
237
238
239
240
241 public interface Logger {
242
243
244
245
246
247 boolean isDebugEnabled();
248
249
250
251
252
253
254 boolean isInfoEnabled();
255
256
257
258
259
260
261
262 void debug(String msg);
263
264
265
266
267
268
269
270 void info(String msg);
271 }
272 }