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

Wed, 12 Mar 2008 13:06:00 -0700

author
jjg
date
Wed, 12 Mar 2008 13:06:00 -0700
changeset 12
7366066839bb
parent 1
9a66ca7c79fa
child 50
b9bcea8bbe24
permissions
-rw-r--r--

6668794: javac puts localized text in raw diagnostics
6668796: bad diagnostic "bad class file" given for source files
Summary: Replace internal use of localized text with JCDiagnostic fragments; fix diagnostic for bad source file
Reviewed-by: mcimadamore

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

mercurial