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

Thu, 24 Jul 2008 19:06:57 +0100

author
mcimadamore
date
Thu, 24 Jul 2008 19:06:57 +0100
changeset 80
5c9cdeb740f2
parent 54
eaf608c64fec
child 113
eff38cc97183
permissions
-rw-r--r--

6717241: some diagnostic argument is prematurely converted into a String object
Summary: removed early toString() conversions applied to diagnostic arguments
Reviewed-by: jjg

     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.Paths;
    59 import com.sun.tools.javac.file.JavacFileManager;
    60 import com.sun.tools.javac.jvm.*;
    61 import com.sun.tools.javac.main.JavaCompiler;
    62 import com.sun.tools.javac.model.JavacElements;
    63 import com.sun.tools.javac.model.JavacTypes;
    64 import com.sun.tools.javac.parser.*;
    65 import com.sun.tools.javac.tree.*;
    66 import com.sun.tools.javac.tree.JCTree.*;
    67 import com.sun.tools.javac.util.Abort;
    68 import com.sun.tools.javac.util.Context;
    69 import com.sun.tools.javac.util.List;
    70 import com.sun.tools.javac.util.ListBuffer;
    71 import com.sun.tools.javac.util.Log;
    72 import com.sun.tools.javac.util.Name;
    73 import com.sun.tools.javac.util.Options;
    75 import static javax.tools.StandardLocation.*;
    77 /**
    78  * Objects of this class hold and manage the state needed to support
    79  * annotation processing.
    80  *
    81  * <p><b>This is NOT part of any API supported by Sun Microsystems.
    82  * If you write code that depends on this, you do so at your own risk.
    83  * This code and its internal interfaces are subject to change or
    84  * deletion without notice.</b>
    85  */
    86 public class JavacProcessingEnvironment implements ProcessingEnvironment, Closeable {
    87     Options options;
    89     private final boolean printProcessorInfo;
    90     private final boolean printRounds;
    91     private final boolean verbose;
    92     private final boolean lint;
    93     private final boolean procOnly;
    94     private final boolean fatalErrors;
    96     private final JavacFiler filer;
    97     private final JavacMessager messager;
    98     private final JavacElements elementUtils;
    99     private final JavacTypes typeUtils;
   101     /**
   102      * Holds relevant state history of which processors have been
   103      * used.
   104      */
   105     private DiscoveredProcessors discoveredProcs;
   107     /**
   108      * Map of processor-specific options.
   109      */
   110     private final Map<String, String> processorOptions;
   112     /**
   113      */
   114     private final Set<String> unmatchedProcessorOptions;
   116     /**
   117      * Annotations implicitly processed and claimed by javac.
   118      */
   119     private final Set<String> platformAnnotations;
   121     /**
   122      * Set of packages given on command line.
   123      */
   124     private Set<PackageSymbol> specifiedPackages = Collections.emptySet();
   126     /** The log to be used for error reporting.
   127      */
   128     Log log;
   130     /**
   131      * Source level of the compile.
   132      */
   133     Source source;
   135     private Context context;
   137    public JavacProcessingEnvironment(Context context, Iterable<? extends Processor> processors) {
   138         options = Options.instance(context);
   139         this.context = context;
   140         log = Log.instance(context);
   141         source = Source.instance(context);
   142         printProcessorInfo = options.get("-XprintProcessorInfo") != null;
   143         printRounds = options.get("-XprintRounds") != null;
   144         verbose = options.get("-verbose") != null;
   145         lint = options.lint("processing");
   146         procOnly = options.get("-proc:only") != null ||
   147             options.get("-Xprint") != null;
   148         fatalErrors = options.get("fatalEnterError") != null;
   149         platformAnnotations = initPlatformAnnotations();
   151         // Initialize services before any processors are initialzied
   152         // in case processors use them.
   153         filer = new JavacFiler(context);
   154         messager = new JavacMessager(context, this);
   155         elementUtils = new JavacElements(context);
   156         typeUtils = new JavacTypes(context);
   157         processorOptions = initProcessorOptions(context);
   158         unmatchedProcessorOptions = initUnmatchedProcessorOptions();
   159         initProcessorIterator(context, processors);
   160     }
   162     private Set<String> initPlatformAnnotations() {
   163         Set<String> platformAnnotations = new HashSet<String>();
   164         platformAnnotations.add("java.lang.Deprecated");
   165         platformAnnotations.add("java.lang.Override");
   166         platformAnnotations.add("java.lang.SuppressWarnings");
   167         platformAnnotations.add("java.lang.annotation.Documented");
   168         platformAnnotations.add("java.lang.annotation.Inherited");
   169         platformAnnotations.add("java.lang.annotation.Retention");
   170         platformAnnotations.add("java.lang.annotation.Target");
   171         return Collections.unmodifiableSet(platformAnnotations);
   172     }
   174     private void initProcessorIterator(Context context, Iterable<? extends Processor> processors) {
   175         Paths paths = Paths.instance(context);
   176         Log   log   = Log.instance(context);
   177         Iterator<? extends Processor> processorIterator;
   179         if (options.get("-Xprint") != null) {
   180             try {
   181                 Processor processor = PrintingProcessor.class.newInstance();
   182                 processorIterator = List.of(processor).iterator();
   183             } catch (Throwable t) {
   184                 AssertionError assertError =
   185                     new AssertionError("Problem instantiating PrintingProcessor.");
   186                 assertError.initCause(t);
   187                 throw assertError;
   188             }
   189         } else if (processors != null) {
   190             processorIterator = processors.iterator();
   191         } else {
   192             String processorNames = options.get("-processor");
   193             JavaFileManager fileManager = context.get(JavaFileManager.class);
   194             try {
   195                 // If processorpath is not explicitly set, use the classpath.
   196                 ClassLoader processorCL = fileManager.hasLocation(ANNOTATION_PROCESSOR_PATH)
   197                     ? fileManager.getClassLoader(ANNOTATION_PROCESSOR_PATH)
   198                     : fileManager.getClassLoader(CLASS_PATH);
   200                 /*
   201                  * If the "-processor" option is used, search the appropriate
   202                  * path for the named class.  Otherwise, use a service
   203                  * provider mechanism to create the processor iterator.
   204                  */
   205                 if (processorNames != null) {
   206                     processorIterator = new NameProcessIterator(processorNames, processorCL, log);
   207                 } else {
   208                     processorIterator = new ServiceIterator(processorCL, log);
   209                 }
   210             } catch (SecurityException e) {
   211                 /*
   212                  * A security exception will occur if we can't create a classloader.
   213                  * Ignore the exception if, with hindsight, we didn't need it anyway
   214                  * (i.e. no processor was specified either explicitly, or implicitly,
   215                  * in service configuration file.) Otherwise, we cannot continue.
   216                  */
   217                 processorIterator = handleServiceLoaderUnavailability("proc.cant.create.loader", e);
   218             }
   219         }
   220         discoveredProcs = new DiscoveredProcessors(processorIterator);
   221     }
   223     /**
   224      * Returns an empty processor iterator if no processors are on the
   225      * relevant path, otherwise if processors are present, logs an
   226      * error.  Called when a service loader is unavailable for some
   227      * reason, either because a service loader class cannot be found
   228      * or because a security policy prevents class loaders from being
   229      * created.
   230      *
   231      * @param key The resource key to use to log an error message
   232      * @param e   If non-null, pass this exception to Abort
   233      */
   234     private Iterator<Processor> handleServiceLoaderUnavailability(String key, Exception e) {
   235         JavaFileManager fileManager = context.get(JavaFileManager.class);
   237         if (fileManager instanceof JavacFileManager) {
   238             StandardJavaFileManager standardFileManager = (JavacFileManager) fileManager;
   239             Iterable<? extends File> workingPath = fileManager.hasLocation(ANNOTATION_PROCESSOR_PATH)
   240                 ? standardFileManager.getLocation(ANNOTATION_PROCESSOR_PATH)
   241                 : standardFileManager.getLocation(CLASS_PATH);
   243             if (needClassLoader(options.get("-processor"), workingPath) )
   244                 handleException(key, e);
   246         } else {
   247             handleException(key, e);
   248         }
   250         java.util.List<Processor> pl = Collections.emptyList();
   251         return pl.iterator();
   252     }
   254     /**
   255      * Handle a security exception thrown during initializing the
   256      * Processor iterator.
   257      */
   258     private void handleException(String key, Exception e) {
   259         if (e != null) {
   260             log.error(key, e.getLocalizedMessage());
   261             throw new Abort(e);
   262         } else {
   263             log.error(key);
   264             throw new Abort();
   265         }
   266     }
   268     /**
   269      * Use a service loader appropriate for the platform to provide an
   270      * iterator over annotations processors.  If
   271      * java.util.ServiceLoader is present use it, otherwise, use
   272      * sun.misc.Service, otherwise fail if a loader is needed.
   273      */
   274     private class ServiceIterator implements Iterator<Processor> {
   275         // The to-be-wrapped iterator.
   276         private Iterator<?> iterator;
   277         private Log log;
   279         ServiceIterator(ClassLoader classLoader, Log log) {
   280             Class<?> loaderClass;
   281             String loadMethodName;
   282             boolean jusl;
   284             this.log = log;
   285             try {
   286                 try {
   287                     loaderClass = Class.forName("java.util.ServiceLoader");
   288                     loadMethodName = "load";
   289                     jusl = true;
   290                 } catch (ClassNotFoundException cnfe) {
   291                     try {
   292                         loaderClass = Class.forName("sun.misc.Service");
   293                         loadMethodName = "providers";
   294                         jusl = false;
   295                     } catch (ClassNotFoundException cnfe2) {
   296                         // Fail softly if a loader is not actually needed.
   297                         this.iterator = handleServiceLoaderUnavailability("proc.no.service",
   298                                                                           null);
   299                         return;
   300                     }
   301                 }
   303                 // java.util.ServiceLoader.load or sun.misc.Service.providers
   304                 Method loadMethod = loaderClass.getMethod(loadMethodName,
   305                                                           Class.class,
   306                                                           ClassLoader.class);
   308                 Object result = loadMethod.invoke(null,
   309                                                   Processor.class,
   310                                                   classLoader);
   312                 // For java.util.ServiceLoader, we have to call another
   313                 // method to get the iterator.
   314                 if (jusl) {
   315                     Method m = loaderClass.getMethod("iterator");
   316                     result = m.invoke(result); // serviceLoader.iterator();
   317                 }
   319                 // The result should now be an iterator.
   320                 this.iterator = (Iterator<?>) result;
   321             } catch (Throwable t) {
   322                 log.error("proc.service.problem");
   323                 throw new Abort(t);
   324             }
   325         }
   327         public boolean hasNext() {
   328             try {
   329                 return iterator.hasNext();
   330             } catch (Throwable t) {
   331                 if ("ServiceConfigurationError".
   332                     equals(t.getClass().getSimpleName())) {
   333                     log.error("proc.bad.config.file", t.getLocalizedMessage());
   334                 }
   335                 throw new Abort(t);
   336             }
   337         }
   339         public Processor next() {
   340             try {
   341                 return (Processor)(iterator.next());
   342             } catch (Throwable t) {
   343                 if ("ServiceConfigurationError".
   344                     equals(t.getClass().getSimpleName())) {
   345                     log.error("proc.bad.config.file", t.getLocalizedMessage());
   346                 } else {
   347                     log.error("proc.processor.constructor.error", t.getLocalizedMessage());
   348                 }
   349                 throw new Abort(t);
   350             }
   351         }
   353         public void remove() {
   354             throw new UnsupportedOperationException();
   355         }
   356     }
   359     private static class NameProcessIterator implements Iterator<Processor> {
   360         Processor nextProc = null;
   361         Iterator<String> names;
   362         ClassLoader processorCL;
   363         Log log;
   365         NameProcessIterator(String names, ClassLoader processorCL, Log log) {
   366             this.names = Arrays.asList(names.split(",")).iterator();
   367             this.processorCL = processorCL;
   368             this.log = log;
   369         }
   371         public boolean hasNext() {
   372             if (nextProc != null)
   373                 return true;
   374             else {
   375                 if (!names.hasNext())
   376                     return false;
   377                 else {
   378                     String processorName = names.next();
   380                     Processor processor;
   381                     try {
   382                         try {
   383                             processor =
   384                                 (Processor) (processorCL.loadClass(processorName).newInstance());
   385                         } catch (ClassNotFoundException cnfe) {
   386                             log.error("proc.processor.not.found", processorName);
   387                             return false;
   388                         } catch (ClassCastException cce) {
   389                             log.error("proc.processor.wrong.type", processorName);
   390                             return false;
   391                         } catch (Exception e ) {
   392                             log.error("proc.processor.cant.instantiate", processorName);
   393                             return false;
   394                         }
   395                     } catch(Throwable t) {
   396                         throw new AnnotationProcessingError(t);
   397                     }
   398                     nextProc = processor;
   399                     return true;
   400                 }
   402             }
   403         }
   405         public Processor next() {
   406             if (hasNext()) {
   407                 Processor p = nextProc;
   408                 nextProc = null;
   409                 return p;
   410             } else
   411                 throw new NoSuchElementException();
   412         }
   414         public void remove () {
   415             throw new UnsupportedOperationException();
   416         }
   417     }
   419     public boolean atLeastOneProcessor() {
   420         return discoveredProcs.iterator().hasNext();
   421     }
   423     private Map<String, String> initProcessorOptions(Context context) {
   424         Options options = Options.instance(context);
   425         Set<String> keySet = options.keySet();
   426         Map<String, String> tempOptions = new LinkedHashMap<String, String>();
   428         for(String key : keySet) {
   429             if (key.startsWith("-A") && key.length() > 2) {
   430                 int sepIndex = key.indexOf('=');
   431                 String candidateKey = null;
   432                 String candidateValue = null;
   434                 if (sepIndex == -1)
   435                     candidateKey = key.substring(2);
   436                 else if (sepIndex >= 3) {
   437                     candidateKey = key.substring(2, sepIndex);
   438                     candidateValue = (sepIndex < key.length()-1)?
   439                         key.substring(sepIndex+1) : null;
   440                 }
   441                 tempOptions.put(candidateKey, candidateValue);
   442             }
   443         }
   445         return Collections.unmodifiableMap(tempOptions);
   446     }
   448     private Set<String> initUnmatchedProcessorOptions() {
   449         Set<String> unmatchedProcessorOptions = new HashSet<String>();
   450         unmatchedProcessorOptions.addAll(processorOptions.keySet());
   451         return unmatchedProcessorOptions;
   452     }
   454     /**
   455      * State about how a processor has been used by the tool.  If a
   456      * processor has been used on a prior round, its process method is
   457      * called on all subsequent rounds, perhaps with an empty set of
   458      * annotations to process.  The {@code annotatedSupported} method
   459      * caches the supported annotation information from the first (and
   460      * only) getSupportedAnnotationTypes call to the processor.
   461      */
   462     static class ProcessorState {
   463         public Processor processor;
   464         public boolean   contributed;
   465         private ArrayList<Pattern> supportedAnnotationPatterns;
   466         private ArrayList<String>  supportedOptionNames;
   468         ProcessorState(Processor p, Log log, Source source, ProcessingEnvironment env) {
   469             processor = p;
   470             contributed = false;
   472             try {
   473                 processor.init(env);
   475                 checkSourceVersionCompatibility(source, log);
   477                 supportedAnnotationPatterns = new ArrayList<Pattern>();
   478                 for (String importString : processor.getSupportedAnnotationTypes()) {
   479                     supportedAnnotationPatterns.add(importStringToPattern(importString,
   480                                                                           processor,
   481                                                                           log));
   482                 }
   484                 supportedOptionNames = new ArrayList<String>();
   485                 for (String optionName : processor.getSupportedOptions() ) {
   486                     if (checkOptionName(optionName, log))
   487                         supportedOptionNames.add(optionName);
   488                 }
   490             } catch (Throwable t) {
   491                 throw new AnnotationProcessingError(t);
   492             }
   493         }
   495         /**
   496          * Checks whether or not a processor's source version is
   497          * compatible with the compilation source version.  The
   498          * processor's source version needs to be greater than or
   499          * equal to the source version of the compile.
   500          */
   501         private void checkSourceVersionCompatibility(Source source, Log log) {
   502             SourceVersion procSourceVersion = processor.getSupportedSourceVersion();
   504             if (procSourceVersion.compareTo(Source.toSourceVersion(source)) < 0 )  {
   505                 log.warning("proc.processor.incompatible.source.version",
   506                             procSourceVersion,
   507                             processor.getClass().getName(),
   508                             source.name);
   509             }
   510         }
   512         private boolean checkOptionName(String optionName, Log log) {
   513             boolean valid = isValidOptionName(optionName);
   514             if (!valid)
   515                 log.error("proc.processor.bad.option.name",
   516                             optionName,
   517                             processor.getClass().getName());
   518             return valid;
   519         }
   521         public boolean annotationSupported(String annotationName) {
   522             for(Pattern p: supportedAnnotationPatterns) {
   523                 if (p.matcher(annotationName).matches())
   524                     return true;
   525             }
   526             return false;
   527         }
   529         /**
   530          * Remove options that are matched by this processor.
   531          */
   532         public void removeSupportedOptions(Set<String> unmatchedProcessorOptions) {
   533             unmatchedProcessorOptions.removeAll(supportedOptionNames);
   534         }
   535     }
   537     // TODO: These two classes can probably be rewritten better...
   538     /**
   539      * This class holds information about the processors that have
   540      * been discoverd so far as well as the means to discover more, if
   541      * necessary.  A single iterator should be used per round of
   542      * annotation processing.  The iterator first visits already
   543      * discovered processors then fails over to the service provided
   544      * mechanism if additional queries are made.
   545      */
   546     class DiscoveredProcessors implements Iterable<ProcessorState> {
   548         class ProcessorStateIterator implements Iterator<ProcessorState> {
   549             DiscoveredProcessors psi;
   550             Iterator<ProcessorState> innerIter;
   551             boolean onProcInterator;
   553             ProcessorStateIterator(DiscoveredProcessors psi) {
   554                 this.psi = psi;
   555                 this.innerIter = psi.procStateList.iterator();
   556                 this.onProcInterator = false;
   557             }
   559             public ProcessorState next() {
   560                 if (!onProcInterator) {
   561                     if (innerIter.hasNext())
   562                         return innerIter.next();
   563                     else
   564                         onProcInterator = true;
   565                 }
   567                 if (psi.processorIterator.hasNext()) {
   568                     ProcessorState ps = new ProcessorState(psi.processorIterator.next(),
   569                                                            log, source, JavacProcessingEnvironment.this);
   570                     psi.procStateList.add(ps);
   571                     return ps;
   572                 } else
   573                     throw new NoSuchElementException();
   574             }
   576             public boolean hasNext() {
   577                 if (onProcInterator)
   578                     return  psi.processorIterator.hasNext();
   579                 else
   580                     return innerIter.hasNext() || psi.processorIterator.hasNext();
   581             }
   583             public void remove () {
   584                 throw new UnsupportedOperationException();
   585             }
   587             /**
   588              * Run all remaining processors on the procStateList that
   589              * have not already run this round with an empty set of
   590              * annotations.
   591              */
   592             public void runContributingProcs(RoundEnvironment re) {
   593                 if (!onProcInterator) {
   594                     Set<TypeElement> emptyTypeElements = Collections.emptySet();
   595                     while(innerIter.hasNext()) {
   596                         ProcessorState ps = innerIter.next();
   597                         if (ps.contributed)
   598                             callProcessor(ps.processor, emptyTypeElements, re);
   599                     }
   600                 }
   601             }
   602         }
   604         Iterator<? extends Processor> processorIterator;
   605         ArrayList<ProcessorState>  procStateList;
   607         public ProcessorStateIterator iterator() {
   608             return new ProcessorStateIterator(this);
   609         }
   611         DiscoveredProcessors(Iterator<? extends Processor> processorIterator) {
   612             this.processorIterator = processorIterator;
   613             this.procStateList = new ArrayList<ProcessorState>();
   614         }
   615     }
   617     private void discoverAndRunProcs(Context context,
   618                                      Set<TypeElement> annotationsPresent,
   619                                      List<ClassSymbol> topLevelClasses,
   620                                      List<PackageSymbol> packageInfoFiles) {
   621         // Writer for -XprintRounds and -XprintProcessorInfo data
   622         PrintWriter xout = context.get(Log.outKey);
   624         Map<String, TypeElement> unmatchedAnnotations =
   625             new HashMap<String, TypeElement>(annotationsPresent.size());
   627         for(TypeElement a  : annotationsPresent) {
   628                 unmatchedAnnotations.put(a.getQualifiedName().toString(),
   629                                          a);
   630         }
   632         // Give "*" processors a chance to match
   633         if (unmatchedAnnotations.size() == 0)
   634             unmatchedAnnotations.put("", null);
   636         DiscoveredProcessors.ProcessorStateIterator psi = discoveredProcs.iterator();
   637         // TODO: Create proper argument values; need past round
   638         // information to fill in this constructor.  Note that the 1
   639         // st round of processing could be the last round if there
   640         // were parse errors on the initial source files; however, we
   641         // are not doing processing in that case.
   643         Set<Element> rootElements = new LinkedHashSet<Element>();
   644         rootElements.addAll(topLevelClasses);
   645         rootElements.addAll(packageInfoFiles);
   646         rootElements = Collections.unmodifiableSet(rootElements);
   648         RoundEnvironment renv = new JavacRoundEnvironment(false,
   649                                                           false,
   650                                                           rootElements,
   651                                                           JavacProcessingEnvironment.this);
   653         while(unmatchedAnnotations.size() > 0 && psi.hasNext() ) {
   654             ProcessorState ps = psi.next();
   655             Set<String>  matchedNames = new HashSet<String>();
   656             Set<TypeElement> typeElements = new LinkedHashSet<TypeElement>();
   657             for (String unmatchedAnnotationName : unmatchedAnnotations.keySet()) {
   658                 if (ps.annotationSupported(unmatchedAnnotationName) ) {
   659                     matchedNames.add(unmatchedAnnotationName);
   660                     TypeElement te = unmatchedAnnotations.get(unmatchedAnnotationName);
   661                     if (te != null)
   662                         typeElements.add(te);
   663                 }
   664             }
   666             if (matchedNames.size() > 0 || ps.contributed) {
   667                 boolean processingResult = callProcessor(ps.processor, typeElements, renv);
   668                 ps.contributed = true;
   669                 ps.removeSupportedOptions(unmatchedProcessorOptions);
   671                 if (printProcessorInfo || verbose) {
   672                     xout.println(Log.getLocalizedString("x.print.processor.info",
   673                                                         ps.processor.getClass().getName(),
   674                                                         matchedNames.toString(),
   675                                                         processingResult));
   676                 }
   678                 if (processingResult) {
   679                     unmatchedAnnotations.keySet().removeAll(matchedNames);
   680                 }
   682             }
   683         }
   684         unmatchedAnnotations.remove("");
   686         if (lint && unmatchedAnnotations.size() > 0) {
   687             // Remove annotations processed by javac
   688             unmatchedAnnotations.keySet().removeAll(platformAnnotations);
   689             if (unmatchedAnnotations.size() > 0) {
   690                 log = Log.instance(context);
   691                 log.warning("proc.annotations.without.processors",
   692                             unmatchedAnnotations.keySet());
   693             }
   694         }
   696         // Run contributing processors that haven't run yet
   697         psi.runContributingProcs(renv);
   699         // Debugging
   700         if (options.get("displayFilerState") != null)
   701             filer.displayState();
   702     }
   704     /**
   705      * Computes the set of annotations on the symbol in question.
   706      * Leave class public for external testing purposes.
   707      */
   708     public static class ComputeAnnotationSet extends
   709         ElementScanner6<Set<TypeElement>, Set<TypeElement>> {
   710         final Elements elements;
   712         public ComputeAnnotationSet(Elements elements) {
   713             super();
   714             this.elements = elements;
   715         }
   717         @Override
   718         public Set<TypeElement> visitPackage(PackageElement e, Set<TypeElement> p) {
   719             // Don't scan enclosed elements of a package
   720             return p;
   721         }
   723         @Override
   724          public Set<TypeElement> scan(Element e, Set<TypeElement> p) {
   725             for (AnnotationMirror annotationMirror :
   726                      elements.getAllAnnotationMirrors(e) ) {
   727                 Element e2 = annotationMirror.getAnnotationType().asElement();
   728                 p.add((TypeElement) e2);
   729             }
   730             return super.scan(e, p);
   731         }
   732     }
   734     private boolean callProcessor(Processor proc,
   735                                          Set<? extends TypeElement> tes,
   736                                          RoundEnvironment renv) {
   737         try {
   738             return proc.process(tes, renv);
   739         } catch (CompletionFailure ex) {
   740             StringWriter out = new StringWriter();
   741             ex.printStackTrace(new PrintWriter(out));
   742             log.error("proc.cant.access", ex.sym, ex.getDetailValue(), out.toString());
   743             return false;
   744         } catch (Throwable t) {
   745             throw new AnnotationProcessingError(t);
   746         }
   747     }
   750     // TODO: internal catch clauses?; catch and rethrow an annotation
   751     // processing error
   752     public JavaCompiler doProcessing(Context context,
   753                                      List<JCCompilationUnit> roots,
   754                                      List<ClassSymbol> classSymbols,
   755                                      Iterable<? extends PackageSymbol> pckSymbols)
   756     throws IOException {
   758         log = Log.instance(context);
   759         // Writer for -XprintRounds and -XprintProcessorInfo data
   760         PrintWriter xout = context.get(Log.outKey);
   761         TaskListener taskListener = context.get(TaskListener.class);
   764         AnnotationCollector collector = new AnnotationCollector();
   766         JavaCompiler compiler = JavaCompiler.instance(context);
   767         compiler.todo.clear(); // free the compiler's resources
   769         int round = 0;
   771         // List<JCAnnotation> annotationsPresentInSource = collector.findAnnotations(roots);
   772         List<ClassSymbol> topLevelClasses = getTopLevelClasses(roots);
   774         for (ClassSymbol classSym : classSymbols)
   775             topLevelClasses = topLevelClasses.prepend(classSym);
   776         List<PackageSymbol> packageInfoFiles =
   777             getPackageInfoFiles(roots);
   779         Set<PackageSymbol> specifiedPackages = new LinkedHashSet<PackageSymbol>();
   780         for (PackageSymbol psym : pckSymbols)
   781             specifiedPackages.add(psym);
   782         this.specifiedPackages = Collections.unmodifiableSet(specifiedPackages);
   784         // Use annotation processing to compute the set of annotations present
   785         Set<TypeElement> annotationsPresent = new LinkedHashSet<TypeElement>();
   786         ComputeAnnotationSet annotationComputer = new ComputeAnnotationSet(elementUtils);
   787         for (ClassSymbol classSym : topLevelClasses)
   788             annotationComputer.scan(classSym, annotationsPresent);
   789         for (PackageSymbol pkgSym : packageInfoFiles)
   790             annotationComputer.scan(pkgSym, annotationsPresent);
   792         Context currentContext = context;
   794         int roundNumber = 0;
   795         boolean errorStatus = false;
   797         runAround:
   798         while(true) {
   799             if (fatalErrors && compiler.errorCount() != 0) {
   800                 errorStatus = true;
   801                 break runAround;
   802             }
   804             this.context = currentContext;
   805             roundNumber++;
   806             printRoundInfo(xout, roundNumber, topLevelClasses, annotationsPresent, false);
   808             if (taskListener != null)
   809                 taskListener.started(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING_ROUND));
   811             try {
   812                 discoverAndRunProcs(currentContext, annotationsPresent, topLevelClasses, packageInfoFiles);
   813             } finally {
   814                 if (taskListener != null)
   815                     taskListener.finished(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING_ROUND));
   816             }
   818             /*
   819              * Processors for round n have run to completion.  Prepare
   820              * for round (n+1) by checked for errors raised by
   821              * annotation processors and then checking for syntax
   822              * errors on any generated source files.
   823              */
   824             if (messager.errorRaised()) {
   825                 errorStatus = true;
   826                 break runAround;
   827             } else {
   828                 if (moreToDo()) {
   829                     // annotationsPresentInSource = List.nil();
   830                     annotationsPresent = new LinkedHashSet<TypeElement>();
   831                     topLevelClasses  = List.nil();
   832                     packageInfoFiles = List.nil();
   834                     compiler.close();
   835                     currentContext = contextForNextRound(currentContext, true);
   837                     JavaFileManager fileManager = currentContext.get(JavaFileManager.class);
   839                     List<JavaFileObject> fileObjects = List.nil();
   840                     for (JavaFileObject jfo : filer.getGeneratedSourceFileObjects() ) {
   841                         fileObjects = fileObjects.prepend(jfo);
   842                     }
   845                     compiler = JavaCompiler.instance(currentContext);
   846                     List<JCCompilationUnit> parsedFiles = compiler.parseFiles(fileObjects);
   847                     roots = cleanTrees(roots).reverse();
   850                     for (JCCompilationUnit unit : parsedFiles)
   851                         roots = roots.prepend(unit);
   852                     roots = roots.reverse();
   854                     // Check for errors after parsing
   855                     if (compiler.parseErrors()) {
   856                         errorStatus = true;
   857                         break runAround;
   858                     } else {
   859                         ListBuffer<ClassSymbol> classes = enterNewClassFiles(currentContext);
   860                         compiler.enterTrees(roots);
   862                         // annotationsPresentInSource =
   863                         // collector.findAnnotations(parsedFiles);
   864                         classes.appendList(getTopLevelClasses(parsedFiles));
   865                         topLevelClasses  = classes.toList();
   866                         packageInfoFiles = getPackageInfoFiles(parsedFiles);
   868                         annotationsPresent = new LinkedHashSet<TypeElement>();
   869                         for (ClassSymbol classSym : topLevelClasses)
   870                             annotationComputer.scan(classSym, annotationsPresent);
   871                         for (PackageSymbol pkgSym : packageInfoFiles)
   872                             annotationComputer.scan(pkgSym, annotationsPresent);
   874                         updateProcessingState(currentContext, false);
   875                     }
   876                 } else
   877                     break runAround; // No new files
   878             }
   879         }
   880         runLastRound(xout, roundNumber, errorStatus, taskListener);
   882         compiler.close();
   883         currentContext = contextForNextRound(currentContext, true);
   884         compiler = JavaCompiler.instance(currentContext);
   885         filer.newRound(currentContext, true);
   886         filer.warnIfUnclosedFiles();
   887         warnIfUnmatchedOptions();
   889        /*
   890         * If an annotation processor raises an error in a round,
   891         * that round runs to completion and one last round occurs.
   892         * The last round may also occur because no more source or
   893         * class files have been generated.  Therefore, if an error
   894         * was raised on either of the last *two* rounds, the compile
   895         * should exit with a nonzero exit code.  The current value of
   896         * errorStatus holds whether or not an error was raised on the
   897         * second to last round; errorRaised() gives the error status
   898         * of the last round.
   899         */
   900        errorStatus = errorStatus || messager.errorRaised();
   903         // Free resources
   904         this.close();
   906         if (taskListener != null)
   907             taskListener.finished(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING));
   909         if (errorStatus) {
   910             compiler.log.nerrors += messager.errorCount();
   911             if (compiler.errorCount() == 0)
   912                 compiler.log.nerrors++;
   913         } else if (procOnly) {
   914             compiler.todo.clear();
   915         } else { // Final compilation
   916             compiler.close();
   917             currentContext = contextForNextRound(currentContext, true);
   918             compiler = JavaCompiler.instance(currentContext);
   920             if (true) {
   921                 compiler.enterTrees(cleanTrees(roots));
   922             } else {
   923                 List<JavaFileObject> fileObjects = List.nil();
   924                 for (JCCompilationUnit unit : roots)
   925                     fileObjects = fileObjects.prepend(unit.getSourceFile());
   926                 roots = null;
   927                 compiler.enterTrees(compiler.parseFiles(fileObjects.reverse()));
   928             }
   929         }
   931         return compiler;
   932     }
   934     // Call the last round of annotation processing
   935     private void runLastRound(PrintWriter xout,
   936                               int roundNumber,
   937                               boolean errorStatus,
   938                               TaskListener taskListener) throws IOException {
   939         roundNumber++;
   940         List<ClassSymbol> noTopLevelClasses = List.nil();
   941         Set<TypeElement> noAnnotations =  Collections.emptySet();
   942         printRoundInfo(xout, roundNumber, noTopLevelClasses, noAnnotations, true);
   944         Set<Element> emptyRootElements = Collections.emptySet(); // immutable
   945         RoundEnvironment renv = new JavacRoundEnvironment(true,
   946                                                           errorStatus,
   947                                                           emptyRootElements,
   948                                                           JavacProcessingEnvironment.this);
   949         if (taskListener != null)
   950             taskListener.started(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING_ROUND));
   952         try {
   953             discoveredProcs.iterator().runContributingProcs(renv);
   954         } finally {
   955             if (taskListener != null)
   956                 taskListener.finished(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING_ROUND));
   957         }
   958     }
   960     private void updateProcessingState(Context currentContext, boolean lastRound) {
   961         filer.newRound(currentContext, lastRound);
   962         messager.newRound(currentContext);
   964         elementUtils.setContext(currentContext);
   965         typeUtils.setContext(currentContext);
   966     }
   968     private void warnIfUnmatchedOptions() {
   969         if (!unmatchedProcessorOptions.isEmpty()) {
   970             log.warning("proc.unmatched.processor.options", unmatchedProcessorOptions.toString());
   971         }
   972     }
   974     private void printRoundInfo(PrintWriter xout,
   975                                 int roundNumber,
   976                                 List<ClassSymbol> topLevelClasses,
   977                                 Set<TypeElement> annotationsPresent,
   978                                 boolean lastRound) {
   979         if (printRounds || verbose) {
   980             xout.println(Log.getLocalizedString("x.print.rounds",
   981                                                 roundNumber,
   982                                                 "{" + topLevelClasses.toString(", ") + "}",
   983                                                 annotationsPresent,
   984                                                 lastRound));
   985         }
   986     }
   988     private ListBuffer<ClassSymbol> enterNewClassFiles(Context currentContext) {
   989         ClassReader reader = ClassReader.instance(currentContext);
   990         Name.Table names = Name.Table.instance(currentContext);
   991         ListBuffer<ClassSymbol> list = new ListBuffer<ClassSymbol>();
   993         for (Map.Entry<String,JavaFileObject> entry : filer.getGeneratedClasses().entrySet()) {
   994             Name name = names.fromString(entry.getKey());
   995             JavaFileObject file = entry.getValue();
   996             if (file.getKind() != JavaFileObject.Kind.CLASS)
   997                 throw new AssertionError(file);
   998             ClassSymbol cs = reader.enterClass(name, file);
   999             list.append(cs);
  1001         return list;
  1004     /**
  1005      * Free resources related to annotation processing.
  1006      */
  1007     public void close() {
  1008         filer.close();
  1009         discoveredProcs = null;
  1012     private List<ClassSymbol> getTopLevelClasses(List<? extends JCCompilationUnit> units) {
  1013         List<ClassSymbol> classes = List.nil();
  1014         for (JCCompilationUnit unit : units) {
  1015             for (JCTree node : unit.defs) {
  1016                 if (node.getTag() == JCTree.CLASSDEF) {
  1017                     classes = classes.prepend(((JCClassDecl) node).sym);
  1021         return classes.reverse();
  1024     private List<PackageSymbol> getPackageInfoFiles(List<? extends JCCompilationUnit> units) {
  1025         List<PackageSymbol> packages = List.nil();
  1026         for (JCCompilationUnit unit : units) {
  1027             boolean isPkgInfo = unit.sourcefile.isNameCompatible("package-info",
  1028                                                                  JavaFileObject.Kind.SOURCE);
  1029             if (isPkgInfo) {
  1030                 packages = packages.prepend(unit.packge);
  1033         return packages.reverse();
  1036     private Context contextForNextRound(Context context, boolean shareNames)
  1037         throws IOException
  1039         Context next = new Context();
  1041         Options options = Options.instance(context);
  1042         assert options != null;
  1043         next.put(Options.optionsKey, options);
  1045         PrintWriter out = context.get(Log.outKey);
  1046         assert out != null;
  1047         next.put(Log.outKey, out);
  1049         if (shareNames) {
  1050             Name.Table names = Name.Table.instance(context);
  1051             assert names != null;
  1052             next.put(Name.Table.namesKey, names);
  1055         DiagnosticListener dl = context.get(DiagnosticListener.class);
  1056         if (dl != null)
  1057             next.put(DiagnosticListener.class, dl);
  1059         TaskListener tl = context.get(TaskListener.class);
  1060         if (tl != null)
  1061             next.put(TaskListener.class, tl);
  1063         JavaFileManager jfm = context.get(JavaFileManager.class);
  1064         assert jfm != null;
  1065         next.put(JavaFileManager.class, jfm);
  1066         if (jfm instanceof JavacFileManager) {
  1067             ((JavacFileManager)jfm).setContext(next);
  1070         Name.Table names = Name.Table.instance(context);
  1071         assert names != null;
  1072         next.put(Name.Table.namesKey, names);
  1074         Keywords keywords = Keywords.instance(context);
  1075         assert(keywords != null);
  1076         next.put(Keywords.keywordsKey, keywords);
  1078         JavaCompiler oldCompiler = JavaCompiler.instance(context);
  1079         JavaCompiler nextCompiler = JavaCompiler.instance(next);
  1080         nextCompiler.initRound(oldCompiler);
  1082         JavacTaskImpl task = context.get(JavacTaskImpl.class);
  1083         if (task != null) {
  1084             next.put(JavacTaskImpl.class, task);
  1085             task.updateContext(next);
  1088         context.clear();
  1089         return next;
  1092     /*
  1093      * Called retroactively to determine if a class loader was required,
  1094      * after we have failed to create one.
  1095      */
  1096     private boolean needClassLoader(String procNames, Iterable<? extends File> workingpath) {
  1097         if (procNames != null)
  1098             return true;
  1100         String procPath;
  1101         URL[] urls = new URL[1];
  1102         for(File pathElement : workingpath) {
  1103             try {
  1104                 urls[0] = pathElement.toURI().toURL();
  1105                 if (ServiceProxy.hasService(Processor.class, urls))
  1106                     return true;
  1107             } catch (MalformedURLException ex) {
  1108                 throw new AssertionError(ex);
  1110             catch (ServiceProxy.ServiceConfigurationError e) {
  1111                 log.error("proc.bad.config.file", e.getLocalizedMessage());
  1112                 return true;
  1116         return false;
  1119     private class AnnotationCollector extends TreeScanner {
  1120         List<JCTree> path = List.nil();
  1121         static final boolean verbose = false;
  1122         List<JCAnnotation> annotations = List.nil();
  1124         public List<JCAnnotation> findAnnotations(List<? extends JCTree> nodes) {
  1125             annotations = List.nil();
  1126             scan(nodes);
  1127             List<JCAnnotation> found = annotations;
  1128             annotations = List.nil();
  1129             return found.reverse();
  1132         public void scan(JCTree node) {
  1133             if (node == null)
  1134                 return;
  1135             Symbol sym = TreeInfo.symbolFor(node);
  1136             if (sym != null)
  1137                 path = path.prepend(node);
  1138             super.scan(node);
  1139             if (sym != null)
  1140                 path = path.tail;
  1143         public void visitAnnotation(JCAnnotation node) {
  1144             annotations = annotations.prepend(node);
  1145             if (verbose) {
  1146                 StringBuilder sb = new StringBuilder();
  1147                 for (JCTree tree : path.reverse()) {
  1148                     System.err.print(sb);
  1149                     System.err.println(TreeInfo.symbolFor(tree));
  1150                     sb.append("  ");
  1152                 System.err.print(sb);
  1153                 System.err.println(node);
  1158     private static <T extends JCTree> List<T> cleanTrees(List<T> nodes) {
  1159         for (T node : nodes)
  1160             treeCleaner.scan(node);
  1161         return nodes;
  1164     private static TreeScanner treeCleaner = new TreeScanner() {
  1165             public void scan(JCTree node) {
  1166                 super.scan(node);
  1167                 if (node != null)
  1168                     node.type = null;
  1170             public void visitTopLevel(JCCompilationUnit node) {
  1171                 node.packge = null;
  1172                 super.visitTopLevel(node);
  1174             public void visitClassDef(JCClassDecl node) {
  1175                 node.sym = null;
  1176                 super.visitClassDef(node);
  1178             public void visitMethodDef(JCMethodDecl node) {
  1179                 node.sym = null;
  1180                 super.visitMethodDef(node);
  1182             public void visitVarDef(JCVariableDecl node) {
  1183                 node.sym = null;
  1184                 super.visitVarDef(node);
  1186             public void visitNewClass(JCNewClass node) {
  1187                 node.constructor = null;
  1188                 super.visitNewClass(node);
  1190             public void visitAssignop(JCAssignOp node) {
  1191                 node.operator = null;
  1192                 super.visitAssignop(node);
  1194             public void visitUnary(JCUnary node) {
  1195                 node.operator = null;
  1196                 super.visitUnary(node);
  1198             public void visitBinary(JCBinary node) {
  1199                 node.operator = null;
  1200                 super.visitBinary(node);
  1202             public void visitSelect(JCFieldAccess node) {
  1203                 node.sym = null;
  1204                 super.visitSelect(node);
  1206             public void visitIdent(JCIdent node) {
  1207                 node.sym = null;
  1208                 super.visitIdent(node);
  1210         };
  1213     private boolean moreToDo() {
  1214         return filer.newFiles();
  1217     /**
  1218      * {@inheritdoc}
  1220      * Command line options suitable for presenting to annotation
  1221      * processors.  "-Afoo=bar" should be "-Afoo" => "bar".
  1222      */
  1223     public Map<String,String> getOptions() {
  1224         return processorOptions;
  1227     public Messager getMessager() {
  1228         return messager;
  1231     public Filer getFiler() {
  1232         return filer;
  1235     public JavacElements getElementUtils() {
  1236         return elementUtils;
  1239     public JavacTypes getTypeUtils() {
  1240         return typeUtils;
  1243     public SourceVersion getSourceVersion() {
  1244         return Source.toSourceVersion(source);
  1247     public Locale getLocale() {
  1248         return Locale.getDefault();
  1251     public Set<Symbol.PackageSymbol> getSpecifiedPackages() {
  1252         return specifiedPackages;
  1255     // Borrowed from DocletInvoker and apt
  1256     // TODO: remove from apt's Main
  1257     /**
  1258      * Utility method for converting a search path string to an array
  1259      * of directory and JAR file URLs.
  1261      * @param path the search path string
  1262      * @return the resulting array of directory and JAR file URLs
  1263      */
  1264     public static URL[] pathToURLs(String path) {
  1265         StringTokenizer st = new StringTokenizer(path, File.pathSeparator);
  1266         URL[] urls = new URL[st.countTokens()];
  1267         int count = 0;
  1268         while (st.hasMoreTokens()) {
  1269             URL url = fileToURL(new File(st.nextToken()));
  1270             if (url != null) {
  1271                 urls[count++] = url;
  1274         if (urls.length != count) {
  1275             URL[] tmp = new URL[count];
  1276             System.arraycopy(urls, 0, tmp, 0, count);
  1277             urls = tmp;
  1279         return urls;
  1282     /**
  1283      * Returns the directory or JAR file URL corresponding to the specified
  1284      * local file name.
  1286      * @param file the File object
  1287      * @return the resulting directory or JAR file URL, or null if unknown
  1288      */
  1289     private static URL fileToURL(File file) {
  1290         String name;
  1291         try {
  1292             name = file.getCanonicalPath();
  1293         } catch (IOException e) {
  1294             name = file.getAbsolutePath();
  1296         name = name.replace(File.separatorChar, '/');
  1297         if (!name.startsWith("/")) {
  1298             name = "/" + name;
  1300         // If the file does not exist, then assume that it's a directory
  1301         if (!file.isFile()) {
  1302             name = name + "/";
  1304         try {
  1305             return new URL("file", "", name);
  1306         } catch (MalformedURLException e) {
  1307             throw new IllegalArgumentException("file");
  1313     private static final Pattern allMatches = Pattern.compile(".*");
  1315     private static final Pattern noMatches  = Pattern.compile("(\\P{all})+");
  1316     /**
  1317      * Convert import-style string to regex matching that string.  If
  1318      * the string is a valid import-style string, return a regex that
  1319      * won't match anything.
  1320      */
  1321     // TODO: remove version in Apt.java
  1322     public static Pattern importStringToPattern(String s, Processor p, Log log) {
  1323         if (s.equals("*")) {
  1324             return allMatches;
  1325         } else {
  1326             String t = s;
  1327             boolean star = false;
  1329             /*
  1330              * Validate string from factory is legal.  If the string
  1331              * has more than one asterisks or the asterisks does not
  1332              * appear as the last character (preceded by a period),
  1333              * the string is not legal.
  1334              */
  1336             boolean valid = true;
  1337             int index = t.indexOf('*');
  1338             if (index != -1) {
  1339                 // '*' must be last character...
  1340                 if (index == t.length() -1) {
  1341                      // ... and preceeding character must be '.'
  1342                     if ( index-1 >= 0 ) {
  1343                         valid = t.charAt(index-1) == '.';
  1344                         // Strip off ".*$" for identifier checks
  1345                         t = t.substring(0, t.length()-2);
  1347                 } else
  1348                     valid = false;
  1351             // Verify string is off the form (javaId \.)+ or javaId
  1352             if (valid) {
  1353                 String[] javaIds = t.split("\\.", t.length()+2);
  1354                 for(String javaId: javaIds)
  1355                     valid &= SourceVersion.isIdentifier(javaId);
  1358             if (!valid) {
  1359                 log.warning("proc.malformed.supported.string", s, p.getClass().getName());
  1360                 return noMatches; // won't match any valid identifier
  1363             String s_prime = s.replaceAll("\\.", "\\\\.");
  1365             if (s_prime.endsWith("*")) {
  1366                 s_prime =  s_prime.substring(0, s_prime.length() - 1) + ".+";
  1369             return Pattern.compile(s_prime);
  1373     /**
  1374      * For internal use by Sun Microsystems only.  This method will be
  1375      * removed without warning.
  1376      */
  1377     public Context getContext() {
  1378         return context;
  1381     public String toString() {
  1382         return "javac ProcessingEnvironment";
  1385     public static boolean isValidOptionName(String optionName) {
  1386         for(String s : optionName.split("\\.", -1)) {
  1387             if (!SourceVersion.isIdentifier(s))
  1388                 return false;
  1390         return true;

mercurial