1
2
3
4
5
6 package gov.nist.secauto.metaschema.databind.codegen;
7
8 import gov.nist.secauto.metaschema.core.util.CollectionUtil;
9
10 import java.io.IOException;
11 import java.io.StringWriter;
12 import java.nio.file.Path;
13 import java.util.LinkedHashSet;
14 import java.util.LinkedList;
15 import java.util.List;
16 import java.util.Set;
17 import java.util.stream.Collectors;
18
19 import javax.tools.DiagnosticCollector;
20 import javax.tools.JavaCompiler;
21 import javax.tools.JavaFileObject;
22 import javax.tools.StandardJavaFileManager;
23 import javax.tools.ToolProvider;
24
25 import edu.umd.cs.findbugs.annotations.NonNull;
26 import edu.umd.cs.findbugs.annotations.Nullable;
27
28 public class JavaCompilerSupport {
29 @Nullable
30 private Logger logger;
31 @NonNull
32 private final Path classDir;
33 @NonNull
34 private final Set<String> classPath = new LinkedHashSet<>();
35 @NonNull
36 private final Set<String> modulePath = new LinkedHashSet<>();
37 @NonNull
38 private final Set<String> rootModuleNames = new LinkedHashSet<>();
39
40 public JavaCompilerSupport(@NonNull Path classDir) {
41 this.classDir = classDir;
42 }
43
44 public Set<String> getClassPath() {
45 return classPath;
46 }
47
48 public Set<String> getModulePath() {
49 return modulePath;
50 }
51
52 public Set<String> getRootModuleNames() {
53 return rootModuleNames;
54 }
55
56 public void addToClassPath(@NonNull String entry) {
57 classPath.add(entry);
58 }
59
60 public void addToModulePath(@NonNull String entry) {
61 modulePath.add(entry);
62 }
63
64 public void addRootModule(@NonNull String entry) {
65 rootModuleNames.add(entry);
66 }
67
68 public void setLogger(@NonNull Logger logger) {
69 this.logger = logger;
70 }
71
72 @NonNull
73 protected List<String> generateCompilerOptions() {
74 List<String> options = new LinkedList<>();
75
76
77 options.add("-d");
78 options.add(classDir.toString());
79
80 if (!classPath.isEmpty()) {
81 options.add("-classpath");
82 options.add(classPath.stream()
83 .collect(Collectors.joining(":")));
84 }
85
86 if (!modulePath.isEmpty()) {
87 options.add("-p");
88 options.add(modulePath.stream()
89 .collect(Collectors.joining(":")));
90 }
91
92 return options;
93 }
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109 public CompilationResult compile(@NonNull List<Path> classFiles) throws IOException {
110 DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<>();
111
112 JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
113
114 List<JavaFileObject> compilationUnits;
115 try (StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, null, null)) {
116
117 compilationUnits = classFiles.stream()
118 .map(fileManager::getJavaFileObjects)
119 .map(CollectionUtil::toList)
120 .flatMap(List::stream)
121 .collect(Collectors.toUnmodifiableList());
122
123 List<String> options = generateCompilerOptions();
124
125 Logger logger = this.logger;
126 if (logger != null && logger.isDebugEnabled()) {
127 logger.debug(String.format("Using options: %s", options));
128 }
129
130 boolean result;
131 try (StringWriter writer = new StringWriter()) {
132 JavaCompiler.CompilationTask task = compiler.getTask(
133 writer,
134 fileManager,
135 diagnostics,
136 options,
137 null,
138 compilationUnits);
139 task.addModules(rootModuleNames);
140
141 result = task.call();
142 writer.flush();
143 String output = writer.toString();
144 if (!output.isBlank() && logger != null && logger.isInfoEnabled()) {
145 logger.info(String.format("compiler output: %s", writer.toString()));
146 }
147 }
148 return new CompilationResult(result, diagnostics);
149 }
150 }
151
152 public static final class CompilationResult {
153 private final boolean successful;
154 @NonNull
155 private final DiagnosticCollector<JavaFileObject> diagnostics;
156
157 private CompilationResult(boolean successful, @NonNull DiagnosticCollector<JavaFileObject> diagnostics) {
158 this.successful = successful;
159 this.diagnostics = diagnostics;
160 }
161
162 public boolean isSuccessful() {
163 return successful;
164 }
165
166 public DiagnosticCollector<?> getDiagnostics() {
167 return diagnostics;
168 }
169 }
170
171 public interface Logger {
172 boolean isDebugEnabled();
173
174 boolean isInfoEnabled();
175
176 void debug(String msg);
177
178 void info(String msg);
179 }
180 }