1 /* 2 * Copyright 2004-2009 Sun Microsystems, Inc. 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. Sun designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Sun in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, 22 * CA 95054 USA or visit www.sun.com if you need additional information or 23 * have any questions. 24 */ 25 26 package com.sun.tools.apt.comp; 27 28 import com.sun.tools.javac.code.*; 29 import com.sun.tools.javac.comp.*; 30 import com.sun.tools.javac.tree.*; 31 import com.sun.tools.javac.util.*; 32 import com.sun.tools.javac.tree.TreeScanner; 33 import com.sun.tools.javac.util.Context; 34 import com.sun.tools.apt.util.Bark; 35 import com.sun.tools.javac.util.Position; 36 37 import java.util.*; 38 import java.util.regex.*; 39 import java.lang.reflect.*; 40 import java.lang.reflect.InvocationTargetException; 41 import java.io.IOException; 42 43 import com.sun.tools.apt.*; 44 import com.sun.tools.apt.comp.*; 45 import com.sun.tools.javac.code.Symbol.*; 46 47 import com.sun.mirror.declaration.TypeDeclaration; 48 import com.sun.mirror.declaration.AnnotationTypeDeclaration; 49 import com.sun.mirror.apt.*; 50 // import com.sun.mirror.apt.AnnotationProcessorFactory; 51 import com.sun.mirror.apt.AnnotationProcessors; 52 53 import com.sun.tools.apt.mirror.AptEnv; 54 import com.sun.tools.apt.mirror.apt.FilerImpl; 55 import com.sun.tools.apt.mirror.apt.AnnotationProcessorEnvironmentImpl; 56 57 58 import static com.sun.tools.apt.mirror.declaration.DeclarationMaker.isJavaIdentifier; 59 60 /** 61 * Apt compiler phase. 62 * 63 * <p><b>This is NOT part of any API supported by Sun Microsystems. 64 * If you write code that depends on this, you do so at your own 65 * risk. This code and its internal interfaces are subject to change 66 * or deletion without notice.</b> 67 */ 68 @SuppressWarnings("deprecation") 69 public class Apt extends ListBuffer<Env<AttrContext>> { 70 java.util.Set<String> genSourceFileNames = new java.util.LinkedHashSet<String>(); 71 public java.util.Set<String> getSourceFileNames() { 72 return genSourceFileNames; 73 } 74 75 /** List of names of generated class files. 76 */ 77 java.util.Set<String> genClassFileNames = new java.util.LinkedHashSet<String>(); 78 public java.util.Set<String> getClassFileNames() { 79 return genClassFileNames; 80 } 81 82 /* AptEnvironment */ 83 AptEnv aptenv; 84 85 private Context context; 86 87 /** The context key for the todo list. */ 88 89 protected static final Context.Key<Apt> aptKey = 90 new Context.Key<Apt>(); 91 92 /** Get the Apt instance for this context. */ 93 public static Apt instance(Context context) { 94 Apt instance = context.get(aptKey); 95 if (instance == null) 96 instance = new Apt(context); 97 return instance; 98 } 99 100 /** Create a new apt list. */ 101 protected Apt(Context context) { 102 this.context = context; 103 104 context.put(aptKey, this); 105 aptenv = AptEnv.instance(context); 106 } 107 108 /** 109 * Used to scan javac trees to build data structures needed for 110 * bootstrapping the apt environment. In particular: 111 * 112 * <ul> 113 * 114 * <li> Generate list of canonical names of annotation types that 115 * appear in source files given on the command line 116 * 117 * <li> Collect list of javac symbols representing source files 118 * given on the command line 119 * 120 * </ul> 121 */ 122 static class AptTreeScanner extends TreeScanner { 123 124 // Set of fully qualified names of annotation types present in 125 // examined source 126 private Set<String> annotationSet; 127 128 // Symbols to build bootstrapping declaration list 129 private Collection<ClassSymbol> specifiedDeclCollection; 130 private Collection<ClassSymbol> declCollection; 131 132 public Set<String> getAnnotationSet() { 133 return annotationSet; 134 } 135 136 public AptTreeScanner() { 137 annotationSet = new LinkedHashSet<String>(); 138 specifiedDeclCollection = new LinkedHashSet<ClassSymbol>(); 139 declCollection = new LinkedHashSet<ClassSymbol>(); 140 } 141 142 public void visitTopLevel(JCTree.JCCompilationUnit tree) { 143 super.visitTopLevel(tree); 144 // Print out contents -- what are we dealing with? 145 146 for(JCTree d: tree.defs) { 147 if (d instanceof JCTree.JCClassDecl) 148 specifiedDeclCollection.add(((JCTree.JCClassDecl) d).sym); 149 } 150 151 } 152 153 public void visitBlock(JCTree.JCBlock tree) { 154 ; // Do nothing. 155 } 156 157 158 // should add nested classes to packages, etc. 159 public void visitClassDef(JCTree.JCClassDecl tree) { 160 if (tree.sym == null) { 161 // could be an anon class w/in an initializer 162 return; 163 } 164 165 super.visitClassDef(tree); 166 167 declCollection.add(tree.sym); 168 } 169 170 public void visitMethodDef(JCTree.JCMethodDecl tree) { 171 super.visitMethodDef(tree); 172 } 173 174 public void visitVarDef(JCTree.JCVariableDecl tree) { 175 super.visitVarDef(tree); 176 } 177 178 public void visitAnnotation(JCTree.JCAnnotation tree) { 179 super.visitAnnotation(tree); 180 annotationSet.add(tree.type.tsym.toString()); 181 } 182 } 183 184 Set<String> computeAnnotationSet(Collection<ClassSymbol> classSymbols) { 185 Set<String> annotationSet = new HashSet<String>(); 186 187 for(ClassSymbol classSymbol: classSymbols) { 188 computeAnnotationSet(classSymbol, annotationSet); 189 } 190 return annotationSet; 191 } 192 193 void computeAnnotationSet(Symbol symbol, Set<String> annotationSet) { 194 if (symbol != null ) { 195 if (symbol.getAnnotationMirrors() != null) 196 for(Attribute.Compound compound: symbol.getAnnotationMirrors()) 197 annotationSet.add(compound.type.tsym.toString()); // should fullName be used instead of toString? 198 199 if (symbol instanceof Symbol.MethodSymbol) // add parameter annotations 200 for(Symbol param: ((MethodSymbol) symbol).params()) 201 computeAnnotationSet(param, annotationSet); 202 203 if (symbol.members() != null) { 204 for(Scope.Entry e = symbol.members().elems; e != null; e = e.sibling) 205 computeAnnotationSet(e.sym, annotationSet); 206 } 207 } 208 } 209 210 public void main(com.sun.tools.javac.util.List<JCTree.JCCompilationUnit> treeList, 211 ListBuffer<ClassSymbol> classes, 212 Map<String, String> origOptions, 213 ClassLoader aptCL, 214 AnnotationProcessorFactory providedFactory, 215 java.util.Set<Class<? extends AnnotationProcessorFactory> > productiveFactories) { 216 Bark bark = Bark.instance(context); 217 java.io.PrintWriter out = bark.warnWriter; 218 Options options = Options.instance(context); 219 220 Collection<TypeDeclaration> spectypedecls = new LinkedHashSet<TypeDeclaration>(); 221 Collection<TypeDeclaration> typedecls = new LinkedHashSet<TypeDeclaration>(); 222 Set<String> unmatchedAnnotations = new LinkedHashSet<String>(); 223 Set<AnnotationTypeDeclaration> emptyATDS = Collections.emptySet(); 224 Set<Class<? extends AnnotationProcessorFactory> > currentRoundFactories = 225 new LinkedHashSet<Class<? extends AnnotationProcessorFactory> >(); 226 227 // Determine what annotations are present on the input source 228 // files, create collections of specified type declarations, 229 // and type declarations. 230 AptTreeScanner ats = new AptTreeScanner(); 231 for(JCTree t: treeList) { 232 t.accept(ats); 233 } 234 235 // Turn collection of ClassSymbols into Collection of apt decls 236 for (ClassSymbol cs : ats.specifiedDeclCollection) { 237 TypeDeclaration decl = aptenv.declMaker.getTypeDeclaration(cs); 238 spectypedecls.add(decl); 239 } 240 241 for (ClassSymbol cs : ats.declCollection) { 242 TypeDeclaration decl = aptenv.declMaker.getTypeDeclaration(cs); 243 typedecls.add(decl); 244 } 245 246 unmatchedAnnotations.addAll(ats.getAnnotationSet()); 247 248 // Process input class files 249 for(ClassSymbol cs : classes) { 250 TypeDeclaration decl = aptenv.declMaker.getTypeDeclaration(cs); 251 // System.out.println("Adding a class to spectypedecls"); 252 spectypedecls.add(decl); 253 typedecls.add(decl); 254 computeAnnotationSet(cs, unmatchedAnnotations); 255 } 256 257 if (options.get("-XListAnnotationTypes") != null) { 258 out.println("Set of annotations found:" + 259 (new TreeSet<String>(unmatchedAnnotations)).toString()); 260 } 261 262 AnnotationProcessorEnvironmentImpl trivAPE = 263 new AnnotationProcessorEnvironmentImpl(spectypedecls, typedecls, origOptions, context); 264 265 if (options.get("-XListDeclarations") != null) { 266 out.println("Set of Specified Declarations:" + 267 spectypedecls); 268 269 out.println("Set of Included Declarations: " + 270 typedecls); 271 } 272 273 if (options.get("-print") != null) { 274 if (spectypedecls.size() == 0 ) 275 throw new UsageMessageNeededException(); 276 277 // Run the printing processor 278 AnnotationProcessor proc = (new BootstrapAPF()).getProcessorFor(new HashSet<AnnotationTypeDeclaration>(), 279 trivAPE); 280 proc.process(); 281 } else { 282 // Discovery process 283 284 // List of annotation processory factory instances 285 java.util.Iterator<AnnotationProcessorFactory> providers = null; 286 { 287 /* 288 * If a factory is provided by the user, the 289 * "-factory" and "-factorypath" options are not used. 290 * 291 * Otherwise, if the "-factory" option is used, search 292 * the appropriate path for the named class. 293 * Otherwise, use sun.misc.Service to implement the 294 * default discovery policy. 295 */ 296 297 java.util.List<AnnotationProcessorFactory> list = 298 new LinkedList<AnnotationProcessorFactory>(); 299 String factoryName = options.get("-factory"); 300 301 if (providedFactory != null) { 302 list.add(providedFactory); 303 providers = list.iterator(); 304 } else if (factoryName != null) { 305 try { 306 AnnotationProcessorFactory factory = 307 (AnnotationProcessorFactory) (aptCL.loadClass(factoryName).newInstance()); 308 list.add(factory); 309 } catch (ClassNotFoundException cnfe) { 310 bark.aptWarning("FactoryNotFound", factoryName); 311 } catch (ClassCastException cce) { 312 bark.aptWarning("FactoryWrongType", factoryName); 313 } catch (Exception e ) { 314 bark.aptWarning("FactoryCantInstantiate", factoryName); 315 } catch(Throwable t) { 316 throw new AnnotationProcessingError(t); 317 } 318 319 providers = list.iterator(); 320 } else { 321 @SuppressWarnings("unchecked") 322 Iterator<AnnotationProcessorFactory> iter = 323 sun.misc.Service.providers(AnnotationProcessorFactory.class, aptCL); 324 providers = iter; 325 326 } 327 } 328 329 java.util.Map<AnnotationProcessorFactory, Set<AnnotationTypeDeclaration>> factoryToAnnotation = 330 new LinkedHashMap<AnnotationProcessorFactory, Set<AnnotationTypeDeclaration>>(); 331 332 if (!providers.hasNext() && productiveFactories.size() == 0) { 333 if (unmatchedAnnotations.size() > 0) 334 bark.aptWarning("NoAnnotationProcessors"); 335 if (spectypedecls.size() == 0) 336 throw new UsageMessageNeededException(); 337 return; // no processors; nothing else to do 338 } else { 339 // If there are no annotations, still give 340 // processors that match everything a chance to 341 // run. 342 343 if(unmatchedAnnotations.size() == 0) 344 unmatchedAnnotations.add(""); 345 346 Set<String> emptyStringSet = new HashSet<String>(); 347 emptyStringSet.add(""); 348 emptyStringSet = Collections.unmodifiableSet(emptyStringSet); 349 350 while (providers.hasNext() ) { 351 Object provider = providers.next(); 352 try { 353 Set<String> matchedStrings = new HashSet<String>(); 354 355 AnnotationProcessorFactory apf = (AnnotationProcessorFactory) provider; 356 Collection<String> supportedTypes = apf.supportedAnnotationTypes(); 357 358 Collection<Pattern> supportedTypePatterns = new LinkedList<Pattern>(); 359 for(String s: supportedTypes) 360 supportedTypePatterns.add(importStringToPattern(s)); 361 362 for(String s: unmatchedAnnotations) { 363 for(Pattern p: supportedTypePatterns) { 364 if (p.matcher(s).matches()) { 365 matchedStrings.add(s); 366 break; 367 } 368 } 369 } 370 371 unmatchedAnnotations.removeAll(matchedStrings); 372 373 if (options.get("-XPrintFactoryInfo") != null) { 374 out.println("Factory " + apf.getClass().getName() + 375 " matches " + 376 ((matchedStrings.size() == 0)? 377 "nothing.": matchedStrings)); 378 } 379 380 if (matchedStrings.size() > 0) { 381 // convert annotation names to annotation 382 // type decls 383 Set<AnnotationTypeDeclaration> atds = new HashSet<AnnotationTypeDeclaration>(); 384 385 // If a "*" processor is called on the 386 // empty string, pass in an empty set of 387 // annotation type declarations. 388 if (!matchedStrings.equals(emptyStringSet)) { 389 for(String s: matchedStrings) { 390 TypeDeclaration decl = aptenv.declMaker.getTypeDeclaration(s); 391 AnnotationTypeDeclaration annotdecl; 392 if (decl == null) { 393 bark.aptError("DeclarationCreation", s); 394 } else { 395 try { 396 annotdecl = (AnnotationTypeDeclaration)decl; 397 atds.add(annotdecl); 398 399 } catch (ClassCastException cce) { 400 bark.aptError("BadDeclaration", s); 401 } 402 } 403 } 404 } 405 406 currentRoundFactories.add(apf.getClass()); 407 productiveFactories.add(apf.getClass()); 408 factoryToAnnotation.put(apf, atds); 409 } else if (productiveFactories.contains(apf.getClass())) { 410 // If a factory provided a processor in a 411 // previous round but doesn't match any 412 // annotations this round, call it with an 413 // empty set of declarations. 414 currentRoundFactories.add(apf.getClass()); 415 factoryToAnnotation.put(apf, emptyATDS ); 416 } 417 418 if (unmatchedAnnotations.size() == 0) 419 break; 420 421 } catch (ClassCastException cce) { 422 bark.aptWarning("BadFactory", cce); 423 } 424 } 425 426 unmatchedAnnotations.remove(""); 427 } 428 429 // If the set difference of productiveFactories and 430 // currentRoundFactories is non-empty, call the remaining 431 // productive factories with an empty set of declarations. 432 { 433 java.util.Set<Class<? extends AnnotationProcessorFactory> > neglectedFactories = 434 new LinkedHashSet<Class<? extends AnnotationProcessorFactory>>(productiveFactories); 435 neglectedFactories.removeAll(currentRoundFactories); 436 for(Class<? extends AnnotationProcessorFactory> working : neglectedFactories) { 437 try { 438 AnnotationProcessorFactory factory = working.newInstance(); 439 factoryToAnnotation.put(factory, emptyATDS); 440 } catch (Exception e ) { 441 bark.aptWarning("FactoryCantInstantiate", working.getName()); 442 } catch(Throwable t) { 443 throw new AnnotationProcessingError(t); 444 } 445 } 446 } 447 448 if (unmatchedAnnotations.size() > 0) 449 bark.aptWarning("AnnotationsWithoutProcessors", unmatchedAnnotations); 450 451 Set<AnnotationProcessor> processors = new LinkedHashSet<AnnotationProcessor>(); 452 453 // If there were no source files AND no factory matching "*", 454 // make sure the usage message is printed 455 if (spectypedecls.size() == 0 && 456 factoryToAnnotation.keySet().size() == 0 ) 457 throw new UsageMessageNeededException(); 458 459 try { 460 for(Map.Entry<AnnotationProcessorFactory, Set<AnnotationTypeDeclaration>> entry : 461 factoryToAnnotation.entrySet()) { 462 AnnotationProcessorFactory apFactory = entry.getKey(); 463 AnnotationProcessor processor = apFactory.getProcessorFor(entry.getValue(), 464 trivAPE); 465 if (processor != null) 466 processors.add(processor); 467 else 468 bark.aptWarning("NullProcessor", apFactory.getClass().getName()); 469 } 470 } catch(Throwable t) { 471 throw new AnnotationProcessingError(t); 472 } 473 474 LinkedList<AnnotationProcessor> temp = new LinkedList<AnnotationProcessor>(); 475 temp.addAll(processors); 476 477 AnnotationProcessor proc = AnnotationProcessors.getCompositeAnnotationProcessor(temp); 478 479 try { 480 proc.process(); 481 } catch (Throwable t) { 482 throw new AnnotationProcessingError(t); 483 } 484 485 // Invoke listener callback mechanism 486 trivAPE.roundComplete(); 487 488 FilerImpl filerimpl = (FilerImpl)trivAPE.getFiler(); 489 genSourceFileNames = filerimpl.getSourceFileNames(); 490 genClassFileNames = filerimpl.getClassFileNames(); 491 filerimpl.flush(); // Make sure new files are written out 492 } 493 } 494 495 /** 496 * Convert import-style string to regex matching that string. If 497 * the string is a valid import-style string, return a regex that 498 * won't match anything. 499 */ 500 Pattern importStringToPattern(String s) { 501 if (com.sun.tools.javac.processing.JavacProcessingEnvironment.isValidImportString(s)) { 502 return com.sun.tools.javac.processing.JavacProcessingEnvironment.validImportStringToPattern(s); 503 } else { 504 Bark bark = Bark.instance(context); 505 bark.aptWarning("MalformedSupportedString", s); 506 return com.sun.tools.javac.processing.JavacProcessingEnvironment.noMatches; 507 } 508 } 509 }