8173636: Results from Processor.getSupportedAnnotationTypes should be intepreted strictly
authorjlahoda
Mon, 06 Feb 2017 15:57:35 +0100
changeset 43585 19e14d35add0
parent 43584 63e67712246b
child 43586 cc7a4eb79b29
8173636: Results from Processor.getSupportedAnnotationTypes should be intepreted strictly Reviewed-by: darcy, jjg Contributed-by: joe.darcy@oracle.com, jan.lahoda@oracle.com
langtools/src/java.compiler/share/classes/javax/annotation/processing/AbstractProcessor.java
langtools/src/java.compiler/share/classes/javax/annotation/processing/Processor.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java
langtools/test/tools/javac/modules/AnnotationProcessing.java
--- a/langtools/src/java.compiler/share/classes/javax/annotation/processing/AbstractProcessor.java	Mon Feb 06 18:14:51 2017 +0530
+++ b/langtools/src/java.compiler/share/classes/javax/annotation/processing/AbstractProcessor.java	Mon Feb 06 15:57:35 2017 +0100
@@ -83,7 +83,7 @@
         if  (so == null)
             return Collections.emptySet();
         else
-            return arrayToSet(so.value());
+            return arrayToSet(so.value(), false);
     }
 
     /**
@@ -92,21 +92,31 @@
      * same set of strings as the annotation.  If the class is not so
      * annotated, an empty set is returned.
      *
+     * If the {@link ProcessingEvironment#getSourceVersion source
+     * version} does not support modules, in other words if it is less
+     * than or equal to {@link SourceVersion#RELEASE_8 RELEASE_8},
+     * then any leading {@link Processor#getSupportedAnnotationTypes
+     * module prefixes} are stripped from the names.
+     *
      * @return the names of the annotation types supported by this
      * processor, or an empty set if none
      */
     public Set<String> getSupportedAnnotationTypes() {
             SupportedAnnotationTypes sat = this.getClass().getAnnotation(SupportedAnnotationTypes.class);
+            boolean initialized = isInitialized();
             if  (sat == null) {
-                if (isInitialized())
+                if (initialized)
                     processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING,
                                                              "No SupportedAnnotationTypes annotation " +
                                                              "found on " + this.getClass().getName() +
                                                              ", returning an empty set.");
                 return Collections.emptySet();
+            } else {
+                boolean stripModulePrefixes =
+                        initialized &&
+                        processingEnv.getSourceVersion().compareTo(SourceVersion.RELEASE_8) <= 0;
+                return arrayToSet(sat.value(), stripModulePrefixes);
             }
