src/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java

Wed, 20 May 2009 13:36:23 -0700

author
jjg
date
Wed, 20 May 2009 13:36:23 -0700
changeset 286
79eb8795a1de
parent 285
4ce1c1400334
child 308
03944ee4fac4
permissions
-rw-r--r--

6827026: Change javac source and target default to 7
Reviewed-by: darcy, ohair

     1 /*
     2  * Copyright 2005-2008 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  */
    26 package com.sun.tools.javac.processing;
    29 import java.lang.reflect.*;
    30 import java.util.*;
    31 import java.util.regex.*;
    33 import java.net.URL;
    34 import java.io.Closeable;
    35 import java.io.File;
    36 import java.io.PrintWriter;
    37 import java.io.IOException;
    38 import java.net.MalformedURLException;
    39 import java.io.StringWriter;
    41 import javax.annotation.processing.*;
    42 import javax.lang.model.SourceVersion;
    43 import javax.lang.model.element.AnnotationMirror;
    44 import javax.lang.model.element.Element;
    45 import javax.lang.model.element.TypeElement;
    46 import javax.lang.model.element.PackageElement;
    47 import javax.lang.model.util.*;
    48 import javax.tools.JavaFileManager;
    49 import javax.tools.StandardJavaFileManager;
    50 import javax.tools.JavaFileObject;
    51 import javax.tools.DiagnosticListener;
    53 import com.sun.source.util.TaskEvent;
    54 import com.sun.source.util.TaskListener;
    55 import com.sun.tools.javac.api.JavacTaskImpl;
    56 import com.sun.tools.javac.code.*;
    57 import com.sun.tools.javac.code.Symbol.*;
    58 import com.sun.tools.javac.file.JavacFileManager;
    59 import com.sun.tools.javac.jvm.*;
    60 import com.sun.tools.javac.main.JavaCompiler;
    61 import com.sun.tools.javac.model.JavacElements;
    62 import com.sun.tools.javac.model.JavacTypes;
    63 import com.sun.tools.javac.parser.*;
    64 import com.sun.tools.javac.tree.*;
    65 import com.sun.tools.javac.tree.JCTree.*;
    66 import com.sun.tools.javac.util.Abort;
    67 import com.sun.tools.javac.util.Context;
    68 import com.sun.tools.javac.util.List;
    69 import com.sun.tools.javac.util.ListBuffer;
    70 import com.sun.tools.javac.util.Log;
    71 import com.sun.tools.javac.util.JavacMessages;
    72 import com.sun.tools.javac.util.Name;
    73 import com.sun.tools.javac.util.Names;
    74 import com.sun.tools.javac.util.Options;
    76 import static javax.tools.StandardLocation.*;
    78 /**
    79  * Objects of this class hold and manage the state needed to support
    80  * annotation processing.
    81  *
    82  * <p><b>This is NOT part of any API supported by Sun Microsystems.
    83  * If you write code that depends on this, you do so at your own risk.
    84  * This code and its internal interfaces are subject to change or
    85  * deletion without notice.</b>
    86  */
    87 public class JavacProcessingEnvironment implements ProcessingEnvironment, Closeable {
    88     Options options;
    90     private final boolean printProcessorInfo;
    91     private final boolean printRounds;
    92     private final boolean verbose;
    93     private final boolean lint;
    94     private final boolean procOnly;
    95     private final boolean fatalErrors;
    97     private final JavacFiler filer;
    98     private final JavacMessager messager;
    99     private final JavacElements elementUtils;
   100     private final JavacTypes typeUtils;
   102     /**
   103      * Holds relevant state history of which processors have been
   104      * used.
   105      */
   106     private DiscoveredProcessors discoveredProcs;
   108     /**
   109      * Map of processor-specific options.
   110      */
   111     private final Map<String, String> processorOptions;
   113     /**
   114      */
   115     private final Set<String> unmatchedProcessorOptions;
   117     /**
   118      * Annotations implicitly processed and claimed by javac.
   119      */
   120     private final Set<String> platformAnnotations;
   122     /**
   123      * Set of packages given on command line.
   124      */
   125     private Set<PackageSymbol> specifiedPackages = Collections.emptySet();
   127     /** The log to be used for error reporting.
   128      */
   129     Log log;
   131     /**
   132      * Source level of the compile.
   133      */
   134     Source source;
   136     /**
   137      * JavacMessages object used for localization
   138      */
   139     private JavacMessages messages;
   141     private Context context;
   143     public JavacProcessingEnvironment(Context context, Iterable<? extends Processor> processors) {
   144         options = Options.instance(context);
   145         this.context = context;
   146         log = Log.instance(context);
   147         source = Source.instance(context);
   148         printProcessorInfo = options.get("-XprintProcessorInfo") != null;
   149         printRounds = options.get("-XprintRounds") != null;
   150         verbose = options.get("-verbose") != null;
   151         lint = options.lint("processing");
   152         procOnly = options.get("-proc:only") != null ||
   153             options.get("-Xprint") != null;
   154         fatalErrors = options.get("fatalEnterError") != null;
   155         platformAnnotations = initPlatformAnnotations();
   157         // Initialize services before any processors are initialzied
   158         // in case processors use them.
   159         filer = new JavacFiler(context);
   160         messager = new JavacMessager(context, this);
   161         elementUtils = new JavacElements(context);
   162         typeUtils = new JavacTypes(context);
   163         processorOptions = initProcessorOptions(context);
   164         unmatchedProcessorOptions = initUnmatchedProcessorOptions();
   165         messages = JavacMessages.instance(context);
   166         initProcessorIterator(context, processors);
   167     }
   169     private Set<String> initPlatformAnnotations() {
   170         Set<String> platformAnnotations = new HashSet<String>();
   171         platformAnnotations.add("java.lang.Deprecated");
   172         platformAnnotations.add("java.lang.Override");
   173         platformAnnotations.add("java.lang.SuppressWarnings");
   174         platformAnnotations.add("java.lang.annotation.Documented");
   175         platformAnnotations.add("java.lang.annotation.Inherited");
   176         platformAnnotations.add("java.lang.annotation.Retention");
   177         platformAnnotations.add("java.lang.annotation.Target");
   178         return Collections.unmodifiableSet(platformAnnotations);
   179     }
   181     private void initProcessorIterator(Context context, Iterable<? extends Processor> processors) {
   182         Log   log   = Log.instance(context);
   183         Iterator<? extends Processor> processorIterator;
   185         if (options.get("-Xprint") != null) {
   186             try {
   187                 Processor processor = PrintingProcessor.class.newInstance();
   188                 processorIterator = List.of(processor).iterator();
   189             } catch (Throwable t) {
   190                 AssertionError assertError =
   191                     new AssertionError("Problem instantiating PrintingProcessor.");
   192                 assertError.initCause(t);
   193                 throw assertError;
   194             }
   195         } else if (processors != null) {
   196             processorIterator = processors.iterator();
   197         } else {
   198             String processorNames = options.get("-processor");
   199             JavaFileManager fileManager = context.get(JavaFileManager.class);
   200             try {
   201                 // If processorpath is not explicitly set, use the classpath.
   202                 ClassLoader processorCL = fileManager.hasLocation(ANNOTATION_PROCESSOR_PATH)
   203                     ? fileManager.getClassLoader(ANNOTATION_PROCESSOR_PATH)
   204                     : fileManager.getClassLoader(CLASS_PATH);
   206                 /*
   207                  * If the "-processor" option is used, search the appropriate
   208                  * path for the named class.  Otherwise, use a service
   209                  * provider mechanism to create the processor iterator.
   210                  */
   211                 if (processorNames != null) {
   212                     processorIterator = new NameProcessIterator(processorNames, processorCL, log);
   213                 } else {
   214                     processorIterator = new ServiceIterator(processorCL, log);
   215                 }
   216             } catch (SecurityException e) {
   217                 /*
   218                  * A security exception will occur if we can't create a classloader.
   219                  * Ignore the exception if, with hindsight, we didn't need it anyway
   220                  * (i.e. no processor was specified either explicitly, or implicitly,
   221                  * in service configuration file.) Otherwise, we cannot continue.
   222                  */
   223                 processorIterator = handleServiceLoaderUnavailability("proc.cant.create.loader", e);
   224             }
   225         }
   226         discoveredProcs = new DiscoveredProcessors(processorIterator);
   227     }
   229     /**
   230      * Returns an empty processor iterator if no processors are on the
   231      * relevant path, otherwise if processors are present, logs an
   232      * error.  Called when a service loader is unavailable for some
   233      * reason, either because a service loader class cannot be found
   234      * or because a security policy prevents class loaders from being
   235      * created.
   236      *
   237      * @param key The resource key to use to log an error message
   238      * @param e   If non-null, pass this exception to Abort
   239      */
   240     private Iterator<Processor> handleServiceLoaderUnavailability(String key, Exception e) {
   241         JavaFileManager fileManager = context.get(JavaFileManager.class);
   243         if (fileManager instanceof JavacFileManager) {
   244             StandardJavaFileManager standardFileManager = (JavacFileManager) fileManager;
   245             Iterable<? extends File> workingPath = fileManager.hasLocation(ANNOTATION_PROCESSOR_PATH)
   246                 ? standardFileManager.getLocation(ANNOTATION_PROCESSOR_PATH)
   247                 : standardFileManager.getLocation(CLASS_PATH);
   249             if (needClassLoader(options.get("-processor"), workingPath) )
   250                 handleException(key, e);
   252         } else {
   253             handleException(key, e);
   254         }
   256         java.util.List<Processor> pl = Collections.emptyList();
   257         return pl.iterator();
   258     }
   260     /**
   261      * Handle a security exception thrown during initializing the
   262      * Processor iterator.
   263      */
   264     private void handleException(String key, Exception e) {
   265         if (e != null) {
   266             log.error(key, e.getLocalizedMessage());
   267             throw new Abort(e);
   268         } else {
   269             log.error(key);
   270             throw new Abort();
   271         }
   272     }
   274     /**
   275      * Use a service loader appropriate for the platform to provide an
   276      * iterator over annotations processors.  If
   277      * java.util.ServiceLoader is present use it, otherwise, use
   278      * sun.misc.Service, otherwise fail if a loader is needed.
   279      */
   280     private class ServiceIterator implements Iterator<Processor> {
   281         // The to-be-wrapped iterator.
   282         private Iterator<?> iterator;
   283         private Log log;
   285         ServiceIterator(ClassLoader classLoader, Log log) {
   286             Class<?> loaderClass;
   287             String loadMethodName;
   288             boolean jusl;
   290             this.log = log;
   291             try {
   292                 try {
   293                     loaderClass = Class.forName("java.util.ServiceLoader");
   294                     loadMethodName = "load";
   295                     jusl = true;
   296                 } catch (ClassNotFoundException cnfe) {
   297                     try {
   298                         loaderClass = Class.forName("sun.misc.Service");
   299                         loadMethodName = "providers";
   300                         jusl = false;
   301                     } catch (ClassNotFoundException cnfe2) {
   302                         // Fail softly if a loader is not actually needed.
   303                         this.iterator = handleServiceLoaderUnavailability("proc.no.service",
   304                                                                           null);
   305                         return;
   306                     }
   307                 }
   309                 // java.util.ServiceLoader.load or sun.misc.Service.providers
   310                 Method loadMethod = loaderClass.getMethod(loadMethodName,
   311                                                           Class.class,
   312                                                           ClassLoader.class);
   314                 Object result = loadMethod.invoke(null,
   315                                                   Processor.class,
   316                                                   classLoader);
   318                 // For java.util.ServiceLoader, we have to call another
   319                 // method to get the iterator.
   320                 if (jusl) {
   321                     Method m = loaderClass.getMethod("iterator");
   322                     result = m.invoke(result); // serviceLoader.iterator();
   323                 }
   325                 // The result should now be an iterator.
   326                 this.iterator = (Iterator<?>) result;
   327             } catch (Throwable t) {
   328                 log.error("proc.service.problem");
   329                 throw new Abort(t);
   330             }
   331         }
   333         public boolean hasNext() {
   334             try {
   335                 return iterator.hasNext();
   336             } catch (Throwable t) {
   337                 if ("ServiceConfigurationError".
   338                     equals(t.getClass().getSimpleName())) {
   339                     log.error("proc.bad.config.file", t.getLocalizedMessage());
   340                 }
   341                 throw new Abort(t);
   342             }
   343         }
   345         public Processor next() {
   346             try {
   347                 return (Processor)(iterator.next());
   348             } catch (Throwable t) {
   349                 if ("ServiceConfigurationError".
   350                     equals(t.getClass().getSimpleName())) {
   351                     log.error("proc.bad.config.file", t.getLocalizedMessage());
   352                 } else {
   353                     log.error("proc.processor.constructor.error", t.getLocalizedMessage());
   354                 }
   355                 throw new Abort(t);
   356             }
   357         }
   359         public void remove() {
   360             throw new UnsupportedOperationException();
   361         }
   362     }
   365     private static class NameProcessIterator implements Iterator<Processor> {
   366         Processor nextProc = null;
   367         Iterator<String> names;
   368         ClassLoader processorCL;
   369         Log log;
   371         NameProcessIterator(String names, ClassLoader processorCL, Log log) {
   372             this.names = Arrays.asList(names.split(",")).iterator();
   373             this.processorCL = processorCL;
   374             this.log = log;
   375         }
   377         public boolean hasNext() {
   378             if (nextProc != null)
   379                 return true;
   380             else {
   381                 if (!names.hasNext())
   382                     return false;
   383                 else {
   384                     String processorName = names.next();
   386                     Processor processor;
   387                     try {
   388                         try {
   389                             processor =
   390                                 (Processor) (processorCL.loadClass(processorName).newInstance());
   391                         } catch (ClassNotFoundException cnfe) {
   392                             log.error("proc.processor.not.found", processorName);
   393                             return false;
   394                         } catch (ClassCastException cce) {
   395                             log.error("proc.processor.wrong.type", processorName);
   396                             return false;
   397                         } catch (Exception e ) {
   398                             log.error("proc.processor.cant.instantiate", processorName);
   399                             return false;
   400                         }
   401                     } catch(Throwable t) {
   402                         throw new AnnotationProcessingError(t);
   403                     }
   404                     nextProc = processor;
   405                     return true;
   406                 }
   408             }
   409         }
   411         public Processor next() {
   412             if (hasNext()) {
   413                 Processor p = nextProc;
   414                 nextProc = null;
   415                 return p;
   416             } else
   417                 throw new NoSuchElementException();
   418         }
   420         public void remove () {
   421             throw new UnsupportedOperationException();
   422         }
   423     }
   425     public boolean atLeastOneProcessor() {
   426         return discoveredProcs.iterator().hasNext();
   427     }
   429     private Map<String, String> initProcessorOptions(Context context) {
   430         Options options = Options.instance(context);
   431         Set<String> keySet = options.keySet();
   432         Map<String, String> tempOptions = new LinkedHashMap<String, String>();
   434         for(String key : keySet) {
   435             if (key.startsWith("-A") && key.length() > 2) {
   436                 int sepIndex = key.indexOf('=');
   437                 String candidateKey = null;
   438                 String candidateValue = null;
   440                 if (sepIndex == -1)
   441                     candidateKey = key.substring(2);
   442                 else if (sepIndex >= 3) {
   443                     candidateKey = key.substring(2, sepIndex);
   444                     candidateValue = (sepIndex < key.length()-1)?
   445                         key.substring(sepIndex+1) : null;
   446                 }
   447                 tempOptions.put(candidateKey, candidateValue);
   448             }
   449         }
   451         return Collections.unmodifiableMap(tempOptions);
   452     }
   454     private Set<String> initUnmatchedProcessorOptions() {
   455         Set<String> unmatchedProcessorOptions = new HashSet<String>();
   456         unmatchedProcessorOptions.addAll(processorOptions.keySet());
   457         return unmatchedProcessorOptions;
   458     }
   460     /**
   461      * State about how a processor has been used by the tool.  If a
   462      * processor has been used on a prior round, its process method is
   463      * called on all subsequent rounds, perhaps with an empty set of
   464      * annotations to process.  The {@code annotatedSupported} method
   465      * caches the supported annotation information from the first (and
   466      * only) getSupportedAnnotationTypes call to the processor.
   467      */
   468     static class ProcessorState {
   469         public Processor processor;
   470         public boolean   contributed;
   471         private ArrayList<Pattern> supportedAnnotationPatterns;
   472         private ArrayList<String>  supportedOptionNames;
   474         ProcessorState(Processor p, Log log, Source source, ProcessingEnvironment env) {
   475             processor = p;
   476             contributed = false;
   478             try {
   479                 processor.init(env);
   481                 checkSourceVersionCompatibility(source, log);
   483                 supportedAnnotationPatterns = new ArrayList<Pattern>();
   484                 for (String importString : processor.getSupportedAnnotationTypes()) {
   485                     supportedAnnotationPatterns.add(importStringToPattern(importString,
   486                                                                           processor,
   487                                                                           log));
   488                 }
   490                 supportedOptionNames = new ArrayList<String>();
   491                 for (String optionName : processor.getSupportedOptions() ) {
   492                     if (checkOptionName(optionName, log))
   493                         supportedOptionNames.add(optionName);
   494                 }
   496             } catch (Throwable t) {
   497                 throw new AnnotationProcessingError(t);
   498             }
   499         }
   501         /**
   502          * Checks whether or not a processor's source version is
   503          * compatible with the compilation source version.  The
   504          * processor's source version needs to be greater than or
   505          * equal to the source version of the compile.
   506          */
   507         private void checkSourceVersionCompatibility(Source source, Log log) {
   508             SourceVersion procSourceVersion = processor.getSupportedSourceVersion();
   510             if (procSourceVersion.compareTo(Source.toSourceVersion(source)) < 0 )  {
   511                 log.warning("proc.processor.incompatible.source.version",
   512                             procSourceVersion,
   513                             processor.getClass().getName(),
   514                             source.name);
   515             }
   516         }
   518         private boolean checkOptionName(String optionName, Log log) {
   519             boolean valid = isValidOptionName(optionName);
   520             if (!valid)
   521                 log.error("proc.processor.bad.option.name",
   522                             optionName,
   523                             processor.getClass().getName());
   524             return valid;
   525         }
   527         public boolean annotationSupported(String annotationName) {
   528             for(Pattern p: supportedAnnotationPatterns) {
   529                 if (p.matcher(annotationName).matches())
   530                     return true;
   531             }
   532             return false;
   533         }
   535         /**
   536          * Remove options that are matched by this processor.
   537          */
   538         public void removeSupportedOptions(Set<String> unmatchedProcessorOptions) {
   539             unmatchedProcessorOptions.removeAll(supportedOptionNames);
   540         }
   541     }
   543     // TODO: These two classes can probably be rewritten better...
   544     /**
   545      * This class holds information about the processors that have
   546      * been discoverd so far as well as the means to discover more, if
   547      * necessary.  A single iterator should be used per round of
   548      * annotation processing.  The iterator first visits already
   549      * discovered processors then fails over to the service provided
   550      * mechanism if additional queries are made.
   551      */
   552     class DiscoveredProcessors implements Iterable<ProcessorState> {
   554         class ProcessorStateIterator implements Iterator<ProcessorState> {
   555             DiscoveredProcessors psi;
   556             Iterator<ProcessorState> innerIter;
   557             boolean onProcInterator;
   559             ProcessorStateIterator(DiscoveredProcessors psi) {
   560                 this.psi = psi;
   561                 this.innerIter = psi.procStateList.iterator();
   562                 this.onProcInterator = false;
   563             }
   565             public ProcessorState next() {
   566                 if (!onProcInterator) {
   567                     if (innerIter.hasNext())
   568                         return innerIter.next();
   569                     else
   570                         onProcInterator = true;
   571                 }
   573                 if (psi.processorIterator.hasNext()) {
   574                     ProcessorState ps = new ProcessorState(psi.processorIterator.next(),
   575                                                            log, source, JavacProcessingEnvironment.this);
   576                     psi.procStateList.add(ps);
   577                     return ps;
   578                 } else
   579                     throw new NoSuchElementException();
   580             }
   582             public boolean hasNext() {
   583                 if (onProcInterator)
   584                     return  psi.processorIterator.hasNext();
   585                 else
   586                     return innerIter.hasNext() || psi.processorIterator.hasNext();
   587             }
   589             public void remove () {
   590                 throw new UnsupportedOperationException();
   591             }
   593             /**
   594              * Run all remaining processors on the procStateList that
   595              * have not already run this round with an empty set of
   596              * annotations.
   597              */
   598             public void runContributingProcs(RoundEnvironment re) {
   599                 if (!onProcInterator) {
   600                     Set<TypeElement> emptyTypeElements = Collections.emptySet();
   601                     while(innerIter.hasNext()) {
   602                         ProcessorState ps = innerIter.next();
   603                         if (ps.contributed)
   604                             callProcessor(ps.processor, emptyTypeElements, re);
   605                     }
   606                 }
   607             }
   608         }
   610         Iterator<? extends Processor> processorIterator;
   611         ArrayList<ProcessorState>  procStateList;
   613         public ProcessorStateIterator iterator() {
   614             return new ProcessorStateIterator(this);
   615         }
   617         DiscoveredProcessors(Iterator<? extends Processor> processorIterator) {
   618             this.processorIterator = processorIterator;
   619             this.procStateList = new ArrayList<ProcessorState>();
   620         }
   621     }
   623     private void discoverAndRunProcs(Context context,
   624                                      Set<TypeElement> annotationsPresent,
   625                                      List<ClassSymbol> topLevelClasses,
   626                                      List<PackageSymbol> packageInfoFiles) {
   627         // Writer for -XprintRounds and -XprintProcessorInfo data
   628         PrintWriter xout = context.get(Log.outKey);
   630         Map<String, TypeElement> unmatchedAnnotations =
   631             new HashMap<String, TypeElement>(annotationsPresent.size());
   633         for(TypeElement a  : annotationsPresent) {
   634                 unmatchedAnnotations.put(a.getQualifiedName().toString(),
   635                                          a);
   636         }
   638         // Give "*" processors a chance to match
   639         if (unmatchedAnnotations.size() == 0)
   640             unmatchedAnnotations.put("", null);
   642         DiscoveredProcessors.ProcessorStateIterator psi = discoveredProcs.iterator();
   643         // TODO: Create proper argument values; need past round
   644         // information to fill in this constructor.  Note that the 1
   645         // st round of processing could be the last round if there
   646         // were parse errors on the initial source files; however, we
   647         // are not doing processing in that case.
   649         Set<Element> rootElements = new LinkedHashSet<Element>();
   650         rootElements.addAll(topLevelClasses);
   651         rootElements.addAll(packageInfoFiles);
   652         rootElements = Collections.unmodifiableSet(rootElements);
   654         RoundEnvironment renv = new JavacRoundEnvironment(false,
   655                                                           false,
   656                                                           rootElements,
   657                                                           JavacProcessingEnvironment.this);
   659         while(unmatchedAnnotations.size() > 0 && psi.hasNext() ) {
   660             ProcessorState ps = psi.next();
   661             Set<String>  matchedNames = new HashSet<String>();
   662             Set<TypeElement> typeElements = new LinkedHashSet<TypeElement>();
   663             for (String unmatchedAnnotationName : unmatchedAnnotations.keySet()) {
   664                 if (ps.annotationSupported(unmatchedAnnotationName) ) {
   665                     matchedNames.add(unmatchedAnnotationName);
   666                     TypeElement te = unmatchedAnnotations.get(unmatchedAnnotationName);
   667                     if (te != null)
   668                         typeElements.add(te);
   669                 }
   670             }
   672             if (matchedNames.size() > 0 || ps.contributed) {
   673                 boolean processingResult = callProcessor(ps.processor, typeElements, renv);
   674                 ps.contributed = true;
   675                 ps.removeSupportedOptions(unmatchedProcessorOptions);
   677                 if (printProcessorInfo || verbose) {
   678                     xout.println(Log.getLocalizedString("x.print.processor.info",
   679                                                         ps.processor.getClass().getName(),
   680                                                         matchedNames.toString(),
   681                                                         processingResult));
   682                 }
   684                 if (processingResult) {
   685                     unmatchedAnnotations.keySet().removeAll(matchedNames);
   686                 }
   688             }
   689         }
   690         unmatchedAnnotations.remove("");
   692         if (lint && unmatchedAnnotations.size() > 0) {
   693             // Remove annotations processed by javac
   694             unmatchedAnnotations.keySet().removeAll(platformAnnotations);
   695             if (unmatchedAnnotations.size() > 0) {
   696                 log = Log.instance(context);
   697                 log.warning("proc.annotations.without.processors",
   698                             unmatchedAnnotations.keySet());
   699             }
   700         }
   702         // Run contributing processors that haven't run yet
   703         psi.runContributingProcs(renv);
   705         // Debugging
   706         if (options.get("displayFilerState") != null)
   707             filer.displayState();
   708     }
   710     /**
   711      * Computes the set of annotations on the symbol in question.
   712      * Leave class public for external testing purposes.
   713      */
   714     public static class ComputeAnnotationSet extends
   715         ElementScanner6<Set<TypeElement>, Set<TypeElement>> {
   716         final Elements elements;
   718         public ComputeAnnotationSet(Elements elements) {
   719             super();
   720             this.elements = elements;
   721         }
   723         @Override
   724         public Set<TypeElement> visitPackage(PackageElement e, Set<TypeElement> p) {
   725             // Don't scan enclosed elements of a package
   726             return p;
   727         }
   729         @Override
   730          public Set<TypeElement> scan(Element e, Set<TypeElement> p) {
   731             for (AnnotationMirror annotationMirror :
   732                      elements.getAllAnnotationMirrors(e) ) {
   733                 Element e2 = annotationMirror.getAnnotationType().asElement();
   734                 p.add((TypeElement) e2);
   735             }
   736             return super.scan(e, p);
   737         }
   738     }
   740     private boolean callProcessor(Processor proc,
   741                                          Set<? extends TypeElement> tes,
   742                                          RoundEnvironment renv) {
   743         try {
   744             return proc.process(tes, renv);
   745         } catch (CompletionFailure ex) {
   746             StringWriter out = new StringWriter();
   747             ex.printStackTrace(new PrintWriter(out));
   748             log.error("proc.cant.access", ex.sym, ex.getDetailValue(), out.toString());
   749             return false;
   750         } catch (Throwable t) {
   751             throw new AnnotationProcessingError(t);
   752         }
   753     }
   756     // TODO: internal catch clauses?; catch and rethrow an annotation
   757     // processing error
   758     public JavaCompiler doProcessing(Context context,
   759                                      List<JCCompilationUnit> roots,
   760                                      List<ClassSymbol> classSymbols,
   761                                      Iterable<? extends PackageSymbol> pckSymbols)
   762     throws IOException {
   764         log = Log.instance(context);
   765         // Writer for -XprintRounds and -XprintProcessorInfo data
   766         PrintWriter xout = context.get(Log.outKey);
   767         TaskListener taskListener = context.get(TaskListener.class);
   770         AnnotationCollector collector = new AnnotationCollector();
   772         JavaCompiler compiler = JavaCompiler.instance(context);
   773         compiler.todo.clear(); // free the compiler's resources
   775         int round = 0;
   777         // List<JCAnnotation> annotationsPresentInSource = collector.findAnnotations(roots);
   778         List<ClassSymbol> topLevelClasses = getTopLevelClasses(roots);
   780         for (ClassSymbol classSym : classSymbols)
   781             topLevelClasses = topLevelClasses.prepend(classSym);
   782         List<PackageSymbol> packageInfoFiles =
   783             getPackageInfoFiles(roots);
   785         Set<PackageSymbol> specifiedPackages = new LinkedHashSet<PackageSymbol>();
   786         for (PackageSymbol psym : pckSymbols)
   787             specifiedPackages.add(psym);
   788         this.specifiedPackages = Collections.unmodifiableSet(specifiedPackages);
   790         // Use annotation processing to compute the set of annotations present
   791         Set<TypeElement> annotationsPresent = new LinkedHashSet<TypeElement>();
   792         ComputeAnnotationSet annotationComputer = new ComputeAnnotationSet(elementUtils);
   793         for (ClassSymbol classSym : topLevelClasses)
   794             annotationComputer.scan(classSym, annotationsPresent);
   795         for (PackageSymbol pkgSym : packageInfoFiles)
   796             annotationComputer.scan(pkgSym, annotationsPresent);
   798         Context currentContext = context;
   800         int roundNumber = 0;
   801         boolean errorStatus = false;
   803         runAround:
   804         while(true) {
   805             if (fatalErrors && compiler.errorCount() != 0) {
   806                 errorStatus = true;
   807                 break runAround;
   808             }
   810             this.context = currentContext;
   811             roundNumber++;
   812             printRoundInfo(xout, roundNumber, topLevelClasses, annotationsPresent, false);
   814             if (taskListener != null)
   815                 taskListener.started(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING_ROUND));
   817             try {
   818                 discoverAndRunProcs(currentContext, annotationsPresent, topLevelClasses, packageInfoFiles);
   819             } finally {
   820                 if (taskListener != null)
   821                     taskListener.finished(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING_ROUND));
   822             }
   824             /*
   825              * Processors for round n have run to completion.  Prepare
   826              * for round (n+1) by checked for errors raised by
   827              * annotation processors and then checking for syntax
   828              * errors on any generated source files.
   829              */
   830             if (messager.errorRaised()) {
   831                 errorStatus = true;
   832                 break runAround;
   833             } else {
   834                 if (moreToDo()) {
   835                     // annotationsPresentInSource = List.nil();
   836                     annotationsPresent = new LinkedHashSet<TypeElement>();
   837                     topLevelClasses  = List.nil();
   838                     packageInfoFiles = List.nil();
   840                     compiler.close(false);
   841                     currentContext = contextForNextRound(currentContext, true);
   843                     JavaFileManager fileManager = currentContext.get(JavaFileManager.class);
   845                     List<JavaFileObject> fileObjects = List.nil();
   846                     for (JavaFileObject jfo : filer.getGeneratedSourceFileObjects() ) {
   847                         fileObjects = fileObjects.prepend(jfo);
   848                     }
   851                     compiler = JavaCompiler.instance(currentContext);
   852                     List<JCCompilationUnit> parsedFiles = compiler.parseFiles(fileObjects);
   853                     roots = cleanTrees(roots).reverse();
   856                     for (JCCompilationUnit unit : parsedFiles)
   857                         roots = roots.prepend(unit);
   858                     roots = roots.reverse();
   860                     // Check for errors after parsing
   861                     if (compiler.parseErrors()) {
   862                         errorStatus = true;
   863                         break runAround;
   864                     } else {
   865                         ListBuffer<ClassSymbol> classes = enterNewClassFiles(currentContext);
   866                         compiler.enterTrees(roots);
   868                         // annotationsPresentInSource =
   869                         // collector.findAnnotations(parsedFiles);
   870                         classes.appendList(getTopLevelClasses(parsedFiles));
   871                         topLevelClasses  = classes.toList();
   872                         packageInfoFiles = getPackageInfoFiles(parsedFiles);
   874                         annotationsPresent = new LinkedHashSet<TypeElement>();
   875                         for (ClassSymbol classSym : topLevelClasses)
   876                             annotationComputer.scan(classSym, annotationsPresent);
   877                         for (PackageSymbol pkgSym : packageInfoFiles)
   878                             annotationComputer.scan(pkgSym, annotationsPresent);
   880                         updateProcessingState(currentContext, false);
   881                     }
   882                 } else
   883                     break runAround; // No new files
   884             }
   885         }
   886         runLastRound(xout, roundNumber, errorStatus, taskListener);
   888         compiler.close(false);
   889         currentContext = contextForNextRound(currentContext, true);
   890         compiler = JavaCompiler.instance(currentContext);
   891         filer.newRound(currentContext, true);
   892         filer.warnIfUnclosedFiles();
   893         warnIfUnmatchedOptions();
   895        /*
   896         * If an annotation processor raises an error in a round,
   897         * that round runs to completion and one last round occurs.
   898         * The last round may also occur because no more source or
   899         * class files have been generated.  Therefore, if an error
   900         * was raised on either of the last *two* rounds, the compile
   901         * should exit with a nonzero exit code.  The current value of
   902         * errorStatus holds whether or not an error was raised on the
   903         * second to last round; errorRaised() gives the error status
   904         * of the last round.
   905         */
   906        errorStatus = errorStatus || messager.errorRaised();
   909         // Free resources
   910         this.close();
   912         if (taskListener != null)
   913             taskListener.finished(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING));
   915         if (errorStatus) {
   916             compiler.log.nerrors += messager.errorCount();
   917             if (compiler.errorCount() == 0)
   918                 compiler.log.nerrors++;
   919         } else if (procOnly) {
   920             compiler.todo.clear();
   921         } else { // Final compilation
   922             compiler.close(false);
   923             currentContext = contextForNextRound(currentContext, true);
   924             compiler = JavaCompiler.instance(currentContext);
   926             if (true) {
   927                 compiler.enterTrees(cleanTrees(roots));
   928             } else {
   929                 List<JavaFileObject> fileObjects = List.nil();
   930                 for (JCCompilationUnit unit : roots)
   931                     fileObjects = fileObjects.prepend(unit.getSourceFile());
   932                 roots = null;
   933                 compiler.enterTrees(compiler.parseFiles(fileObjects.reverse()));
   934             }
   935         }
   937         return compiler;
   938     }
   940     // Call the last round of annotation processing
   941     private void runLastRound(PrintWriter xout,
   942                               int roundNumber,
   943                               boolean errorStatus,
   944                               TaskListener taskListener) throws IOException {
   945         roundNumber++;
   946         List<ClassSymbol> noTopLevelClasses = List.nil();
   947         Set<TypeElement> noAnnotations =  Collections.emptySet();
   948         printRoundInfo(xout, roundNumber, noTopLevelClasses, noAnnotations, true);
   950         Set<Element> emptyRootElements = Collections.emptySet(); // immutable
   951         RoundEnvironment renv = new JavacRoundEnvironment(true,
   952                                                           errorStatus,
   953                                                           emptyRootElements,
   954                                                           JavacProcessingEnvironment.this);
   955         if (taskListener != null)
   956             taskListener.started(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING_ROUND));
   958         try {
   959             discoveredProcs.iterator().runContributingProcs(renv);
   960         } finally {
   961             if (taskListener != null)
   962                 taskListener.finished(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING_ROUND));
   963         }
   964     }
   966     private void updateProcessingState(Context currentContext, boolean lastRound) {
   967         filer.newRound(currentContext, lastRound);
   968         messager.newRound(currentContext);
   970         elementUtils.setContext(currentContext);
   971         typeUtils.setContext(currentContext);
   972     }
   974     private void warnIfUnmatchedOptions() {
   975         if (!unmatchedProcessorOptions.isEmpty()) {
   976             log.warning("proc.unmatched.processor.options", unmatchedProcessorOptions.toString());
   977         }
   978     }
   980     private void printRoundInfo(PrintWriter xout,
   981                                 int roundNumber,
   982                                 List<ClassSymbol> topLevelClasses,
   983                                 Set<TypeElement> annotationsPresent,
   984                                 boolean lastRound) {
   985         if (printRounds || verbose) {
   986             xout.println(Log.getLocalizedString("x.print.rounds",
   987                                                 roundNumber,
   988                                                 "{" + topLevelClasses.toString(", ") + "}",
   989                                                 annotationsPresent,
   990                                                 lastRound));
   991         }
   992     }
   994     private ListBuffer<ClassSymbol> enterNewClassFiles(Context currentContext) {
   995         ClassReader reader = ClassReader.instance(currentContext);
   996         Names names = Names.instance(currentContext);
   997         ListBuffer<ClassSymbol> list = new ListBuffer<ClassSymbol>();
   999         for (Map.Entry<String,JavaFileObject> entry : filer.getGeneratedClasses().entrySet()) {
  1000             Name name = names.fromString(entry.getKey());
  1001             JavaFileObject file = entry.getValue();
  1002             if (file.getKind() != JavaFileObject.Kind.CLASS)
  1003                 throw new AssertionError(file);
  1004             ClassSymbol cs = reader.enterClass(name, file);
  1005             list.append(cs);
  1007         return list;
  1010     /**
  1011      * Free resources related to annotation processing.
  1012      */
  1013     public void close() {
  1014         filer.close();
  1015         discoveredProcs = null;
  1018     private List<ClassSymbol> getTopLevelClasses(List<? extends JCCompilationUnit> units) {
  1019         List<ClassSymbol> classes = List.nil();
  1020         for (JCCompilationUnit unit : units) {
  1021             for (JCTree node : unit.defs) {
  1022                 if (node.getTag() == JCTree.CLASSDEF) {
  1023                     classes = classes.prepend(((JCClassDecl) node).sym);
  1027         return classes.reverse();
  1030     private List<PackageSymbol> getPackageInfoFiles(List<? extends JCCompilationUnit> units) {
  1031         List<PackageSymbol> packages = List.nil();
  1032         for (JCCompilationUnit unit : units) {
  1033             boolean isPkgInfo = unit.sourcefile.isNameCompatible("package-info",
  1034                                                                  JavaFileObject.Kind.SOURCE);
  1035             if (isPkgInfo) {
  1036                 packages = packages.prepend(unit.packge);
  1039         return packages.reverse();
  1042     private Context contextForNextRound(Context context, boolean shareNames)
  1043         throws IOException
  1045         Context next = new Context();
  1047         Options options = Options.instance(context);
  1048         assert options != null;
  1049         next.put(Options.optionsKey, options);
  1051         PrintWriter out = context.get(Log.outKey);
  1052         assert out != null;
  1053         next.put(Log.outKey, out);
  1055         if (shareNames) {
  1056             Names names = Names.instance(context);
  1057             assert names != null;
  1058             next.put(Names.namesKey, names);
  1061         DiagnosticListener<?> dl = context.get(DiagnosticListener.class);
  1062         if (dl != null)
  1063             next.put(DiagnosticListener.class, dl);
  1065         TaskListener tl = context.get(TaskListener.class);
  1066         if (tl != null)
  1067             next.put(TaskListener.class, tl);
  1069         JavaFileManager jfm = context.get(JavaFileManager.class);
  1070         assert jfm != null;
  1071         next.put(JavaFileManager.class, jfm);
  1072         if (jfm instanceof JavacFileManager) {
  1073             ((JavacFileManager)jfm).setContext(next);
  1076         Names names = Names.instance(context);
  1077         assert names != null;
  1078         next.put(Names.namesKey, names);
  1080         Keywords keywords = Keywords.instance(context);
  1081         assert(keywords != null);
  1082         next.put(Keywords.keywordsKey, keywords);
  1084         JavaCompiler oldCompiler = JavaCompiler.instance(context);
  1085         JavaCompiler nextCompiler = JavaCompiler.instance(next);
  1086         nextCompiler.initRound(oldCompiler);
  1088         JavacTaskImpl task = context.get(JavacTaskImpl.class);
  1089         if (task != null) {
  1090             next.put(JavacTaskImpl.class, task);
  1091             task.updateContext(next);
  1094         context.clear();
  1095         return next;
  1098     /*
  1099      * Called retroactively to determine if a class loader was required,
  1100      * after we have failed to create one.
  1101      */
  1102     private boolean needClassLoader(String procNames, Iterable<? extends File> workingpath) {
  1103         if (procNames != null)
  1104             return true;
  1106         String procPath;
  1107         URL[] urls = new URL[1];
  1108         for(File pathElement : workingpath) {
  1109             try {
  1110                 urls[0] = pathElement.toURI().toURL();
  1111                 if (ServiceProxy.hasService(Processor.class, urls))
  1112                     return true;
  1113             } catch (MalformedURLException ex) {
  1114                 throw new AssertionError(ex);
  1116             catch (ServiceProxy.ServiceConfigurationError e) {
  1117                 log.error("proc.bad.config.file", e.getLocalizedMessage());
  1118                 return true;
  1122         return false;
  1125     private class AnnotationCollector extends TreeScanner {
  1126         List<JCTree> path = List.nil();
  1127         static final boolean verbose = false;
  1128         List<JCAnnotation> annotations = List.nil();
  1130         public List<JCAnnotation> findAnnotations(List<? extends JCTree> nodes) {
  1131             annotations = List.nil();
  1132             scan(nodes);
  1133             List<JCAnnotation> found = annotations;
  1134             annotations = List.nil();
  1135             return found.reverse();
  1138         public void scan(JCTree node) {
  1139             if (node == null)
  1140                 return;
  1141             Symbol sym = TreeInfo.symbolFor(node);
  1142             if (sym != null)
  1143                 path = path.prepend(node);
  1144             super.scan(node);
  1145             if (sym != null)
  1146                 path = path.tail;
  1149         public void visitAnnotation(JCAnnotation node) {
  1150             annotations = annotations.prepend(node);
  1151             if (verbose) {
  1152                 StringBuilder sb = new StringBuilder();
  1153                 for (JCTree tree : path.reverse()) {
  1154                     System.err.print(sb);
  1155                     System.err.println(TreeInfo.symbolFor(tree));
  1156                     sb.append("  ");
  1158                 System.err.print(sb);
  1159                 System.err.println(node);
  1164     private static <T extends JCTree> List<T> cleanTrees(List<T> nodes) {
  1165         for (T node : nodes)
  1166             treeCleaner.scan(node);
  1167         return nodes;
  1170     private static TreeScanner treeCleaner = new TreeScanner() {
  1171             public void scan(JCTree node) {
  1172                 super.scan(node);
  1173                 if (node != null)
  1174                     node.type = null;
  1176             public void visitTopLevel(JCCompilationUnit node) {
  1177                 node.packge = null;
  1178                 super.visitTopLevel(node);
  1180             public void visitClassDef(JCClassDecl node) {
  1181                 node.sym = null;
  1182                 super.visitClassDef(node);
  1184             public void visitMethodDef(JCMethodDecl node) {
  1185                 node.sym = null;
  1186                 super.visitMethodDef(node);
  1188             public void visitVarDef(JCVariableDecl node) {
  1189                 node.sym = null;
  1190                 super.visitVarDef(node);
  1192             public void visitNewClass(JCNewClass node) {
  1193                 node.constructor = null;
  1194                 super.visitNewClass(node);
  1196             public void visitAssignop(JCAssignOp node) {
  1197                 node.operator = null;
  1198                 super.visitAssignop(node);
  1200             public void visitUnary(JCUnary node) {
  1201                 node.operator = null;
  1202                 super.visitUnary(node);
  1204             public void visitBinary(JCBinary node) {
  1205                 node.operator = null;
  1206                 super.visitBinary(node);
  1208             public void visitSelect(JCFieldAccess node) {
  1209                 node.sym = null;
  1210                 super.visitSelect(node);
  1212             public void visitIdent(JCIdent node) {
  1213                 node.sym = null;
  1214                 super.visitIdent(node);
  1216         };
  1219     private boolean moreToDo() {
  1220         return filer.newFiles();
  1223     /**
  1224      * {@inheritdoc}
  1226      * Command line options suitable for presenting to annotation
  1227      * processors.  "-Afoo=bar" should be "-Afoo" => "bar".
  1228      */
  1229     public Map<String,String> getOptions() {
  1230         return processorOptions;
  1233     public Messager getMessager() {
  1234         return messager;
  1237     public Filer getFiler() {
  1238         return filer;
  1241     public JavacElements getElementUtils() {
  1242         return elementUtils;
  1245     public JavacTypes getTypeUtils() {
  1246         return typeUtils;
  1249     public SourceVersion getSourceVersion() {
  1250         return Source.toSourceVersion(source);
  1253     public Locale getLocale() {
  1254         return messages.getCurrentLocale();
  1257     public Set<Symbol.PackageSymbol> getSpecifiedPackages() {
  1258         return specifiedPackages;
  1261     // Borrowed from DocletInvoker and apt
  1262     // TODO: remove from apt's Main
  1263     /**
  1264      * Utility method for converting a search path string to an array
  1265      * of directory and JAR file URLs.
  1267      * @param path the search path string
  1268      * @return the resulting array of directory and JAR file URLs
  1269      */
  1270     public static URL[] pathToURLs(String path) {
  1271         StringTokenizer st = new StringTokenizer(path, File.pathSeparator);
  1272         URL[] urls = new URL[st.countTokens()];
  1273         int count = 0;
  1274         while (st.hasMoreTokens()) {
  1275             URL url = fileToURL(new File(st.nextToken()));
  1276             if (url != null) {
  1277                 urls[count++] = url;
  1280         if (urls.length != count) {
  1281             URL[] tmp = new URL[count];
  1282             System.arraycopy(urls, 0, tmp, 0, count);
  1283             urls = tmp;
  1285         return urls;
  1288     /**
  1289      * Returns the directory or JAR file URL corresponding to the specified
  1290      * local file name.
  1292      * @param file the File object
  1293      * @return the resulting directory or JAR file URL, or null if unknown
  1294      */
  1295     private static URL fileToURL(File file) {
  1296         String name;
  1297         try {
  1298             name = file.getCanonicalPath();
  1299         } catch (IOException e) {
  1300             name = file.getAbsolutePath();
  1302         name = name.replace(File.separatorChar, '/');
  1303         if (!name.startsWith("/")) {
  1304             name = "/" + name;
  1306         // If the file does not exist, then assume that it's a directory
  1307         if (!file.isFile()) {
  1308             name = name + "/";
  1310         try {
  1311             return new URL("file", "", name);
  1312         } catch (MalformedURLException e) {
  1313             throw new IllegalArgumentException("file");
  1319     private static final Pattern allMatches = Pattern.compile(".*");
  1321     private static final Pattern noMatches  = Pattern.compile("(\\P{all})+");
  1322     /**
  1323      * Convert import-style string to regex matching that string.  If
  1324      * the string is a valid import-style string, return a regex that
  1325      * won't match anything.
  1326      */
  1327     // TODO: remove version in Apt.java
  1328     public static Pattern importStringToPattern(String s, Processor p, Log log) {
  1329         if (s.equals("*")) {
  1330             return allMatches;
  1331         } else {
  1332             String t = s;
  1333             boolean star = false;
  1335             /*
  1336              * Validate string from factory is legal.  If the string
  1337              * has more than one asterisks or the asterisks does not
  1338              * appear as the last character (preceded by a period),
  1339              * the string is not legal.
  1340              */
  1342             boolean valid = true;
  1343             int index = t.indexOf('*');
  1344             if (index != -1) {
  1345                 // '*' must be last character...
  1346                 if (index == t.length() -1) {
  1347                      // ... and preceeding character must be '.'
  1348                     if ( index-1 >= 0 ) {
  1349                         valid = t.charAt(index-1) == '.';
  1350                         // Strip off ".*$" for identifier checks
  1351                         t = t.substring(0, t.length()-2);
  1353                 } else
  1354                     valid = false;
  1357             // Verify string is off the form (javaId \.)+ or javaId
  1358             if (valid) {
  1359                 String[] javaIds = t.split("\\.", t.length()+2);
  1360                 for(String javaId: javaIds)
  1361                     valid &= SourceVersion.isIdentifier(javaId);
  1364             if (!valid) {
  1365                 log.warning("proc.malformed.supported.string", s, p.getClass().getName());
  1366                 return noMatches; // won't match any valid identifier
  1369             String s_prime = s.replaceAll("\\.", "\\\\.");
  1371             if (s_prime.endsWith("*")) {
  1372                 s_prime =  s_prime.substring(0, s_prime.length() - 1) + ".+";
  1375             return Pattern.compile(s_prime);
  1379     /**
  1380      * For internal use by Sun Microsystems only.  This method will be
  1381      * removed without warning.
  1382      */
  1383     public Context getContext() {
  1384         return context;
  1387     public String toString() {
  1388         return "javac ProcessingEnvironment";
  1391     public static boolean isValidOptionName(String optionName) {
  1392         for(String s : optionName.split("\\.", -1)) {
  1393             if (!SourceVersion.isIdentifier(s))
  1394                 return false;
  1396         return true;

mercurial