1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.plugin.compiler;
20
21 import javax.lang.model.element.Modifier;
22 import javax.lang.model.element.NestingKind;
23 import javax.tools.FileObject;
24 import javax.tools.JavaFileObject;
25 import javax.tools.StandardJavaFileManager;
26 import javax.tools.StandardLocation;
27
28 import java.io.File;
29 import java.io.IOException;
30 import java.io.InputStream;
31 import java.io.OutputStream;
32 import java.io.Reader;
33 import java.io.UncheckedIOException;
34 import java.io.Writer;
35 import java.net.URI;
36 import java.nio.charset.Charset;
37 import java.nio.file.Files;
38 import java.nio.file.Path;
39 import java.util.Arrays;
40 import java.util.Collection;
41 import java.util.HashMap;
42 import java.util.Iterator;
43 import java.util.List;
44 import java.util.Map;
45 import java.util.Optional;
46 import java.util.Set;
47 import java.util.StringJoiner;
48 import java.util.stream.Stream;
49 import java.util.stream.StreamSupport;
50
51 import org.apache.maven.api.JavaPathType;
52 import org.apache.maven.api.PathType;
53
54
55
56
57
58
59
60
61 final class ForkedToolSources implements StandardJavaFileManager {
62
63
64
65
66 private record OtherPathType(String name, String optionString, String moduleName) implements PathType {
67
68
69
70 static final OtherPathType SOURCES = new OtherPathType("SOURCES", "--source-path", null);
71
72
73
74
75 static final OtherPathType GENERATED_SOURCES = new OtherPathType("GENERATED_SOURCES", "-s", null);
76
77
78
79
80 static final OtherPathType OUTPUT = new OtherPathType("OUTPUT", "-d", null);
81
82 @Override
83 public String id() {
84 return name;
85 }
86
87 @Override
88 public Optional<String> option() {
89 return Optional.of(optionString);
90 }
91
92
93
94
95
96
97 @Override
98 public String[] option(Iterable<? extends Path> paths) {
99 String prefix = (moduleName == null) ? "" : (moduleName + '=');
100 var builder = new StringJoiner(File.pathSeparator, prefix, "");
101 paths.forEach((path) -> builder.add(path.toString()));
102 return new String[] {optionString, builder.toString()};
103 }
104 }
105
106
107
108
109
110
111
112
113 private final Map<PathType, Collection<? extends Path>> locations;
114
115
116
117
118 final Charset encoding;
119
120
121
122
123 ForkedToolSources(Charset encoding) {
124 if (encoding == null) {
125 encoding = Charset.defaultCharset();
126 }
127 this.encoding = encoding;
128 locations = new HashMap<>();
129 }
130
131
132
133
134
135 @Override
136 public int isSupportedOption(String option) {
137 return -1;
138 }
139
140
141
142
143 @Override
144 public boolean handleOption(String current, Iterator<String> remaining) {
145 return false;
146 }
147
148
149
150
151 @Override
152 public Path asPath(FileObject file) {
153 return (file instanceof Item) ? ((Item) file).path : Path.of(file.toUri());
154 }
155
156
157
158
159
160 @Override
161 public boolean isSameFile(FileObject a, FileObject b) {
162 return asPath(a).equals(asPath(b));
163 }
164
165
166
167
168 @Override
169 public Iterable<? extends JavaFileObject> getJavaFileObjects(String... names) {
170 return fromNames(Arrays.stream(names));
171 }
172
173
174
175
176 @Override
177 public Iterable<? extends JavaFileObject> getJavaFileObjects(File... files) {
178 return fromFiles(Arrays.stream(files));
179 }
180
181
182
183
184 @Override
185 public Iterable<? extends JavaFileObject> getJavaFileObjectsFromStrings(Iterable<String> names) {
186 return fromNames(StreamSupport.stream(names.spliterator(), false));
187 }
188
189
190
191
192 @Override
193 public Iterable<? extends JavaFileObject> getJavaFileObjectsFromFiles(Iterable<? extends File> files) {
194 return fromFiles(StreamSupport.stream(files.spliterator(), false));
195 }
196
197
198
199
200 @Override
201 public Iterable<? extends JavaFileObject> getJavaFileObjectsFromPaths(Collection<? extends Path> paths) {
202 return paths.stream().map(Item::new).toList();
203 }
204
205
206
207
208 private Iterable<? extends JavaFileObject> fromFiles(Stream<? extends File> files) {
209 return files.map((file) -> new Item(file.toPath())).toList();
210 }
211
212
213
214
215 private Iterable<? extends JavaFileObject> fromNames(Stream<? extends String> names) {
216 return names.map((name) -> new Item(Path.of(name))).toList();
217 }
218
219
220
221
222
223
224 private final class Item implements JavaFileObject {
225
226
227
228 final Path path;
229
230
231
232
233 Item(Path path) {
234 this.path = path;
235 }
236
237
238
239
240 @Override
241 public String getName() {
242 return path.toString();
243 }
244
245
246
247
248 @Override
249 public String toString() {
250 return getName();
251 }
252
253
254
255
256 @Override
257 public URI toUri() {
258 return path.toUri();
259 }
260
261
262
263
264 @Override
265 public Kind getKind() {
266 String filename = path.getFileName().toString();
267 for (Kind k : Kind.values()) {
268 if (filename.endsWith(k.extension)) {
269 return k;
270 }
271 }
272 return Kind.OTHER;
273 }
274
275
276
277
278 @Override
279 public boolean isNameCompatible(String simpleName, Kind kind) {
280 return path.getFileName().toString().equals(simpleName.concat(kind.extension));
281 }
282
283
284
285
286 @Override
287 public NestingKind getNestingKind() {
288 return null;
289 }
290
291
292
293
294 @Override
295 public Modifier getAccessLevel() {
296 return null;
297 }
298
299
300
301
302 @Override
303 public long getLastModified() {
304 try {
305 return Files.getLastModifiedTime(path).toMillis();
306 } catch (IOException e) {
307 throw new UncheckedIOException(e);
308 }
309 }
310
311
312
313
314 @Override
315 public boolean delete() {
316 try {
317 return Files.deleteIfExists(path);
318 } catch (IOException e) {
319 throw new UncheckedIOException(e);
320 }
321 }
322
323
324
325
326
327 @Override
328 public InputStream openInputStream() throws IOException {
329 return Files.newInputStream(path);
330 }
331
332
333
334
335
336 @Override
337 public OutputStream openOutputStream() throws IOException {
338 return Files.newOutputStream(path);
339 }
340
341
342
343
344
345 @Override
346 public Reader openReader(boolean ignoreEncodingErrors) throws IOException {
347 return Files.newBufferedReader(path, encoding);
348 }
349
350
351
352
353
354 @Override
355 public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
356 return Files.readString(path, encoding);
357 }
358
359
360
361
362
363 @Override
364 public Writer openWriter() throws IOException {
365 return Files.newBufferedWriter(path, encoding);
366 }
367 }
368
369
370
371
372
373
374
375 @Override
376 public void setLocation(Location location, Iterable<? extends File> files) {
377 List<Path> paths = null;
378 if (files != null) {
379 paths = StreamSupport.stream(files.spliterator(), false)
380 .map(File::toPath)
381 .toList();
382 }
383 setLocationFromPaths(location, paths);
384 }
385
386
387
388
389
390
391
392 @Override
393 public Iterable<? extends File> getLocation(Location location) {
394 var paths = getLocationAsPaths(location);
395 if (paths != null) {
396 return paths.stream().map(Path::toFile).toList();
397 }
398 return null;
399 }
400
401
402
403
404
405
406 @Override
407 public void setLocationFromPaths(Location location, Collection<? extends Path> paths) {
408 PathType type = JavaPathType.valueOf(location).orElse(null);
409 if (type == null) {
410 if (location == StandardLocation.SOURCE_OUTPUT) {
411 type = OtherPathType.GENERATED_SOURCES;
412 } else if (location == StandardLocation.SOURCE_PATH) {
413 type = OtherPathType.SOURCES;
414 } else if (location == StandardLocation.CLASS_OUTPUT) {
415 type = OtherPathType.OUTPUT;
416 } else {
417 throw new IllegalArgumentException("Unsupported location: " + location);
418 }
419 }
420 if (paths == null || paths.isEmpty()) {
421 locations.remove(type);
422 } else {
423 locations.put(type, paths);
424 }
425 }
426
427
428
429
430
431 @Override
432 public void setLocationForModule(Location location, String moduleName, Collection<? extends Path> paths)
433 throws IOException {
434 PathType type;
435 if (location == StandardLocation.PATCH_MODULE_PATH) {
436 type = JavaPathType.patchModule(moduleName);
437 } else if (location == StandardLocation.MODULE_SOURCE_PATH) {
438 type = new OtherPathType("MODULE_SOURCE_PATH", "--module-source-path", moduleName);
439 } else {
440 throw new IllegalArgumentException("Unsupported location: " + location);
441 }
442 if (paths == null || paths.isEmpty()) {
443 locations.remove(type);
444 } else {
445 locations.put(type, paths);
446 }
447 }
448
449
450
451
452 @Override
453 public Collection<? extends Path> getLocationAsPaths(Location location) {
454 return locations.get(JavaPathType.valueOf(location).orElse(null));
455 }
456
457
458
459
460
461 @Override
462 public boolean hasLocation(Location location) {
463 return getLocationAsPaths(location) != null;
464 }
465
466
467
468
469
470
471 void addAllLocations(List<String> command) {
472 for (Map.Entry<PathType, Collection<? extends Path>> entry : locations.entrySet()) {
473 command.addAll(Arrays.asList(entry.getKey().option(entry.getValue())));
474 }
475 }
476
477
478
479
480 @Override
481 public Iterable<JavaFileObject> list(
482 Location location, String packageName, Set<JavaFileObject.Kind> kinds, boolean recurse) throws IOException {
483 throw new UnsupportedOperationException("Not supported yet.");
484 }
485
486
487
488
489 @Override
490 public String inferBinaryName(Location location, JavaFileObject file) {
491 throw new UnsupportedOperationException("Not supported yet.");
492 }
493
494
495
496
497 @Override
498 public JavaFileObject getJavaFileForInput(Location location, String className, JavaFileObject.Kind kind)
499 throws IOException {
500 throw new UnsupportedOperationException("Not supported yet.");
501 }
502
503
504
505
506 @Override
507 public JavaFileObject getJavaFileForOutput(
508 Location location, String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException {
509 throw new UnsupportedOperationException("Not supported yet.");
510 }
511
512
513
514
515 @Override
516 public FileObject getFileForInput(Location location, String packageName, String relativeName) throws IOException {
517 throw new UnsupportedOperationException("Not supported yet.");
518 }
519
520
521
522
523 @Override
524 public FileObject getFileForOutput(Location location, String packageName, String relativeName, FileObject sibling)
525 throws IOException {
526 throw new UnsupportedOperationException("Not supported yet.");
527 }
528
529
530
531
532 @Override
533 public ClassLoader getClassLoader(Location location) {
534 return null;
535 }
536
537
538
539
540 @Override
541 public void flush() {}
542
543
544
545
546 @Override
547 public void close() {
548 locations.clear();
549 }
550 }