-            else
-                return arrayToSet(sat.value());
         }
 
     /**
@@ -185,11 +195,18 @@
         return initialized;
     }
 
-    private static Set<String> arrayToSet(String[] array) {
+    private static Set<String> arrayToSet(String[] array,
+                                          boolean stripModulePrefixes) {
         assert array != null;
         Set<String> set = new HashSet<>(array.length);
-        for (String s : array)
+        for (String s : array) {
+            if (stripModulePrefixes) {
+                int index = s.indexOf('/');
+                if (index != -1)
+                    s = s.substring(index + 1);
+            }
             set.add(s);
+        }
         return Collections.unmodifiableSet(set);
     }
 }
--- a/langtools/src/java.compiler/share/classes/javax/annotation/processing/Processor.java	Mon Feb 06 18:14:51 2017 +0530
+++ b/langtools/src/java.compiler/share/classes/javax/annotation/processing/Processor.java	Mon Feb 06 15:57:35 2017 +0100
@@ -254,6 +254,14 @@
      * a.B} which reside in different modules. To only support {@code
      * a.B} in the {@code Foo} module, instead use {@code "Foo/a.B"}.
      *
+     * If a module name is included, only an annotation in that module
+     * is matched. In particular, if a module name is given in an
+     * environment where modules are not supported, such as an
+     * annotation processing environment configured for a {@linkplain
+     * javax.annotation.processing.ProcessingEnvironment#getSourceVersion
+     * source version} without modules, then the annotation types with
+     * a module name do <em>not</em> match.
+     *
      * Finally, {@code "*"} by itself represents the set of all
      * annotation types, including the empty set.  Note that a
      * processor should not claim {@code "*"} unless it is actually
@@ -280,6 +288,14 @@
      * where <i>TypeName</i> is as defined in
      * <cite>The Java&trade; Language Specification</cite>.
      *
+     * @apiNote When running in an environment which supports modules,
+     * processors are encouraged to include the module prefix when
+     * describing their supported annotation types. The method {@link
+     * AbstractProcessor.getSupportedAnnotationTypes
+     * AbstractProcessor.getSupportedAnnotationTypes} provides support
+     * for stripping off the module prefix when running in an
+     * environment without modules.
+     *
      * @return the names of the annotation types supported by this processor
      * @see javax.annotation.processing.SupportedAnnotationTypes
      * @jls 3.8 Identifiers
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java	Mon Feb 06 18:14:51 2017 +0530
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java	Mon Feb 06 15:57:35 2017 +0100
@@ -1671,14 +1671,14 @@
             if (s.equals("*")) {
                 return MatchingUtils.validImportStringToPattern(s);
             }
-            module = ".*/";
+            module = allowModules ? ".*/" : "";
             pkg = s;
         } else {
             module = Pattern.quote(s.substring(0, slash + 1));
             pkg = s.substring(slash + 1);
         }
         if (MatchingUtils.isValidImportString(pkg)) {
-            return Pattern.compile((allowModules ? module : "") + MatchingUtils.validImportStringToPatternString(pkg));
+            return Pattern.compile(module + MatchingUtils.validImportStringToPatternString(pkg));
         } else {
             log.warning("proc.malformed.supported.string", s, p.getClass().getName());
             return noMatches; // won't match any valid identifier
--- a/langtools/test/tools/javac/modules/AnnotationProcessing.java	Mon Feb 06 18:14:51 2017 +0530
+++ b/langtools/test/tools/javac/modules/AnnotationProcessing.java	Mon Feb 06 15:57:35 2017 +0100
@@ -23,7 +23,7 @@
 
 /**
  * @test
- * @bug 8133884 8162711 8133896 8172158 8172262
+ * @bug 8133884 8162711 8133896 8172158 8172262 8173636
  * @summary Verify that annotation processing works.
  * @library /tools/lib
  * @modules
@@ -173,7 +173,7 @@
                     String[] module2Packages = moduleDef.split("=>");
 
                     module2ExpectedEnclosedElements.put(module2Packages[0],
-                                                        Arrays.asList(module2Packages[1].split(":")));
+                                                        List.of(module2Packages[1].split(":")));
                 }
             }
 
@@ -359,7 +359,7 @@
             .writeAll()
             .getOutput(Task.OutputKind.DIRECT);
 
-        List<String> expected = Arrays.asList("Note: field: m1x");
+        List<String> expected = List.of("Note: field: m1x");
 
         for (Mode mode : new Mode[] {Mode.API, Mode.CMDLINE}) {
             List<String> log = new JavacTask(tb, mode)
@@ -424,7 +424,7 @@
                 .writeAll()
                 .getOutputLines(Task.OutputKind.STDERR);
 
-        assertEquals(Arrays.asList("module: m1x"), log);
+        assertEquals(List.of("module: m1x"), log);
     }
 
     @SupportedAnnotationTypes("*")
@@ -472,8 +472,8 @@
                 .writeAll()
                 .getOutputLines(Task.OutputKind.DIRECT);
 
-        List<String> expectedLog = Arrays.asList("Note: AP Invoked",
-                                                 "Note: AP Invoked");
+        List<String> expectedLog = List.of("Note: AP Invoked",
+                                           "Note: AP Invoked");
 
         assertEquals(expectedLog, log);
 
@@ -600,7 +600,7 @@
 
                 JavaCompiler comp = ToolProvider.getSystemJavaCompiler();
                 try (StandardJavaFileManager fm = comp.getStandardFileManager(null, null, null)) {
-                    List<String> options = Arrays.asList("-d", scratchClasses.toString());
+                    List<String> options = List.of("-d", scratchClasses.toString());
                     Iterable<? extends JavaFileObject> files = fm.getJavaFileObjects(scratchSrc);
                     CompilationTask task = comp.getTask(null, fm, null, options, null, files);
 
@@ -939,7 +939,7 @@
             .writeAll()
             .getOutputLines(OutputKind.STDERR);
 
-        expected = Arrays.asList("");
+        expected = List.of("");
 
         if (!expected.equals(log)) {
             throw new AssertionError("Output does not match; output: " + log);
@@ -954,15 +954,17 @@
             .writeAll()
             .getOutputLines(OutputKind.STDERR);
 
-        expected = Arrays.asList("SelectAnnotationBTestAP",
-                                 "SelectAnnotationBTestAP");
+        expected = List.of("SelectAnnotationBTestAP",
+                           "SelectAnnotationBTestAP");
 
         if (!expected.equals(log)) {
             throw new AssertionError("Output does not match; output: " + log);
         }
 
         log = new JavacTask(tb)
-            .options("-processor", SelectAnnotationATestAP.class.getName() + "," + SelectAnnotationBTestAP.class.getName(),
+            .options("-processor", SelectAnnotationATestAP.class.getName() + "," +
+                                   SelectAnnotationBTestAP.class.getName() + "," +
+                                   SelectAnnotationAStrictTestAP.class.getName(),
                      "--module-source-path", src.toString(),
                      "-m", "m4x")
             .outdir(classes)
@@ -970,10 +972,43 @@
             .writeAll()
             .getOutputLines(OutputKind.STDERR);
 
-        expected = Arrays.asList("SelectAnnotationATestAP",
-                                 "SelectAnnotationBTestAP",
-                                 "SelectAnnotationATestAP",
-                                 "SelectAnnotationBTestAP");
+        expected = List.of("SelectAnnotationATestAP",
+                           "SelectAnnotationBTestAP",
+                           "SelectAnnotationAStrictTestAP",
+                           "SelectAnnotationATestAP",
+                           "SelectAnnotationBTestAP",
+                           "SelectAnnotationAStrictTestAP");
+
+        if (!expected.equals(log)) {
+            throw new AssertionError("Output does not match; output: " + log);
+        }
+    }
+
+    @Test
+    public void testDisambiguateAnnotationsUnnamedModule(Path base) throws Exception {
+        Path classes = base.resolve("classes");
+
+        Files.createDirectories(classes);
+
+        Path src = base.resolve("src");
+
+        tb.writeJavaFiles(src,
+                          "package api; public @interface A {}",
+                          "package api; public @interface B {}",
+                          "package impl; import api.*; @A @B public class T {}");
+
+        List<String> log = new JavacTask(tb)
+            .options("-processor", SelectAnnotationATestAP.class.getName() + "," +
+                                   SelectAnnotationBTestAP.class.getName() + "," +
+                                   SelectAnnotationAStrictTestAP.class.getName())
+            .outdir(classes)
+            .files(findJavaFiles(src))
+            .run()
+            .writeAll()
+            .getOutputLines(OutputKind.STDERR);
+
+        List<String> expected = List.of("SelectAnnotationBTestAP",
+                                        "SelectAnnotationBTestAP");
 
         if (!expected.equals(log)) {
             throw new AssertionError("Output does not match; output: " + log);
@@ -994,7 +1029,9 @@
                           "package impl; import api.*; @A @B public class T {}");
 
         List<String> log = new JavacTask(tb)
-            .options("-processor", SelectAnnotationATestAP.class.getName() + "," + SelectAnnotationBTestAP.class.getName(),
+            .options("-processor", SelectAnnotationATestAP.class.getName() + "," +
+                                   SelectAnnotationBTestAP.class.getName() + "," +
+                                   SelectAnnotationAStrictTestAP.class.getName(),
                      "-source", "8", "-target", "8")
             .outdir(classes)
             .files(findJavaFiles(src))
@@ -1002,10 +1039,10 @@
             .writeAll()
             .getOutputLines(OutputKind.STDERR);
 
-        List<String> expected = Arrays.asList("SelectAnnotationATestAP",
-                                              "SelectAnnotationBTestAP",
-                                              "SelectAnnotationATestAP",
-                                              "SelectAnnotationBTestAP");
+        List<String> expected = List.of("SelectAnnotationATestAP",
+                                        "SelectAnnotationBTestAP",
+                                        "SelectAnnotationATestAP",
+                                        "SelectAnnotationBTestAP");
 
         if (!expected.equals(log)) {
             throw new AssertionError("Output does not match; output: " + log);
@@ -1036,6 +1073,22 @@
 
     }
 
+    public static final class SelectAnnotationAStrictTestAP extends AbstractProcessor {
+
+        @Override
+        public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+            System.err.println("SelectAnnotationAStrictTestAP");
+
+            return false;
+        }
+
+        @Override
+        public Set<String> getSupportedAnnotationTypes() {
+            return Set.of("m2x/api.A");
+        }
+
+    }
+
     private static void writeFile(String content, Path base, String... pathElements) throws IOException {
         Path file = resolveFile(base, pathElements);