test/langtools/tools/javac/modules/AnnotationProcessing.java
changeset 47216 71c04702a3d5
parent 44291 e1b620ac6c98
child 50704 1a3f1cf62456
equal deleted inserted replaced
47215:4ebc2e2fb97c 47216:71c04702a3d5
       
     1 /*
       
     2  * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.
       
     8  *
       
     9  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    12  * version 2 for more details (a copy is included in the LICENSE file that
       
    13  * accompanied this code).
       
    14  *
       
    15  * You should have received a copy of the GNU General Public License version
       
    16  * 2 along with this work; if not, write to the Free Software Foundation,
       
    17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    18  *
       
    19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    20  * or visit www.oracle.com if you need additional information or have any
       
    21  * questions.
       
    22  */
       
    23 
       
    24 /**
       
    25  * @test
       
    26  * @bug 8133884 8162711 8133896 8172158 8172262 8173636 8175119
       
    27  * @summary Verify that annotation processing works.
       
    28  * @library /tools/lib
       
    29  * @modules
       
    30  *      jdk.compiler/com.sun.tools.javac.api
       
    31  *      jdk.compiler/com.sun.tools.javac.main
       
    32  * @build toolbox.ToolBox toolbox.JavacTask ModuleTestBase
       
    33  * @run main AnnotationProcessing
       
    34  */
       
    35 
       
    36 import java.io.File;
       
    37 import java.io.IOException;
       
    38 import java.io.OutputStream;
       
    39 import java.io.Reader;
       
    40 import java.io.UncheckedIOException;
       
    41 import java.io.Writer;
       
    42 import java.nio.file.Files;
       
    43 import java.nio.file.Path;
       
    44 import java.nio.file.Paths;
       
    45 import java.util.ArrayList;
       
    46 import java.util.Arrays;
       
    47 import java.util.HashMap;
       
    48 import java.util.HashSet;
       
    49 import java.util.List;
       
    50 import java.util.Map;
       
    51 import java.util.Objects;
       
    52 import java.util.Set;
       
    53 import java.util.concurrent.Callable;
       
    54 import java.util.function.Consumer;
       
    55 import java.util.function.Function;
       
    56 import java.util.regex.Pattern;
       
    57 import java.util.stream.Collectors;
       
    58 
       
    59 import javax.annotation.processing.AbstractProcessor;
       
    60 import javax.annotation.processing.Filer;
       
    61 import javax.annotation.processing.FilerException;
       
    62 import javax.annotation.processing.Messager;
       
    63 import javax.annotation.processing.ProcessingEnvironment;
       
    64 import javax.annotation.processing.RoundEnvironment;
       
    65 import javax.annotation.processing.SupportedAnnotationTypes;
       
    66 import javax.annotation.processing.SupportedOptions;
       
    67 import javax.lang.model.SourceVersion;
       
    68 import javax.lang.model.element.Element;
       
    69 import javax.lang.model.element.ElementKind;
       
    70 import javax.lang.model.element.ModuleElement;
       
    71 import javax.lang.model.element.ModuleElement.ProvidesDirective;
       
    72 import javax.lang.model.element.ModuleElement.UsesDirective;
       
    73 import javax.lang.model.element.PackageElement;
       
    74 import javax.lang.model.element.TypeElement;
       
    75 import javax.lang.model.element.VariableElement;
       
    76 import javax.lang.model.type.TypeKind;
       
    77 import javax.lang.model.util.ElementFilter;
       
    78 import javax.lang.model.util.ElementScanner9;
       
    79 import javax.tools.Diagnostic.Kind;
       
    80 import javax.tools.FileObject;
       
    81 import javax.tools.JavaCompiler;
       
    82 import javax.tools.JavaCompiler.CompilationTask;
       
    83 import javax.tools.JavaFileManager;
       
    84 import javax.tools.JavaFileManager.Location;
       
    85 import javax.tools.JavaFileObject;
       
    86 import javax.tools.StandardJavaFileManager;
       
    87 import javax.tools.StandardLocation;
       
    88 import javax.tools.ToolProvider;
       
    89 
       
    90 import toolbox.JavacTask;
       
    91 import toolbox.Task;
       
    92 import toolbox.Task.Mode;
       
    93 import toolbox.Task.OutputKind;
       
    94 
       
    95 public class AnnotationProcessing extends ModuleTestBase {
       
    96 
       
    97     public static void main(String... args) throws Exception {
       
    98         new AnnotationProcessing().runTests();
       
    99     }
       
   100 
       
   101     @Test
       
   102     public void testAPSingleModule(Path base) throws Exception {
       
   103         Path moduleSrc = base.resolve("module-src");
       
   104         Path m1 = moduleSrc.resolve("m1x");
       
   105 
       
   106         Path classes = base.resolve("classes");
       
   107 
       
   108         Files.createDirectories(classes);
       
   109 
       
   110         tb.writeJavaFiles(m1,
       
   111                           "module m1x { }",
       
   112                           "package impl; public class Impl { }");
       
   113 
       
   114         String log = new JavacTask(tb)
       
   115                 .options("--module-source-path", moduleSrc.toString(),
       
   116                          "-processor", AP.class.getName(),
       
   117                          "-AexpectedEnclosedElements=m1x=>impl")
       
   118                 .outdir(classes)
       
   119                 .files(findJavaFiles(moduleSrc))
       
   120                 .run()
       
   121                 .writeAll()
       
   122                 .getOutput(Task.OutputKind.DIRECT);
       
   123 
       
   124         if (!log.isEmpty())
       
   125             throw new AssertionError("Unexpected output: " + log);
       
   126     }
       
   127 
       
   128     @Test
       
   129     public void testAPMultiModule(Path base) throws Exception {
       
   130         Path moduleSrc = base.resolve("module-src");
       
   131         Path m1 = moduleSrc.resolve("m1x");
       
   132         Path m2 = moduleSrc.resolve("m2x");
       
   133 
       
   134         Path classes = base.resolve("classes");
       
   135 
       
   136         Files.createDirectories(classes);
       
   137 
       
   138         tb.writeJavaFiles(m1,
       
   139                           "module m1x { }",
       
   140                           "package impl1; public class Impl1 { }");
       
   141 
       
   142         tb.writeJavaFiles(m2,
       
   143                           "module m2x { }",
       
   144                           "package impl2; public class Impl2 { }");
       
   145 
       
   146         String log = new JavacTask(tb)
       
   147                 .options("--module-source-path", moduleSrc.toString(),
       
   148                          "-processor", AP.class.getName(),
       
   149                          "-AexpectedEnclosedElements=m1x=>impl1,m2x=>impl2")
       
   150                 .outdir(classes)
       
   151                 .files(findJavaFiles(moduleSrc))
       
   152                 .run()
       
   153                 .writeAll()
       
   154                 .getOutput(Task.OutputKind.DIRECT);
       
   155 
       
   156         if (!log.isEmpty())
       
   157             throw new AssertionError("Unexpected output: " + log);
       
   158     }
       
   159 
       
   160     @SupportedAnnotationTypes("*")
       
   161     @SupportedOptions("expectedEnclosedElements")
       
   162     public static final class AP extends AbstractProcessor {
       
   163 
       
   164         private Map<String, List<String>> module2ExpectedEnclosedElements;
       
   165         private Set<String> seenModules = new HashSet<>();
       
   166 
       
   167         @Override
       
   168         public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
       
   169             if (module2ExpectedEnclosedElements == null) {
       
   170                 module2ExpectedEnclosedElements = new HashMap<>();
       
   171 
       
   172                 String expectedEnclosedElements =
       
   173                         processingEnv.getOptions().get("expectedEnclosedElements");
       
   174 
       
   175                 for (String moduleDef : expectedEnclosedElements.split(",")) {
       
   176                     String[] module2Packages = moduleDef.split("=>");
       
   177 
       
   178                     module2ExpectedEnclosedElements.put(module2Packages[0],
       
   179                                                         List.of(module2Packages[1].split(":")));
       
   180                 }
       
   181             }
       
   182 
       
   183             //verify ModuleType and ModuleSymbol behavior:
       
   184             for (Element root : roundEnv.getRootElements()) {
       
   185                 ModuleElement module = processingEnv.getElementUtils().getModuleOf(root);
       
   186 
       
   187                 assertEquals(TypeKind.MODULE, module.asType().getKind());
       
   188 
       
   189                 boolean[] seenModule = new boolean[1];
       
   190 
       
   191                 module.accept(new ElementScanner9<Void, Void>() {
       
   192                     @Override
       
   193                     public Void visitModule(ModuleElement e, Void p) {
       
   194                         seenModule[0] = true;
       
   195                         return null;
       
   196                     }
       
   197                     @Override
       
   198                     public Void scan(Element e, Void p) {
       
   199                         throw new AssertionError("Shouldn't get here.");
       
   200                     }
       
   201                 }, null);
       
   202 
       
   203                 assertEquals(true, seenModule[0]);
       
   204 
       
   205                 List<String> actualElements =
       
   206                         module.getEnclosedElements()
       
   207                               .stream()
       
   208                               .map(s -> (PackageElement) s)
       
   209                               .map(p -> p.getQualifiedName().toString())
       
   210                               .collect(Collectors.toList());
       
   211 
       
   212                 String moduleName = module.getQualifiedName().toString();
       
   213 
       
   214                 assertEquals(module2ExpectedEnclosedElements.get(moduleName),
       
   215                              actualElements);
       
   216 
       
   217                 seenModules.add(moduleName);
       
   218             }
       
   219 
       
   220             if (roundEnv.processingOver()) {
       
   221                 assertEquals(module2ExpectedEnclosedElements.keySet(), seenModules);
       
   222             }
       
   223 
       
   224             return false;
       
   225         }
       
   226 
       
   227         @Override
       
   228         public SourceVersion getSupportedSourceVersion() {
       
   229             return SourceVersion.latest();
       
   230         }
       
   231 
       
   232     }
       
   233 
       
   234     @Test
       
   235     public void testVerifyUsesProvides(Path base) throws Exception {
       
   236         Path moduleSrc = base.resolve("module-src");
       
   237         Path m1 = moduleSrc.resolve("m1x");
       
   238 
       
   239         Path classes = base.resolve("classes");
       
   240 
       
   241         Files.createDirectories(classes);
       
   242 
       
   243         tb.writeJavaFiles(m1,
       
   244                           "module m1x { exports api; uses api.Api; provides api.Api with impl.Impl; }",
       
   245                           "package api; public class Api { }",
       
   246                           "package impl; public class Impl extends api.Api { }");
       
   247 
       
   248         String log = new JavacTask(tb)
       
   249                 .options("-doe", "-processor", VerifyUsesProvidesAP.class.getName())
       
   250                 .outdir(classes)
       
   251                 .files(findJavaFiles(moduleSrc))
       
   252                 .run()
       
   253                 .writeAll()
       
   254                 .getOutput(Task.OutputKind.DIRECT);
       
   255 
       
   256         if (!log.isEmpty())
       
   257             throw new AssertionError("Unexpected output: " + log);
       
   258     }
       
   259 
       
   260     @SupportedAnnotationTypes("*")
       
   261     public static final class VerifyUsesProvidesAP extends AbstractProcessor {
       
   262 
       
   263         @Override
       
   264         public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
       
   265             TypeElement api = processingEnv.getElementUtils().getTypeElement("api.Api");
       
   266 
       
   267             assertNonNull("Cannot find api.Api", api);
       
   268 
       
   269             ModuleElement modle = (ModuleElement) processingEnv.getElementUtils().getPackageOf(api).getEnclosingElement();
       
   270 
       
   271             assertNonNull("modle is null", modle);
       
   272 
       
   273             List<? extends UsesDirective> uses = ElementFilter.usesIn(modle.getDirectives());
       
   274             assertEquals(1, uses.size());
       
   275             assertEquals("api.Api", uses.iterator().next().getService().getQualifiedName().toString());
       
   276 
       
   277             List<? extends ProvidesDirective> provides = ElementFilter.providesIn(modle.getDirectives());
       
   278             assertEquals(1, provides.size());
       
   279             assertEquals("api.Api", provides.iterator().next().getService().getQualifiedName().toString());
       
   280             assertEquals("impl.Impl", provides.iterator().next().getImplementations().get(0).getQualifiedName().toString());
       
   281 
       
   282             return false;
       
   283         }
       
   284 
       
   285         @Override
       
   286         public SourceVersion getSupportedSourceVersion() {
       
   287             return SourceVersion.latest();
       
   288         }
       
   289 
       
   290     }
       
   291 
       
   292     @Test
       
   293     public void testPackageNoModule(Path base) throws Exception {
       
   294         Path src = base.resolve("src");
       
   295         Path classes = base.resolve("classes");
       
   296 
       
   297         Files.createDirectories(classes);
       
   298 
       
   299         tb.writeJavaFiles(src,
       
   300                           "package api; public class Api { }");
       
   301 
       
   302         String log = new JavacTask(tb)
       
   303                 .options("-processor", VerifyPackageNoModule.class.getName(),
       
   304                          "-source", "8",
       
   305                          "-Xlint:-options")
       
   306                 .outdir(classes)
       
   307                 .files(findJavaFiles(src))
       
   308                 .run()
       
   309                 .writeAll()
       
   310                 .getOutput(Task.OutputKind.DIRECT);
       
   311 
       
   312         if (!log.isEmpty())
       
   313             throw new AssertionError("Unexpected output: " + log);
       
   314     }
       
   315 
       
   316     @SupportedAnnotationTypes("*")
       
   317     public static final class VerifyPackageNoModule extends AbstractProcessor {
       
   318 
       
   319         @Override
       
   320         public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
       
   321             TypeElement api = processingEnv.getElementUtils().getTypeElement("api.Api");
       
   322 
       
   323             assertNonNull("Cannot find api.Api", api);
       
   324 
       
   325             ModuleElement modle = (ModuleElement) processingEnv.getElementUtils().getPackageOf(api).getEnclosingElement();
       
   326 
       
   327             assertNull("modle is not null", modle);
       
   328 
       
   329             return false;
       
   330         }
       
   331 
       
   332         @Override
       
   333         public SourceVersion getSupportedSourceVersion() {
       
   334             return SourceVersion.latest();
       
   335         }
       
   336 
       
   337     }
       
   338 
       
   339     @Test
       
   340     public void testQualifiedClassForProcessing(Path base) throws Exception {
       
   341         Path moduleSrc = base.resolve("module-src");
       
   342         Path m1 = moduleSrc.resolve("m1x");
       
   343         Path m2 = moduleSrc.resolve("m2x");
       
   344 
       
   345         Path classes = base.resolve("classes");
       
   346 
       
   347         Files.createDirectories(classes);
       
   348 
       
   349         tb.writeJavaFiles(m1,
       
   350                           "module m1x { }",
       
   351                           "package impl; public class Impl { int m1x; }");
       
   352 
       
   353         tb.writeJavaFiles(m2,
       
   354                           "module m2x { }",
       
   355                           "package impl; public class Impl { int m2x; }");
       
   356 
       
   357         new JavacTask(tb)
       
   358             .options("--module-source-path", moduleSrc.toString())
       
   359             .outdir(classes)
       
   360             .files(findJavaFiles(moduleSrc))
       
   361             .run()
       
   362             .writeAll()
       
   363             .getOutput(Task.OutputKind.DIRECT);
       
   364 
       
   365         List<String> expected = List.of("Note: field: m1x");
       
   366 
       
   367         for (Mode mode : new Mode[] {Mode.API, Mode.CMDLINE}) {
       
   368             List<String> log = new JavacTask(tb, mode)
       
   369                     .options("-processor", QualifiedClassForProcessing.class.getName(),
       
   370                              "--module-path", classes.toString())
       
   371                     .classes("m1x/impl.Impl")
       
   372                     .outdir(classes)
       
   373                     .run()
       
   374                     .writeAll()
       
   375                     .getOutputLines(Task.OutputKind.DIRECT);
       
   376 
       
   377             if (!expected.equals(log))
       
   378                 throw new AssertionError("Unexpected output: " + log);
       
   379         }
       
   380     }
       
   381 
       
   382     @SupportedAnnotationTypes("*")
       
   383     public static final class QualifiedClassForProcessing extends AbstractProcessor {
       
   384 
       
   385         @Override
       
   386         public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
       
   387             if (processingEnv.getElementUtils().getModuleElement("m1x") == null) {
       
   388                 throw new AssertionError("No m1x module found.");
       
   389             }
       
   390 
       
   391             Messager messager = processingEnv.getMessager();
       
   392 
       
   393             for (TypeElement clazz : ElementFilter.typesIn(roundEnv.getRootElements())) {
       
   394                 for (VariableElement field : ElementFilter.fieldsIn(clazz.getEnclosedElements())) {
       
   395                     messager.printMessage(Kind.NOTE, "field: " + field.getSimpleName());
       
   396                 }
       
   397             }
       
   398 
       
   399             return false;
       
   400         }
       
   401 
       
   402         @Override
       
   403         public SourceVersion getSupportedSourceVersion() {
       
   404             return SourceVersion.latest();
       
   405         }
       
   406 
       
   407     }
       
   408 
       
   409     @Test
       
   410     public void testModuleInRootElements(Path base) throws Exception {
       
   411         Path moduleSrc = base.resolve("module-src");
       
   412         Path m1 = moduleSrc.resolve("m1");
       
   413 
       
   414         Path classes = base.resolve("classes");
       
   415 
       
   416         Files.createDirectories(classes);
       
   417 
       
   418         tb.writeJavaFiles(m1,
       
   419                           "module m1x { exports api; }",
       
   420                           "package api; public class Api { }");
       
   421 
       
   422         List<String> log = new JavacTask(tb)
       
   423                 .options("-processor", ModuleInRootElementsAP.class.getName())
       
   424                 .outdir(classes)
       
   425                 .files(findJavaFiles(moduleSrc))
       
   426                 .run()
       
   427                 .writeAll()
       
   428                 .getOutputLines(Task.OutputKind.STDERR);
       
   429 
       
   430         assertEquals(List.of("module: m1x"), log);
       
   431     }
       
   432 
       
   433     @SupportedAnnotationTypes("*")
       
   434     public static final class ModuleInRootElementsAP extends AbstractProcessor {
       
   435 
       
   436         @Override
       
   437         public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
       
   438             roundEnv.getRootElements()
       
   439                     .stream()
       
   440                     .filter(el -> el.getKind() == ElementKind.MODULE)
       
   441                     .forEach(mod -> System.err.println("module: " + mod.getSimpleName()));
       
   442 
       
   443             return false;
       
   444         }
       
   445 
       
   446         @Override
       
   447         public SourceVersion getSupportedSourceVersion() {
       
   448             return SourceVersion.latest();
       
   449         }
       
   450 
       
   451     }
       
   452 
       
   453     @Test
       
   454     public void testAnnotationsInModuleInfo(Path base) throws Exception {
       
   455         Path moduleSrc = base.resolve("module-src");
       
   456         Path m1 = moduleSrc.resolve("m1");
       
   457 
       
   458         tb.writeJavaFiles(m1,
       
   459                           "@Deprecated module m1x { }");
       
   460 
       
   461         Path m2 = moduleSrc.resolve("m2x");
       
   462 
       
   463         tb.writeJavaFiles(m2,
       
   464                           "@SuppressWarnings(\"\") module m2x { }");
       
   465 
       
   466         Path classes = base.resolve("classes");
       
   467 
       
   468         Files.createDirectories(classes);
       
   469 
       
   470         List<String> log = new JavacTask(tb)
       
   471                 .options("-processor", AnnotationsInModuleInfoPrint.class.getName())
       
   472                 .outdir(classes)
       
   473                 .files(findJavaFiles(m1))
       
   474                 .run()
       
   475                 .writeAll()
       
   476                 .getOutputLines(Task.OutputKind.DIRECT);
       
   477 
       
   478         List<String> expectedLog = List.of("Note: AP Invoked",
       
   479                                            "Note: AP Invoked");
       
   480 
       
   481         assertEquals(expectedLog, log);
       
   482 
       
   483         new JavacTask(tb)
       
   484             .options("-processor", AnnotationsInModuleInfoFail.class.getName())
       
   485             .outdir(classes)
       
   486             .files(findJavaFiles(m2))
       
   487             .run()
       
   488             .writeAll();
       
   489     }
       
   490 
       
   491     @SupportedAnnotationTypes("java.lang.Deprecated")
       
   492     public static final class AnnotationsInModuleInfoPrint extends AbstractProcessor {
       
   493 
       
   494         @Override
       
   495         public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
       
   496             processingEnv.getMessager().printMessage(Kind.NOTE, "AP Invoked");
       
   497             return false;
       
   498         }
       
   499 
       
   500         @Override
       
   501         public SourceVersion getSupportedSourceVersion() {
       
   502             return SourceVersion.latest();
       
   503         }
       
   504 
       
   505     }
       
   506 
       
   507     @SupportedAnnotationTypes("java.lang.Deprecated")
       
   508     public static final class AnnotationsInModuleInfoFail extends AbstractProcessor {
       
   509 
       
   510         @Override
       
   511         public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
       
   512             throw new AssertionError();
       
   513         }
       
   514 
       
   515         @Override
       
   516         public SourceVersion getSupportedSourceVersion() {
       
   517             return SourceVersion.latest();
       
   518         }
       
   519 
       
   520     }
       
   521 
       
   522     @Test
       
   523     public void testGenerateInMultiModeAPI(Path base) throws Exception {
       
   524         Path moduleSrc = base.resolve("module-src");
       
   525         Path classes = base.resolve("classes");
       
   526 
       
   527         Files.createDirectories(classes);
       
   528 
       
   529         Path m1 = moduleSrc.resolve("m1x");
       
   530 
       
   531         tb.writeJavaFiles(m1,
       
   532                           "module m1x { exports api1; }",
       
   533                           "package api1; public class Api { }",
       
   534                           "package clash; public class C { }");
       
   535 
       
   536         writeFile("1", m1, "api1", "api");
       
   537         writeFile("2", m1, "clash", "clash");
       
   538 
       
   539         Path m2 = moduleSrc.resolve("m2x");
       
   540 
       
   541         tb.writeJavaFiles(m2,
       
   542                           "module m2x { requires m1x; exports api2; }",
       
   543                           "package api2; public class Api { }",
       
   544                           "package clash; public class C { }");
       
   545 
       
   546         writeFile("3", m2, "api2", "api");
       
   547         writeFile("4", m2, "clash", "api");
       
   548 
       
   549         //passing testcases:
       
   550         for (String module : Arrays.asList("", "m1x/")) {
       
   551             for (String originating : Arrays.asList("", ", jlObject")) {
       
   552                 tb.writeJavaFiles(m1,
       
   553                                   "package test; class Test { api1.Impl i; }");
       
   554 
       
   555                 //source:
       
   556                 runCompiler(base,
       
   557                             moduleSrc,
       
   558                             classes,
       
   559                             "createSource(() -> filer.createSourceFile(\"" + module + "api1.Impl\"" + originating + "), \"api1.Impl\", \"package api1; public class Impl {}\")",
       
   560                             "--module-source-path", moduleSrc.toString());
       
   561                 assertFileExists(classes, "m1x", "api1", "Impl.class");
       
   562 
       
   563                 //class:
       
   564                 runCompiler(base,
       
   565                             moduleSrc,
       
   566                             classes,
       
   567                             "createClass(() -> filer.createClassFile(\"" + module + "api1.Impl\"" + originating + "), \"api1.Impl\", \"package api1; public class Impl {}\")",
       
   568                             "--module-source-path", moduleSrc.toString());
       
   569                 assertFileExists(classes, "m1x", "api1", "Impl.class");
       
   570 
       
   571                 Files.delete(m1.resolve("test").resolve("Test.java"));
       
   572 
       
   573                 //resource class output:
       
   574                 runCompiler(base,
       
   575                             moduleSrc,
       
   576                             classes,
       
   577                             "createSource(() -> filer.createResource(StandardLocation.CLASS_OUTPUT, \"" + module + "api1\", \"impl\"" + originating + "), \"impl\", \"impl\")",
       
   578                             "--module-source-path", moduleSrc.toString());
       
   579                 assertFileExists(classes, "m1x", "api1", "impl");
       
   580             }
       
   581         }
       
   582 
       
   583         //get resource module source path:
       
   584         runCompiler(base,
       
   585                     m1,
       
   586                     classes,
       
   587                     "doReadResource(() -> filer.getResource(StandardLocation.MODULE_SOURCE_PATH, \"m1x/api1\", \"api\"), \"1\")",
       
   588                     "--module-source-path", moduleSrc.toString());
       
   589 
       
   590         //can generate resources to the single root module:
       
   591         runCompiler(base,
       
   592                     m1,
       
   593                     classes,
       
   594                     "createSource(() -> filer.createResource(StandardLocation.CLASS_OUTPUT, \"m1x/impl\", \"impl\"), \"impl\", \"impl\")",
       
   595                     "--module-source-path", moduleSrc.toString());
       
   596         assertFileExists(classes, "m1x", "impl", "impl");
       
   597 
       
   598         //check --default-module-for-created-files option:
       
   599         for (String pack : Arrays.asList("clash", "doesnotexist")) {
       
   600             tb.writeJavaFiles(m1,
       
   601                               "package test; class Test { " + pack + ".Pass i; }");
       
   602             runCompiler(base,
       
   603                         moduleSrc,
       
   604                         classes,
       
   605                         "createSource(() -> filer.createSourceFile(\"" + pack + ".Pass\")," +
       
   606                         "                                          \"" + pack + ".Pass\"," +
       
   607                         "                                          \"package " + pack + ";" +
       
   608                         "                                            public class Pass { }\")",
       
   609                         "--module-source-path", moduleSrc.toString(),
       
   610                         "--default-module-for-created-files=m1x");
       
   611             assertFileExists(classes, "m1x", pack, "Pass.class");
       
   612             assertFileNotExists(classes, "m2x", pack, "Pass.class");
       
   613 
       
   614             runCompiler(base,
       
   615                         moduleSrc,
       
   616                         classes,
       
   617                         "createClass(() -> filer.createClassFile(\"" + pack + ".Pass\")," +
       
   618                         "                                        \"" + pack + ".Pass\"," +
       
   619                         "                                        \"package " + pack + ";" +
       
   620                         "                                          public class Pass { }\")",
       
   621                         "--module-source-path", moduleSrc.toString(),
       
   622                         "--default-module-for-created-files=m1x");
       
   623             assertFileExists(classes, "m1x", pack, "Pass.class");
       
   624             assertFileNotExists(classes, "m2x", pack, "Pass.class");
       
   625 
       
   626             Files.delete(m1.resolve("test").resolve("Test.java"));
       
   627 
       
   628             runCompiler(base,
       
   629                         moduleSrc,
       
   630                         classes,
       
   631                         "createSource(() -> filer.createResource(StandardLocation.CLASS_OUTPUT," +
       
   632                         "                                        \"" + pack + "\", \"impl\"), \"impl\", \"impl\")",
       
   633                         "--module-source-path", moduleSrc.toString(),
       
   634                         "--default-module-for-created-files=m1x");
       
   635             assertFileExists(classes, "m1x", pack, "impl");
       
   636             assertFileNotExists(classes, "m2x", pack, "impl");
       
   637 
       
   638             runCompiler(base,
       
   639                         moduleSrc,
       
   640                         classes,
       
   641                         "doReadResource(() -> filer.getResource(StandardLocation.CLASS_OUTPUT," +
       
   642                         "                                       \"" + pack + "\", \"resource\"), \"1\")",
       
   643                         p -> writeFile("1", p.resolve("m1x"), pack, "resource"),
       
   644                         "--module-source-path", moduleSrc.toString(),
       
   645                         "--default-module-for-created-files=m1x");
       
   646         }
       
   647 
       
   648         //wrong default module:
       
   649         runCompiler(base,
       
   650                     moduleSrc,
       
   651                     classes,
       
   652                     "expectFilerException(() -> filer.createResource(StandardLocation.CLASS_OUTPUT," +
       
   653                     "                                                \"clash\", \"impl\"))",
       
   654                     "--module-source-path", moduleSrc.toString(),
       
   655                     "--default-module-for-created-files=doesnotexist");
       
   656 
       
   657         String[] failingCases = {
       
   658             //must not generate to unnamed package:
       
   659             "expectFilerException(() -> filer.createSourceFile(\"Fail\"))",
       
   660             "expectFilerException(() -> filer.createClassFile(\"Fail\"))",
       
   661             "expectFilerException(() -> filer.createSourceFile(\"m1x/Fail\"))",
       
   662             "expectFilerException(() -> filer.createClassFile(\"m1x/Fail\"))",
       
   663 
       
   664             //cannot infer module name, package clash:
       
   665             "expectFilerException(() -> filer.createSourceFile(\"clash.Fail\"))",
       
   666             "expectFilerException(() -> filer.createClassFile(\"clash.Fail\"))",
       
   667             "expectFilerException(() -> filer.createResource(StandardLocation.CLASS_OUTPUT, \"clash\", \"impl\"))",
       
   668             "expectFilerException(() -> filer.getResource(StandardLocation.CLASS_OUTPUT, \"clash\", \"impl\"))",
       
   669 
       
   670             //cannot infer module name, package does not exist:
       
   671             "expectFilerException(() -> filer.createSourceFile(\"doesnotexist.Fail\"))",
       
   672             "expectFilerException(() -> filer.createClassFile(\"doesnotexist.Fail\"))",
       
   673             "expectFilerException(() -> filer.createResource(StandardLocation.CLASS_OUTPUT, \"doesnotexist\", \"impl\"))",
       
   674             "expectFilerException(() -> filer.getResource(StandardLocation.CLASS_OUTPUT, \"doesnotexist\", \"impl\"))",
       
   675 
       
   676             //cannot generate sources/classes to modules that are not root modules:
       
   677             "expectFilerException(() -> filer.createSourceFile(\"java.base/fail.Fail\"))",
       
   678             "expectFilerException(() -> filer.createClassFile(\"java.base/fail.Fail\"))",
       
   679 
       
   680             //cannot read from module locations if module not given and not inferable:
       
   681             "expectFilerException(() -> filer.getResource(StandardLocation.SYSTEM_MODULES, \"fail\", \"Fail\"))",
       
   682 
       
   683             //wrong module given:
       
   684             "expectException(() -> filer.getResource(StandardLocation.SYSTEM_MODULES, \"java.compiler/java.lang\", \"Object.class\"))",
       
   685         };
       
   686 
       
   687         for (String failingCode : failingCases) {
       
   688             System.err.println("failing code: " + failingCode);
       
   689             runCompiler(base,
       
   690                         moduleSrc,
       
   691                         classes,
       
   692                         failingCode,
       
   693                         "--module-source-path", moduleSrc.toString());
       
   694         }
       
   695     }
       
   696 
       
   697     public static abstract class GeneratingAP extends AbstractProcessor {
       
   698 
       
   699         public void createSource(CreateFileObject file, String name, String content) {
       
   700             try (Writer out = file.create().openWriter()) {
       
   701                 out.write(content);
       
   702             } catch (IOException ex) {
       
   703                 throw new IllegalStateException(ex);
       
   704             }
       
   705         }
       
   706 
       
   707         public void createClass(CreateFileObject file, String name, String content) {
       
   708             String fileNameStub = name.replace(".", File.separator);
       
   709 
       
   710             try (OutputStream out = file.create().openOutputStream()) {
       
   711                 Path scratch = Files.createDirectories(Paths.get(""));
       
   712                 Path scratchSrc = scratch.resolve(fileNameStub + ".java").toAbsolutePath();
       
   713 
       
   714                 Files.createDirectories(scratchSrc.getParent());
       
   715 
       
   716                 try (Writer w = Files.newBufferedWriter(scratchSrc)) {
       
   717                     w.write(content);
       
   718                 }
       
   719 
       
   720                 Path scratchClasses = scratch.resolve("classes");
       
   721 
       
   722                 Files.createDirectories(scratchClasses);
       
   723 
       
   724                 JavaCompiler comp = ToolProvider.getSystemJavaCompiler();
       
   725                 try (StandardJavaFileManager fm = comp.getStandardFileManager(null, null, null)) {
       
   726                     List<String> options = List.of("-d", scratchClasses.toString());
       
   727                     Iterable<? extends JavaFileObject> files = fm.getJavaFileObjects(scratchSrc);
       
   728                     CompilationTask task = comp.getTask(null, fm, null, options, null, files);
       
   729 
       
   730                     if (!task.call()) {
       
   731                         throw new AssertionError("compilation failed");
       
   732                     }
       
   733                 }
       
   734 
       
   735                 Path classfile = scratchClasses.resolve(fileNameStub + ".class");
       
   736 
       
   737                 Files.copy(classfile, out);
       
   738             } catch (IOException ex) {
       
   739                 throw new IllegalStateException(ex);
       
   740             }
       
   741         }
       
   742 
       
   743         public void doReadResource(CreateFileObject file, String expectedContent) {
       
   744             try {
       
   745                 StringBuilder actualContent = new StringBuilder();
       
   746 
       
   747                 try (Reader r = file.create().openReader(true)) {
       
   748                     int read;
       
   749 
       
   750                     while ((read = r.read()) != (-1)) {
       
   751                         actualContent.append((char) read);
       
   752                     }
       
   753 
       
   754                 }
       
   755 
       
   756                 assertEquals(expectedContent, actualContent.toString());
       
   757             } catch (IOException ex) {
       
   758                 throw new IllegalStateException(ex);
       
   759             }
       
   760         }
       
   761 
       
   762         public void checkResourceExists(CreateFileObject file) {
       
   763             try {
       
   764                 file.create().openInputStream().close();
       
   765             } catch (IOException ex) {
       
   766                 throw new IllegalStateException(ex);
       
   767             }
       
   768         }
       
   769 
       
   770         public interface CreateFileObject {
       
   771             public FileObject create() throws IOException;
       
   772         }
       
   773 
       
   774         public void expectFilerException(Callable<Object> c) {
       
   775             try {
       
   776                 c.call();
       
   777                 throw new AssertionError("Expected exception not thrown");
       
   778             } catch (FilerException ex) {
       
   779                 //expected
       
   780             } catch (Exception ex) {
       
   781                 throw new IllegalStateException(ex);
       
   782             }
       
   783         }
       
   784 
       
   785         public void expectException(Callable<Object> c) {
       
   786             try {
       
   787                 c.call();
       
   788                 throw new AssertionError("Expected exception not thrown");
       
   789             } catch (IOException ex) {
       
   790                 //expected
       
   791             } catch (Exception ex) {
       
   792                 throw new IllegalStateException(ex);
       
   793             }
       
   794         }
       
   795 
       
   796         @Override
       
   797         public SourceVersion getSupportedSourceVersion() {
       
   798             return SourceVersion.latest();
       
   799         }
       
   800 
       
   801     }
       
   802 
       
   803     @Test
       
   804     public void testGenerateSingleModule(Path base) throws Exception {
       
   805         Path classes = base.resolve("classes");
       
   806 
       
   807         Files.createDirectories(classes);
       
   808 
       
   809         Path src = base.resolve("module-src");
       
   810         Path m1 = src.resolve("m1x");
       
   811 
       
   812         tb.writeJavaFiles(m1,
       
   813                           "module m1x { }",
       
   814                           "package test; class Test { impl.Impl i; }");
       
   815         Path m2 = src.resolve("m2x");
       
   816 
       
   817         tb.writeJavaFiles(m2,
       
   818                           "module m2x { }");
       
   819 
       
   820         for (String[] options : new String[][] {new String[] {"-sourcepath", m1.toString()},
       
   821                                                 new String[] {"--module-source-path", src.toString()}}) {
       
   822             String modulePath = options[0].equals("--module-source-path") ? "m1x" : "";
       
   823             //passing testcases:
       
   824             for (String module : Arrays.asList("", "m1x/")) {
       
   825                 for (String originating : Arrays.asList("", ", jlObject")) {
       
   826                     tb.writeJavaFiles(m1,
       
   827                                       "package test; class Test { impl.Impl i; }");
       
   828 
       
   829                     //source:
       
   830                     runCompiler(base,
       
   831                                 m1,
       
   832                                 classes,
       
   833                                 "createSource(() -> filer.createSourceFile(\"" + module + "impl.Impl\"" + originating + "), \"impl.Impl\", \"package impl; public class Impl {}\")",
       
   834                                 options);
       
   835                     assertFileExists(classes, modulePath, "impl", "Impl.class");
       
   836 
       
   837                     //class:
       
   838                     runCompiler(base,
       
   839                                 m1,
       
   840                                 classes,
       
   841                                 "createClass(() -> filer.createClassFile(\"" + module + "impl.Impl\"" + originating + "), \"impl.Impl\", \"package impl; public class Impl {}\")",
       
   842                                 options);
       
   843                     assertFileExists(classes, modulePath, "impl", "Impl.class");
       
   844 
       
   845                     Files.delete(m1.resolve("test").resolve("Test.java"));
       
   846 
       
   847                     //resource class output:
       
   848                     runCompiler(base,
       
   849                                 m1,
       
   850                                 classes,
       
   851                                 "createSource(() -> filer.createResource(StandardLocation.CLASS_OUTPUT, \"impl\", \"impl\"" + originating + "), \"impl\", \"impl\")",
       
   852                                 options);
       
   853                     assertFileExists(classes, modulePath, "impl", "impl");
       
   854                 }
       
   855             }
       
   856         }
       
   857 
       
   858         //get resource source path:
       
   859         writeFile("1", m1, "impl", "resource");
       
   860         runCompiler(base,
       
   861                     m1,
       
   862                     classes,
       
   863                     "doReadResource(() -> filer.getResource(StandardLocation.SOURCE_PATH, \"impl\", \"resource\"), \"1\")",
       
   864                     "-sourcepath", m1.toString());
       
   865         //must not specify module when reading non-module oriented locations:
       
   866         runCompiler(base,
       
   867                     m1,
       
   868                     classes,
       
   869                     "expectFilerException(() -> filer.getResource(StandardLocation.SOURCE_PATH, \"m1x/impl\", \"resource\"))",
       
   870                     "-sourcepath", m1.toString());
       
   871 
       
   872         Files.delete(m1.resolve("impl").resolve("resource"));
       
   873 
       
   874         //can read resources from the system module path if module name given:
       
   875         runCompiler(base,
       
   876                     m1,
       
   877                     classes,
       
   878                     "checkResourceExists(() -> filer.getResource(StandardLocation.SYSTEM_MODULES, \"java.base/java.lang\", \"Object.class\"))",
       
   879                     "-sourcepath", m1.toString());
       
   880 
       
   881         //can read resources from the system module path if module inferable:
       
   882         runCompiler(base,
       
   883                     m1,
       
   884                     classes,
       
   885                     "expectFilerException(() -> filer.getResource(StandardLocation.SYSTEM_MODULES, \"java.lang\", \"Object.class\"))",
       
   886                     "-sourcepath", m1.toString());
       
   887 
       
   888         //cannot generate resources to modules that are not root modules:
       
   889         runCompiler(base,
       
   890                     m1,
       
   891                     classes,
       
   892                     "expectFilerException(() -> filer.createResource(StandardLocation.CLASS_OUTPUT, \"java.base/fail\", \"Fail\"))",
       
   893                     "--module-source-path", src.toString());
       
   894 
       
   895         //can generate resources to the single root module:
       
   896         runCompiler(base,
       
   897                     m1,
       
   898                     classes,
       
   899                     "createSource(() -> filer.createResource(StandardLocation.CLASS_OUTPUT, \"impl\", \"impl\"), \"impl\", \"impl\")",
       
   900                     "--module-source-path", src.toString());
       
   901         assertFileExists(classes, "m1x", "impl", "impl");
       
   902 
       
   903         String[] failingCases = {
       
   904             //must not generate to unnamed package:
       
   905             "expectFilerException(() -> filer.createSourceFile(\"Fail\"))",
       
   906             "expectFilerException(() -> filer.createClassFile(\"Fail\"))",
       
   907             "expectFilerException(() -> filer.createSourceFile(\"m1x/Fail\"))",
       
   908             "expectFilerException(() -> filer.createClassFile(\"m1x/Fail\"))",
       
   909 
       
   910             //cannot generate sources/classes to modules that are not root modules:
       
   911             "expectFilerException(() -> filer.createSourceFile(\"java.base/fail.Fail\"))",
       
   912             "expectFilerException(() -> filer.createClassFile(\"java.base/fail.Fail\"))",
       
   913 
       
   914             //cannot specify module name for class output when not in the multi-module mode:
       
   915             "expectFilerException(() -> filer.createResource(StandardLocation.CLASS_OUTPUT, \"m1x/fail\", \"Fail\"))",
       
   916 
       
   917             //cannot read from module locations if module not given:
       
   918             "expectFilerException(() -> filer.getResource(StandardLocation.SYSTEM_MODULES, \"fail\", \"Fail\"))",
       
   919 
       
   920             //wrong module given:
       
   921             "expectException(() -> filer.getResource(StandardLocation.SYSTEM_MODULES, \"java.compiler/java.lang\", \"Object.class\"))",
       
   922         };
       
   923 
       
   924         for (String failingCode : failingCases) {
       
   925             System.err.println("failing code: " + failingCode);
       
   926             runCompiler(base,
       
   927                         m1,
       
   928                         classes,
       
   929                         failingCode,
       
   930                         "-sourcepath", m1.toString());
       
   931         }
       
   932 
       
   933         Files.delete(m1.resolve("module-info.java"));
       
   934         tb.writeJavaFiles(m1,
       
   935                           "package test; class Test { }");
       
   936 
       
   937         runCompiler(base,
       
   938                     m1,
       
   939                     classes,
       
   940                     "expectFilerException(() -> filer.createSourceFile(\"m1x/impl.Impl\"))",
       
   941                     "-sourcepath", m1.toString(),
       
   942                     "-source", "8");
       
   943 
       
   944         runCompiler(base,
       
   945                     m1,
       
   946                     classes,
       
   947                     "expectFilerException(() -> filer.createClassFile(\"m1x/impl.Impl\"))",
       
   948                     "-sourcepath", m1.toString(),
       
   949                     "-source", "8");
       
   950     }
       
   951 
       
   952     private void runCompiler(Path base, Path src, Path classes,
       
   953                              String code, String... options) throws IOException {
       
   954         runCompiler(base, src, classes, code, p -> {}, options);
       
   955     }
       
   956 
       
   957     private void runCompiler(Path base, Path src, Path classes,
       
   958                              String code, Consumer<Path> generateToClasses,
       
   959                              String... options) throws IOException {
       
   960         Path apClasses = base.resolve("ap-classes");
       
   961         if (Files.exists(apClasses)) {
       
   962             tb.cleanDirectory(apClasses);
       
   963         } else {
       
   964             Files.createDirectories(apClasses);
       
   965         }
       
   966         compileAP(apClasses, code);
       
   967         if (Files.exists(classes)) {
       
   968             tb.cleanDirectory(classes);
       
   969         } else {
       
   970             Files.createDirectories(classes);
       
   971         }
       
   972         generateToClasses.accept(classes);
       
   973         List<String> opts = new ArrayList<>();
       
   974         opts.addAll(Arrays.asList(options));
       
   975         opts.add("-processorpath");
       
   976         opts.add(System.getProperty("test.class.path") + File.pathSeparator + apClasses.toString());
       
   977         opts.add("-processor");
       
   978         opts.add("AP");
       
   979         new JavacTask(tb)
       
   980           .options(opts)
       
   981           .outdir(classes)
       
   982           .files(findJavaFiles(src))
       
   983           .run()
       
   984           .writeAll();
       
   985     }
       
   986 
       
   987     private void compileAP(Path target, String code) {
       
   988         String processorCode =
       
   989             "import java.util.*;\n" +
       
   990             "import javax.annotation.processing.*;\n" +
       
   991             "import javax.lang.model.*;\n" +
       
   992             "import javax.lang.model.element.*;\n" +
       
   993             "import javax.lang.model.type.*;\n" +
       
   994             "import javax.lang.model.util.*;\n" +
       
   995             "import javax.tools.*;\n" +
       
   996             "@SupportedAnnotationTypes(\"*\")\n" +
       
   997             "public final class AP extends AnnotationProcessing.GeneratingAP {\n" +
       
   998             "\n" +
       
   999             "        int round;\n" +
       
  1000             "\n" +
       
  1001             "        @Override\n" +
       
  1002             "        public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {\n" +
       
  1003             "            if (round++ != 0)\n" +
       
  1004             "                return false;\n" +
       
  1005             "            Filer filer = processingEnv.getFiler();\n" +
       
  1006             "            TypeElement jlObject = processingEnv.getElementUtils().getTypeElement(\"java.lang.Object\");\n" +
       
  1007             code + ";\n" +
       
  1008             "            return false;\n" +
       
  1009             "        }\n" +
       
  1010             "    }\n";
       
  1011         new JavacTask(tb)
       
  1012           .options("-classpath", System.getProperty("test.class.path"))
       
  1013           .sources(processorCode)
       
  1014           .outdir(target)
       
  1015           .run()
       
  1016           .writeAll();
       
  1017     }
       
  1018 
       
  1019     @Test
       
  1020     public void testGenerateInUnnamedModeAPI(Path base) throws Exception {
       
  1021         Path classes = base.resolve("classes");
       
  1022 
       
  1023         Files.createDirectories(classes);
       
  1024 
       
  1025         Path src = base.resolve("src");
       
  1026 
       
  1027         tb.writeJavaFiles(src,
       
  1028                           "class T {}");
       
  1029 
       
  1030         new JavacTask(tb)
       
  1031           .options("-processor", UnnamedModeAPITestAP.class.getName(),
       
  1032                    "-sourcepath", src.toString())
       
  1033           .outdir(classes)
       
  1034           .files(findJavaFiles(src))
       
  1035           .run()
       
  1036           .writeAll();
       
  1037 
       
  1038         assertFileExists(classes, "Impl1.class");
       
  1039         assertFileExists(classes, "Impl2.class");
       
  1040     }
       
  1041 
       
  1042     @Test
       
  1043     public void testGenerateInNoModeAPI(Path base) throws Exception {
       
  1044         Path classes = base.resolve("classes");
       
  1045 
       
  1046         Files.createDirectories(classes);
       
  1047 
       
  1048         Path src = base.resolve("src");
       
  1049 
       
  1050         tb.writeJavaFiles(src,
       
  1051                           "class T {}");
       
  1052 
       
  1053         new JavacTask(tb)
       
  1054           .options("-processor", UnnamedModeAPITestAP.class.getName(),
       
  1055                    "-source", "8", "-target", "8",
       
  1056                    "-sourcepath", src.toString())
       
  1057           .outdir(classes)
       
  1058           .files(findJavaFiles(src))
       
  1059           .run()
       
  1060           .writeAll();
       
  1061 
       
  1062         assertFileExists(classes, "Impl1.class");
       
  1063         assertFileExists(classes, "Impl2.class");
       
  1064     }
       
  1065 
       
  1066     @SupportedAnnotationTypes("*")
       
  1067     public static final class UnnamedModeAPITestAP extends GeneratingAP {
       
  1068 
       
  1069         int round;
       
  1070 
       
  1071         @Override
       
  1072         public synchronized void init(ProcessingEnvironment processingEnv) {
       
  1073             super.init(processingEnv);
       
  1074         }
       
  1075 
       
  1076         @Override
       
  1077         public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
       
  1078             if (round++ != 0)
       
  1079                 return false;
       
  1080 
       
  1081             Filer filer = processingEnv.getFiler();
       
  1082 
       
  1083             //must not generate to unnamed package:
       
  1084             createSource(() -> filer.createSourceFile("Impl1"), "Impl1", "class Impl1 {}");
       
  1085             createClass(() -> filer.createClassFile("Impl2"), "Impl2", "class Impl2 {}");
       
  1086 
       
  1087             return false;
       
  1088         }
       
  1089 
       
  1090     }
       
  1091 
       
  1092     @Test
       
  1093     public void testDisambiguateAnnotations(Path base) throws Exception {
       
  1094         Path classes = base.resolve("classes");
       
  1095 
       
  1096         Files.createDirectories(classes);
       
  1097 
       
  1098         Path src = base.resolve("src");
       
  1099         Path m1 = src.resolve("m1x");
       
  1100 
       
  1101         tb.writeJavaFiles(m1,
       
  1102                           "module m1x { exports api; }",
       
  1103                           "package api; public @interface A {}",
       
  1104                           "package api; public @interface B {}");
       
  1105 
       
  1106         Path m2 = src.resolve("m2x");
       
  1107 
       
  1108         tb.writeJavaFiles(m2,
       
  1109                           "module m2x { exports api; }",
       
  1110                           "package api; public @interface A {}",
       
  1111                           "package api; public @interface B {}");
       
  1112 
       
  1113         Path m3 = src.resolve("m3x");
       
  1114 
       
  1115         tb.writeJavaFiles(m3,
       
  1116                           "module m3x { requires m1x; }",
       
  1117                           "package impl; import api.*; @A @B public class T {}");
       
  1118 
       
  1119         Path m4 = src.resolve("m4x");
       
  1120 
       
  1121         tb.writeJavaFiles(m4,
       
  1122                           "module m4x { requires m2x; }",
       
  1123                           "package impl; import api.*; @A @B public class T {}");
       
  1124 
       
  1125         List<String> log;
       
  1126         List<String> expected;
       
  1127 
       
  1128         log = new JavacTask(tb)
       
  1129             .options("-processor", SelectAnnotationATestAP.class.getName() + "," + SelectAnnotationBTestAP.class.getName(),
       
  1130                      "--module-source-path", src.toString(),
       
  1131                      "-m", "m1x,m2x")
       
  1132             .outdir(classes)
       
  1133             .run()
       
  1134             .writeAll()
       
  1135             .getOutputLines(OutputKind.STDERR);
       
  1136 
       
  1137         expected = List.of("");
       
  1138 
       
  1139         if (!expected.equals(log)) {
       
  1140             throw new AssertionError("Output does not match; output: " + log);
       
  1141         }
       
  1142 
       
  1143         log = new JavacTask(tb)
       
  1144             .options("-processor", SelectAnnotationATestAP.class.getName() + "," + SelectAnnotationBTestAP.class.getName(),
       
  1145                      "--module-source-path", src.toString(),
       
  1146                      "-m", "m3x")
       
  1147             .outdir(classes)
       
  1148             .run()
       
  1149             .writeAll()
       
  1150             .getOutputLines(OutputKind.STDERR);
       
  1151 
       
  1152         expected = List.of("SelectAnnotationBTestAP",
       
  1153                            "SelectAnnotationBTestAP");
       
  1154 
       
  1155         if (!expected.equals(log)) {
       
  1156             throw new AssertionError("Output does not match; output: " + log);
       
  1157         }
       
  1158 
       
  1159         log = new JavacTask(tb)
       
  1160             .options("-processor", SelectAnnotationATestAP.class.getName() + "," +
       
  1161                                    SelectAnnotationBTestAP.class.getName() + "," +
       
  1162                                    SelectAnnotationAStrictTestAP.class.getName(),
       
  1163                      "--module-source-path", src.toString(),
       
  1164                      "-m", "m4x")
       
  1165             .outdir(classes)
       
  1166             .run()
       
  1167             .writeAll()
       
  1168             .getOutputLines(OutputKind.STDERR);
       
  1169 
       
  1170         expected = List.of("SelectAnnotationATestAP",
       
  1171                            "SelectAnnotationBTestAP",
       
  1172                            "SelectAnnotationAStrictTestAP",
       
  1173                            "SelectAnnotationATestAP",
       
  1174                            "SelectAnnotationBTestAP",
       
  1175                            "SelectAnnotationAStrictTestAP");
       
  1176 
       
  1177         if (!expected.equals(log)) {
       
  1178             throw new AssertionError("Output does not match; output: " + log);
       
  1179         }
       
  1180     }
       
  1181 
       
  1182     @Test
       
  1183     public void testDisambiguateAnnotationsUnnamedModule(Path base) throws Exception {
       
  1184         Path classes = base.resolve("classes");
       
  1185 
       
  1186         Files.createDirectories(classes);
       
  1187 
       
  1188         Path src = base.resolve("src");
       
  1189 
       
  1190         tb.writeJavaFiles(src,
       
  1191                           "package api; public @interface A {}",
       
  1192                           "package api; public @interface B {}",
       
  1193                           "package impl; import api.*; @A @B public class T {}");
       
  1194 
       
  1195         List<String> log = new JavacTask(tb)
       
  1196             .options("-processor", SelectAnnotationATestAP.class.getName() + "," +
       
  1197                                    SelectAnnotationBTestAP.class.getName() + "," +
       
  1198                                    SelectAnnotationAStrictTestAP.class.getName())
       
  1199             .outdir(classes)
       
  1200             .files(findJavaFiles(src))
       
  1201             .run()
       
  1202             .writeAll()
       
  1203             .getOutputLines(OutputKind.STDERR);
       
  1204 
       
  1205         List<String> expected = List.of("SelectAnnotationBTestAP",
       
  1206                                         "SelectAnnotationBTestAP");
       
  1207 
       
  1208         if (!expected.equals(log)) {
       
  1209             throw new AssertionError("Output does not match; output: " + log);
       
  1210         }
       
  1211     }
       
  1212 
       
  1213     @Test
       
  1214     public void testDisambiguateAnnotationsNoModules(Path base) throws Exception {
       
  1215         Path classes = base.resolve("classes");
       
  1216 
       
  1217         Files.createDirectories(classes);
       
  1218 
       
  1219         Path src = base.resolve("src");
       
  1220 
       
  1221         tb.writeJavaFiles(src,
       
  1222                           "package api; public @interface A {}",
       
  1223                           "package api; public @interface B {}",
       
  1224                           "package impl; import api.*; @A @B public class T {}");
       
  1225 
       
  1226         List<String> log = new JavacTask(tb)
       
  1227             .options("-processor", SelectAnnotationATestAP.class.getName() + "," +
       
  1228                                    SelectAnnotationBTestAP.class.getName() + "," +
       
  1229                                    SelectAnnotationAStrictTestAP.class.getName(),
       
  1230                      "-source", "8", "-target", "8")
       
  1231             .outdir(classes)
       
  1232             .files(findJavaFiles(src))
       
  1233             .run()
       
  1234             .writeAll()
       
  1235             .getOutputLines(OutputKind.STDERR);
       
  1236 
       
  1237         List<String> expected = List.of("SelectAnnotationATestAP",
       
  1238                                         "SelectAnnotationBTestAP",
       
  1239                                         "SelectAnnotationATestAP",
       
  1240                                         "SelectAnnotationBTestAP");
       
  1241 
       
  1242         if (!expected.equals(log)) {
       
  1243             throw new AssertionError("Output does not match; output: " + log);
       
  1244         }
       
  1245     }
       
  1246 
       
  1247     @SupportedAnnotationTypes("m2x/api.A")
       
  1248     public static final class SelectAnnotationATestAP extends AbstractProcessor {
       
  1249 
       
  1250         @Override
       
  1251         public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
       
  1252             System.err.println("SelectAnnotationATestAP");
       
  1253 
       
  1254             return false;
       
  1255         }
       
  1256 
       
  1257     }
       
  1258 
       
  1259     @SupportedAnnotationTypes("api.B")
       
  1260     public static final class SelectAnnotationBTestAP extends AbstractProcessor {
       
  1261 
       
  1262         @Override
       
  1263         public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
       
  1264             System.err.println("SelectAnnotationBTestAP");
       
  1265 
       
  1266             return false;
       
  1267         }
       
  1268 
       
  1269     }
       
  1270 
       
  1271     public static final class SelectAnnotationAStrictTestAP extends AbstractProcessor {
       
  1272 
       
  1273         @Override
       
  1274         public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
       
  1275             System.err.println("SelectAnnotationAStrictTestAP");
       
  1276 
       
  1277             return false;
       
  1278         }
       
  1279 
       
  1280         @Override
       
  1281         public Set<String> getSupportedAnnotationTypes() {
       
  1282             return Set.of("m2x/api.A");
       
  1283         }
       
  1284 
       
  1285     }
       
  1286 
       
  1287     private static void writeFile(String content, Path base, String... pathElements) {
       
  1288         try {
       
  1289             Path file = resolveFile(base, pathElements);
       
  1290 
       
  1291             Files.createDirectories(file.getParent());
       
  1292 
       
  1293             try (Writer out = Files.newBufferedWriter(file)) {
       
  1294                 out.append(content);
       
  1295             }
       
  1296         } catch (IOException ex) {
       
  1297             throw new UncheckedIOException(ex);
       
  1298         }
       
  1299     }
       
  1300 
       
  1301     @Test
       
  1302     public void testUnboundLookup(Path base) throws Exception {
       
  1303         Path src = base.resolve("src");
       
  1304 
       
  1305         tb.writeJavaFiles(src,
       
  1306                           "package impl.conflict.src; public class Impl { }");
       
  1307 
       
  1308         Path moduleSrc = base.resolve("module-src");
       
  1309         Path m1 = moduleSrc.resolve("m1x");
       
  1310         Path m2 = moduleSrc.resolve("m2x");
       
  1311 
       
  1312         Path classes = base.resolve("classes");
       
  1313         Path cpClasses = base.resolve("cpClasses");
       
  1314 
       
  1315         Files.createDirectories(classes);
       
  1316         Files.createDirectories(cpClasses);
       
  1317 
       
  1318         tb.writeJavaFiles(m1,
       
  1319                           "module m1x { }",
       
  1320                           "package impl1; public class Impl { }",
       
  1321                           "package impl.conflict.module; class Impl { }",
       
  1322                           "package impl.conflict.clazz; public class pkg { public static class I { } }",
       
  1323                           "package impl.conflict.src; public class Impl { }",
       
  1324                           "package nested.pack.pack; public class Impl { }",
       
  1325                           "package unique.nested; public class Impl { }");
       
  1326 
       
  1327         tb.writeJavaFiles(m2,
       
  1328                           "module m2x { }",
       
  1329                           "package impl2; public class Impl { }",
       
  1330                           "package impl.conflict.module; class Impl { }",
       
  1331                           "package impl.conflict; public class clazz { public static class pkg { } }",
       
  1332                           "package nested.pack; public class Impl { }");
       
  1333 
       
  1334         //from source:
       
  1335         String log = new JavacTask(tb)
       
  1336             .options("--module-source-path", moduleSrc.toString(),
       
  1337                      "--source-path", src.toString(),
       
  1338                      "-processorpath", System.getProperty("test.class.path"),
       
  1339                      "-processor", UnboundLookup.class.getName(),
       
  1340                      "-XDrawDiagnostics")
       
  1341             .outdir(classes)
       
  1342             .files(findJavaFiles(moduleSrc))
       
  1343             .run()
       
  1344             .writeAll()
       
  1345             .getOutput(OutputKind.DIRECT);
       
  1346 
       
  1347         String moduleImplConflictString =
       
  1348                 "- compiler.note.multiple.elements: getTypeElement, impl.conflict.module.Impl, m2x, m1x";
       
  1349         String srcConflictString =
       
  1350                 "- compiler.note.multiple.elements: getTypeElement, impl.conflict.src.Impl, m1x, unnamed module";
       
  1351 
       
  1352         if (!log.contains(moduleImplConflictString) ||
       
  1353             !log.contains(srcConflictString)) {
       
  1354             throw new AssertionError("Expected output not found: " + log);
       
  1355         }
       
  1356 
       
  1357         if (log.split(Pattern.quote(moduleImplConflictString)).length > 2) {
       
  1358             throw new AssertionError("Too many warnings in: " + log);
       
  1359         }
       
  1360 
       
  1361         new JavacTask(tb)
       
  1362             .options("--source-path", src.toString())
       
  1363             .outdir(cpClasses)
       
  1364             .files(findJavaFiles(src))
       
  1365             .run()
       
  1366             .writeAll();
       
  1367 
       
  1368         //from classfiles:
       
  1369         new JavacTask(tb)
       
  1370             .options("--module-path", classes.toString(),
       
  1371                      "--class-path", cpClasses.toString(),
       
  1372                      "--add-modules", "m1x,m2x",
       
  1373                      "-processorpath", System.getProperty("test.class.path"),
       
  1374                      "-processor", UnboundLookup.class.getName(),
       
  1375                      "-proc:only")
       
  1376             .classes("java.lang.Object")
       
  1377             .run()
       
  1378             .writeAll();
       
  1379 
       
  1380         //source 8:
       
  1381         new JavacTask(tb)
       
  1382             .options("--source-path", src.toString(),
       
  1383                      "-source", "8",
       
  1384                      "-processorpath", System.getProperty("test.class.path"),
       
  1385                      "-processor", UnboundLookup8.class.getName())
       
  1386             .outdir(cpClasses)
       
  1387             .files(findJavaFiles(src))
       
  1388             .run()
       
  1389             .writeAll();
       
  1390 
       
  1391     }
       
  1392 
       
  1393     @SupportedAnnotationTypes("*")
       
  1394     public static final class UnboundLookup extends AbstractProcessor {
       
  1395 
       
  1396         @Override
       
  1397         public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
       
  1398             assertTypeElementExists("impl1.Impl", "m1x");
       
  1399             assertPackageElementExists("impl1", "m1x");
       
  1400             assertTypeElementExists("impl2.Impl", "m2x");
       
  1401             assertTypeElementExists("impl.conflict.clazz.pkg.I", "m1x");
       
  1402             assertTypeElementExists("impl.conflict.clazz", "m2x");
       
  1403             assertPackageElementExists("impl.conflict.clazz", "m1x");
       
  1404             assertPackageElementExists("impl2", "m2x");
       
  1405             assertPackageElementExists("nested.pack.pack", "m1x");
       
  1406             assertPackageElementExists("nested.pack", "m2x");
       
  1407             assertTypeElementExists("unique.nested.Impl", "m1x");
       
  1408             assertTypeElementNotFound("impl.conflict.module.Impl");
       
  1409             assertTypeElementNotFound("impl.conflict.module.Impl"); //check that the warning/note is produced only once
       
  1410             assertPackageElementNotFound("impl.conflict.module");
       
  1411             assertTypeElementNotFound("impl.conflict.src.Impl");
       
  1412             assertPackageElementNotFound("impl.conflict.src");
       
  1413             assertTypeElementNotFound("impl.conflict.clazz.pkg");
       
  1414             assertPackageElementNotFound("unique"); //do not return packages without members in module mode
       
  1415             assertTypeElementNotFound("nested"); //cannot distinguish between m1x and m2x
       
  1416 
       
  1417             return false;
       
  1418         }
       
  1419 
       
  1420         private void assertTypeElementExists(String name, String expectedModule) {
       
  1421             assertElementExists(name, "class", processingEnv.getElementUtils() :: getTypeElement, expectedModule);
       
  1422         }
       
  1423 
       
  1424         private void assertPackageElementExists(String name, String expectedModule) {
       
  1425             assertElementExists(name, "package", processingEnv.getElementUtils() :: getPackageElement, expectedModule);
       
  1426         }
       
  1427 
       
  1428         private void assertElementExists(String name, String type, Function<String, Element> getter, String expectedModule) {
       
  1429             Element clazz = getter.apply(name);
       
  1430 
       
  1431             if (clazz == null) {
       
  1432                 throw new AssertionError("No " + name + " " + type + " found.");
       
  1433             }
       
  1434 
       
  1435             ModuleElement mod = processingEnv.getElementUtils().getModuleOf(clazz);
       
  1436 
       
  1437             if (!mod.getQualifiedName().contentEquals(expectedModule)) {
       
  1438                 throw new AssertionError(name + " found in an unexpected module: " + mod.getQualifiedName());
       
  1439             }
       
  1440         }
       
  1441 
       
  1442         private void assertTypeElementNotFound(String name) {
       
  1443             assertElementNotFound(name, processingEnv.getElementUtils() :: getTypeElement);
       
  1444         }
       
  1445 
       
  1446         private void assertPackageElementNotFound(String name) {
       
  1447             assertElementNotFound(name, processingEnv.getElementUtils() :: getPackageElement);
       
  1448         }
       
  1449 
       
  1450         private void assertElementNotFound(String name, Function<String, Element> getter) {
       
  1451             Element found = getter.apply(name);
       
  1452 
       
  1453             if (found != null) {
       
  1454                 fail("Element found unexpectedly: " + found);
       
  1455             }
       
  1456         }
       
  1457 
       
  1458         @Override
       
  1459         public SourceVersion getSupportedSourceVersion() {
       
  1460             return SourceVersion.latest();
       
  1461         }
       
  1462 
       
  1463     }
       
  1464 
       
  1465     @SupportedAnnotationTypes("*")
       
  1466     public static final class UnboundLookup8 extends AbstractProcessor {
       
  1467 
       
  1468         @Override
       
  1469         public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
       
  1470             if (processingEnv.getElementUtils().getTypeElement("impl.conflict.src.Impl") == null) {
       
  1471                 throw new AssertionError("impl.conflict.src.Impl.");
       
  1472             }
       
  1473 
       
  1474             if (processingEnv.getElementUtils().getModuleElement("java.base") != null) {
       
  1475                 throw new AssertionError("getModuleElement != null for -source 8");
       
  1476             }
       
  1477 
       
  1478             return false;
       
  1479         }
       
  1480 
       
  1481         @Override
       
  1482         public SourceVersion getSupportedSourceVersion() {
       
  1483             return SourceVersion.latest();
       
  1484         }
       
  1485 
       
  1486     }
       
  1487 
       
  1488     @Test
       
  1489     public void testWrongDefaultTargetModule(Path base) throws Exception {
       
  1490         Path src = base.resolve("src");
       
  1491 
       
  1492         tb.writeJavaFiles(src,
       
  1493                           "package test; public class Test { }");
       
  1494 
       
  1495         Path classes = base.resolve("classes");
       
  1496 
       
  1497         Files.createDirectories(classes);
       
  1498 
       
  1499         List<String> log = new JavacTask(tb)
       
  1500             .options("--default-module-for-created-files=m!",
       
  1501                      "-XDrawDiagnostics")
       
  1502             .outdir(classes)
       
  1503             .files(findJavaFiles(src))
       
  1504             .run(Task.Expect.FAIL)
       
  1505             .writeAll()
       
  1506             .getOutputLines(OutputKind.DIRECT);
       
  1507 
       
  1508         List<String> expected = Arrays.asList(
       
  1509             "- compiler.err.bad.name.for.option: --default-module-for-created-files, m!"
       
  1510         );
       
  1511 
       
  1512         if (!log.equals(expected)) {
       
  1513             throw new AssertionError("Expected output not found.");
       
  1514         }
       
  1515     }
       
  1516 
       
  1517     private static void assertNonNull(String msg, Object val) {
       
  1518         if (val == null) {
       
  1519             throw new AssertionError(msg);
       
  1520         }
       
  1521     }
       
  1522 
       
  1523     private static void assertNull(String msg, Object val) {
       
  1524         if (val != null) {
       
  1525             throw new AssertionError(msg);
       
  1526         }
       
  1527     }
       
  1528 
       
  1529     private static void assertEquals(Object expected, Object actual) {
       
  1530         if (!Objects.equals(expected, actual)) {
       
  1531             throw new AssertionError("expected: " + expected + "; actual=" + actual);
       
  1532         }
       
  1533     }
       
  1534 
       
  1535     private static void assertFileExists(Path base, String... pathElements) {
       
  1536         Path file = resolveFile(base, pathElements);
       
  1537 
       
  1538         if (!Files.exists(file)) {
       
  1539             throw new AssertionError("Expected file: " + file + " exist, but it does not.");
       
  1540         }
       
  1541     }
       
  1542 
       
  1543     private static void assertFileNotExists(Path base, String... pathElements) {
       
  1544         Path file = resolveFile(base, pathElements);
       
  1545 
       
  1546         if (Files.exists(file)) {
       
  1547             throw new AssertionError("Expected file: " + file + " exist, but it does not.");
       
  1548         }
       
  1549     }
       
  1550 
       
  1551     static Path resolveFile(Path base, String... pathElements) {
       
  1552         Path file = base;
       
  1553 
       
  1554         for (String el : pathElements) {
       
  1555             file = file.resolve(el);
       
  1556         }
       
  1557 
       
  1558         return file;
       
  1559     }
       
  1560 
       
  1561     private static void fail(String msg) {
       
  1562         throw new AssertionError(msg);
       
  1563     }
       
  1564 
       
  1565 }