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