8173636: Results from Processor.getSupportedAnnotationTypes should be intepreted strictly
Reviewed-by: darcy, jjg
Contributed-by: joe.darcy@oracle.com, jan.lahoda@oracle.com
--- 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™ 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);