/* * Copyright 2004-2009 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Sun designates this * particular file as subject to the "Classpath" exception as provided * by Sun in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, * CA 95054 USA or visit www.sun.com if you need additional information or * have any questions. */ package com.sun.tools.apt.comp; import com.sun.tools.javac.code.*; import com.sun.tools.javac.comp.*; import com.sun.tools.javac.tree.*; import com.sun.tools.javac.util.*; import com.sun.tools.javac.tree.TreeScanner; import com.sun.tools.javac.util.Context; import com.sun.tools.apt.util.Bark; import com.sun.tools.javac.util.Position; import java.util.*; import java.util.regex.*; import java.lang.reflect.*; import java.lang.reflect.InvocationTargetException; import java.io.IOException; import com.sun.tools.apt.*; import com.sun.tools.apt.comp.*; import com.sun.tools.javac.code.Symbol.*; import com.sun.mirror.declaration.TypeDeclaration; import com.sun.mirror.declaration.AnnotationTypeDeclaration; import com.sun.mirror.apt.*; // import com.sun.mirror.apt.AnnotationProcessorFactory; import com.sun.mirror.apt.AnnotationProcessors; import com.sun.tools.apt.mirror.AptEnv; import com.sun.tools.apt.mirror.apt.FilerImpl; import com.sun.tools.apt.mirror.apt.AnnotationProcessorEnvironmentImpl; import static com.sun.tools.apt.mirror.declaration.DeclarationMaker.isJavaIdentifier; /** * Apt compiler phase. * *

