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