AnnotationProcessing.java revision 3822:d8766c39123a
1/* 2 * Copyright (c) 2015, 2016, 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 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 36import java.nio.file.Files; 37import java.nio.file.Path; 38import java.util.Arrays; 39import java.util.HashMap; 40import java.util.List; 41import java.util.Map; 42import java.util.Objects; 43import java.util.Set; 44import java.util.stream.Collectors; 45 46import javax.annotation.processing.AbstractProcessor; 47import javax.annotation.processing.Messager; 48import javax.annotation.processing.RoundEnvironment; 49import javax.annotation.processing.SupportedAnnotationTypes; 50import javax.annotation.processing.SupportedOptions; 51import javax.lang.model.SourceVersion; 52import javax.lang.model.element.Element; 53import javax.lang.model.element.ModuleElement; 54import javax.lang.model.element.ModuleElement.ProvidesDirective; 55import javax.lang.model.element.ModuleElement.UsesDirective; 56import javax.lang.model.element.PackageElement; 57import javax.lang.model.element.TypeElement; 58import javax.lang.model.element.VariableElement; 59import javax.lang.model.type.TypeKind; 60import javax.lang.model.util.ElementFilter; 61import javax.lang.model.util.ElementScanner9; 62import javax.tools.Diagnostic.Kind; 63 64import toolbox.JavacTask; 65import toolbox.Task; 66import toolbox.Task.Mode; 67 68public class AnnotationProcessing extends ModuleTestBase { 69 70 public static void main(String... args) throws Exception { 71 new AnnotationProcessing().runTests(); 72 } 73 74 @Test 75 public void testAPSingleModule(Path base) throws Exception { 76 Path moduleSrc = base.resolve("module-src"); 77 Path m1 = moduleSrc.resolve("m1x"); 78 79 Path classes = base.resolve("classes"); 80 81 Files.createDirectories(classes); 82 83 tb.writeJavaFiles(m1, 84 "module m1x { }", 85 "package impl; public class Impl { }"); 86 87 String log = new JavacTask(tb) 88 .options("--module-source-path", moduleSrc.toString(), 89 "-processor", AP.class.getName(), 90 "-AexpectedEnclosedElements=m1x=>impl") 91 .outdir(classes) 92 .files(findJavaFiles(moduleSrc)) 93 .run() 94 .writeAll() 95 .getOutput(Task.OutputKind.DIRECT); 96 97 if (!log.isEmpty()) 98 throw new AssertionError("Unexpected output: " + log); 99 } 100 101 @Test 102 public void testAPMultiModule(Path base) throws Exception { 103 Path moduleSrc = base.resolve("module-src"); 104 Path m1 = moduleSrc.resolve("m1x"); 105 Path m2 = moduleSrc.resolve("m2x"); 106 107 Path classes = base.resolve("classes"); 108 109 Files.createDirectories(classes); 110 111 tb.writeJavaFiles(m1, 112 "module m1x { }", 113 "package impl1; public class Impl1 { }"); 114 115 tb.writeJavaFiles(m2, 116 "module m2x { }", 117 "package impl2; public class Impl2 { }"); 118 119 String log = new JavacTask(tb) 120 .options("--module-source-path", moduleSrc.toString(), 121 "-processor", AP.class.getName(), 122 "-AexpectedEnclosedElements=m1x=>impl1,m2x=>impl2") 123 .outdir(classes) 124 .files(findJavaFiles(moduleSrc)) 125 .run() 126 .writeAll() 127 .getOutput(Task.OutputKind.DIRECT); 128 129 if (!log.isEmpty()) 130 throw new AssertionError("Unexpected output: " + log); 131 } 132 133 @SupportedAnnotationTypes("*") 134 @SupportedOptions("expectedEnclosedElements") 135 public static final class AP extends AbstractProcessor { 136 137 private Map<String, List<String>> module2ExpectedEnclosedElements; 138 139 @Override 140 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { 141 if (module2ExpectedEnclosedElements == null) { 142 module2ExpectedEnclosedElements = new HashMap<>(); 143 144 String expectedEnclosedElements = 145 processingEnv.getOptions().get("expectedEnclosedElements"); 146 147 for (String moduleDef : expectedEnclosedElements.split(",")) { 148 String[] module2Packages = moduleDef.split("=>"); 149 150 module2ExpectedEnclosedElements.put(module2Packages[0], 151 Arrays.asList(module2Packages[1].split(":"))); 152 } 153 } 154 155 //verify ModuleType and ModuleSymbol behavior: 156 for (Element root : roundEnv.getRootElements()) { 157 ModuleElement module = processingEnv.getElementUtils().getModuleOf(root); 158 159 assertEquals(TypeKind.MODULE, module.asType().getKind()); 160 161 boolean[] seenModule = new boolean[1]; 162 163 module.accept(new ElementScanner9<Void, Void>() { 164 @Override 165 public Void visitModule(ModuleElement e, Void p) { 166 seenModule[0] = true; 167 return null; 168 } 169 @Override 170 public Void scan(Element e, Void p) { 171 throw new AssertionError("Shouldn't get here."); 172 } 173 }, null); 174 175 assertEquals(true, seenModule[0]); 176 177 List<String> actualElements = 178 module.getEnclosedElements() 179 .stream() 180 .map(s -> (PackageElement) s) 181 .map(p -> p.getQualifiedName().toString()) 182 .collect(Collectors.toList()); 183 184 assertEquals(module2ExpectedEnclosedElements.remove(module.getQualifiedName().toString()), 185 actualElements); 186 } 187 188 if (roundEnv.processingOver()) { 189 assertEquals(true, module2ExpectedEnclosedElements.isEmpty()); 190 } 191 192 return false; 193 } 194 195 @Override 196 public SourceVersion getSupportedSourceVersion() { 197 return SourceVersion.latest(); 198 } 199 200 } 201 202 @Test 203 public void testVerifyUsesProvides(Path base) throws Exception { 204 Path moduleSrc = base.resolve("module-src"); 205 Path m1 = moduleSrc.resolve("m1x"); 206 207 Path classes = base.resolve("classes"); 208 209 Files.createDirectories(classes); 210 211 tb.writeJavaFiles(m1, 212 "module m1x { exports api; uses api.Api; provides api.Api with impl.Impl; }", 213 "package api; public class Api { }", 214 "package impl; public class Impl extends api.Api { }"); 215 216 String log = new JavacTask(tb) 217 .options("-doe", "-processor", VerifyUsesProvidesAP.class.getName()) 218 .outdir(classes) 219 .files(findJavaFiles(moduleSrc)) 220 .run() 221 .writeAll() 222 .getOutput(Task.OutputKind.DIRECT); 223 224 if (!log.isEmpty()) 225 throw new AssertionError("Unexpected output: " + log); 226 } 227 228 @SupportedAnnotationTypes("*") 229 public static final class VerifyUsesProvidesAP extends AbstractProcessor { 230 231 @Override 232 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { 233 TypeElement api = processingEnv.getElementUtils().getTypeElement("api.Api"); 234 235 assertNonNull("Cannot find api.Api", api); 236 237 ModuleElement modle = (ModuleElement) processingEnv.getElementUtils().getPackageOf(api).getEnclosingElement(); 238 239 assertNonNull("modle is null", modle); 240 241 List<? extends UsesDirective> uses = ElementFilter.usesIn(modle.getDirectives()); 242 assertEquals(1, uses.size()); 243 assertEquals("api.Api", uses.iterator().next().getService().getQualifiedName().toString()); 244 245 List<? extends ProvidesDirective> provides = ElementFilter.providesIn(modle.getDirectives()); 246 assertEquals(1, provides.size()); 247 assertEquals("api.Api", provides.iterator().next().getService().getQualifiedName().toString()); 248 assertEquals("impl.Impl", provides.iterator().next().getImplementations().get(0).getQualifiedName().toString()); 249 250 return false; 251 } 252 253 @Override 254 public SourceVersion getSupportedSourceVersion() { 255 return SourceVersion.latest(); 256 } 257 258 } 259 260 @Test 261 public void testPackageNoModule(Path base) throws Exception { 262 Path src = base.resolve("src"); 263 Path classes = base.resolve("classes"); 264 265 Files.createDirectories(classes); 266 267 tb.writeJavaFiles(src, 268 "package api; public class Api { }"); 269 270 String log = new JavacTask(tb) 271 .options("-processor", VerifyPackageNoModule.class.getName(), 272 "-source", "8", 273 "-Xlint:-options") 274 .outdir(classes) 275 .files(findJavaFiles(src)) 276 .run() 277 .writeAll() 278 .getOutput(Task.OutputKind.DIRECT); 279 280 if (!log.isEmpty()) 281 throw new AssertionError("Unexpected output: " + log); 282 } 283 284 @SupportedAnnotationTypes("*") 285 public static final class VerifyPackageNoModule extends AbstractProcessor { 286 287 @Override 288 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { 289 TypeElement api = processingEnv.getElementUtils().getTypeElement("api.Api"); 290 291 assertNonNull("Cannot find api.Api", api); 292 293 ModuleElement modle = (ModuleElement) processingEnv.getElementUtils().getPackageOf(api).getEnclosingElement(); 294 295 assertNull("modle is not null", modle); 296 297 return false; 298 } 299 300 @Override 301 public SourceVersion getSupportedSourceVersion() { 302 return SourceVersion.latest(); 303 } 304 305 } 306 307 @Test 308 public void testQualifiedClassForProcessing(Path base) throws Exception { 309 Path moduleSrc = base.resolve("module-src"); 310 Path m1 = moduleSrc.resolve("m1x"); 311 Path m2 = moduleSrc.resolve("m2x"); 312 313 Path classes = base.resolve("classes"); 314 315 Files.createDirectories(classes); 316 317 tb.writeJavaFiles(m1, 318 "module m1x { }", 319 "package impl; public class Impl { int m1x; }"); 320 321 tb.writeJavaFiles(m2, 322 "module m2x { }", 323 "package impl; public class Impl { int m2x; }"); 324 325 new JavacTask(tb) 326 .options("--module-source-path", moduleSrc.toString()) 327 .outdir(classes) 328 .files(findJavaFiles(moduleSrc)) 329 .run() 330 .writeAll() 331 .getOutput(Task.OutputKind.DIRECT); 332 333 List<String> expected = Arrays.asList("Note: field: m1x"); 334 335 for (Mode mode : new Mode[] {Mode.API, Mode.CMDLINE}) { 336 List<String> log = new JavacTask(tb, mode) 337 .options("-processor", QualifiedClassForProcessing.class.getName(), 338 "--module-path", classes.toString()) 339 .classes("m1x/impl.Impl") 340 .outdir(classes) 341 .run() 342 .writeAll() 343 .getOutputLines(Task.OutputKind.DIRECT); 344 345 if (!expected.equals(log)) 346 throw new AssertionError("Unexpected output: " + log); 347 } 348 } 349 350 @SupportedAnnotationTypes("*") 351 public static final class QualifiedClassForProcessing extends AbstractProcessor { 352 353 @Override 354 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { 355 if (processingEnv.getElementUtils().getModuleElement("m1x") == null) { 356 throw new AssertionError("No m1x module found."); 357 } 358 359 Messager messager = processingEnv.getMessager(); 360 361 for (TypeElement clazz : ElementFilter.typesIn(roundEnv.getRootElements())) { 362 for (VariableElement field : ElementFilter.fieldsIn(clazz.getEnclosedElements())) { 363 messager.printMessage(Kind.NOTE, "field: " + field.getSimpleName()); 364 } 365 } 366 367 return false; 368 } 369 370 @Override 371 public SourceVersion getSupportedSourceVersion() { 372 return SourceVersion.latest(); 373 } 374 375 } 376 377 private static void assertNonNull(String msg, Object val) { 378 if (val == null) { 379 throw new AssertionError(msg); 380 } 381 } 382 383 private static void assertNull(String msg, Object val) { 384 if (val != null) { 385 throw new AssertionError(msg); 386 } 387 } 388 389 private static void assertEquals(Object expected, Object actual) { 390 if (!Objects.equals(expected, actual)) { 391 throw new AssertionError("expected: " + expected + "; actual=" + actual); 392 } 393 } 394 395} 396