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 }