This is NOT part of any API supported by Sun Microsystems. * If you write code that depends on this, you do so at your own * risk. This code and its internal interfaces are subject to change * or deletion without notice. */ @SuppressWarnings("deprecation") public class Apt extends ListBuffer> { java.util.Set genSourceFileNames = new java.util.LinkedHashSet(); public java.util.Set getSourceFileNames() { return genSourceFileNames; } /** List of names of generated class files. */ java.util.Set genClassFileNames = new java.util.LinkedHashSet(); public java.util.Set getClassFileNames() { return genClassFileNames; } /* AptEnvironment */ AptEnv aptenv; private Context context; /** The context key for the todo list. */ protected static final Context.Key aptKey = new Context.Key(); /** Get the Apt instance for this context. */ public static Apt instance(Context context) { Apt instance = context.get(aptKey); if (instance == null) instance = new Apt(context); return instance; } /** Create a new apt list. */ protected Apt(Context context) { this.context = context; context.put(aptKey, this); aptenv = AptEnv.instance(context); } /** * Used to scan javac trees to build data structures needed for * bootstrapping the apt environment. In particular: * *

*/ static class AptTreeScanner extends TreeScanner { // Set of fully qualified names of annotation types present in // examined source private Set annotationSet; // Symbols to build bootstrapping declaration list private Collection specifiedDeclCollection; private Collection declCollection; public Set getAnnotationSet() { return annotationSet; } public AptTreeScanner() { annotationSet = new LinkedHashSet(); specifiedDeclCollection = new LinkedHashSet(); declCollection = new LinkedHashSet(); } public void visitTopLevel(JCTree.JCCompilationUnit tree) { super.visitTopLevel(tree); // Print out contents -- what are we dealing with? for(JCTree d: tree.defs) { if (d instanceof JCTree.JCClassDecl) specifiedDeclCollection.add(((JCTree.JCClassDecl) d).sym); } } public void visitBlock(JCTree.JCBlock tree) { ; // Do nothing. } // should add nested classes to packages, etc. public void visitClassDef(JCTree.JCClassDecl tree) { if (tree.sym == null) { // could be an anon class w/in an initializer return; } super.visitClassDef(tree); declCollection.add(tree.sym); } public void visitMethodDef(JCTree.JCMethodDecl tree) { super.visitMethodDef(tree); } public void visitVarDef(JCTree.JCVariableDecl tree) { super.visitVarDef(tree); } public void visitAnnotation(JCTree.JCAnnotation tree) { super.visitAnnotation(tree); annotationSet.add(tree.type.tsym.toString()); } } Set computeAnnotationSet(Collection classSymbols) { Set annotationSet = new HashSet(); for(ClassSymbol classSymbol: classSymbols) { computeAnnotationSet(classSymbol, annotationSet); } return annotationSet; } void computeAnnotationSet(Symbol symbol, Set annotationSet) { if (symbol != null ) { if (symbol.getAnnotationMirrors() != null) for(Attribute.Compound compound: symbol.getAnnotationMirrors()) annotationSet.add(compound.type.tsym.toString()); // should fullName be used instead of toString? if (symbol instanceof Symbol.MethodSymbol) // add parameter annotations for(Symbol param: ((MethodSymbol) symbol).params()) computeAnnotationSet(param, annotationSet); if (symbol.members() != null) { for(Scope.Entry e = symbol.members().elems; e != null; e = e.sibling) computeAnnotationSet(e.sym, annotationSet); } } } public void main(com.sun.tools.javac.util.List treeList, ListBuffer classes, Map origOptions, ClassLoader aptCL, AnnotationProcessorFactory providedFactory, java.util.Set > productiveFactories) { Bark bark = Bark.instance(context); java.io.PrintWriter out = bark.warnWriter; Options options = Options.instance(context); Collection spectypedecls = new LinkedHashSet(); Collection typedecls = new LinkedHashSet(); Set unmatchedAnnotations = new LinkedHashSet(); Set emptyATDS = Collections.emptySet(); Set > currentRoundFactories = new LinkedHashSet >(); // Determine what annotations are present on the input source // files, create collections of specified type declarations, // and type declarations. AptTreeScanner ats = new AptTreeScanner(); for(JCTree t: treeList) { t.accept(ats); } // Turn collection of ClassSymbols into Collection of apt decls for (ClassSymbol cs : ats.specifiedDeclCollection) { TypeDeclaration decl = aptenv.declMaker.getTypeDeclaration(cs); spectypedecls.add(decl); } for (ClassSymbol cs : ats.declCollection) { TypeDeclaration decl = aptenv.declMaker.getTypeDeclaration(cs); typedecls.add(decl); } unmatchedAnnotations.addAll(ats.getAnnotationSet()); // Process input class files for(ClassSymbol cs : classes) { TypeDeclaration decl = aptenv.declMaker.getTypeDeclaration(cs); // System.out.println("Adding a class to spectypedecls"); spectypedecls.add(decl); typedecls.add(decl); computeAnnotationSet(cs, unmatchedAnnotations); } if (options.get("-XListAnnotationTypes") != null) { out.println("Set of annotations found:" + (new TreeSet(unmatchedAnnotations)).toString()); } AnnotationProcessorEnvironmentImpl trivAPE = new AnnotationProcessorEnvironmentImpl(spectypedecls, typedecls, origOptions, context); if (options.get("-XListDeclarations") != null) { out.println("Set of Specified Declarations:" + spectypedecls); out.println("Set of Included Declarations: " + typedecls); } if (options.get("-print") != null) { if (spectypedecls.size() == 0 ) throw new UsageMessageNeededException(); // Run the printing processor AnnotationProcessor proc = (new BootstrapAPF()).getProcessorFor(new HashSet(), trivAPE); proc.process(); } else { // Discovery process // List of annotation processory factory instances java.util.Iterator providers = null; { /* * If a factory is provided by the user, the * "-factory" and "-factorypath" options are not used. * * Otherwise, if the "-factory" option is used, search * the appropriate path for the named class. * Otherwise, use sun.misc.Service to implement the * default discovery policy. */ java.util.List list = new LinkedList(); String factoryName = options.get("-factory"); if (providedFactory != null) { list.add(providedFactory); providers = list.iterator(); } else if (factoryName != null) { try { AnnotationProcessorFactory factory = (AnnotationProcessorFactory) (aptCL.loadClass(factoryName).newInstance()); list.add(factory); } catch (ClassNotFoundException cnfe) { bark.aptWarning("FactoryNotFound", factoryName); } catch (ClassCastException cce) { bark.aptWarning("FactoryWrongType", factoryName); } catch (Exception e ) { bark.aptWarning("FactoryCantInstantiate", factoryName); } catch(Throwable t) { throw new AnnotationProcessingError(t); } providers = list.iterator(); } else { @SuppressWarnings("unchecked") Iterator iter = sun.misc.Service.providers(AnnotationProcessorFactory.class, aptCL); providers = iter; } } java.util.Map> factoryToAnnotation = new LinkedHashMap>(); if (!providers.hasNext() && productiveFactories.size() == 0) { if (unmatchedAnnotations.size() > 0) bark.aptWarning("NoAnnotationProcessors"); if (spectypedecls.size() == 0) throw new UsageMessageNeededException(); return; // no processors; nothing else to do } else { // If there are no annotations, still give // processors that match everything a chance to // run. if(unmatchedAnnotations.size() == 0) unmatchedAnnotations.add(""); Set emptyStringSet = new HashSet(); emptyStringSet.add(""); emptyStringSet = Collections.unmodifiableSet(emptyStringSet); while (providers.hasNext() ) { Object provider = providers.next(); try { Set matchedStrings = new HashSet(); AnnotationProcessorFactory apf = (AnnotationProcessorFactory) provider; Collection supportedTypes = apf.supportedAnnotationTypes(); Collection supportedTypePatterns = new LinkedList(); for(String s: supportedTypes) supportedTypePatterns.add(importStringToPattern(s)); for(String s: unmatchedAnnotations) { for(Pattern p: supportedTypePatterns) { if (p.matcher(s).matches()) { matchedStrings.add(s); break; } } } unmatchedAnnotations.removeAll(matchedStrings); if (options.get("-XPrintFactoryInfo") != null) { out.println("Factory " + apf.getClass().getName() + " matches " + ((matchedStrings.size() == 0)? "nothing.": matchedStrings)); } if (matchedStrings.size() > 0) { // convert annotation names to annotation // type decls Set atds = new HashSet(); // If a "*" processor is called on the // empty string, pass in an empty set of // annotation type declarations. if (!matchedStrings.equals(emptyStringSet)) { for(String s: matchedStrings) { TypeDeclaration decl = aptenv.declMaker.getTypeDeclaration(s); AnnotationTypeDeclaration annotdecl; if (decl == null) { bark.aptError("DeclarationCreation", s); } else { try { annotdecl = (AnnotationTypeDeclaration)decl; atds.add(annotdecl); } catch (ClassCastException cce) { bark.aptError("BadDeclaration", s); } } } } currentRoundFactories.add(apf.getClass()); productiveFactories.add(apf.getClass()); factoryToAnnotation.put(apf, atds); } else if (productiveFactories.contains(apf.getClass())) { // If a factory provided a processor in a // previous round but doesn't match any // annotations this round, call it with an // empty set of declarations. currentRoundFactories.add(apf.getClass()); factoryToAnnotation.put(apf, emptyATDS ); } if (unmatchedAnnotations.size() == 0) break; } catch (ClassCastException cce) { bark.aptWarning("BadFactory", cce); } } unmatchedAnnotations.remove(""); } // If the set difference of productiveFactories and // currentRoundFactories is non-empty, call the remaining // productive factories with an empty set of declarations. { java.util.Set > neglectedFactories = new LinkedHashSet>(productiveFactories); neglectedFactories.removeAll(currentRoundFactories); for(Class working : neglectedFactories) { try { AnnotationProcessorFactory factory = working.newInstance(); factoryToAnnotation.put(factory, emptyATDS); } catch (Exception e ) { bark.aptWarning("FactoryCantInstantiate", working.getName()); } catch(Throwable t) { throw new AnnotationProcessingError(t); } } } if (unmatchedAnnotations.size() > 0) bark.aptWarning("AnnotationsWithoutProcessors", unmatchedAnnotations); Set processors = new LinkedHashSet(); // If there were no source files AND no factory matching "*", // make sure the usage message is printed if (spectypedecls.size() == 0 && factoryToAnnotation.keySet().size() == 0 ) throw new UsageMessageNeededException(); try { for(Map.Entry> entry : factoryToAnnotation.entrySet()) { AnnotationProcessorFactory apFactory = entry.getKey(); AnnotationProcessor processor = apFactory.getProcessorFor(entry.getValue(), trivAPE); if (processor != null) processors.add(processor); else bark.aptWarning("NullProcessor", apFactory.getClass().getName()); } } catch(Throwable t) { throw new AnnotationProcessingError(t); } LinkedList temp = new LinkedList(); temp.addAll(processors); AnnotationProcessor proc = AnnotationProcessors.getCompositeAnnotationProcessor(temp); try { proc.process(); } catch (Throwable t) { throw new AnnotationProcessingError(t); } // Invoke listener callback mechanism trivAPE.roundComplete(); FilerImpl filerimpl = (FilerImpl)trivAPE.getFiler(); genSourceFileNames = filerimpl.getSourceFileNames(); genClassFileNames = filerimpl.getClassFileNames(); filerimpl.flush(); // Make sure new files are written out } } /** * Convert import-style string to regex matching that string. If * the string is a valid import-style string, return a regex that * won't match anything. */ Pattern importStringToPattern(String s) { if (com.sun.tools.javac.processing.JavacProcessingEnvironment.isValidImportString(s)) { return com.sun.tools.javac.processing.JavacProcessingEnvironment.validImportStringToPattern(s); } else { Bark bark = Bark.instance(context); bark.aptWarning("MalformedSupportedString", s); return com.sun.tools.javac.processing.JavacProcessingEnvironment.noMatches; } } }