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

Tue, 24 Aug 2010 15:09:21 -0700

author
jjg
date
Tue, 24 Aug 2010 15:09:21 -0700
changeset 655
f3323b1c65ee
parent 642
6b95dd682538
child 663
eb7c263aab73
permissions
-rw-r--r--

6929404: Filer.getResource(SOURCE_PATH, ...) does not work when -sourcepath contains >1 entry
Reviewed-by: darcy

     1 /*
     2  * Copyright (c) 2005, 2009, Oracle and/or its affiliates. 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.  Oracle designates this
     8  * particular file as subject to the "Classpath" exception as provided
     9  * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    22  * or visit www.oracle.com if you need additional information or have any
    23  * questions.
    24  */
    26 package com.sun.tools.javac.processing;
    28 import java.lang.reflect.*;
    29 import java.util.*;
    30 import java.util.regex.*;
    32 import java.net.URL;
    33 import java.io.Closeable;
    34 import java.io.File;
    35 import java.io.PrintWriter;
    36 import java.io.IOException;
    37 import java.net.MalformedURLException;
    38 import java.io.StringWriter;
    40 import javax.annotation.processing.*;
    41 import javax.lang.model.SourceVersion;
    42 import javax.lang.model.element.AnnotationMirror;
    43 import javax.lang.model.element.Element;
    44 import javax.lang.model.element.TypeElement;
    45 import javax.lang.model.element.PackageElement;
    46 import javax.lang.model.util.*;
    47 import javax.tools.JavaFileManager;
    48 import javax.tools.StandardJavaFileManager;
    49 import javax.tools.JavaFileObject;
    50 import javax.tools.DiagnosticListener;
    52 import com.sun.source.util.AbstractTypeProcessor;
    53 import com.sun.source.util.TaskEvent;
    54 import com.sun.source.util.TaskListener;
    55 import com.sun.tools.javac.api.JavacTaskImpl;
    56 import com.sun.tools.javac.code.*;
    57 import com.sun.tools.javac.code.Symbol.*;
    58 import com.sun.tools.javac.file.JavacFileManager;
    59 import com.sun.tools.javac.jvm.*;
    60 import com.sun.tools.javac.main.JavaCompiler;
    61 import com.sun.tools.javac.main.JavaCompiler.CompileState;
    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.Convert;
    70 import com.sun.tools.javac.util.List;
    71 import com.sun.tools.javac.util.Log;
    72 import com.sun.tools.javac.util.JavacMessages;
    73 import com.sun.tools.javac.util.Name;
    74 import com.sun.tools.javac.util.Names;
    75 import com.sun.tools.javac.util.Options;
    77 import static javax.tools.StandardLocation.*;
    79 /**
    80  * Objects of this class hold and manage the state needed to support
    81  * annotation processing.
    82  *
    83  * <p><b>This is NOT part of any supported API.
    84  * If you write code that depends on this, you do so at your own risk.
    85  * This code and its internal interfaces are subject to change or
    86  * deletion without notice.</b>
    87  */
    88 public class JavacProcessingEnvironment implements ProcessingEnvironment, Closeable {
    89     Options options;
    91     private final boolean printProcessorInfo;
    92     private final boolean printRounds;
    93     private final boolean verbose;
    94     private final boolean lint;
    95     private final boolean procOnly;
    96     private final boolean fatalErrors;
    97     private final boolean werror;
    98     private boolean foundTypeProcessors;
   100     private final JavacFiler filer;
   101     private final JavacMessager messager;
   102     private final JavacElements elementUtils;
   103     private final JavacTypes typeUtils;
   105     /**
   106      * Holds relevant state history of which processors have been
   107      * used.
   108      */
   109     private DiscoveredProcessors discoveredProcs;
   111     /**
   112      * Map of processor-specific options.
   113      */
   114     private final Map<String, String> processorOptions;
   116     /**
   117      */
   118     private final Set<String> unmatchedProcessorOptions;
   120     /**
   121      * Annotations implicitly processed and claimed by javac.
   122      */
   123     private final Set<String> platformAnnotations;
   125     /**
   126      * Set of packages given on command line.
   127      */
   128     private Set<PackageSymbol> specifiedPackages = Collections.emptySet();
   130     /** The log to be used for error reporting.
   131      */
   132     Log log;
   134     /**
   135      * Source level of the compile.
   136      */
   137     Source source;
   139     private ClassLoader processorClassLoader;
   141     /**
   142      * JavacMessages object used for localization
   143      */
   144     private JavacMessages messages;
   146     private Context context;
   148     public JavacProcessingEnvironment(Context context, Iterable<? extends Processor> processors) {
   149         options = Options.instance(context);
   150         this.context = context;
   151         log = Log.instance(context);
   152         source = Source.instance(context);
   153         printProcessorInfo = options.get("-XprintProcessorInfo") != null;
   154         printRounds = options.get("-XprintRounds") != null;
   155         verbose = options.get("-verbose") != null;
   156         lint = options.lint("processing");
   157         procOnly = options.get("-proc:only") != null ||
   158             options.get("-Xprint") != null;
   159         fatalErrors = options.get("fatalEnterError") != null;
   160         werror = options.get("-Werror") != null;
   161         platformAnnotations = initPlatformAnnotations();
   162         foundTypeProcessors = false;
   164         // Initialize services before any processors are initialzied
   165         // in case processors use them.
   166         filer = new JavacFiler(context);
   167         messager = new JavacMessager(context, this);
   168         elementUtils = new JavacElements(context);
   169         typeUtils = new JavacTypes(context);
   170         processorOptions = initProcessorOptions(context);
   171         unmatchedProcessorOptions = initUnmatchedProcessorOptions();
   172         messages = JavacMessages.instance(context);
   173         initProcessorIterator(context, processors);
   174     }
   176     private Set<String> initPlatformAnnotations() {
   177         Set<String> platformAnnotations = new HashSet<String>();
   178         platformAnnotations.add("java.lang.Deprecated");
   179         platformAnnotations.add("java.lang.Override");
   180         platformAnnotations.add("java.lang.SuppressWarnings");
   181         platformAnnotations.add("java.lang.annotation.Documented");
   182         platformAnnotations.add("java.lang.annotation.Inherited");
   183         platformAnnotations.add("java.lang.annotation.Retention");
   184         platformAnnotations.add("java.lang.annotation.Target");
   185         return Collections.unmodifiableSet(platformAnnotations);
   186     }
   188     private void initProcessorIterator(Context context, Iterable<? extends Processor> processors) {
   189         Log   log   = Log.instance(context);
   190         Iterator<? extends Processor> processorIterator;
   192         if (options.get("-Xprint") != null) {
   193             try {
   194                 Processor processor = PrintingProcessor.class.newInstance();
   195                 processorIterator = List.of(processor).iterator();
   196             } catch (Throwable t) {
   197                 AssertionError assertError =
   198                     new AssertionError("Problem instantiating PrintingProcessor.");
   199                 assertError.initCause(t);
   200                 throw assertError;
   201             }
   202         } else if (processors != null) {
   203             processorIterator = processors.iterator();
   204         } else {
   205             String processorNames = options.get("-processor");
   206             JavaFileManager fileManager = context.get(JavaFileManager.class);
   207             try {
   208                 // If processorpath is not explicitly set, use the classpath.
   209                 processorClassLoader = fileManager.hasLocation(ANNOTATION_PROCESSOR_PATH)
   210                     ? fileManager.getClassLoader(ANNOTATION_PROCESSOR_PATH)
   211                     : fileManager.getClassLoader(CLASS_PATH);
   213                 /*
   214                  * If the "-processor" option is used, search the appropriate
   215                  * path for the named class.  Otherwise, use a service
   216                  * provider mechanism to create the processor iterator.
   217                  */
   218                 if (processorNames != null) {
   219                     processorIterator = new NameProcessIterator(processorNames, processorClassLoader, log);
   220                 } else {
   221                     processorIterator = new ServiceIterator(processorClassLoader, log);
   222                 }
   223             } catch (SecurityException e) {
   224                 /*
   225                  * A security exception will occur if we can't create a classloader.
   226                  * Ignore the exception if, with hindsight, we didn't need it anyway
   227                  * (i.e. no processor was specified either explicitly, or implicitly,
   228                  * in service configuration file.) Otherwise, we cannot continue.
   229                  */
   230                 processorIterator = handleServiceLoaderUnavailability("proc.cant.create.loader", e);
   231             }
   232         }
   233         discoveredProcs = new DiscoveredProcessors(processorIterator);
   234     }
   236     /**
   237      * Returns an empty processor iterator if no processors are on the
   238      * relevant path, otherwise if processors are present, logs an
   239      * error.  Called when a service loader is unavailable for some
   240      * reason, either because a service loader class cannot be found
   241      * or because a security policy prevents class loaders from being
   242      * created.
   243      *
   244      * @param key The resource key to use to log an error message
   245      * @param e   If non-null, pass this exception to Abort
   246      */
   247     private Iterator<Processor> handleServiceLoaderUnavailability(String key, Exception e) {
   248         JavaFileManager fileManager = context.get(JavaFileManager.class);
   250         if (fileManager instanceof JavacFileManager) {
   251             StandardJavaFileManager standardFileManager = (JavacFileManager) fileManager;
   252             Iterable<? extends File> workingPath = fileManager.hasLocation(ANNOTATION_PROCESSOR_PATH)
   253                 ? standardFileManager.getLocation(ANNOTATION_PROCESSOR_PATH)
   254                 : standardFileManager.getLocation(CLASS_PATH);
   256             if (needClassLoader(options.get("-processor"), workingPath) )
   257                 handleException(key, e);
   259         } else {
   260             handleException(key, e);
   261         }
   263         java.util.List<Processor> pl = Collections.emptyList();
   264         return pl.iterator();
   265     }
   267     /**
   268      * Handle a security exception thrown during initializing the
   269      * Processor iterator.
   270      */
   271     private void handleException(String key, Exception e) {
   272         if (e != null) {
   273             log.error(key, e.getLocalizedMessage());
   274             throw new Abort(e);
   275         } else {
   276             log.error(key);
   277             throw new Abort();
   278         }
   279     }
   281     /**
   282      * Use a service loader appropriate for the platform to provide an
   283      * iterator over annotations processors.  If
   284      * java.util.ServiceLoader is present use it, otherwise, use
   285      * sun.misc.Service, otherwise fail if a loader is needed.
   286      */
   287     private class ServiceIterator implements Iterator<Processor> {
   288         // The to-be-wrapped iterator.
   289         private Iterator<?> iterator;
   290         private Log log;
   291         private Class<?> loaderClass;
   292         private boolean jusl;
   293         private Object loader;
   295         ServiceIterator(ClassLoader classLoader, Log log) {
   296             String loadMethodName;
   298             this.log = log;
   299             try {
   300                 try {
   301                     loaderClass = Class.forName("java.util.ServiceLoader");
   302                     loadMethodName = "load";
   303                     jusl = true;
   304                 } catch (ClassNotFoundException cnfe) {
   305                     try {
   306                         loaderClass = Class.forName("sun.misc.Service");
   307                         loadMethodName = "providers";
   308                         jusl = false;
   309                     } catch (ClassNotFoundException cnfe2) {
   310                         // Fail softly if a loader is not actually needed.
   311                         this.iterator = handleServiceLoaderUnavailability("proc.no.service",
   312                                                                           null);
   313                         return;
   314                     }
   315                 }
   317                 // java.util.ServiceLoader.load or sun.misc.Service.providers
   318                 Method loadMethod = loaderClass.getMethod(loadMethodName,
   319                                                           Class.class,
   320                                                           ClassLoader.class);
   322                 Object result = loadMethod.invoke(null,
   323                                                   Processor.class,
   324                                                   classLoader);
   326                 // For java.util.ServiceLoader, we have to call another
   327                 // method to get the iterator.
   328                 if (jusl) {
   329                     loader = result; // Store ServiceLoader to call reload later
   330                     Method m = loaderClass.getMethod("iterator");
   331                     result = m.invoke(result); // serviceLoader.iterator();
   332                 }
   334                 // The result should now be an iterator.
   335                 this.iterator = (Iterator<?>) result;
   336             } catch (Throwable t) {
   337                 log.error("proc.service.problem");
   338                 throw new Abort(t);
   339             }
   340         }
   342         public boolean hasNext() {
   343             try {
   344                 return iterator.hasNext();
   345             } catch (Throwable t) {
   346                 if ("ServiceConfigurationError".
   347                     equals(t.getClass().getSimpleName())) {
   348                     log.error("proc.bad.config.file", t.getLocalizedMessage());
   349                 }
   350                 throw new Abort(t);
   351             }
   352         }
   354         public Processor next() {
   355             try {
   356                 return (Processor)(iterator.next());
   357             } catch (Throwable t) {
   358                 if ("ServiceConfigurationError".
   359                     equals(t.getClass().getSimpleName())) {
   360                     log.error("proc.bad.config.file", t.getLocalizedMessage());
   361                 } else {
   362                     log.error("proc.processor.constructor.error", t.getLocalizedMessage());
   363                 }
   364                 throw new Abort(t);
   365             }
   366         }
   368         public void remove() {
   369             throw new UnsupportedOperationException();
   370         }
   372         public void close() {
   373             if (jusl) {
   374                 try {
   375                     // Call java.util.ServiceLoader.reload
   376                     Method reloadMethod = loaderClass.getMethod("reload");
   377                     reloadMethod.invoke(loader);
   378                 } catch(Exception e) {
   379                     ; // Ignore problems during a call to reload.
   380                 }
   381             }
   382         }
   383     }
   386     private static class NameProcessIterator implements Iterator<Processor> {
   387         Processor nextProc = null;
   388         Iterator<String> names;
   389         ClassLoader processorCL;
   390         Log log;
   392         NameProcessIterator(String names, ClassLoader processorCL, Log log) {
   393             this.names = Arrays.asList(names.split(",")).iterator();
   394             this.processorCL = processorCL;
   395             this.log = log;
   396         }
   398         public boolean hasNext() {
   399             if (nextProc != null)
   400                 return true;
   401             else {
   402                 if (!names.hasNext())
   403                     return false;
   404                 else {
   405                     String processorName = names.next();
   407                     Processor processor;
   408                     try {
   409                         try {
   410                             processor =
   411                                 (Processor) (processorCL.loadClass(processorName).newInstance());
   412                         } catch (ClassNotFoundException cnfe) {
   413                             log.error("proc.processor.not.found", processorName);
   414                             return false;
   415                         } catch (ClassCastException cce) {
   416                             log.error("proc.processor.wrong.type", processorName);
   417                             return false;
   418                         } catch (Exception e ) {
   419                             log.error("proc.processor.cant.instantiate", processorName);
   420                             return false;
   421                         }
   422                     } catch(Throwable t) {
   423                         throw new AnnotationProcessingError(t);
   424                     }
   425                     nextProc = processor;
   426                     return true;
   427                 }
   429             }
   430         }
   432         public Processor next() {
   433             if (hasNext()) {
   434                 Processor p = nextProc;
   435                 nextProc = null;
   436                 return p;
   437             } else
   438                 throw new NoSuchElementException();
   439         }
   441         public void remove () {
   442             throw new UnsupportedOperationException();
   443         }
   444     }
   446     public boolean atLeastOneProcessor() {
   447         return discoveredProcs.iterator().hasNext();
   448     }
   450     private Map<String, String> initProcessorOptions(Context context) {
   451         Options options = Options.instance(context);
   452         Set<String> keySet = options.keySet();
   453         Map<String, String> tempOptions = new LinkedHashMap<String, String>();
   455         for(String key : keySet) {
   456             if (key.startsWith("-A") && key.length() > 2) {
   457                 int sepIndex = key.indexOf('=');
   458                 String candidateKey = null;
   459                 String candidateValue = null;
   461                 if (sepIndex == -1)
   462                     candidateKey = key.substring(2);
   463                 else if (sepIndex >= 3) {
   464                     candidateKey = key.substring(2, sepIndex);
   465                     candidateValue = (sepIndex < key.length()-1)?
   466                         key.substring(sepIndex+1) : null;
   467                 }
   468                 tempOptions.put(candidateKey, candidateValue);
   469             }
   470         }
   472         return Collections.unmodifiableMap(tempOptions);
   473     }
   475     private Set<String> initUnmatchedProcessorOptions() {
   476         Set<String> unmatchedProcessorOptions = new HashSet<String>();
   477         unmatchedProcessorOptions.addAll(processorOptions.keySet());
   478         return unmatchedProcessorOptions;
   479     }
   481     /**
   482      * State about how a processor has been used by the tool.  If a
   483      * processor has been used on a prior round, its process method is
   484      * called on all subsequent rounds, perhaps with an empty set of
   485      * annotations to process.  The {@code annotatedSupported} method
   486      * caches the supported annotation information from the first (and
   487      * only) getSupportedAnnotationTypes call to the processor.
   488      */
   489     static class ProcessorState {
   490         public Processor processor;
   491         public boolean   contributed;
   492         private ArrayList<Pattern> supportedAnnotationPatterns;
   493         private ArrayList<String>  supportedOptionNames;
   495         ProcessorState(Processor p, Log log, Source source, ProcessingEnvironment env) {
   496             processor = p;
   497             contributed = false;
   499             try {
   500                 processor.init(env);
   502                 checkSourceVersionCompatibility(source, log);
   504                 supportedAnnotationPatterns = new ArrayList<Pattern>();
   505                 for (String importString : processor.getSupportedAnnotationTypes()) {
   506                     supportedAnnotationPatterns.add(importStringToPattern(importString,
   507                                                                           processor,
   508                                                                           log));
   509                 }
   511                 supportedOptionNames = new ArrayList<String>();
   512                 for (String optionName : processor.getSupportedOptions() ) {
   513                     if (checkOptionName(optionName, log))
   514                         supportedOptionNames.add(optionName);
   515                 }
   517             } catch (Throwable t) {
   518                 throw new AnnotationProcessingError(t);
   519             }
   520         }
   522         /**
   523          * Checks whether or not a processor's source version is
   524          * compatible with the compilation source version.  The
   525          * processor's source version needs to be greater than or
   526          * equal to the source version of the compile.
   527          */
   528         private void checkSourceVersionCompatibility(Source source, Log log) {
   529             SourceVersion procSourceVersion = processor.getSupportedSourceVersion();
   531             if (procSourceVersion.compareTo(Source.toSourceVersion(source)) < 0 )  {
   532                 log.warning("proc.processor.incompatible.source.version",
   533                             procSourceVersion,
   534                             processor.getClass().getName(),
   535                             source.name);
   536             }
   537         }
   539         private boolean checkOptionName(String optionName, Log log) {
   540             boolean valid = isValidOptionName(optionName);
   541             if (!valid)
   542                 log.error("proc.processor.bad.option.name",
   543                             optionName,
   544                             processor.getClass().getName());
   545             return valid;
   546         }
   548         public boolean annotationSupported(String annotationName) {
   549             for(Pattern p: supportedAnnotationPatterns) {
   550                 if (p.matcher(annotationName).matches())
   551                     return true;
   552             }
   553             return false;
   554         }
   556         /**
   557          * Remove options that are matched by this processor.
   558          */
   559         public void removeSupportedOptions(Set<String> unmatchedProcessorOptions) {
   560             unmatchedProcessorOptions.removeAll(supportedOptionNames);
   561         }
   562     }
   564     // TODO: These two classes can probably be rewritten better...
   565     /**
   566      * This class holds information about the processors that have
   567      * been discoverd so far as well as the means to discover more, if
   568      * necessary.  A single iterator should be used per round of
   569      * annotation processing.  The iterator first visits already
   570      * discovered processors then fails over to the service provider
   571      * mechanism if additional queries are made.
   572      */
   573     class DiscoveredProcessors implements Iterable<ProcessorState> {
   575         class ProcessorStateIterator implements Iterator<ProcessorState> {
   576             DiscoveredProcessors psi;
   577             Iterator<ProcessorState> innerIter;
   578             boolean onProcInterator;
   580             ProcessorStateIterator(DiscoveredProcessors psi) {
   581                 this.psi = psi;
   582                 this.innerIter = psi.procStateList.iterator();
   583                 this.onProcInterator = false;
   584             }
   586             public ProcessorState next() {
   587                 if (!onProcInterator) {
   588                     if (innerIter.hasNext())
   589                         return innerIter.next();
   590                     else
   591                         onProcInterator = true;
   592                 }
   594                 if (psi.processorIterator.hasNext()) {
   595                     ProcessorState ps = new ProcessorState(psi.processorIterator.next(),
   596                                                            log, source, JavacProcessingEnvironment.this);
   597                     psi.procStateList.add(ps);
   598                     return ps;
   599                 } else
   600                     throw new NoSuchElementException();
   601             }
   603             public boolean hasNext() {
   604                 if (onProcInterator)
   605                     return  psi.processorIterator.hasNext();
   606                 else
   607                     return innerIter.hasNext() || psi.processorIterator.hasNext();
   608             }
   610             public void remove () {
   611                 throw new UnsupportedOperationException();
   612             }
   614             /**
   615              * Run all remaining processors on the procStateList that
   616              * have not already run this round with an empty set of
   617              * annotations.
   618              */
   619             public void runContributingProcs(RoundEnvironment re) {
   620                 if (!onProcInterator) {
   621                     Set<TypeElement> emptyTypeElements = Collections.emptySet();
   622                     while(innerIter.hasNext()) {
   623                         ProcessorState ps = innerIter.next();
   624                         if (ps.contributed)
   625                             callProcessor(ps.processor, emptyTypeElements, re);
   626                     }
   627                 }
   628             }
   629         }
   631         Iterator<? extends Processor> processorIterator;
   632         ArrayList<ProcessorState>  procStateList;
   634         public ProcessorStateIterator iterator() {
   635             return new ProcessorStateIterator(this);
   636         }
   638         DiscoveredProcessors(Iterator<? extends Processor> processorIterator) {
   639             this.processorIterator = processorIterator;
   640             this.procStateList = new ArrayList<ProcessorState>();
   641         }
   643         /**
   644          * Free jar files, etc. if using a service loader.
   645          */
   646         public void close() {
   647             if (processorIterator != null &&
   648                 processorIterator instanceof ServiceIterator) {
   649                 ((ServiceIterator) processorIterator).close();
   650             }
   651         }
   652     }
   654     private void discoverAndRunProcs(Context context,
   655                                      Set<TypeElement> annotationsPresent,
   656                                      List<ClassSymbol> topLevelClasses,
   657                                      List<PackageSymbol> packageInfoFiles) {
   658         Map<String, TypeElement> unmatchedAnnotations =
   659             new HashMap<String, TypeElement>(annotationsPresent.size());
   661         for(TypeElement a  : annotationsPresent) {
   662                 unmatchedAnnotations.put(a.getQualifiedName().toString(),
   663                                          a);
   664         }
   666         // Give "*" processors a chance to match
   667         if (unmatchedAnnotations.size() == 0)
   668             unmatchedAnnotations.put("", null);
   670         DiscoveredProcessors.ProcessorStateIterator psi = discoveredProcs.iterator();
   671         // TODO: Create proper argument values; need past round
   672         // information to fill in this constructor.  Note that the 1
   673         // st round of processing could be the last round if there
   674         // were parse errors on the initial source files; however, we
   675         // are not doing processing in that case.
   677         Set<Element> rootElements = new LinkedHashSet<Element>();
   678         rootElements.addAll(topLevelClasses);
   679         rootElements.addAll(packageInfoFiles);
   680         rootElements = Collections.unmodifiableSet(rootElements);
   682         RoundEnvironment renv = new JavacRoundEnvironment(false,
   683                                                           false,
   684                                                           rootElements,
   685                                                           JavacProcessingEnvironment.this);
   687         while(unmatchedAnnotations.size() > 0 && psi.hasNext() ) {
   688             ProcessorState ps = psi.next();
   689             Set<String>  matchedNames = new HashSet<String>();
   690             Set<TypeElement> typeElements = new LinkedHashSet<TypeElement>();
   692             for (Map.Entry<String, TypeElement> entry: unmatchedAnnotations.entrySet()) {
   693                 String unmatchedAnnotationName = entry.getKey();
   694                 if (ps.annotationSupported(unmatchedAnnotationName) ) {
   695                     matchedNames.add(unmatchedAnnotationName);
   696                     TypeElement te = entry.getValue();
   697                     if (te != null)
   698                         typeElements.add(te);
   699                 }
   700             }
   702             if (matchedNames.size() > 0 || ps.contributed) {
   703                 foundTypeProcessors = foundTypeProcessors || (ps.processor instanceof AbstractTypeProcessor);
   704                 boolean processingResult = callProcessor(ps.processor, typeElements, renv);
   705                 ps.contributed = true;
   706                 ps.removeSupportedOptions(unmatchedProcessorOptions);
   708                 if (printProcessorInfo || verbose) {
   709                     log.printNoteLines("x.print.processor.info",
   710                             ps.processor.getClass().getName(),
   711                             matchedNames.toString(),
   712                             processingResult);
   713                 }
   715                 if (processingResult) {
   716                     unmatchedAnnotations.keySet().removeAll(matchedNames);
   717                 }
   719             }
   720         }
   721         unmatchedAnnotations.remove("");
   723         if (lint && unmatchedAnnotations.size() > 0) {
   724             // Remove annotations processed by javac
   725             unmatchedAnnotations.keySet().removeAll(platformAnnotations);
   726             if (unmatchedAnnotations.size() > 0) {
   727                 log = Log.instance(context);
   728                 log.warning("proc.annotations.without.processors",
   729                             unmatchedAnnotations.keySet());
   730             }
   731         }
   733         // Run contributing processors that haven't run yet
   734         psi.runContributingProcs(renv);
   736         // Debugging
   737         if (options.get("displayFilerState") != null)
   738             filer.displayState();
   739     }
   741     /**
   742      * Computes the set of annotations on the symbol in question.
   743      * Leave class public for external testing purposes.
   744      */
   745     public static class ComputeAnnotationSet extends
   746         ElementScanner7<Set<TypeElement>, Set<TypeElement>> {
   747         final Elements elements;
   749         public ComputeAnnotationSet(Elements elements) {
   750             super();
   751             this.elements = elements;
   752         }
   754         @Override
   755         public Set<TypeElement> visitPackage(PackageElement e, Set<TypeElement> p) {
   756             // Don't scan enclosed elements of a package
   757             return p;
   758         }
   760         @Override
   761         public Set<TypeElement> scan(Element e, Set<TypeElement> p) {
   762             for (AnnotationMirror annotationMirror :
   763                      elements.getAllAnnotationMirrors(e) ) {
   764                 Element e2 = annotationMirror.getAnnotationType().asElement();
   765                 p.add((TypeElement) e2);
   766             }
   767             return super.scan(e, p);
   768         }
   769     }
   771     private boolean callProcessor(Processor proc,
   772                                          Set<? extends TypeElement> tes,
   773                                          RoundEnvironment renv) {
   774         try {
   775             return proc.process(tes, renv);
   776         } catch (CompletionFailure ex) {
   777             StringWriter out = new StringWriter();
   778             ex.printStackTrace(new PrintWriter(out));
   779             log.error("proc.cant.access", ex.sym, ex.getDetailValue(), out.toString());
   780             return false;
   781         } catch (Throwable t) {
   782             throw new AnnotationProcessingError(t);
   783         }
   784     }
   786     /**
   787      * Helper object for a single round of annotation processing.
   788      */
   789     class Round {
   790         /** The round number. */
   791         final int number;
   792         /** The context for the round. */
   793         final Context context;
   794         /** The compiler for the round. */
   795         final JavaCompiler compiler;
   796         /** The log for the round. */
   797         final Log log;
   798         /** The number of warnings in the previous round. */
   799         final int priorWarnings;
   801         /** The ASTs to be compiled. */
   802         List<JCCompilationUnit> roots;
   803         /** The classes to be compiler that have were generated. */
   804         Map<String, JavaFileObject> genClassFiles;
   806         /** The set of annotations to be processed this round. */
   807         Set<TypeElement> annotationsPresent;
   808         /** The set of top level classes to be processed this round. */
   809         List<ClassSymbol> topLevelClasses;
   810         /** The set of package-info files to be processed this round. */
   811         List<PackageSymbol> packageInfoFiles;
   813         /** Create a round (common code). */
   814         private Round(Context context, int number, int priorWarnings) {
   815             this.context = context;
   816             this.number = number;
   817             this.priorWarnings = priorWarnings;
   819             compiler = JavaCompiler.instance(context);
   820             log = Log.instance(context);
   822             // the following is for the benefit of JavacProcessingEnvironment.getContext()
   823             JavacProcessingEnvironment.this.context = context;
   825             // the following will be populated as needed
   826             topLevelClasses  = List.nil();
   827             packageInfoFiles = List.nil();
   828         }
   830         /** Create the first round. */
   831         Round(Context context, List<JCCompilationUnit> roots, List<ClassSymbol> classSymbols) {
   832             this(context, 1, 0);
   833             this.roots = roots;
   834             genClassFiles = new HashMap<String,JavaFileObject>();
   836             compiler.todo.clear(); // free the compiler's resources
   838             // The reverse() in the following line is to maintain behavioural
   839             // compatibility with the previous revision of the code. Strictly speaking,
   840             // it should not be necessary, but a javah golden file test fails without it.
   841             topLevelClasses =
   842                 getTopLevelClasses(roots).prependList(classSymbols.reverse());
   844             packageInfoFiles = getPackageInfoFiles(roots);
   846             findAnnotationsPresent();
   847         }
   849         /** Create a new round. */
   850         private Round(Round prev,
   851                 Set<JavaFileObject> newSourceFiles, Map<String,JavaFileObject> newClassFiles)
   852                 throws IOException {
   853             this(prev.nextContext(), prev.number+1, prev.compiler.log.nwarnings);
   854             this.genClassFiles = prev.genClassFiles;
   856             updateProcessingState();
   858             List<JCCompilationUnit> parsedFiles = compiler.parseFiles(newSourceFiles);
   859             roots = cleanTrees(prev.roots).appendList(parsedFiles);
   861             // Check for errors after parsing
   862             if (unrecoverableError())
   863                 return;
   865             enterClassFiles(genClassFiles);
   866             List<ClassSymbol> newClasses = enterClassFiles(newClassFiles);
   867             genClassFiles.putAll(newClassFiles);
   868             enterTrees(roots);
   870             if (unrecoverableError())
   871                 return;
   873             topLevelClasses = join(
   874                     getTopLevelClasses(parsedFiles),
   875                     getTopLevelClassesFromClasses(newClasses));
   877             packageInfoFiles = join(
   878                     getPackageInfoFiles(parsedFiles),
   879                     getPackageInfoFilesFromClasses(newClasses));
   881             findAnnotationsPresent();
   882         }
   884         /** Create the next round to be used. */
   885         Round next(Set<JavaFileObject> newSourceFiles, Map<String, JavaFileObject> newClassFiles)
   886                 throws IOException {
   887             try {
   888                 return new Round(this, newSourceFiles, newClassFiles);
   889             } finally {
   890                 compiler.close(false);
   891             }
   892         }
   894         /** Create the compiler to be used for the final compilation. */
   895         JavaCompiler finalCompiler(boolean errorStatus) {
   896             try {
   897                 JavaCompiler c = JavaCompiler.instance(nextContext());
   898                 if (errorStatus) {
   899                     c.log.nwarnings += priorWarnings + compiler.log.nwarnings;
   900                     c.log.nerrors += compiler.log.nerrors;
   901                 }
   902                 return c;
   903             } finally {
   904                 compiler.close(false);
   905             }
   906         }
   908         /** Return the number of errors found so far in this round.
   909          * This may include uncoverable errors, such as parse errors,
   910          * and transient errors, such as missing symbols. */
   911         int errorCount() {
   912             return compiler.errorCount();
   913         }
   915         /** Return the number of warnings found so far in this round. */
   916         int warningCount() {
   917             return compiler.warningCount();
   918         }
   920         /** Return whether or not an unrecoverable error has occurred. */
   921         boolean unrecoverableError() {
   922             return log.unrecoverableError
   923                     || messager.errorRaised()
   924                     || (werror && log.nwarnings > 0)
   925                     || (fatalErrors && log.nerrors > 0);
   926         }
   928         /** Find the set of annotations present in the set of top level
   929          *  classes and package info files to be processed this round. */
   930         void findAnnotationsPresent() {
   931             ComputeAnnotationSet annotationComputer = new ComputeAnnotationSet(elementUtils);
   932             // Use annotation processing to compute the set of annotations present
   933             annotationsPresent = new LinkedHashSet<TypeElement>();
   934             for (ClassSymbol classSym : topLevelClasses)
   935                 annotationComputer.scan(classSym, annotationsPresent);
   936             for (PackageSymbol pkgSym : packageInfoFiles)
   937                 annotationComputer.scan(pkgSym, annotationsPresent);
   938         }
   940         /** Enter a set of generated class files. */
   941         List<ClassSymbol> enterClassFiles(Map<String, JavaFileObject> classFiles) {
   942             ClassReader reader = ClassReader.instance(context);
   943             Names names = Names.instance(context);
   944             List<ClassSymbol> list = List.nil();
   946             for (Map.Entry<String,JavaFileObject> entry : classFiles.entrySet()) {
   947                 Name name = names.fromString(entry.getKey());
   948                 JavaFileObject file = entry.getValue();
   949                 if (file.getKind() != JavaFileObject.Kind.CLASS)
   950                     throw new AssertionError(file);
   951                 ClassSymbol cs;
   952                 if (isPkgInfo(file, JavaFileObject.Kind.CLASS)) {
   953                     Name packageName = Convert.packagePart(name);
   954                     PackageSymbol p = reader.enterPackage(packageName);
   955                     if (p.package_info == null)
   956                         p.package_info = reader.enterClass(Convert.shortName(name), p);
   957                     cs = p.package_info;
   958                     if (cs.classfile == null)
   959                         cs.classfile = file;
   960                 } else
   961                     cs = reader.enterClass(name, file);
   962                 list = list.prepend(cs);
   963             }
   964             return list.reverse();
   965         }
   967         /** Enter a set of syntax trees. */
   968         void enterTrees(List<JCCompilationUnit> roots) {
   969             compiler.enterTrees(roots);
   970         }
   972         /** Run a processing round. */
   973         void run(boolean lastRound, boolean errorStatus) {
   974             printRoundInfo(lastRound);
   976             TaskListener taskListener = context.get(TaskListener.class);
   977             if (taskListener != null)
   978                 taskListener.started(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING_ROUND));
   980             try {
   981                 if (lastRound) {
   982                     filer.setLastRound(true);
   983                     Set<Element> emptyRootElements = Collections.emptySet(); // immutable
   984                     RoundEnvironment renv = new JavacRoundEnvironment(true,
   985                             errorStatus,
   986                             emptyRootElements,
   987                             JavacProcessingEnvironment.this);
   988                     discoveredProcs.iterator().runContributingProcs(renv);
   989                 } else {
   990                     discoverAndRunProcs(context, annotationsPresent, topLevelClasses, packageInfoFiles);
   991                 }
   992             } finally {
   993                 if (taskListener != null)
   994                     taskListener.finished(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING_ROUND));
   995             }
   996         }
   998         /** Update the processing state for the current context. */
   999         private void updateProcessingState() {
  1000             filer.newRound(context);
  1001             messager.newRound(context);
  1003             elementUtils.setContext(context);
  1004             typeUtils.setContext(context);
  1007         /** Print info about this round. */
  1008         private void printRoundInfo(boolean lastRound) {
  1009             if (printRounds || verbose) {
  1010                 List<ClassSymbol> tlc = lastRound ? List.<ClassSymbol>nil() : topLevelClasses;
  1011                 Set<TypeElement> ap = lastRound ? Collections.<TypeElement>emptySet() : annotationsPresent;
  1012                 log.printNoteLines("x.print.rounds",
  1013                         number,
  1014                         "{" + tlc.toString(", ") + "}",
  1015                         ap,
  1016                         lastRound);
  1020         /** Get the context for the next round of processing.
  1021          * Important values are propogated from round to round;
  1022          * other values are implicitly reset.
  1023          */
  1024         private Context nextContext() {
  1025             Context next = new Context();
  1027             Options options = Options.instance(context);
  1028             assert options != null;
  1029             next.put(Options.optionsKey, options);
  1031             PrintWriter out = context.get(Log.outKey);
  1032             assert out != null;
  1033             next.put(Log.outKey, out);
  1035             final boolean shareNames = true;
  1036             if (shareNames) {
  1037                 Names names = Names.instance(context);
  1038                 assert names != null;
  1039                 next.put(Names.namesKey, names);
  1042             DiagnosticListener<?> dl = context.get(DiagnosticListener.class);
  1043             if (dl != null)
  1044                 next.put(DiagnosticListener.class, dl);
  1046             TaskListener tl = context.get(TaskListener.class);
  1047             if (tl != null)
  1048                 next.put(TaskListener.class, tl);
  1050             JavaFileManager jfm = context.get(JavaFileManager.class);
  1051             assert jfm != null;
  1052             next.put(JavaFileManager.class, jfm);
  1053             if (jfm instanceof JavacFileManager) {
  1054                 ((JavacFileManager)jfm).setContext(next);
  1057             Names names = Names.instance(context);
  1058             assert names != null;
  1059             next.put(Names.namesKey, names);
  1061             Keywords keywords = Keywords.instance(context);
  1062             assert(keywords != null);
  1063             next.put(Keywords.keywordsKey, keywords);
  1065             JavaCompiler oldCompiler = JavaCompiler.instance(context);
  1066             JavaCompiler nextCompiler = JavaCompiler.instance(next);
  1067             nextCompiler.initRound(oldCompiler);
  1069             JavacTaskImpl task = context.get(JavacTaskImpl.class);
  1070             if (task != null) {
  1071                 next.put(JavacTaskImpl.class, task);
  1072                 task.updateContext(next);
  1075             context.clear();
  1076             return next;
  1081     // TODO: internal catch clauses?; catch and rethrow an annotation
  1082     // processing error
  1083     public JavaCompiler doProcessing(Context context,
  1084                                      List<JCCompilationUnit> roots,
  1085                                      List<ClassSymbol> classSymbols,
  1086                                      Iterable<? extends PackageSymbol> pckSymbols)
  1087         throws IOException {
  1089         TaskListener taskListener = context.get(TaskListener.class);
  1090         log = Log.instance(context);
  1092         Set<PackageSymbol> specifiedPackages = new LinkedHashSet<PackageSymbol>();
  1093         for (PackageSymbol psym : pckSymbols)
  1094             specifiedPackages.add(psym);
  1095         this.specifiedPackages = Collections.unmodifiableSet(specifiedPackages);
  1097         Round round = new Round(context, roots, classSymbols);
  1099         boolean errorStatus;
  1100         boolean moreToDo;
  1101         do {
  1102             // Run processors for round n
  1103             round.run(false, false);
  1105             // Processors for round n have run to completion.
  1106             // Check for errors and whether there is more work to do.
  1107             errorStatus = round.unrecoverableError();
  1108             moreToDo = moreToDo();
  1110             // Set up next round.
  1111             // Copy mutable collections returned from filer.
  1112             round = round.next(
  1113                     new LinkedHashSet<JavaFileObject>(filer.getGeneratedSourceFileObjects()),
  1114                     new LinkedHashMap<String,JavaFileObject>(filer.getGeneratedClasses()));
  1116              // Check for errors during setup.
  1117             if (round.unrecoverableError())
  1118                 errorStatus = true;
  1120         } while (moreToDo && !errorStatus);
  1122         // run last round
  1123         round.run(true, errorStatus);
  1125         filer.warnIfUnclosedFiles();
  1126         warnIfUnmatchedOptions();
  1128         /*
  1129          * If an annotation processor raises an error in a round,
  1130          * that round runs to completion and one last round occurs.
  1131          * The last round may also occur because no more source or
  1132          * class files have been generated.  Therefore, if an error
  1133          * was raised on either of the last *two* rounds, the compile
  1134          * should exit with a nonzero exit code.  The current value of
  1135          * errorStatus holds whether or not an error was raised on the
  1136          * second to last round; errorRaised() gives the error status
  1137          * of the last round.
  1138          */
  1139         if (messager.errorRaised()
  1140                 || werror && round.warningCount() > 0 && round.errorCount() > 0)
  1141             errorStatus = true;
  1143         Set<JavaFileObject> newSourceFiles =
  1144                 new LinkedHashSet<JavaFileObject>(filer.getGeneratedSourceFileObjects());
  1145         roots = cleanTrees(round.roots);
  1147         JavaCompiler compiler = round.finalCompiler(errorStatus);
  1149         if (newSourceFiles.size() > 0)
  1150             roots = roots.appendList(compiler.parseFiles(newSourceFiles));
  1152         errorStatus = errorStatus || (compiler.errorCount() > 0);
  1154         // Free resources
  1155         this.close();
  1157         if (taskListener != null)
  1158             taskListener.finished(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING));
  1160         if (errorStatus) {
  1161             if (compiler.errorCount() == 0)
  1162                 compiler.log.nerrors++;
  1163             return compiler;
  1166         if (procOnly && !foundTypeProcessors) {
  1167             compiler.todo.clear();
  1168         } else {
  1169             if (procOnly && foundTypeProcessors)
  1170                 compiler.shouldStopPolicy = CompileState.FLOW;
  1172             compiler.enterTrees(roots);
  1175         return compiler;
  1178     private void warnIfUnmatchedOptions() {
  1179         if (!unmatchedProcessorOptions.isEmpty()) {
  1180             log.warning("proc.unmatched.processor.options", unmatchedProcessorOptions.toString());
  1184     /**
  1185      * Free resources related to annotation processing.
  1186      */
  1187     public void close() throws IOException {
  1188         filer.close();
  1189         if (discoveredProcs != null) // Make calling close idempotent
  1190             discoveredProcs.close();
  1191         discoveredProcs = null;
  1192         if (processorClassLoader != null && processorClassLoader instanceof Closeable)
  1193             ((Closeable) processorClassLoader).close();
  1196     private List<ClassSymbol> getTopLevelClasses(List<? extends JCCompilationUnit> units) {
  1197         List<ClassSymbol> classes = List.nil();
  1198         for (JCCompilationUnit unit : units) {
  1199             for (JCTree node : unit.defs) {
  1200                 if (node.getTag() == JCTree.CLASSDEF) {
  1201                     ClassSymbol sym = ((JCClassDecl) node).sym;
  1202                     assert sym != null;
  1203                     classes = classes.prepend(sym);
  1207         return classes.reverse();
  1210     private List<ClassSymbol> getTopLevelClassesFromClasses(List<? extends ClassSymbol> syms) {
  1211         List<ClassSymbol> classes = List.nil();
  1212         for (ClassSymbol sym : syms) {
  1213             if (!isPkgInfo(sym)) {
  1214                 classes = classes.prepend(sym);
  1217         return classes.reverse();
  1220     private List<PackageSymbol> getPackageInfoFiles(List<? extends JCCompilationUnit> units) {
  1221         List<PackageSymbol> packages = List.nil();
  1222         for (JCCompilationUnit unit : units) {
  1223             if (isPkgInfo(unit.sourcefile, JavaFileObject.Kind.SOURCE)) {
  1224                 packages = packages.prepend(unit.packge);
  1227         return packages.reverse();
  1230     private List<PackageSymbol> getPackageInfoFilesFromClasses(List<? extends ClassSymbol> syms) {
  1231         List<PackageSymbol> packages = List.nil();
  1232         for (ClassSymbol sym : syms) {
  1233             if (isPkgInfo(sym)) {
  1234                 packages = packages.prepend((PackageSymbol) sym.owner);
  1237         return packages.reverse();
  1240     // avoid unchecked warning from use of varargs
  1241     private static <T> List<T> join(List<T> list1, List<T> list2) {
  1242         return list1.appendList(list2);
  1245     private boolean isPkgInfo(JavaFileObject fo, JavaFileObject.Kind kind) {
  1246         return fo.isNameCompatible("package-info", kind);
  1249     private boolean isPkgInfo(ClassSymbol sym) {
  1250         return isPkgInfo(sym.classfile, JavaFileObject.Kind.CLASS) && (sym.packge().package_info == sym);
  1253     /*
  1254      * Called retroactively to determine if a class loader was required,
  1255      * after we have failed to create one.
  1256      */
  1257     private boolean needClassLoader(String procNames, Iterable<? extends File> workingpath) {
  1258         if (procNames != null)
  1259             return true;
  1261         String procPath;
  1262         URL[] urls = new URL[1];
  1263         for(File pathElement : workingpath) {
  1264             try {
  1265                 urls[0] = pathElement.toURI().toURL();
  1266                 if (ServiceProxy.hasService(Processor.class, urls))
  1267                     return true;
  1268             } catch (MalformedURLException ex) {
  1269                 throw new AssertionError(ex);
  1271             catch (ServiceProxy.ServiceConfigurationError e) {
  1272                 log.error("proc.bad.config.file", e.getLocalizedMessage());
  1273                 return true;
  1277         return false;
  1280     private static <T extends JCTree> List<T> cleanTrees(List<T> nodes) {
  1281         for (T node : nodes)
  1282             treeCleaner.scan(node);
  1283         return nodes;
  1286     private static TreeScanner treeCleaner = new TreeScanner() {
  1287             public void scan(JCTree node) {
  1288                 super.scan(node);
  1289                 if (node != null)
  1290                     node.type = null;
  1292             public void visitTopLevel(JCCompilationUnit node) {
  1293                 node.packge = null;
  1294                 super.visitTopLevel(node);
  1296             public void visitClassDef(JCClassDecl node) {
  1297                 node.sym = null;
  1298                 super.visitClassDef(node);
  1300             public void visitMethodDef(JCMethodDecl node) {
  1301                 node.sym = null;
  1302                 super.visitMethodDef(node);
  1304             public void visitVarDef(JCVariableDecl node) {
  1305                 node.sym = null;
  1306                 super.visitVarDef(node);
  1308             public void visitNewClass(JCNewClass node) {
  1309                 node.constructor = null;
  1310                 super.visitNewClass(node);
  1312             public void visitAssignop(JCAssignOp node) {
  1313                 node.operator = null;
  1314                 super.visitAssignop(node);
  1316             public void visitUnary(JCUnary node) {
  1317                 node.operator = null;
  1318                 super.visitUnary(node);
  1320             public void visitBinary(JCBinary node) {
  1321                 node.operator = null;
  1322                 super.visitBinary(node);
  1324             public void visitSelect(JCFieldAccess node) {
  1325                 node.sym = null;
  1326                 super.visitSelect(node);
  1328             public void visitIdent(JCIdent node) {
  1329                 node.sym = null;
  1330                 super.visitIdent(node);
  1332         };
  1335     private boolean moreToDo() {
  1336         return filer.newFiles();
  1339     /**
  1340      * {@inheritdoc}
  1342      * Command line options suitable for presenting to annotation
  1343      * processors.  "-Afoo=bar" should be "-Afoo" => "bar".
  1344      */
  1345     public Map<String,String> getOptions() {
  1346         return processorOptions;
  1349     public Messager getMessager() {
  1350         return messager;
  1353     public Filer getFiler() {
  1354         return filer;
  1357     public JavacElements getElementUtils() {
  1358         return elementUtils;
  1361     public JavacTypes getTypeUtils() {
  1362         return typeUtils;
  1365     public SourceVersion getSourceVersion() {
  1366         return Source.toSourceVersion(source);
  1369     public Locale getLocale() {
  1370         return messages.getCurrentLocale();
  1373     public Set<Symbol.PackageSymbol> getSpecifiedPackages() {
  1374         return specifiedPackages;
  1377     private static final Pattern allMatches = Pattern.compile(".*");
  1378     public static final Pattern noMatches  = Pattern.compile("(\\P{all})+");
  1380     /**
  1381      * Convert import-style string for supported annotations into a
  1382      * regex matching that string.  If the string is a valid
  1383      * import-style string, return a regex that won't match anything.
  1384      */
  1385     private static Pattern importStringToPattern(String s, Processor p, Log log) {
  1386         if (isValidImportString(s)) {
  1387             return validImportStringToPattern(s);
  1388         } else {
  1389             log.warning("proc.malformed.supported.string", s, p.getClass().getName());
  1390             return noMatches; // won't match any valid identifier
  1394     /**
  1395      * Return true if the argument string is a valid import-style
  1396      * string specifying claimed annotations; return false otherwise.
  1397      */
  1398     public static boolean isValidImportString(String s) {
  1399         if (s.equals("*"))
  1400             return true;
  1402         boolean valid = true;
  1403         String t = s;
  1404         int index = t.indexOf('*');
  1406         if (index != -1) {
  1407             // '*' must be last character...
  1408             if (index == t.length() -1) {
  1409                 // ... any and preceding character must be '.'
  1410                 if ( index-1 >= 0 ) {
  1411                     valid = t.charAt(index-1) == '.';
  1412                     // Strip off ".*$" for identifier checks
  1413                     t = t.substring(0, t.length()-2);
  1415             } else
  1416                 return false;
  1419         // Verify string is off the form (javaId \.)+ or javaId
  1420         if (valid) {
  1421             String[] javaIds = t.split("\\.", t.length()+2);
  1422             for(String javaId: javaIds)
  1423                 valid &= SourceVersion.isIdentifier(javaId);
  1425         return valid;
  1428     public static Pattern validImportStringToPattern(String s) {
  1429         if (s.equals("*")) {
  1430             return allMatches;
  1431         } else {
  1432             String s_prime = s.replace(".", "\\.");
  1434             if (s_prime.endsWith("*")) {
  1435                 s_prime =  s_prime.substring(0, s_prime.length() - 1) + ".+";
  1438             return Pattern.compile(s_prime);
  1442     /**
  1443      * For internal use only.  This method will be
  1444      * removed without warning.
  1445      */
  1446     public Context getContext() {
  1447         return context;
  1450     public String toString() {
  1451         return "javac ProcessingEnvironment";
  1454     public static boolean isValidOptionName(String optionName) {
  1455         for(String s : optionName.split("\\.", -1)) {
  1456             if (!SourceVersion.isIdentifier(s))
  1457                 return false;
  1459         return true;

mercurial