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 * @summary Verify that annotation processing works. 27 * @library /tools/lib 28 * @modules 29 * jdk.compiler/com.sun.tools.javac.api 30 * jdk.compiler/com.sun.tools.javac.main 31 * @build toolbox.ToolBox toolbox.JavacTask ModuleTestBase 32 * @run main AnnotationProcessing 33 */ 34 35 import java.nio.file.Files; 36 import java.nio.file.Path; 37 import java.util.Arrays; 38 import java.util.HashMap; 39 import java.util.List; 40 import java.util.Map; 41 import java.util.Objects; 42 import java.util.Set; 43 import java.util.stream.Collectors; 44 45 import javax.annotation.processing.AbstractProcessor; 46 import javax.annotation.processing.RoundEnvironment; 47 import javax.annotation.processing.SupportedAnnotationTypes; 48 import javax.annotation.processing.SupportedOptions; 49 import javax.lang.model.SourceVersion; 50 import javax.lang.model.element.Element; 51 import javax.lang.model.element.ModuleElement; 52 import javax.lang.model.element.ModuleElement.ProvidesDirective; 53 import javax.lang.model.element.ModuleElement.UsesDirective; 54 import javax.lang.model.element.PackageElement; 55 import javax.lang.model.element.TypeElement; 56 import javax.lang.model.type.TypeKind; 57 import javax.lang.model.util.ElementFilter; 58 import javax.lang.model.util.ElementScanner9; 59 60 import toolbox.JavacTask; 61 import toolbox.Task; 62 import toolbox.ToolBox; 63 64 public class AnnotationProcessing extends ModuleTestBase { 65 66 public static void main(String... args) throws Exception { 67 new AnnotationProcessing().runTests(); 68 } 69 70 @Test 71 public void testAPSingleModule(Path base) throws Exception { 72 Path moduleSrc = base.resolve("module-src"); 73 Path m1 = moduleSrc.resolve("m1"); 74 75 Path classes = base.resolve("classes"); 76 77 Files.createDirectories(classes); 78 79 tb.writeJavaFiles(m1, 80 "module m1 { }", 81 "package impl; public class Impl { }"); 82 83 String log = new JavacTask(tb) 84 .options("-modulesourcepath", moduleSrc.toString(), 85 "-processor", AP.class.getName(), 86 "-AexpectedEnclosedElements=m1=>impl") 87 .outdir(classes) 88 .files(findJavaFiles(moduleSrc)) 89 .run() 90 .writeAll() 91 .getOutput(Task.OutputKind.DIRECT); 92 93 if (!log.isEmpty()) 94 throw new AssertionError("Unexpected output: " + log); 95 } 96 97 @Test 98 public void testAPMultiModule(Path base) throws Exception { 99 Path moduleSrc = base.resolve("module-src"); 100 Path m1 = moduleSrc.resolve("m1"); 101 Path m2 = moduleSrc.resolve("m2"); 102 103 Path classes = base.resolve("classes"); 104 105 Files.createDirectories(classes); 106 107 tb.writeJavaFiles(m1, 108 "module m1 { }", 109 "package impl1; public class Impl1 { }"); 110 111 tb.writeJavaFiles(m2, 112 "module m2 { }", 113 "package impl2; public class Impl2 { }"); 114 115 String log = new JavacTask(tb) 116 .options("-modulesourcepath", moduleSrc.toString(), 117 "-processor", AP.class.getName(), 118 "-AexpectedEnclosedElements=m1=>impl1,m2=>impl2") 119 .outdir(classes) 120 .files(findJavaFiles(moduleSrc)) 121 .run() 122 .writeAll() 123 .getOutput(Task.OutputKind.DIRECT); 124 125 if (!log.isEmpty()) 126 throw new AssertionError("Unexpected output: " + log); 127 } 128 129 @SupportedAnnotationTypes("*") 130 @SupportedOptions("expectedEnclosedElements") 131 public static final class AP extends AbstractProcessor { 132 133 private Map<String, List<String>> module2ExpectedEnclosedElements; 134 135 @Override 136 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { 137 if (module2ExpectedEnclosedElements == null) { 138 module2ExpectedEnclosedElements = new HashMap<>(); 139 140 String expectedEnclosedElements = 141 processingEnv.getOptions().get("expectedEnclosedElements"); 142 143 for (String moduleDef : expectedEnclosedElements.split(",")) { 144 String[] module2Packages = moduleDef.split("=>"); 145 146 module2ExpectedEnclosedElements.put(module2Packages[0], 147 Arrays.asList(module2Packages[1].split(":"))); 148 } 149 } 150 151 //verify ModuleType and ModuleSymbol behavior: 152 for (Element root : roundEnv.getRootElements()) { 153 ModuleElement module = processingEnv.getElementUtils().getModuleOf(root); 154 155 assertEquals(TypeKind.MODULE, module.asType().getKind()); 156 157 boolean[] seenModule = new boolean[1]; 158 159 module.accept(new ElementScanner9<Void, Void>() { 160 @Override 161 public Void visitModule(ModuleElement e, Void p) { 162 seenModule[0] = true; 163 return null; 164 } 165 @Override 166 public Void scan(Element e, Void p) { 167 throw new AssertionError("Shouldn't get here."); 168 } 169 }, null); 170 171 assertEquals(true, seenModule[0]); 172 173 List<String> actualElements = 174 module.getEnclosedElements() 175 .stream() 176 .map(s -> (PackageElement) s) 177 .map(p -> p.getQualifiedName().toString()) 178 .collect(Collectors.toList()); 179 180 assertEquals(module2ExpectedEnclosedElements.remove(module.getQualifiedName().toString()), 181 actualElements); 182 } 183 184 if (roundEnv.processingOver()) { 185 assertEquals(true, module2ExpectedEnclosedElements.isEmpty()); 186 } 187 188 return false; 189 } 190 191 @Override 192 public SourceVersion getSupportedSourceVersion() { 193 return SourceVersion.latest(); 194 } 195 196 } 197 198 @Test 199 public void testVerifyUsesProvides(Path base) throws Exception { 200 Path moduleSrc = base.resolve("module-src"); 201 Path m1 = moduleSrc.resolve("m1"); 202 203 Path classes = base.resolve("classes"); 204 205 Files.createDirectories(classes); 206 207 tb.writeJavaFiles(m1, 208 "module m1 { exports api; uses api.Api; provides api.Api with impl.Impl; }", 209 "package api; public class Api { }", 210 "package impl; public class Impl extends api.Api { }"); 211 212 String log = new JavacTask(tb) 213 .options("-doe", "-processor", VerifyUsesProvidesAP.class.getName()) 214 .outdir(classes) 215 .files(findJavaFiles(moduleSrc)) 216 .run() 217 .writeAll() 218 .getOutput(Task.OutputKind.DIRECT); 219 220 if (!log.isEmpty()) 221 throw new AssertionError("Unexpected output: " + log); 222 } 223 224 @SupportedAnnotationTypes("*") 225 public static final class VerifyUsesProvidesAP extends AbstractProcessor { 226 227 @Override 228 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { 229 TypeElement api = processingEnv.getElementUtils().getTypeElement("api.Api"); 230 231 assertNonNull("Cannot find api.Api", api); 232 233 ModuleElement modle = (ModuleElement) processingEnv.getElementUtils().getPackageOf(api).getEnclosingElement(); 234 235 assertNonNull("modle is null", modle); 236 237 List<? extends UsesDirective> uses = ElementFilter.usesIn(modle.getDirectives()); 238 assertEquals(1, uses.size()); 239 assertEquals("api.Api", uses.iterator().next().getService().getQualifiedName().toString()); 240 241 List<? extends ProvidesDirective> provides = ElementFilter.providesIn(modle.getDirectives()); 242 assertEquals(1, provides.size()); 243 assertEquals("api.Api", provides.iterator().next().getService().getQualifiedName().toString()); 244 assertEquals("impl.Impl", provides.iterator().next().getImplementation().getQualifiedName().toString()); 245 246 return false; 247 } 248 249 @Override 250 public SourceVersion getSupportedSourceVersion() { 251 return SourceVersion.latest(); 252 } 253 254 } 255 256 @Test 257 public void testPackageNoModule(Path base) throws Exception { 258 Path src = base.resolve("src"); 259 Path classes = base.resolve("classes"); 260 261 Files.createDirectories(classes); 262 263 tb.writeJavaFiles(src, 264 "package api; public class Api { }"); 265 266 String log = new JavacTask(tb) 267 .options("-processor", VerifyPackageNoModule.class.getName(), 268 "-source", "8", 269 "-Xlint:-options") 270 .outdir(classes) 271 .files(findJavaFiles(src)) 272 .run() 273 .writeAll() 274 .getOutput(Task.OutputKind.DIRECT); 275 276 if (!log.isEmpty()) 277 throw new AssertionError("Unexpected output: " + log); 278 } 279 280 @SupportedAnnotationTypes("*") 281 public static final class VerifyPackageNoModule extends AbstractProcessor { 282 283 @Override 284 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { 285 TypeElement api = processingEnv.getElementUtils().getTypeElement("api.Api"); 286 287 assertNonNull("Cannot find api.Api", api); 288 289 ModuleElement modle = (ModuleElement) processingEnv.getElementUtils().getPackageOf(api).getEnclosingElement(); 290 291 assertNull("modle is not null", modle); 292 293 return false; 294 } 295 296 @Override 297 public SourceVersion getSupportedSourceVersion() { 298 return SourceVersion.latest(); 299 } 300 301 } 302 303 private static void assertNonNull(String msg, Object val) { 304 if (val == null) { 305 throw new AssertionError(msg); 306 } 307 } 308 309 private static void assertNull(String msg, Object val) { 310 if (val != null) { 311 throw new AssertionError(msg); 312 } 313 } 314 315 private static void assertEquals(Object expected, Object actual) { 316 if (!Objects.equals(expected, actual)) { 317 throw new AssertionError("expected: " + expected + "; actual=" + actual); 318 } 319 } 320 321 }