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 public class Apt extends ListBuffer<Env<AttrContext>> {
  69     java.util.Set<String> genSourceFileNames = new java.util.LinkedHashSet<String>();
  70     public java.util.Set<String> getSourceFileNames() {
  71         return genSourceFileNames;
  72     }
  73 
  74     /** List of names of generated class files.
  75      */
  76     java.util.Set<String> genClassFileNames  = new java.util.LinkedHashSet<String>();
  77     public java.util.Set<String> getClassFileNames() {
  78         return genClassFileNames;
  79     }
  80 
  81     /* AptEnvironment */
  82     AptEnv aptenv;
  83 
  84     private Context context;
  85 
  86     /** The context key for the todo list. */
  87 
  88     protected static final Context.Key<Apt> aptKey =
  89         new Context.Key<Apt>();
  90 
  91     /** Get the Apt instance for this context. */
  92     public static Apt instance(Context context) {
  93         Apt instance = context.get(aptKey);
  94         if (instance == null)
  95             instance = new Apt(context);
  96         return instance;
  97     }
  98 
  99     /** Create a new apt list. */
 100     protected Apt(Context context) {
 101         this.context = context;
 102 
 103         context.put(aptKey, this);
 104         aptenv = AptEnv.instance(context);
 105     }
 106 
 107     /**
 108      * Used to scan javac trees to build data structures needed for
 109      * bootstrapping the apt environment.  In particular:
 110      *
 111      * <ul>
 112      *
 113      * <li> Generate list of canonical names of annotation types that
 114      * appear in source files given on the command line
 115      *
 116      * <li> Collect list of javac symbols representing source files
 117      * given on the command line
 118      *
 119      * </ul>
 120      */
 121     static class AptTreeScanner extends TreeScanner {
 122 
 123         // Set of fully qualified names of annotation types present in
 124         // examined source
 125         private Set<String> annotationSet;
 126 
 127         // Symbols to build bootstrapping declaration list
 128         private Collection<ClassSymbol> specifiedDeclCollection;
 129         private Collection<ClassSymbol> declCollection;
 130 
 131         public Set<String> getAnnotationSet() {
 132             return annotationSet;
 133         }
 134 
 135         public AptTreeScanner() {
 136             annotationSet = new  LinkedHashSet<String>();
 137             specifiedDeclCollection = new LinkedHashSet<ClassSymbol>();
 138             declCollection = new LinkedHashSet<ClassSymbol>();
 139         }
 140 
 141         public void visitTopLevel(JCTree.JCCompilationUnit tree) {
 142             super.visitTopLevel(tree);
 143             // Print out contents -- what are we dealing with?
 144 
 145             for(JCTree d: tree.defs) {
 146                 if (d instanceof JCTree.JCClassDecl)
 147                     specifiedDeclCollection.add(((JCTree.JCClassDecl) d).sym);
 148             }
 149 
 150         }
 151 
 152         public void visitBlock(JCTree.JCBlock tree) {
 153             ; // Do nothing.
 154         }
 155 
 156 
 157         // should add nested classes to packages, etc.
 158         public void visitClassDef(JCTree.JCClassDecl tree) {
 159             if (tree.sym == null) {
 160                 // could be an anon class w/in an initializer
 161                 return;
 162             }
 163 
 164             super.visitClassDef(tree);
 165 
 166             declCollection.add(tree.sym);
 167         }
 168 
 169         public void visitMethodDef(JCTree.JCMethodDecl tree) {
 170             super.visitMethodDef(tree);
 171         }
 172 
 173         public void visitVarDef(JCTree.JCVariableDecl tree) {
 174             super.visitVarDef(tree);
 175         }
 176 
 177         public void visitAnnotation(JCTree.JCAnnotation tree) {
 178             super.visitAnnotation(tree);
 179             annotationSet.add(tree.type.tsym.toString());
 180         }
 181     }
 182 
 183     Set<String> computeAnnotationSet(Collection<ClassSymbol> classSymbols) {
 184         Set<String> annotationSet = new HashSet<String>();
 185 
 186         for(ClassSymbol classSymbol: classSymbols) {
 187             computeAnnotationSet(classSymbol, annotationSet);
 188         }
 189         return annotationSet;
 190     }
 191 
 192     void computeAnnotationSet(Symbol symbol, Set<String> annotationSet) {
 193         if (symbol != null ) {
 194             if (symbol.getAnnotationMirrors() != null)
 195                 for(Attribute.Compound compound: symbol.getAnnotationMirrors())
 196                     annotationSet.add(compound.type.tsym.toString()); // should fullName be used instead of toString?
 197 
 198             if (symbol instanceof Symbol.MethodSymbol) // add parameter annotations
 199                 for(Symbol param: ((MethodSymbol) symbol).params())
 200                     computeAnnotationSet(param, annotationSet);
 201 
 202             if (symbol.members() != null) {
 203                 for(Scope.Entry e: symbol.members().table)
 204                     computeAnnotationSet(e.sym, annotationSet);
 205             }
 206         }
 207     }
 208 
 209     public void main(com.sun.tools.javac.util.List<JCTree.JCCompilationUnit> treeList,
 210                      ListBuffer<ClassSymbol> classes,
 211                      Map<String, String> origOptions,
 212                      ClassLoader aptCL,
 213                      AnnotationProcessorFactory providedFactory,
 214                      java.util.Set<Class<? extends AnnotationProcessorFactory> > productiveFactories) {
 215         Bark bark = Bark.instance(context);
 216         java.io.PrintWriter out = bark.warnWriter;
 217         Options options = Options.instance(context);
 218 
 219         Collection<TypeDeclaration> spectypedecls =     new LinkedHashSet<TypeDeclaration>();
 220         Collection<TypeDeclaration> typedecls =         new LinkedHashSet<TypeDeclaration>();
 221         Set<String> unmatchedAnnotations =              new LinkedHashSet<String>();
 222         Set<AnnotationTypeDeclaration> emptyATDS =      Collections.emptySet();
 223         Set<Class<? extends AnnotationProcessorFactory> > currentRoundFactories =
 224             new LinkedHashSet<Class<? extends AnnotationProcessorFactory> >();
 225 
 226         // Determine what annotations are present on the input source
 227         // files, create collections of specified type declarations,
 228         // and type declarations.
 229         AptTreeScanner ats = new AptTreeScanner();
 230         for(JCTree t: treeList) {
 231             t.accept(ats);
 232         }
 233 
 234         // Turn collection of ClassSymbols into Collection of apt decls
 235         for (ClassSymbol cs : ats.specifiedDeclCollection) {
 236             TypeDeclaration decl = aptenv.declMaker.getTypeDeclaration(cs);
 237             spectypedecls.add(decl);
 238         }
 239 
 240         for (ClassSymbol cs : ats.declCollection) {
 241             TypeDeclaration decl = aptenv.declMaker.getTypeDeclaration(cs);
 242             typedecls.add(decl);
 243         }
 244 
 245         unmatchedAnnotations.addAll(ats.getAnnotationSet());
 246 
 247         // Process input class files
 248         for(ClassSymbol cs : classes) {
 249             TypeDeclaration decl = aptenv.declMaker.getTypeDeclaration(cs);
 250             // System.out.println("Adding a class to spectypedecls");
 251             spectypedecls.add(decl);
 252             typedecls.add(decl);
 253             computeAnnotationSet(cs, unmatchedAnnotations);
 254         }
 255 
 256         if (options.get("-XListAnnotationTypes") != null) {
 257             out.println("Set of annotations found:" +
 258                         (new TreeSet<String>(unmatchedAnnotations)).toString());
 259         }
 260 
 261         AnnotationProcessorEnvironmentImpl trivAPE =
 262             new AnnotationProcessorEnvironmentImpl(spectypedecls, typedecls, origOptions, context);
 263 
 264         if (options.get("-XListDeclarations") != null) {
 265             out.println("Set of Specified Declarations:" +
 266                         spectypedecls);
 267 
 268             out.println("Set of Included Declarations: " +
 269                            typedecls);
 270         }
 271 
 272         if (options.get("-print") != null) {
 273             if (spectypedecls.size() == 0 )
 274                 throw new UsageMessageNeededException();
 275 
 276             // Run the printing processor
 277             AnnotationProcessor proc = (new BootstrapAPF()).getProcessorFor(new HashSet<AnnotationTypeDeclaration>(),
 278                                                                             trivAPE);
 279             proc.process();
 280         } else {
 281             // Discovery process
 282 
 283             // List of annotation processory factory instances
 284             java.util.Iterator<AnnotationProcessorFactory> providers = null;
 285             {
 286                 /*
 287                  * If a factory is provided by the user, the
 288                  * "-factory" and "-factorypath" options are not used.
 289                  *
 290                  * Otherwise, if the "-factory" option is used, search
 291                  * the appropriate path for the named class.
 292                  * Otherwise, use sun.misc.Service to implement the
 293                  * default discovery policy.
 294                  */
 295 
 296                 java.util.List<AnnotationProcessorFactory> list =
 297                     new LinkedList<AnnotationProcessorFactory>();
 298                 String factoryName = options.get("-factory");
 299 
 300                 if (providedFactory != null) {
 301                     list.add(providedFactory);
 302                     providers = list.iterator();
 303                 } else if (factoryName != null) {
 304                     try {
 305                         AnnotationProcessorFactory factory =
 306                             (AnnotationProcessorFactory) (aptCL.loadClass(factoryName).newInstance());
 307                         list.add(factory);
 308                     } catch (ClassNotFoundException cnfe) {
 309                         bark.aptWarning("FactoryNotFound", factoryName);
 310                     } catch (ClassCastException cce) {
 311                         bark.aptWarning("FactoryWrongType", factoryName);
 312                     } catch (Exception e ) {
 313                         bark.aptWarning("FactoryCantInstantiate", factoryName);
 314                     } catch(Throwable t) {
 315                         throw new AnnotationProcessingError(t);
 316                     }
 317 
 318                     providers = list.iterator();
 319                 } else {
 320                     @SuppressWarnings("unchecked")
 321                     Iterator<AnnotationProcessorFactory> iter =
 322                             sun.misc.Service.providers(AnnotationProcessorFactory.class, aptCL);
 323                     providers = iter;
 324 
 325                 }
 326             }
 327 
 328             java.util.Map<AnnotationProcessorFactory, Set<AnnotationTypeDeclaration>> factoryToAnnotation =
 329                 new LinkedHashMap<AnnotationProcessorFactory, Set<AnnotationTypeDeclaration>>();
 330 
 331             if (!providers.hasNext() && productiveFactories.size() == 0) {
 332                 if (unmatchedAnnotations.size() > 0)
 333                     bark.aptWarning("NoAnnotationProcessors");
 334                 if (spectypedecls.size() == 0)
 335                     throw new UsageMessageNeededException();
 336                 return; // no processors; nothing else to do
 337             } else {
 338                 // If there are no annotations, still give
 339                 // processors that match everything a chance to
 340                 // run.
 341 
 342                 if(unmatchedAnnotations.size() == 0)
 343                     unmatchedAnnotations.add("");
 344 
 345                 Set<String> emptyStringSet = new HashSet<String>();
 346                 emptyStringSet.add("");
 347                 emptyStringSet = Collections.unmodifiableSet(emptyStringSet);
 348 
 349                 while (providers.hasNext() ) {
 350                     Object provider = providers.next();
 351                     try {
 352                         Set<String> matchedStrings = new HashSet<String>();
 353 
 354                         AnnotationProcessorFactory apf = (AnnotationProcessorFactory) provider;
 355                         Collection<String> supportedTypes = apf.supportedAnnotationTypes();
 356 
 357                         Collection<Pattern> supportedTypePatterns = new LinkedList<Pattern>();
 358                         for(String s: supportedTypes)
 359                             supportedTypePatterns.add(importStringToPattern(s));
 360 
 361                         for(String s: unmatchedAnnotations) {
 362                             for(Pattern p: supportedTypePatterns) {
 363                                 if (p.matcher(s).matches()) {
 364                                     matchedStrings.add(s);
 365                                     break;
 366                                 }
 367                             }
 368                         }
 369 
 370                         unmatchedAnnotations.removeAll(matchedStrings);
 371 
 372                         if (options.get("-XPrintFactoryInfo") != null) {
 373                             out.println("Factory " + apf.getClass().getName() +
 374                                         " matches " +
 375                                         ((matchedStrings.size() == 0)?
 376                                          "nothing.": matchedStrings));
 377                         }
 378 
 379                         if (matchedStrings.size() > 0) {
 380                             // convert annotation names to annotation
 381                             // type decls
 382                             Set<AnnotationTypeDeclaration> atds = new HashSet<AnnotationTypeDeclaration>();
 383 
 384                             // If a "*" processor is called on the
 385                             // empty string, pass in an empty set of
 386                             // annotation type declarations.
 387                             if (!matchedStrings.equals(emptyStringSet)) {
 388                                 for(String s: matchedStrings) {
 389                                     TypeDeclaration decl = aptenv.declMaker.getTypeDeclaration(s);
 390                                     AnnotationTypeDeclaration annotdecl;
 391                                     if (decl == null) {
 392                                         bark.aptError("DeclarationCreation", s);
 393                                     } else {
 394                                         try {
 395                                             annotdecl = (AnnotationTypeDeclaration)decl;
 396                                             atds.add(annotdecl);
 397 
 398                                         } catch (ClassCastException cce) {
 399                                             bark.aptError("BadDeclaration", s);
 400                                         }
 401                                     }
 402                                 }
 403                             }
 404 
 405                             currentRoundFactories.add(apf.getClass());
 406                             productiveFactories.add(apf.getClass());
 407                             factoryToAnnotation.put(apf, atds);
 408                         } else if (productiveFactories.contains(apf.getClass())) {
 409                             // If a factory provided a processor in a
 410                             // previous round but doesn't match any
 411                             // annotations this round, call it with an
 412                             // empty set of declarations.
 413                             currentRoundFactories.add(apf.getClass());
 414                             factoryToAnnotation.put(apf, emptyATDS );
 415                         }
 416 
 417                         if (unmatchedAnnotations.size() == 0)
 418                             break;
 419 
 420                     } catch (ClassCastException cce) {
 421                         bark.aptWarning("BadFactory", cce);
 422                     }
 423                 }
 424 
 425                 unmatchedAnnotations.remove("");
 426             }
 427 
 428             // If the set difference of productiveFactories and
 429             // currentRoundFactories is non-empty, call the remaining
 430             // productive factories with an empty set of declarations.
 431             {
 432                 java.util.Set<Class<? extends AnnotationProcessorFactory> > neglectedFactories =
 433                     new LinkedHashSet<Class<? extends AnnotationProcessorFactory>>(productiveFactories);
 434                 neglectedFactories.removeAll(currentRoundFactories);
 435                 for(Class<? extends AnnotationProcessorFactory> working : neglectedFactories) {
 436                     try {
 437                         AnnotationProcessorFactory factory = working.newInstance();
 438                         factoryToAnnotation.put(factory, emptyATDS);
 439                     } catch (Exception e ) {
 440                         bark.aptWarning("FactoryCantInstantiate", working.getName());
 441                     } catch(Throwable t) {
 442                         throw new AnnotationProcessingError(t);
 443                     }
 444                 }
 445             }
 446 
 447             if (unmatchedAnnotations.size() > 0)
 448                 bark.aptWarning("AnnotationsWithoutProcessors", unmatchedAnnotations);
 449 
 450             Set<AnnotationProcessor> processors = new LinkedHashSet<AnnotationProcessor>();
 451 
 452             // If there were no source files AND no factory matching "*",
 453             // make sure the usage message is printed
 454             if (spectypedecls.size() == 0 &&
 455                 factoryToAnnotation.keySet().size() == 0 )
 456                 throw new UsageMessageNeededException();
 457 
 458             try {
 459                 for(AnnotationProcessorFactory apFactory: factoryToAnnotation.keySet()) {
 460                     AnnotationProcessor processor = apFactory.getProcessorFor(factoryToAnnotation.get(apFactory),
 461                                                                               trivAPE);
 462                     if (processor != null)
 463                         processors.add(processor);
 464                     else
 465                         bark.aptWarning("NullProcessor", apFactory.getClass().getName());
 466                 }
 467             } catch(Throwable t) {
 468                 throw new AnnotationProcessingError(t);
 469             }
 470 
 471             LinkedList<AnnotationProcessor> temp = new LinkedList<AnnotationProcessor>();
 472             temp.addAll(processors);
 473 
 474             AnnotationProcessor proc = AnnotationProcessors.getCompositeAnnotationProcessor(temp);
 475 
 476             try {
 477                 proc.process();
 478             } catch (Throwable t) {
 479                 throw new AnnotationProcessingError(t);
 480             }
 481 
 482             // Invoke listener callback mechanism
 483             trivAPE.roundComplete();
 484 
 485             FilerImpl filerimpl = (FilerImpl)trivAPE.getFiler();
 486             genSourceFileNames = filerimpl.getSourceFileNames();
 487             genClassFileNames = filerimpl.getClassFileNames();
 488             filerimpl.flush(); // Make sure new files are written out
 489         }
 490     }
 491 
 492     /**
 493      * Convert import-style string to regex matching that string.  If
 494      * the string is a valid import-style string, return a regex that
 495      * won't match anything.
 496      */
 497     Pattern importStringToPattern(String s) {
 498         if (s.equals("*")) {
 499             return allMatches;
 500         } else {
 501             String t = s;
 502             boolean star = false;
 503 
 504             /*
 505              * Validate string from factory is legal.  If the string
 506              * has more than one asterisks or the asterisks does not
 507              * appear as the last character (preceded by a period),
 508              * the string is not legal.
 509              */
 510 
 511             boolean valid = true;
 512             int index = t.indexOf('*');
 513             if (index != -1) {
 514                 // '*' must be last character...
 515                 if (index == t.length() -1) {
 516                      // ... and preceeding character must be '.'
 517                     if ( index-1 >= 0 ) {
 518                         valid = t.charAt(index-1) == '.';
 519                         // Strip off ".*$" for identifier checks
 520                         t = t.substring(0, t.length()-2);
 521                     }
 522                 } else
 523                     valid = false;
 524             }
 525 
 526             // Verify string is off the form (javaId \.)+ or javaId
 527             if (valid) {
 528                 String[] javaIds = t.split("\\.", t.length()+2);
 529                 for(String javaId: javaIds)
 530                     valid &= isJavaIdentifier(javaId);
 531             }
 532 
 533             if (!valid) {
 534                 Bark bark = Bark.instance(context);
 535                 bark.aptWarning("MalformedSupportedString", s);
 536                 return noMatches; // won't match any valid identifier
 537             }
 538 
 539             String s_prime = s.replaceAll("\\.", "\\\\.");
 540 
 541             if (s_prime.endsWith("*")) {
 542                 s_prime =  s_prime.substring(0, s_prime.length() - 1) + ".+";
 543             }
 544 
 545             return Pattern.compile(s_prime);
 546         }
 547     }
 548 
 549     private static final Pattern allMatches = Pattern.compile(".*");
 550     private static final Pattern noMatches  = Pattern.compile("(\\P{all})+");
 551 }