001    /*
002     * Copyright 2004-2005 Sun Microsystems, Inc.  All Rights Reserved.
003     * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004     *
005     * This code is free software; you can redistribute it and/or modify it
006     * under the terms of the GNU General Public License version 2 only, as
007     * published by the Free Software Foundation.  Sun designates this
008     * particular file as subject to the "Classpath" exception as provided
009     * by Sun in the LICENSE file that accompanied this code.
010     *
011     * This code is distributed in the hope that it will be useful, but WITHOUT
012     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013     * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
014     * version 2 for more details (a copy is included in the LICENSE file that
015     * accompanied this code).
016     *
017     * You should have received a copy of the GNU General Public License version
018     * 2 along with this work; if not, write to the Free Software Foundation,
019     * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020     *
021     * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022     * CA 95054 USA or visit www.sun.com if you need additional information or
023     * have any questions.
024     */
025    
026    package com.sun.tools.apt.comp;
027    
028    import com.sun.tools.javac.code.*;
029    import com.sun.tools.javac.comp.*;
030    import com.sun.tools.javac.tree.*;
031    import com.sun.tools.javac.util.*;
032    import com.sun.tools.javac.tree.TreeScanner;
033    import com.sun.tools.javac.util.Context;
034    import com.sun.tools.apt.util.Bark;
035    import com.sun.tools.javac.util.Position;
036    
037    import java.util.*;
038    import java.util.regex.*;
039    import java.lang.reflect.*;
040    import java.lang.reflect.InvocationTargetException;
041    import java.io.IOException;
042    
043    import com.sun.tools.apt.*;
044    import com.sun.tools.apt.comp.*;
045    import com.sun.tools.javac.code.Symbol.*;
046    
047    import com.sun.mirror.declaration.TypeDeclaration;
048    import com.sun.mirror.declaration.AnnotationTypeDeclaration;
049    import com.sun.mirror.apt.*;
050    // import com.sun.mirror.apt.AnnotationProcessorFactory;
051    import com.sun.mirror.apt.AnnotationProcessors;
052    
053    import com.sun.tools.apt.mirror.AptEnv;
054    import com.sun.tools.apt.mirror.apt.FilerImpl;
055    import com.sun.tools.apt.mirror.apt.AnnotationProcessorEnvironmentImpl;
056    
057    
058    import static com.sun.tools.apt.mirror.declaration.DeclarationMaker.isJavaIdentifier;
059    
060    /**
061     * Apt compiler phase.
062     *
063     *  <p><b>This is NOT part of any API supported by Sun Microsystems.
064     *  If you write code that depends on this, you do so at your own
065     *  risk.  This code and its internal interfaces are subject to change
066     *  or deletion without notice.</b>
067     */
068    public class Apt extends ListBuffer<Env<AttrContext>> {
069        java.util.Set<String> genSourceFileNames = new java.util.LinkedHashSet<String>();
070        public java.util.Set<String> getSourceFileNames() {
071            return genSourceFileNames;
072        }
073    
074        /** List of names of generated class files.
075         */
076        java.util.Set<String> genClassFileNames  = new java.util.LinkedHashSet<String>();
077        public java.util.Set<String> getClassFileNames() {
078            return genClassFileNames;
079        }
080    
081        /* AptEnvironment */
082        AptEnv aptenv;
083    
084        private Context context;
085    
086        /** The context key for the todo list. */
087    
088        protected static final Context.Key<Apt> aptKey =
089            new Context.Key<Apt>();
090    
091        /** Get the Apt instance for this context. */
092        public static Apt instance(Context context) {
093            Apt instance = context.get(aptKey);
094            if (instance == null)
095                instance = new Apt(context);
096            return instance;
097        }
098    
099        /** 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    }