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().table)
 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(AnnotationProcessorFactory apFactory: factoryToAnnotation.keySet()) {
 461                     AnnotationProcessor processor = apFactory.getProcessorFor(factoryToAnnotation.get(apFactory),
 462                                                                               trivAPE);
 463                     if (processor != null)
 464                         processors.add(processor);
 465                     else
 466                         bark.aptWarning("NullProcessor", apFactory.getClass().getName());
 467                 }
 468             } catch(Throwable t) {
 469                 throw new AnnotationProcessingError(t);
 470             }
 471 
 472             LinkedList<AnnotationProcessor> temp = new LinkedList<AnnotationProcessor>();
 473             temp.addAll(processors);
 474 
 475             AnnotationProcessor proc = AnnotationProcessors.getCompositeAnnotationProcessor(temp);
 476 
 477             try {
 478                 proc.process();
 479             } catch (Throwable t) {
 480                 throw new AnnotationProcessingError(t);
 481             }
 482 
 483             // Invoke listener callback mechanism
 484             trivAPE.roundComplete();
 485 
 486             FilerImpl filerimpl = (FilerImpl)trivAPE.getFiler();
 487             genSourceFileNames = filerimpl.getSourceFileNames();
 488             genClassFileNames = filerimpl.getClassFileNames();
 489             filerimpl.flush(); // Make sure new files are written out
 490         }
 491     }
 492 
 493     /**
 494      * Convert import-style string to regex matching that string.  If
 495      * the string is a valid import-style string, return a regex that
 496      * won't match anything.
 497      */
 498     Pattern importStringToPattern(String s) {
 499         if (s.equals("*")) {
 500             return allMatches;
 501         } else {
 502             String t = s;
 503             boolean star = false;
 504 
 505             /*
 506              * Validate string from factory is legal.  If the string
 507              * has more than one asterisks or the asterisks does not
 508              * appear as the last character (preceded by a period),
 509              * the string is not legal.
 510              */
 511 
 512             boolean valid = true;
 513             int index = t.indexOf('*');
 514             if (index != -1) {
 515                 // '*' must be last character...
 516                 if (index == t.length() -1) {
 517                      // ... and preceeding character must be '.'
 518                     if ( index-1 >= 0 ) {
 519                         valid = t.charAt(index-1) == '.';
 520                         // Strip off ".*$" for identifier checks
 521                         t = t.substring(0, t.length()-2);
 522                     }
 523                 } else
 524                     valid = false;
 525             }
 526 
 527             // Verify string is off the form (javaId \.)+ or javaId
 528             if (valid) {
 529                 String[] javaIds = t.split("\\.", t.length()+2);
 530                 for(String javaId: javaIds)
 531                     valid &= isJavaIdentifier(javaId);
 532             }
 533 
 534             if (!valid) {
 535                 Bark bark = Bark.instance(context);
 536                 bark.aptWarning("MalformedSupportedString", s);
 537                 return noMatches; // won't match any valid identifier
 538             }
 539 
 540             String s_prime = s.replaceAll("\\.", "\\\\.");
 541 
 542             if (s_prime.endsWith("*")) {
 543                 s_prime =  s_prime.substring(0, s_prime.length() - 1) + ".+";
 544             }
 545 
 546             return Pattern.compile(s_prime);
 547         }
 548     }
 549 
 550     private static final Pattern allMatches = Pattern.compile(".*");
 551     private static final Pattern noMatches  = Pattern.compile("(\\P{all})+");
 552 }