AnnotationProcessing.java revision 3954:dee2b0b32d77
1184610Salfred/*
2184610Salfred * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
3184610Salfred * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4184610Salfred *
5184610Salfred * This code is free software; you can redistribute it and/or modify it
6184610Salfred * under the terms of the GNU General Public License version 2 only, as
7184610Salfred * published by the Free Software Foundation.
8184610Salfred *
9184610Salfred * This code is distributed in the hope that it will be useful, but WITHOUT
10184610Salfred * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11184610Salfred * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12184610Salfred * version 2 for more details (a copy is included in the LICENSE file that
13184610Salfred * accompanied this code).
14184610Salfred *
15184610Salfred * You should have received a copy of the GNU General Public License version
16184610Salfred * 2 along with this work; if not, write to the Free Software Foundation,
17184610Salfred * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18184610Salfred *
19184610Salfred * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20184610Salfred * or visit www.oracle.com if you need additional information or have any
21184610Salfred * questions.
22184610Salfred */
23184610Salfred
24184610Salfred/**
25184610Salfred * @test
26184610Salfred * @bug 8133884 8162711 8133896 8172158 8172262 8173636
27184610Salfred * @summary Verify that annotation processing works.
28184610Salfred * @library /tools/lib
29184610Salfred * @modules
30193640Sariff *      jdk.compiler/com.sun.tools.javac.api
31193640Sariff *      jdk.compiler/com.sun.tools.javac.main
32193640Sariff * @build toolbox.ToolBox toolbox.JavacTask ModuleTestBase
33193640Sariff * @run main AnnotationProcessing
34184610Salfred */
35184610Salfred
36188957Sthompsaimport java.io.File;
37184610Salfredimport java.io.IOException;
38184610Salfredimport java.io.OutputStream;
39184610Salfredimport java.io.Reader;
40184610Salfredimport java.io.Writer;
41184610Salfredimport java.nio.file.Files;
42184610Salfredimport java.nio.file.Path;
43184610Salfredimport java.nio.file.Paths;
44184610Salfredimport java.util.Arrays;
45184610Salfredimport java.util.HashMap;
46184610Salfredimport java.util.HashSet;
47184610Salfredimport java.util.List;
48184610Salfredimport java.util.Map;
49184610Salfredimport java.util.Objects;
50184610Salfredimport java.util.Set;
51184610Salfredimport java.util.concurrent.Callable;
52184610Salfredimport java.util.function.Function;
53184610Salfredimport java.util.regex.Pattern;
54184610Salfredimport java.util.stream.Collectors;
55184610Salfred
56184610Salfredimport javax.annotation.processing.AbstractProcessor;
57184610Salfredimport javax.annotation.processing.Filer;
58184610Salfredimport javax.annotation.processing.FilerException;
59184610Salfredimport javax.annotation.processing.Messager;
60184610Salfredimport javax.annotation.processing.ProcessingEnvironment;
61184610Salfredimport javax.annotation.processing.RoundEnvironment;
62184610Salfredimport javax.annotation.processing.SupportedAnnotationTypes;
63193640Sariffimport javax.annotation.processing.SupportedOptions;
64184610Salfredimport javax.lang.model.SourceVersion;
65184610Salfredimport javax.lang.model.element.Element;
66184610Salfredimport javax.lang.model.element.ElementKind;
67184610Salfredimport javax.lang.model.element.ModuleElement;
68184610Salfredimport javax.lang.model.element.ModuleElement.ProvidesDirective;
69193640Sariffimport javax.lang.model.element.ModuleElement.UsesDirective;
70184610Salfredimport javax.lang.model.element.PackageElement;
71184610Salfredimport javax.lang.model.element.TypeElement;
72184610Salfredimport javax.lang.model.element.VariableElement;
73184610Salfredimport javax.lang.model.type.TypeKind;
74184610Salfredimport javax.lang.model.util.ElementFilter;
75184610Salfredimport javax.lang.model.util.ElementScanner9;
76184610Salfredimport javax.tools.Diagnostic.Kind;
77184610Salfredimport javax.tools.FileObject;
78184610Salfredimport javax.tools.JavaCompiler;
79184610Salfredimport javax.tools.JavaCompiler.CompilationTask;
80184610Salfredimport javax.tools.JavaFileManager;
81184610Salfredimport javax.tools.JavaFileManager.Location;
82184610Salfredimport javax.tools.JavaFileObject;
83184610Salfredimport javax.tools.StandardJavaFileManager;
84184610Salfredimport javax.tools.StandardLocation;
85184610Salfredimport javax.tools.ToolProvider;
86184610Salfred
87184610Salfredimport toolbox.JavacTask;
88184610Salfredimport toolbox.Task;
89184610Salfredimport toolbox.Task.Mode;
90184610Salfredimport toolbox.Task.OutputKind;
91184610Salfred
92184610Salfredpublic class AnnotationProcessing extends ModuleTestBase {
93184610Salfred
94193640Sariff    public static void main(String... args) throws Exception {
95184610Salfred        new AnnotationProcessing().runTests();
96184610Salfred    }
97184610Salfred
98184610Salfred    @Test
99184610Salfred    public void testAPSingleModule(Path base) throws Exception {
100184610Salfred        Path moduleSrc = base.resolve("module-src");
101184610Salfred        Path m1 = moduleSrc.resolve("m1x");
102184610Salfred
103184610Salfred        Path classes = base.resolve("classes");
104184610Salfred
105184610Salfred        Files.createDirectories(classes);
106193640Sariff
107193640Sariff        tb.writeJavaFiles(m1,
108193640Sariff                          "module m1x { }",
109193640Sariff                          "package impl; public class Impl { }");
110193640Sariff
111193640Sariff        String log = new JavacTask(tb)
112184610Salfred                .options("--module-source-path", moduleSrc.toString(),
113184610Salfred                         "-processor", AP.class.getName(),
114184610Salfred                         "-AexpectedEnclosedElements=m1x=>impl")
115184610Salfred                .outdir(classes)
116184610Salfred                .files(findJavaFiles(moduleSrc))
117184610Salfred                .run()
118184610Salfred                .writeAll()
119184610Salfred                .getOutput(Task.OutputKind.DIRECT);
120184610Salfred
121184610Salfred        if (!log.isEmpty())
122193640Sariff            throw new AssertionError("Unexpected output: " + log);
123193640Sariff    }
124184610Salfred
125184610Salfred    @Test
126184610Salfred    public void testAPMultiModule(Path base) throws Exception {
127184610Salfred        Path moduleSrc = base.resolve("module-src");
128184610Salfred        Path m1 = moduleSrc.resolve("m1x");
129184610Salfred        Path m2 = moduleSrc.resolve("m2x");
130184610Salfred
131184610Salfred        Path classes = base.resolve("classes");
132184610Salfred
133184610Salfred        Files.createDirectories(classes);
134184610Salfred
135184610Salfred        tb.writeJavaFiles(m1,
136184610Salfred                          "module m1x { }",
137184610Salfred                          "package impl1; public class Impl1 { }");
138184610Salfred
139184610Salfred        tb.writeJavaFiles(m2,
140184610Salfred                          "module m2x { }",
141184610Salfred                          "package impl2; public class Impl2 { }");
142184610Salfred
143184610Salfred        String log = new JavacTask(tb)
144184610Salfred                .options("--module-source-path", moduleSrc.toString(),
145184610Salfred                         "-processor", AP.class.getName(),
146184610Salfred                         "-AexpectedEnclosedElements=m1x=>impl1,m2x=>impl2")
147184610Salfred                .outdir(classes)
148184610Salfred                .files(findJavaFiles(moduleSrc))
149184610Salfred                .run()
150184610Salfred                .writeAll()
151184610Salfred                .getOutput(Task.OutputKind.DIRECT);
152184610Salfred
153184610Salfred        if (!log.isEmpty())
154193640Sariff            throw new AssertionError("Unexpected output: " + log);
155184610Salfred    }
156184610Salfred
157184610Salfred    @SupportedAnnotationTypes("*")
158184610Salfred    @SupportedOptions("expectedEnclosedElements")
159184610Salfred    public static final class AP extends AbstractProcessor {
160184610Salfred
161184610Salfred        private Map<String, List<String>> module2ExpectedEnclosedElements;
162184610Salfred        private Set<String> seenModules = new HashSet<>();
163184610Salfred
164184610Salfred        @Override
165184610Salfred        public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
166184610Salfred            if (module2ExpectedEnclosedElements == null) {
167184610Salfred                module2ExpectedEnclosedElements = new HashMap<>();
168184610Salfred
169184610Salfred                String expectedEnclosedElements =
170184610Salfred                        processingEnv.getOptions().get("expectedEnclosedElements");
171184610Salfred
172184610Salfred                for (String moduleDef : expectedEnclosedElements.split(",")) {
173184610Salfred                    String[] module2Packages = moduleDef.split("=>");
174184610Salfred
175184610Salfred                    module2ExpectedEnclosedElements.put(module2Packages[0],
176184610Salfred                                                        List.of(module2Packages[1].split(":")));
177184610Salfred                }
178184610Salfred            }
179184610Salfred
180184610Salfred            //verify ModuleType and ModuleSymbol behavior:
181184610Salfred            for (Element root : roundEnv.getRootElements()) {
182184610Salfred                ModuleElement module = processingEnv.getElementUtils().getModuleOf(root);
183184610Salfred
184184610Salfred                assertEquals(TypeKind.MODULE, module.asType().getKind());
185193640Sariff
186184610Salfred                boolean[] seenModule = new boolean[1];
187184610Salfred
188184610Salfred                module.accept(new ElementScanner9<Void, Void>() {
189184610Salfred                    @Override
190184610Salfred                    public Void visitModule(ModuleElement e, Void p) {
191184610Salfred                        seenModule[0] = true;
192184610Salfred                        return null;
193184610Salfred                    }
194184610Salfred                    @Override
195184610Salfred                    public Void scan(Element e, Void p) {
196184610Salfred                        throw new AssertionError("Shouldn't get here.");
197184610Salfred                    }
198184610Salfred                }, null);
199184610Salfred
200184610Salfred                assertEquals(true, seenModule[0]);
201184610Salfred
202184610Salfred                List<String> actualElements =
203184610Salfred                        module.getEnclosedElements()
204184610Salfred                              .stream()
205184610Salfred                              .map(s -> (PackageElement) s)
206184610Salfred                              .map(p -> p.getQualifiedName().toString())
207184610Salfred                              .collect(Collectors.toList());
208184610Salfred
209184610Salfred                String moduleName = module.getQualifiedName().toString();
210184610Salfred
211184610Salfred                assertEquals(module2ExpectedEnclosedElements.get(moduleName),
212184610Salfred                             actualElements);
213184610Salfred
214184610Salfred                seenModules.add(moduleName);
215184610Salfred            }
216184610Salfred
217184610Salfred            if (roundEnv.processingOver()) {
218184610Salfred                assertEquals(module2ExpectedEnclosedElements.keySet(), seenModules);
219184610Salfred            }
220184610Salfred
221184610Salfred            return false;
222184610Salfred        }
223184610Salfred
224184610Salfred        @Override
225184610Salfred        public SourceVersion getSupportedSourceVersion() {
226184610Salfred            return SourceVersion.latest();
227184610Salfred        }
228184610Salfred
229184610Salfred    }
230184610Salfred
231184610Salfred    @Test
232184610Salfred    public void testVerifyUsesProvides(Path base) throws Exception {
233184610Salfred        Path moduleSrc = base.resolve("module-src");
234184610Salfred        Path m1 = moduleSrc.resolve("m1x");
235184610Salfred
236184610Salfred        Path classes = base.resolve("classes");
237184610Salfred
238184610Salfred        Files.createDirectories(classes);
239184610Salfred
240184610Salfred        tb.writeJavaFiles(m1,
241184610Salfred                          "module m1x { exports api; uses api.Api; provides api.Api with impl.Impl; }",
242184610Salfred                          "package api; public class Api { }",
243                          "package impl; public class Impl extends api.Api { }");
244
245        String log = new JavacTask(tb)
246                .options("-doe", "-processor", VerifyUsesProvidesAP.class.getName())
247                .outdir(classes)
248                .files(findJavaFiles(moduleSrc))
249                .run()
250                .writeAll()
251                .getOutput(Task.OutputKind.DIRECT);
252
253        if (!log.isEmpty())
254            throw new AssertionError("Unexpected output: " + log);
255    }
256
257    @SupportedAnnotationTypes("*")
258    public static final class VerifyUsesProvidesAP extends AbstractProcessor {
259
260        @Override
261        public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
262            TypeElement api = processingEnv.getElementUtils().getTypeElement("api.Api");
263
264            assertNonNull("Cannot find api.Api", api);
265
266            ModuleElement modle = (ModuleElement) processingEnv.getElementUtils().getPackageOf(api).getEnclosingElement();
267
268            assertNonNull("modle is null", modle);
269
270            List<? extends UsesDirective> uses = ElementFilter.usesIn(modle.getDirectives());
271            assertEquals(1, uses.size());
272            assertEquals("api.Api", uses.iterator().next().getService().getQualifiedName().toString());
273
274            List<? extends ProvidesDirective> provides = ElementFilter.providesIn(modle.getDirectives());
275            assertEquals(1, provides.size());
276            assertEquals("api.Api", provides.iterator().next().getService().getQualifiedName().toString());
277            assertEquals("impl.Impl", provides.iterator().next().getImplementations().get(0).getQualifiedName().toString());
278
279            return false;
280        }
281
282        @Override
283        public SourceVersion getSupportedSourceVersion() {
284            return SourceVersion.latest();
285        }
286
287    }
288
289    @Test
290    public void testPackageNoModule(Path base) throws Exception {
291        Path src = base.resolve("src");
292        Path classes = base.resolve("classes");
293
294        Files.createDirectories(classes);
295
296        tb.writeJavaFiles(src,
297                          "package api; public class Api { }");
298
299        String log = new JavacTask(tb)
300                .options("-processor", VerifyPackageNoModule.class.getName(),
301                         "-source", "8",
302                         "-Xlint:-options")
303                .outdir(classes)
304                .files(findJavaFiles(src))
305                .run()
306                .writeAll()
307                .getOutput(Task.OutputKind.DIRECT);
308
309        if (!log.isEmpty())
310            throw new AssertionError("Unexpected output: " + log);
311    }
312
313    @SupportedAnnotationTypes("*")
314    public static final class VerifyPackageNoModule extends AbstractProcessor {
315
316        @Override
317        public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
318            TypeElement api = processingEnv.getElementUtils().getTypeElement("api.Api");
319
320            assertNonNull("Cannot find api.Api", api);
321
322            ModuleElement modle = (ModuleElement) processingEnv.getElementUtils().getPackageOf(api).getEnclosingElement();
323
324            assertNull("modle is not null", modle);
325
326            return false;
327        }
328
329        @Override
330        public SourceVersion getSupportedSourceVersion() {
331            return SourceVersion.latest();
332        }
333
334    }
335
336    @Test
337    public void testQualifiedClassForProcessing(Path base) throws Exception {
338        Path moduleSrc = base.resolve("module-src");
339        Path m1 = moduleSrc.resolve("m1x");
340        Path m2 = moduleSrc.resolve("m2x");
341
342        Path classes = base.resolve("classes");
343
344        Files.createDirectories(classes);
345
346        tb.writeJavaFiles(m1,
347                          "module m1x { }",
348                          "package impl; public class Impl { int m1x; }");
349
350        tb.writeJavaFiles(m2,
351                          "module m2x { }",
352                          "package impl; public class Impl { int m2x; }");
353
354        new JavacTask(tb)
355            .options("--module-source-path", moduleSrc.toString())
356            .outdir(classes)
357            .files(findJavaFiles(moduleSrc))
358            .run()
359            .writeAll()
360            .getOutput(Task.OutputKind.DIRECT);
361
362        List<String> expected = List.of("Note: field: m1x");
363
364        for (Mode mode : new Mode[] {Mode.API, Mode.CMDLINE}) {
365            List<String> log = new JavacTask(tb, mode)
366                    .options("-processor", QualifiedClassForProcessing.class.getName(),
367                             "--module-path", classes.toString())
368                    .classes("m1x/impl.Impl")
369                    .outdir(classes)
370                    .run()
371                    .writeAll()
372                    .getOutputLines(Task.OutputKind.DIRECT);
373
374            if (!expected.equals(log))
375                throw new AssertionError("Unexpected output: " + log);
376        }
377    }
378
379    @SupportedAnnotationTypes("*")
380    public static final class QualifiedClassForProcessing extends AbstractProcessor {
381
382        @Override
383        public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
384            if (processingEnv.getElementUtils().getModuleElement("m1x") == null) {
385                throw new AssertionError("No m1x module found.");
386            }
387
388            Messager messager = processingEnv.getMessager();
389
390            for (TypeElement clazz : ElementFilter.typesIn(roundEnv.getRootElements())) {
391                for (VariableElement field : ElementFilter.fieldsIn(clazz.getEnclosedElements())) {
392                    messager.printMessage(Kind.NOTE, "field: " + field.getSimpleName());
393                }
394            }
395
396            return false;
397        }
398
399        @Override
400        public SourceVersion getSupportedSourceVersion() {
401            return SourceVersion.latest();
402        }
403
404    }
405
406    @Test
407    public void testModuleInRootElements(Path base) throws Exception {
408        Path moduleSrc = base.resolve("module-src");
409        Path m1 = moduleSrc.resolve("m1");
410
411        Path classes = base.resolve("classes");
412
413        Files.createDirectories(classes);
414
415        tb.writeJavaFiles(m1,
416                          "module m1x { exports api; }",
417                          "package api; public class Api { }");
418
419        List<String> log = new JavacTask(tb)
420                .options("-processor", ModuleInRootElementsAP.class.getName())
421                .outdir(classes)
422                .files(findJavaFiles(moduleSrc))
423                .run()
424                .writeAll()
425                .getOutputLines(Task.OutputKind.STDERR);
426
427        assertEquals(List.of("module: m1x"), log);
428    }
429
430    @SupportedAnnotationTypes("*")
431    public static final class ModuleInRootElementsAP extends AbstractProcessor {
432
433        @Override
434        public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
435            roundEnv.getRootElements()
436                    .stream()
437                    .filter(el -> el.getKind() == ElementKind.MODULE)
438                    .forEach(mod -> System.err.println("module: " + mod.getSimpleName()));
439
440            return false;
441        }
442
443        @Override
444        public SourceVersion getSupportedSourceVersion() {
445            return SourceVersion.latest();
446        }
447
448    }
449
450    @Test
451    public void testAnnotationsInModuleInfo(Path base) throws Exception {
452        Path moduleSrc = base.resolve("module-src");
453        Path m1 = moduleSrc.resolve("m1");
454
455        tb.writeJavaFiles(m1,
456                          "@Deprecated module m1x { }");
457
458        Path m2 = moduleSrc.resolve("m2x");
459
460        tb.writeJavaFiles(m2,
461                          "@SuppressWarnings(\"\") module m2x { }");
462
463        Path classes = base.resolve("classes");
464
465        Files.createDirectories(classes);
466
467        List<String> log = new JavacTask(tb)
468                .options("-processor", AnnotationsInModuleInfoPrint.class.getName())
469                .outdir(classes)
470                .files(findJavaFiles(m1))
471                .run()
472                .writeAll()
473                .getOutputLines(Task.OutputKind.DIRECT);
474
475        List<String> expectedLog = List.of("Note: AP Invoked",
476                                           "Note: AP Invoked");
477
478        assertEquals(expectedLog, log);
479
480        new JavacTask(tb)
481            .options("-processor", AnnotationsInModuleInfoFail.class.getName())
482            .outdir(classes)
483            .files(findJavaFiles(m2))
484            .run()
485            .writeAll();
486    }
487
488    @SupportedAnnotationTypes("java.lang.Deprecated")
489    public static final class AnnotationsInModuleInfoPrint extends AbstractProcessor {
490
491        @Override
492        public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
493            processingEnv.getMessager().printMessage(Kind.NOTE, "AP Invoked");
494            return false;
495        }
496
497        @Override
498        public SourceVersion getSupportedSourceVersion() {
499            return SourceVersion.latest();
500        }
501
502    }
503
504    @SupportedAnnotationTypes("java.lang.Deprecated")
505    public static final class AnnotationsInModuleInfoFail extends AbstractProcessor {
506
507        @Override
508        public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
509            throw new AssertionError();
510        }
511
512        @Override
513        public SourceVersion getSupportedSourceVersion() {
514            return SourceVersion.latest();
515        }
516
517    }
518
519    @Test
520    public void testGenerateInMultiModeAPI(Path base) throws Exception {
521        Path moduleSrc = base.resolve("module-src");
522        Path classes = base.resolve("classes");
523
524        Files.createDirectories(classes);
525
526        Path m1 = moduleSrc.resolve("m1x");
527
528        tb.writeJavaFiles(m1,
529                          "module m1x { exports api1; }",
530                          "package api1; public class Api { GenApi ga; impl.Impl i; }");
531
532        writeFile("1", m1, "api1", "api");
533        writeFile("1", m1, "impl", "impl");
534
535        Path m2 = moduleSrc.resolve("m2x");
536
537        tb.writeJavaFiles(m2,
538                          "module m2x { requires m1x; exports api2; }",
539                          "package api2; public class Api { api1.GenApi ga1; GenApi qa2; impl.Impl i;}");
540
541        writeFile("2", m2, "api2", "api");
542        writeFile("2", m2, "impl", "impl");
543
544        for (FileType fileType : FileType.values()) {
545            if (Files.isDirectory(classes)) {
546                tb.cleanDirectory(classes);
547            } else {
548                Files.createDirectories(classes);
549            }
550
551            new JavacTask(tb)
552              .options("-processor", MultiModeAPITestAP.class.getName(),
553                       "--module-source-path", moduleSrc.toString(),
554                       "-Afiletype=" + fileType.name())
555              .outdir(classes)
556              .files(findJavaFiles(moduleSrc))
557              .run()
558              .writeAll();
559
560            assertFileExists(classes, "m1x", "api1", "GenApi.class");
561            assertFileExists(classes, "m1x", "impl", "Impl.class");
562            assertFileExists(classes, "m1x", "api1", "gen1");
563            assertFileExists(classes, "m2x", "api2", "GenApi.class");
564            assertFileExists(classes, "m2x", "impl", "Impl.class");
565            assertFileExists(classes, "m2x", "api2", "gen1");
566        }
567    }
568
569    enum FileType {
570        SOURCE,
571        CLASS;
572    }
573
574    public static abstract class GeneratingAP extends AbstractProcessor {
575
576        void createSource(CreateFileObject file, String name, String content) {
577            try (Writer out = file.create().openWriter()) {
578                out.write(content);
579            } catch (IOException ex) {
580                throw new IllegalStateException(ex);
581            }
582        }
583
584        void createClass(CreateFileObject file, String name, String content) {
585            String fileNameStub = name.replace(".", File.separator);
586
587            try (OutputStream out = file.create().openOutputStream()) {
588                Path scratch = Files.createDirectories(Paths.get(""));
589                Path scratchSrc = scratch.resolve(fileNameStub + ".java").toAbsolutePath();
590
591                Files.createDirectories(scratchSrc.getParent());
592
593                try (Writer w = Files.newBufferedWriter(scratchSrc)) {
594                    w.write(content);
595                }
596
597                Path scratchClasses = scratch.resolve("classes");
598
599                Files.createDirectories(scratchClasses);
600
601                JavaCompiler comp = ToolProvider.getSystemJavaCompiler();
602                try (StandardJavaFileManager fm = comp.getStandardFileManager(null, null, null)) {
603                    List<String> options = List.of("-d", scratchClasses.toString());
604                    Iterable<? extends JavaFileObject> files = fm.getJavaFileObjects(scratchSrc);
605                    CompilationTask task = comp.getTask(null, fm, null, options, null, files);
606
607                    if (!task.call()) {
608                        throw new AssertionError("compilation failed");
609                    }
610                }
611
612                Path classfile = scratchClasses.resolve(fileNameStub + ".class");
613
614                Files.copy(classfile, out);
615            } catch (IOException ex) {
616                throw new IllegalStateException(ex);
617            }
618        }
619
620        void doReadResource(CreateFileObject file, String expectedContent) {
621            try {
622                StringBuilder actualContent = new StringBuilder();
623
624                try (Reader r = file.create().openReader(true)) {
625                    int read;
626
627                    while ((read = r.read()) != (-1)) {
628                        actualContent.append((char) read);
629                    }
630
631                }
632
633                assertEquals(expectedContent, actualContent.toString());
634            } catch (IOException ex) {
635                throw new IllegalStateException(ex);
636            }
637        }
638
639        public interface CreateFileObject {
640            public FileObject create() throws IOException;
641        }
642
643        void expectFilerException(Callable<Object> c) {
644            try {
645                c.call();
646                throw new AssertionError("Expected exception not thrown");
647            } catch (FilerException ex) {
648                //expected
649            } catch (Exception ex) {
650                throw new IllegalStateException(ex);
651            }
652        }
653
654        @Override
655        public SourceVersion getSupportedSourceVersion() {
656            return SourceVersion.latest();
657        }
658
659    }
660
661    @SupportedAnnotationTypes("*")
662    @SupportedOptions({"filetype", "modulename"})
663    public static final class MultiModeAPITestAP extends GeneratingAP {
664
665        int round;
666
667        @Override
668        public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
669            if (round++ != 0)
670                return false;
671
672            createClass("m1x", "api1.GenApi", "package api1; public class GenApi {}");
673            createClass("m1x", "impl.Impl", "package impl; public class Impl {}");
674            createClass("m2x", "api2.GenApi", "package api2; public class GenApi {}");
675            createClass("m2x", "impl.Impl", "package impl; public class Impl {}");
676
677            createResource("m1x", "api1", "gen1");
678            createResource("m2x", "api2", "gen1");
679
680            readResource("m1x", "api1", "api", "1");
681            readResource("m1x", "impl", "impl", "1");
682            readResource("m2x", "api2", "api", "2");
683            readResource("m2x", "impl", "impl", "2");
684
685            Filer filer = processingEnv.getFiler();
686
687            expectFilerException(() -> filer.createSourceFile("fail.Fail"));
688            expectFilerException(() -> filer.createClassFile("fail.Fail"));
689            expectFilerException(() -> filer.createResource(StandardLocation.CLASS_OUTPUT, "fail", "fail"));
690            expectFilerException(() -> filer.getResource(StandardLocation.MODULE_SOURCE_PATH, "fail", "fail"));
691
692            //must not generate to unnamed package:
693            expectFilerException(() -> filer.createSourceFile("m1/Fail"));
694            expectFilerException(() -> filer.createClassFile("m1/Fail"));
695
696            //cannot generate resources to modules that are not root modules:
697            expectFilerException(() -> filer.createSourceFile("java.base/fail.Fail"));
698            expectFilerException(() -> filer.createClassFile("java.base/fail.Fail"));
699            expectFilerException(() -> filer.createResource(StandardLocation.CLASS_OUTPUT, "java.base/fail", "Fail"));
700
701            return false;
702        }
703
704        void createClass(String expectedModule, String name, String content) {
705            Filer filer = processingEnv.getFiler();
706            FileType filetype = FileType.valueOf(processingEnv.getOptions().getOrDefault("filetype", ""));
707
708            switch (filetype) {
709                case SOURCE:
710                    createSource(() -> filer.createSourceFile(expectedModule + "/" + name), name, content);
711                    break;
712                case CLASS:
713                    createClass(() -> filer.createClassFile(expectedModule + "/" + name), name, content);
714                    break;
715                default:
716                    throw new AssertionError("Unexpected filetype: " + filetype);
717            }
718        }
719
720        void createResource(String expectedModule, String pkg, String relName) {
721            try {
722                Filer filer = processingEnv.getFiler();
723
724                filer.createResource(StandardLocation.CLASS_OUTPUT, expectedModule + "/" + pkg, relName)
725                     .openOutputStream()
726                     .close();
727            } catch (IOException ex) {
728                throw new IllegalStateException(ex);
729            }
730        }
731
732        void readResource(String expectedModule, String pkg, String relName, String expectedContent) {
733            Filer filer = processingEnv.getFiler();
734
735            doReadResource(() -> filer.getResource(StandardLocation.MODULE_SOURCE_PATH, expectedModule + "/" + pkg, relName),
736                           expectedContent);
737        }
738
739    }
740
741    @Test
742    public void testGenerateInSingleNameModeAPI(Path base) throws Exception {
743        Path classes = base.resolve("classes");
744
745        Files.createDirectories(classes);
746
747        Path m1 = base.resolve("module-src");
748
749        tb.writeJavaFiles(m1,
750                          "module m1x { }");
751
752        writeFile("3", m1, "impl", "resource");
753
754        new JavacTask(tb)
755          .options("-processor", SingleNameModeAPITestAP.class.getName(),
756                   "-sourcepath", m1.toString())
757          .outdir(classes)
758          .files(findJavaFiles(m1))
759          .run()
760          .writeAll();
761
762        assertFileExists(classes, "impl", "Impl1.class");
763        assertFileExists(classes, "impl", "Impl2.class");
764        assertFileExists(classes, "impl", "Impl3");
765        assertFileExists(classes, "impl", "Impl4.class");
766        assertFileExists(classes, "impl", "Impl5.class");
767        assertFileExists(classes, "impl", "Impl6");
768        assertFileExists(classes, "impl", "Impl7.class");
769        assertFileExists(classes, "impl", "Impl8.class");
770        assertFileExists(classes, "impl", "Impl9");
771    }
772
773
774    @SupportedAnnotationTypes("*")
775    public static final class SingleNameModeAPITestAP extends GeneratingAP {
776
777        int round;
778
779        @Override
780        public synchronized void init(ProcessingEnvironment processingEnv) {
781            super.init(processingEnv);
782        }
783
784        @Override
785        public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
786            if (round++ != 0)
787                return false;
788
789            Filer filer = processingEnv.getFiler();
790
791            createSource(() -> filer.createSourceFile("impl.Impl1"), "impl.Impl1", "package impl; class Impl1 {}");
792            createClass(() -> filer.createClassFile("impl.Impl2"), "impl.Impl2", "package impl; class Impl2 {}");
793            createSource(() -> filer.createResource(StandardLocation.CLASS_OUTPUT, "impl", "Impl3"), "impl.Impl3", "");
794            doReadResource(() -> filer.getResource(StandardLocation.SOURCE_PATH, "impl", "resource"), "3");
795
796            createSource(() -> filer.createSourceFile("m1x/impl.Impl4"), "impl.Impl4", "package impl; class Impl4 {}");
797            createClass(() -> filer.createClassFile("m1x/impl.Impl5"), "impl.Impl5", "package impl; class Impl5 {}");
798            createSource(() -> filer.createResource(StandardLocation.CLASS_OUTPUT, "m1x/impl", "Impl6"), "impl.Impl6", "");
799            doReadResource(() -> filer.getResource(StandardLocation.SOURCE_PATH, "m1x/impl", "resource"), "3");
800
801            TypeElement jlObject = processingEnv.getElementUtils().getTypeElement("java.lang.Object");
802
803            //"broken" originating element:
804            createSource(() -> filer.createSourceFile("impl.Impl7", jlObject), "impl.Impl7", "package impl; class Impl7 {}");
805            createClass(() -> filer.createClassFile("impl.Impl8", jlObject), "impl.Impl8", "package impl; class Impl8 {}");
806            createSource(() -> filer.createResource(StandardLocation.CLASS_OUTPUT, "impl", "Impl9", jlObject), "impl.Impl9", "");
807
808            //must not generate to unnamed package:
809            expectFilerException(() -> filer.createSourceFile("Fail"));
810            expectFilerException(() -> filer.createClassFile("Fail"));
811            expectFilerException(() -> filer.createSourceFile("m1x/Fail"));
812            expectFilerException(() -> filer.createClassFile("m1x/Fail"));
813
814            //cannot generate resources to modules that are not root modules:
815            expectFilerException(() -> filer.createSourceFile("java.base/fail.Fail"));
816            expectFilerException(() -> filer.createClassFile("java.base/fail.Fail"));
817            expectFilerException(() -> filer.createResource(StandardLocation.CLASS_OUTPUT, "java.base/fail", "Fail"));
818
819            return false;
820        }
821
822    }
823
824    @Test
825    public void testGenerateInUnnamedModeAPI(Path base) throws Exception {
826        Path classes = base.resolve("classes");
827
828        Files.createDirectories(classes);
829
830        Path src = base.resolve("src");
831
832        tb.writeJavaFiles(src,
833                          "class T {}");
834
835        new JavacTask(tb)
836          .options("-processor", UnnamedModeAPITestAP.class.getName(),
837                   "-sourcepath", src.toString())
838          .outdir(classes)
839          .files(findJavaFiles(src))
840          .run()
841          .writeAll();
842
843        assertFileExists(classes, "Impl1.class");
844        assertFileExists(classes, "Impl2.class");
845    }
846
847    @Test
848    public void testGenerateInNoModeAPI(Path base) throws Exception {
849        Path classes = base.resolve("classes");
850
851        Files.createDirectories(classes);
852
853        Path src = base.resolve("src");
854
855        tb.writeJavaFiles(src,
856                          "class T {}");
857
858        new JavacTask(tb)
859          .options("-processor", UnnamedModeAPITestAP.class.getName(),
860                   "-source", "8", "-target", "8",
861                   "-sourcepath", src.toString())
862          .outdir(classes)
863          .files(findJavaFiles(src))
864          .run()
865          .writeAll();
866
867        assertFileExists(classes, "Impl1.class");
868        assertFileExists(classes, "Impl2.class");
869    }
870
871    @SupportedAnnotationTypes("*")
872    public static final class UnnamedModeAPITestAP extends GeneratingAP {
873
874        int round;
875
876        @Override
877        public synchronized void init(ProcessingEnvironment processingEnv) {
878            super.init(processingEnv);
879        }
880
881        @Override
882        public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
883            if (round++ != 0)
884                return false;
885
886            Filer filer = processingEnv.getFiler();
887
888            //must not generate to unnamed package:
889            createSource(() -> filer.createSourceFile("Impl1"), "Impl1", "class Impl1 {}");
890            createClass(() -> filer.createClassFile("Impl2"), "Impl2", "class Impl2 {}");
891
892            return false;
893        }
894
895    }
896
897    @Test
898    public void testDisambiguateAnnotations(Path base) throws Exception {
899        Path classes = base.resolve("classes");
900
901        Files.createDirectories(classes);
902
903        Path src = base.resolve("src");
904        Path m1 = src.resolve("m1x");
905
906        tb.writeJavaFiles(m1,
907                          "module m1x { exports api; }",
908                          "package api; public @interface A {}",
909                          "package api; public @interface B {}");
910
911        Path m2 = src.resolve("m2x");
912
913        tb.writeJavaFiles(m2,
914                          "module m2x { exports api; }",
915                          "package api; public @interface A {}",
916                          "package api; public @interface B {}");
917
918        Path m3 = src.resolve("m3x");
919
920        tb.writeJavaFiles(m3,
921                          "module m3x { requires m1x; }",
922                          "package impl; import api.*; @A @B public class T {}");
923
924        Path m4 = src.resolve("m4x");
925
926        tb.writeJavaFiles(m4,
927                          "module m4x { requires m2x; }",
928                          "package impl; import api.*; @A @B public class T {}");
929
930        List<String> log;
931        List<String> expected;
932
933        log = new JavacTask(tb)
934            .options("-processor", SelectAnnotationATestAP.class.getName() + "," + SelectAnnotationBTestAP.class.getName(),
935                     "--module-source-path", src.toString(),
936                     "-m", "m1x,m2x")
937            .outdir(classes)
938            .run()
939            .writeAll()
940            .getOutputLines(OutputKind.STDERR);
941
942        expected = List.of("");
943
944        if (!expected.equals(log)) {
945            throw new AssertionError("Output does not match; output: " + log);
946        }
947
948        log = new JavacTask(tb)
949            .options("-processor", SelectAnnotationATestAP.class.getName() + "," + SelectAnnotationBTestAP.class.getName(),
950                     "--module-source-path", src.toString(),
951                     "-m", "m3x")
952            .outdir(classes)
953            .run()
954            .writeAll()
955            .getOutputLines(OutputKind.STDERR);
956
957        expected = List.of("SelectAnnotationBTestAP",
958                           "SelectAnnotationBTestAP");
959
960        if (!expected.equals(log)) {
961            throw new AssertionError("Output does not match; output: " + log);
962        }
963
964        log = new JavacTask(tb)
965            .options("-processor", SelectAnnotationATestAP.class.getName() + "," +
966                                   SelectAnnotationBTestAP.class.getName() + "," +
967                                   SelectAnnotationAStrictTestAP.class.getName(),
968                     "--module-source-path", src.toString(),
969                     "-m", "m4x")
970            .outdir(classes)
971            .run()
972            .writeAll()
973            .getOutputLines(OutputKind.STDERR);
974
975        expected = List.of("SelectAnnotationATestAP",
976                           "SelectAnnotationBTestAP",
977                           "SelectAnnotationAStrictTestAP",
978                           "SelectAnnotationATestAP",
979                           "SelectAnnotationBTestAP",
980                           "SelectAnnotationAStrictTestAP");
981
982        if (!expected.equals(log)) {
983            throw new AssertionError("Output does not match; output: " + log);
984        }
985    }
986
987    @Test
988    public void testDisambiguateAnnotationsUnnamedModule(Path base) throws Exception {
989        Path classes = base.resolve("classes");
990
991        Files.createDirectories(classes);
992
993        Path src = base.resolve("src");
994
995        tb.writeJavaFiles(src,
996                          "package api; public @interface A {}",
997                          "package api; public @interface B {}",
998                          "package impl; import api.*; @A @B public class T {}");
999
1000        List<String> log = new JavacTask(tb)
1001            .options("-processor", SelectAnnotationATestAP.class.getName() + "," +
1002                                   SelectAnnotationBTestAP.class.getName() + "," +
1003                                   SelectAnnotationAStrictTestAP.class.getName())
1004            .outdir(classes)
1005            .files(findJavaFiles(src))
1006            .run()
1007            .writeAll()
1008            .getOutputLines(OutputKind.STDERR);
1009
1010        List<String> expected = List.of("SelectAnnotationBTestAP",
1011                                        "SelectAnnotationBTestAP");
1012
1013        if (!expected.equals(log)) {
1014            throw new AssertionError("Output does not match; output: " + log);
1015        }
1016    }
1017
1018    @Test
1019    public void testDisambiguateAnnotationsNoModules(Path base) throws Exception {
1020        Path classes = base.resolve("classes");
1021
1022        Files.createDirectories(classes);
1023
1024        Path src = base.resolve("src");
1025
1026        tb.writeJavaFiles(src,
1027                          "package api; public @interface A {}",
1028                          "package api; public @interface B {}",
1029                          "package impl; import api.*; @A @B public class T {}");
1030
1031        List<String> log = new JavacTask(tb)
1032            .options("-processor", SelectAnnotationATestAP.class.getName() + "," +
1033                                   SelectAnnotationBTestAP.class.getName() + "," +
1034                                   SelectAnnotationAStrictTestAP.class.getName(),
1035                     "-source", "8", "-target", "8")
1036            .outdir(classes)
1037            .files(findJavaFiles(src))
1038            .run()
1039            .writeAll()
1040            .getOutputLines(OutputKind.STDERR);
1041
1042        List<String> expected = List.of("SelectAnnotationATestAP",
1043                                        "SelectAnnotationBTestAP",
1044                                        "SelectAnnotationATestAP",
1045                                        "SelectAnnotationBTestAP");
1046
1047        if (!expected.equals(log)) {
1048            throw new AssertionError("Output does not match; output: " + log);
1049        }
1050    }
1051
1052    @SupportedAnnotationTypes("m2x/api.A")
1053    public static final class SelectAnnotationATestAP extends AbstractProcessor {
1054
1055        @Override
1056        public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
1057            System.err.println("SelectAnnotationATestAP");
1058
1059            return false;
1060        }
1061
1062    }
1063
1064    @SupportedAnnotationTypes("api.B")
1065    public static final class SelectAnnotationBTestAP extends AbstractProcessor {
1066
1067        @Override
1068        public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
1069            System.err.println("SelectAnnotationBTestAP");
1070
1071            return false;
1072        }
1073
1074    }
1075
1076    public static final class SelectAnnotationAStrictTestAP extends AbstractProcessor {
1077
1078        @Override
1079        public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
1080            System.err.println("SelectAnnotationAStrictTestAP");
1081
1082            return false;
1083        }
1084
1085        @Override
1086        public Set<String> getSupportedAnnotationTypes() {
1087            return Set.of("m2x/api.A");
1088        }
1089
1090    }
1091
1092    private static void writeFile(String content, Path base, String... pathElements) throws IOException {
1093        Path file = resolveFile(base, pathElements);
1094
1095        Files.createDirectories(file.getParent());
1096
1097        try (Writer out = Files.newBufferedWriter(file)) {
1098            out.append(content);
1099        }
1100    }
1101
1102    @Test
1103    public void testUnboundLookup(Path base) throws Exception {
1104        Path src = base.resolve("src");
1105
1106        tb.writeJavaFiles(src,
1107                          "package impl.conflict.src; public class Impl { }");
1108
1109        Path moduleSrc = base.resolve("module-src");
1110        Path m1 = moduleSrc.resolve("m1x");
1111        Path m2 = moduleSrc.resolve("m2x");
1112
1113        Path classes = base.resolve("classes");
1114        Path cpClasses = base.resolve("cpClasses");
1115
1116        Files.createDirectories(classes);
1117        Files.createDirectories(cpClasses);
1118
1119        tb.writeJavaFiles(m1,
1120                          "module m1x { }",
1121                          "package impl1; public class Impl { }",
1122                          "package impl.conflict.module; class Impl { }",
1123                          "package impl.conflict.clazz; public class pkg { public static class I { } }",
1124                          "package impl.conflict.src; public class Impl { }",
1125                          "package nested.pack.pack; public class Impl { }",
1126                          "package unique.nested; public class Impl { }");
1127
1128        tb.writeJavaFiles(m2,
1129                          "module m2x { }",
1130                          "package impl2; public class Impl { }",
1131                          "package impl.conflict.module; class Impl { }",
1132                          "package impl.conflict; public class clazz { public static class pkg { } }",
1133                          "package nested.pack; public class Impl { }");
1134
1135        //from source:
1136        String log = new JavacTask(tb)
1137            .options("--module-source-path", moduleSrc.toString(),
1138                     "--source-path", src.toString(),
1139                     "-processorpath", System.getProperty("test.class.path"),
1140                     "-processor", UnboundLookup.class.getName(),
1141                     "-XDrawDiagnostics")
1142            .outdir(classes)
1143            .files(findJavaFiles(moduleSrc))
1144            .run()
1145            .writeAll()
1146            .getOutput(OutputKind.DIRECT);
1147
1148        String moduleImplConflictString =
1149                "- compiler.note.multiple.elements: getTypeElement, impl.conflict.module.Impl, m2x, m1x";
1150        String srcConflictString =
1151                "- compiler.note.multiple.elements: getTypeElement, impl.conflict.src.Impl, m1x, unnamed module";
1152
1153        if (!log.contains(moduleImplConflictString) ||
1154            !log.contains(srcConflictString)) {
1155            throw new AssertionError("Expected output not found: " + log);
1156        }
1157
1158        if (log.split(Pattern.quote(moduleImplConflictString)).length > 2) {
1159            throw new AssertionError("Too many warnings in: " + log);
1160        }
1161
1162        new JavacTask(tb)
1163            .options("--source-path", src.toString())
1164            .outdir(cpClasses)
1165            .files(findJavaFiles(src))
1166            .run()
1167            .writeAll();
1168
1169        //from classfiles:
1170        new JavacTask(tb)
1171            .options("--module-path", classes.toString(),
1172                     "--class-path", cpClasses.toString(),
1173                     "--add-modules", "m1x,m2x",
1174                     "-processorpath", System.getProperty("test.class.path"),
1175                     "-processor", UnboundLookup.class.getName(),
1176                     "-proc:only")
1177            .classes("java.lang.Object")
1178            .run()
1179            .writeAll();
1180
1181        //source 8:
1182        new JavacTask(tb)
1183            .options("--source-path", src.toString(),
1184                     "-source", "8",
1185                     "-processorpath", System.getProperty("test.class.path"),
1186                     "-processor", UnboundLookup8.class.getName())
1187            .outdir(cpClasses)
1188            .files(findJavaFiles(src))
1189            .run()
1190            .writeAll();
1191
1192    }
1193
1194    @SupportedAnnotationTypes("*")
1195    public static final class UnboundLookup extends AbstractProcessor {
1196
1197        @Override
1198        public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
1199            assertTypeElementExists("impl1.Impl", "m1x");
1200            assertPackageElementExists("impl1", "m1x");
1201            assertTypeElementExists("impl2.Impl", "m2x");
1202            assertTypeElementExists("impl.conflict.clazz.pkg.I", "m1x");
1203            assertTypeElementExists("impl.conflict.clazz", "m2x");
1204            assertPackageElementExists("impl.conflict.clazz", "m1x");
1205            assertPackageElementExists("impl2", "m2x");
1206            assertPackageElementExists("nested.pack.pack", "m1x");
1207            assertPackageElementExists("nested.pack", "m2x");
1208            assertTypeElementExists("unique.nested.Impl", "m1x");
1209            assertTypeElementNotFound("impl.conflict.module.Impl");
1210            assertTypeElementNotFound("impl.conflict.module.Impl"); //check that the warning/note is produced only once
1211            assertPackageElementNotFound("impl.conflict.module");
1212            assertTypeElementNotFound("impl.conflict.src.Impl");
1213            assertPackageElementNotFound("impl.conflict.src");
1214            assertTypeElementNotFound("impl.conflict.clazz.pkg");
1215            assertPackageElementNotFound("unique"); //do not return packages without members in module mode
1216            assertTypeElementNotFound("nested"); //cannot distinguish between m1x and m2x
1217
1218            return false;
1219        }
1220
1221        private void assertTypeElementExists(String name, String expectedModule) {
1222            assertElementExists(name, "class", processingEnv.getElementUtils() :: getTypeElement, expectedModule);
1223        }
1224
1225        private void assertPackageElementExists(String name, String expectedModule) {
1226            assertElementExists(name, "package", processingEnv.getElementUtils() :: getPackageElement, expectedModule);
1227        }
1228
1229        private void assertElementExists(String name, String type, Function<String, Element> getter, String expectedModule) {
1230            Element clazz = getter.apply(name);
1231
1232            if (clazz == null) {
1233                throw new AssertionError("No " + name + " " + type + " found.");
1234            }
1235
1236            ModuleElement mod = processingEnv.getElementUtils().getModuleOf(clazz);
1237
1238            if (!mod.getQualifiedName().contentEquals(expectedModule)) {
1239                throw new AssertionError(name + " found in an unexpected module: " + mod.getQualifiedName());
1240            }
1241        }
1242
1243        private void assertTypeElementNotFound(String name) {
1244            assertElementNotFound(name, processingEnv.getElementUtils() :: getTypeElement);
1245        }
1246
1247        private void assertPackageElementNotFound(String name) {
1248            assertElementNotFound(name, processingEnv.getElementUtils() :: getPackageElement);
1249        }
1250
1251        private void assertElementNotFound(String name, Function<String, Element> getter) {
1252            Element found = getter.apply(name);
1253
1254            if (found != null) {
1255                fail("Element found unexpectedly: " + found);
1256            }
1257        }
1258
1259        @Override
1260        public SourceVersion getSupportedSourceVersion() {
1261            return SourceVersion.latest();
1262        }
1263
1264    }
1265
1266    @SupportedAnnotationTypes("*")
1267    public static final class UnboundLookup8 extends AbstractProcessor {
1268
1269        @Override
1270        public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
1271            if (processingEnv.getElementUtils().getTypeElement("impl.conflict.src.Impl") == null) {
1272                throw new AssertionError("impl.conflict.src.Impl.");
1273            }
1274
1275            if (processingEnv.getElementUtils().getModuleElement("java.base") != null) {
1276                throw new AssertionError("getModuleElement != null for -source 8");
1277            }
1278
1279            return false;
1280        }
1281
1282        @Override
1283        public SourceVersion getSupportedSourceVersion() {
1284            return SourceVersion.latest();
1285        }
1286
1287    }
1288
1289    private static void assertNonNull(String msg, Object val) {
1290        if (val == null) {
1291            throw new AssertionError(msg);
1292        }
1293    }
1294
1295    private static void assertNull(String msg, Object val) {
1296        if (val != null) {
1297            throw new AssertionError(msg);
1298        }
1299    }
1300
1301    private static void assertEquals(Object expected, Object actual) {
1302        if (!Objects.equals(expected, actual)) {
1303            throw new AssertionError("expected: " + expected + "; actual=" + actual);
1304        }
1305    }
1306
1307    private static void assertFileExists(Path base, String... pathElements) {
1308        Path file = resolveFile(base, pathElements);
1309
1310        if (!Files.exists(file)) {
1311            throw new AssertionError("Expected file: " + file + " exist, but it does not.");
1312        }
1313    }
1314
1315    static Path resolveFile(Path base, String... pathElements) {
1316        Path file = base;
1317
1318        for (String el : pathElements) {
1319            file = file.resolve(el);
1320        }
1321
1322        return file;
1323    }
1324
1325    private static void fail(String msg) {
1326        throw new AssertionError(msg);
1327    }
1328
1329}
1330