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