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

Mon, 15 Feb 2010 18:20:57 -0800

author
darcy
date
Mon, 15 Feb 2010 18:20:57 -0800
changeset 494
af18e3956985
parent 489
4b4e282a3146
child 497
16b9b7f45933
permissions
-rw-r--r--

6634138: Source generated in last round not compiled
Reviewed-by: jjg

     1 /*
     2  * Copyright 2005-2009 Sun Microsystems, Inc.  All Rights Reserved.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.  Sun designates this
     8  * particular file as subject to the "Classpath" exception as provided
     9  * by Sun in the LICENSE file that accompanied this code.
    10  *
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    14  * version 2 for more details (a copy is included in the LICENSE file that
    15  * accompanied this code).
    16  *
    17  * You should have received a copy of the GNU General Public License version
    18  * 2 along with this work; if not, write to the Free Software Foundation,
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    20  *
    21  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
    22  * CA 95054 USA or visit www.sun.com if you need additional information or
    23  * have any questions.
    24  */
    26 package com.sun.tools.javac.processing;
    29 import java.lang.reflect.*;
    30 import java.util.*;
    31 import java.util.regex.*;
    33 import java.net.URL;
    34 import java.io.Closeable;
    35 import java.io.File;
    36 import java.io.PrintWriter;
    37 import java.io.IOException;
    38 import java.net.MalformedURLException;
    39 import java.io.StringWriter;
    41 import javax.annotation.processing.*;
    42 import javax.lang.model.SourceVersion;
    43 import javax.lang.model.element.AnnotationMirror;
    44 import javax.lang.model.element.Element;
    45 import javax.lang.model.element.TypeElement;
    46 import javax.lang.model.element.PackageElement;
    47 import javax.lang.model.util.*;
    48 import javax.tools.JavaFileManager;
    49 import javax.tools.StandardJavaFileManager;
    50 import javax.tools.JavaFileObject;
    51 import javax.tools.DiagnosticListener;
    53 import com.sun.source.util.AbstractTypeProcessor;
    54 import com.sun.source.util.TaskEvent;
    55 import com.sun.source.util.TaskListener;
    56 import com.sun.tools.javac.api.JavacTaskImpl;
    57 import com.sun.tools.javac.code.*;
    58 import com.sun.tools.javac.code.Symbol.*;
    59 import com.sun.tools.javac.file.JavacFileManager;
    60 import com.sun.tools.javac.jvm.*;
    61 import com.sun.tools.javac.main.JavaCompiler;
    62 import com.sun.tools.javac.main.JavaCompiler.CompileState;
    63 import com.sun.tools.javac.model.JavacElements;
    64 import com.sun.tools.javac.model.JavacTypes;
    65 import com.sun.tools.javac.parser.*;
    66 import com.sun.tools.javac.tree.*;
    67 import com.sun.tools.javac.tree.JCTree.*;
    68 import com.sun.tools.javac.util.Abort;
    69 import com.sun.tools.javac.util.Context;
    70 import com.sun.tools.javac.util.Convert;
    71 import com.sun.tools.javac.util.List;
    72 import com.sun.tools.javac.util.ListBuffer;
    73 import com.sun.tools.javac.util.Log;
    74 import com.sun.tools.javac.util.JavacMessages;
    75 import com.sun.tools.javac.util.Name;
    76 import com.sun.tools.javac.util.Names;
    77 import com.sun.tools.javac.util.Options;
    79 import static javax.tools.StandardLocation.*;
    81 /**
    82  * Objects of this class hold and manage the state needed to support
    83  * annotation processing.
    84  *
    85  * <p><b>This is NOT part of any API supported by Sun Microsystems.
    86  * If you write code that depends on this, you do so at your own risk.
    87  * This code and its internal interfaces are subject to change or
    88  * deletion without notice.</b>
    89  */
    90 public class JavacProcessingEnvironment implements ProcessingEnvironment, Closeable {
    91     Options options;
    93     private final boolean printProcessorInfo;
    94     private final boolean printRounds;
    95     private final boolean verbose;
    96     private final boolean lint;
    97     private final boolean procOnly;
    98     private final boolean fatalErrors;
    99     private boolean foundTypeProcessors;
   101     private final JavacFiler filer;
   102     private final JavacMessager messager;
   103     private final JavacElements elementUtils;
   104     private final JavacTypes typeUtils;
   106     /**
   107      * Holds relevant state history of which processors have been
   108      * used.
   109      */
   110     private DiscoveredProcessors discoveredProcs;
   112     /**
   113      * Map of processor-specific options.
   114      */
   115     private final Map<String, String> processorOptions;
   117     /**
   118      */
   119     private final Set<String> unmatchedProcessorOptions;
   121     /**
   122      * Annotations implicitly processed and claimed by javac.
   123      */
   124     private final Set<String> platformAnnotations;
   126     /**
   127      * Set of packages given on command line.
   128      */
   129     private Set<PackageSymbol> specifiedPackages = Collections.emptySet();
   131     /** The log to be used for error reporting.
   132      */
   133     Log log;
   135     /**
   136      * Source level of the compile.
   137      */
   138     Source source;
   140     private ClassLoader processorClassLoader;
   142     /**
   143      * JavacMessages object used for localization
   144      */
   145     private JavacMessages messages;
   147     private Context context;
   149     public JavacProcessingEnvironment(Context context, Iterable<? extends Processor> processors) {
   150         options = Options.instance(context);
   151         this.context = context;
   152         log = Log.instance(context);
   153         source = Source.instance(context);
   154         printProcessorInfo = options.get("-XprintProcessorInfo") != null;
   155         printRounds = options.get("-XprintRounds") != null;
   156         verbose = options.get("-verbose") != null;
   157         lint = options.lint("processing");
   158         procOnly = options.get("-proc:only") != null ||
   159             options.get("-Xprint") != null;
   160         fatalErrors = options.get("fatalEnterError") != 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         // Writer for -XprintRounds and -XprintProcessorInfo data
   659         PrintWriter xout = context.get(Log.outKey);
   661         Map<String, TypeElement> unmatchedAnnotations =
   662             new HashMap<String, TypeElement>(annotationsPresent.size());
   664         for(TypeElement a  : annotationsPresent) {
   665                 unmatchedAnnotations.put(a.getQualifiedName().toString(),
   666                                          a);
   667         }
   669         // Give "*" processors a chance to match
   670         if (unmatchedAnnotations.size() == 0)
   671             unmatchedAnnotations.put("", null);
   673         DiscoveredProcessors.ProcessorStateIterator psi = discoveredProcs.iterator();
   674         // TODO: Create proper argument values; need past round
   675         // information to fill in this constructor.  Note that the 1
   676         // st round of processing could be the last round if there
   677         // were parse errors on the initial source files; however, we
   678         // are not doing processing in that case.
   680         Set<Element> rootElements = new LinkedHashSet<Element>();
   681         rootElements.addAll(topLevelClasses);
   682         rootElements.addAll(packageInfoFiles);
   683         rootElements = Collections.unmodifiableSet(rootElements);
   685         RoundEnvironment renv = new JavacRoundEnvironment(false,
   686                                                           false,
   687                                                           rootElements,
   688                                                           JavacProcessingEnvironment.this);
   690         while(unmatchedAnnotations.size() > 0 && psi.hasNext() ) {
   691             ProcessorState ps = psi.next();
   692             Set<String>  matchedNames = new HashSet<String>();
   693             Set<TypeElement> typeElements = new LinkedHashSet<TypeElement>();
   694             for (String unmatchedAnnotationName : unmatchedAnnotations.keySet()) {
   695                 if (ps.annotationSupported(unmatchedAnnotationName) ) {
   696                     matchedNames.add(unmatchedAnnotationName);
   697                     TypeElement te = unmatchedAnnotations.get(unmatchedAnnotationName);
   698                     if (te != null)
   699                         typeElements.add(te);
   700                 }
   701             }
   703             if (matchedNames.size() > 0 || ps.contributed) {
   704                 foundTypeProcessors = foundTypeProcessors || (ps.processor instanceof AbstractTypeProcessor);
   705                 boolean processingResult = callProcessor(ps.processor, typeElements, renv);
   706                 ps.contributed = true;
   707                 ps.removeSupportedOptions(unmatchedProcessorOptions);
   709                 if (printProcessorInfo || verbose) {
   710                     xout.println(Log.getLocalizedString("x.print.processor.info",
   711                                                         ps.processor.getClass().getName(),
   712                                                         matchedNames.toString(),
   713                                                         processingResult));
   714                 }
   716                 if (processingResult) {
   717                     unmatchedAnnotations.keySet().removeAll(matchedNames);
   718                 }
   720             }
   721         }
   722         unmatchedAnnotations.remove("");
   724         if (lint && unmatchedAnnotations.size() > 0) {
   725             // Remove annotations processed by javac
   726             unmatchedAnnotations.keySet().removeAll(platformAnnotations);
   727             if (unmatchedAnnotations.size() > 0) {
   728                 log = Log.instance(context);
   729                 log.warning("proc.annotations.without.processors",
   730                             unmatchedAnnotations.keySet());
   731             }
   732         }
   734         // Run contributing processors that haven't run yet
   735         psi.runContributingProcs(renv);
   737         // Debugging
   738         if (options.get("displayFilerState") != null)
   739             filer.displayState();
   740     }
   742     /**
   743      * Computes the set of annotations on the symbol in question.
   744      * Leave class public for external testing purposes.
   745      */
   746     public static class ComputeAnnotationSet extends
   747         ElementScanner6<Set<TypeElement>, Set<TypeElement>> {
   748         final Elements elements;
   750         public ComputeAnnotationSet(Elements elements) {
   751             super();
   752             this.elements = elements;
   753         }
   755         @Override
   756         public Set<TypeElement> visitPackage(PackageElement e, Set<TypeElement> p) {
   757             // Don't scan enclosed elements of a package
   758             return p;
   759         }
   761         @Override
   762          public Set<TypeElement> scan(Element e, Set<TypeElement> p) {
   763             for (AnnotationMirror annotationMirror :
   764                      elements.getAllAnnotationMirrors(e) ) {
   765                 Element e2 = annotationMirror.getAnnotationType().asElement();
   766                 p.add((TypeElement) e2);
   767             }
   768             return super.scan(e, p);
   769         }
   770     }
   772     private boolean callProcessor(Processor proc,
   773                                          Set<? extends TypeElement> tes,
   774                                          RoundEnvironment renv) {
   775         try {
   776             return proc.process(tes, renv);
   777         } catch (CompletionFailure ex) {
   778             StringWriter out = new StringWriter();
   779             ex.printStackTrace(new PrintWriter(out));
   780             log.error("proc.cant.access", ex.sym, ex.getDetailValue(), out.toString());
   781             return false;
   782         } catch (Throwable t) {
   783             throw new AnnotationProcessingError(t);
   784         }
   785     }
   788     // TODO: internal catch clauses?; catch and rethrow an annotation
   789     // processing error
   790     public JavaCompiler doProcessing(Context context,
   791                                      List<JCCompilationUnit> roots,
   792                                      List<ClassSymbol> classSymbols,
   793                                      Iterable<? extends PackageSymbol> pckSymbols)
   794     throws IOException {
   796         log = Log.instance(context);
   797         // Writer for -XprintRounds and -XprintProcessorInfo data
   798         PrintWriter xout = context.get(Log.outKey);
   799         TaskListener taskListener = context.get(TaskListener.class);
   802         AnnotationCollector collector = new AnnotationCollector();
   804         JavaCompiler compiler = JavaCompiler.instance(context);
   805         compiler.todo.clear(); // free the compiler's resources
   807         int round = 0;
   809         // List<JCAnnotation> annotationsPresentInSource = collector.findAnnotations(roots);
   810         List<ClassSymbol> topLevelClasses = getTopLevelClasses(roots);
   812         for (ClassSymbol classSym : classSymbols)
   813             topLevelClasses = topLevelClasses.prepend(classSym);
   814         List<PackageSymbol> packageInfoFiles =
   815             getPackageInfoFiles(roots);
   817         Set<PackageSymbol> specifiedPackages = new LinkedHashSet<PackageSymbol>();
   818         for (PackageSymbol psym : pckSymbols)
   819             specifiedPackages.add(psym);
   820         this.specifiedPackages = Collections.unmodifiableSet(specifiedPackages);
   822         // Use annotation processing to compute the set of annotations present
   823         Set<TypeElement> annotationsPresent = new LinkedHashSet<TypeElement>();
   824         ComputeAnnotationSet annotationComputer = new ComputeAnnotationSet(elementUtils);
   825         for (ClassSymbol classSym : topLevelClasses)
   826             annotationComputer.scan(classSym, annotationsPresent);
   827         for (PackageSymbol pkgSym : packageInfoFiles)
   828             annotationComputer.scan(pkgSym, annotationsPresent);
   830         Context currentContext = context;
   832         int roundNumber = 0;
   833         boolean errorStatus = false;
   835         runAround:
   836         while(true) {
   837             if (fatalErrors && compiler.errorCount() != 0) {
   838                 errorStatus = true;
   839                 break runAround;
   840             }
   842             this.context = currentContext;
   843             roundNumber++;
   844             printRoundInfo(xout, roundNumber, topLevelClasses, annotationsPresent, false);
   846             if (taskListener != null)
   847                 taskListener.started(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING_ROUND));
   849             try {
   850                 discoverAndRunProcs(currentContext, annotationsPresent, topLevelClasses, packageInfoFiles);
   851             } finally {
   852                 if (taskListener != null)
   853                     taskListener.finished(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING_ROUND));
   854             }
   856             /*
   857              * Processors for round n have run to completion.  Prepare
   858              * for round (n+1) by checked for errors raised by
   859              * annotation processors and then checking for syntax
   860              * errors on any generated source files.
   861              */
   862             if (messager.errorRaised()) {
   863                 errorStatus = true;
   864                 break runAround;
   865             } else {
   866                 if (moreToDo()) {
   867                     // annotationsPresentInSource = List.nil();
   868                     annotationsPresent = new LinkedHashSet<TypeElement>();
   869                     topLevelClasses  = List.nil();
   870                     packageInfoFiles = List.nil();
   872                     compiler.close(false);
   873                     currentContext = contextForNextRound(currentContext, true);
   875                     JavaFileManager fileManager = currentContext.get(JavaFileManager.class);
   877                     compiler = JavaCompiler.instance(currentContext);
   878                     List<JCCompilationUnit> parsedFiles = sourcesToParsedFiles(compiler);
   879                     roots = cleanTrees(roots).appendList(parsedFiles);
   881                     // Check for errors after parsing
   882                     if (compiler.parseErrors()) {
   883                         errorStatus = true;
   884                         break runAround;
   885                     } else {
   886                         List<ClassSymbol> newClasses = enterNewClassFiles(currentContext);
   887                         compiler.enterTrees(roots);
   889                         // annotationsPresentInSource =
   890                         // collector.findAnnotations(parsedFiles);
   891                         ListBuffer<ClassSymbol> tlc = new ListBuffer<ClassSymbol>();
   892                         tlc.appendList(getTopLevelClasses(parsedFiles));
   893                         tlc.appendList(getTopLevelClassesFromClasses(newClasses));
   894                         topLevelClasses  = tlc.toList();
   896                         ListBuffer<PackageSymbol> pif = new ListBuffer<PackageSymbol>();
   897                         pif.appendList(getPackageInfoFiles(parsedFiles));
   898                         pif.appendList(getPackageInfoFilesFromClasses(newClasses));
   899                         packageInfoFiles = pif.toList();
   901                         annotationsPresent = new LinkedHashSet<TypeElement>();
   902                         for (ClassSymbol classSym : topLevelClasses)
   903                             annotationComputer.scan(classSym, annotationsPresent);
   904                         for (PackageSymbol pkgSym : packageInfoFiles)
   905                             annotationComputer.scan(pkgSym, annotationsPresent);
   907                         updateProcessingState(currentContext, false);
   908                     }
   909                 } else
   910                     break runAround; // No new files
   911             }
   912         }
   913         roots = runLastRound(xout, roundNumber, errorStatus, compiler, roots, taskListener);
   914         // Set error status for any files compiled and generated in
   915         // the last round
   916         if (compiler.parseErrors())
   917             errorStatus = true;
   919         compiler.close(false);
   920         currentContext = contextForNextRound(currentContext, true);
   921         compiler = JavaCompiler.instance(currentContext);
   923         filer.newRound(currentContext, true);
   924         filer.warnIfUnclosedFiles();
   925         warnIfUnmatchedOptions();
   927        /*
   928         * If an annotation processor raises an error in a round,
   929         * that round runs to completion and one last round occurs.
   930         * The last round may also occur because no more source or
   931         * class files have been generated.  Therefore, if an error
   932         * was raised on either of the last *two* rounds, the compile
   933         * should exit with a nonzero exit code.  The current value of
   934         * errorStatus holds whether or not an error was raised on the
   935         * second to last round; errorRaised() gives the error status
   936         * of the last round.
   937         */
   938         errorStatus = errorStatus || messager.errorRaised();
   941         // Free resources
   942         this.close();
   944         if (taskListener != null)
   945             taskListener.finished(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING));
   947         if (errorStatus) {
   948             compiler.log.nerrors += messager.errorCount();
   949             if (compiler.errorCount() == 0)
   950                 compiler.log.nerrors++;
   951         } else if (procOnly && !foundTypeProcessors) {
   952             compiler.todo.clear();
   953         } else { // Final compilation
   954             compiler.close(false);
   955             currentContext = contextForNextRound(currentContext, true);
   956             this.context = currentContext;
   957             updateProcessingState(currentContext, true);
   958             compiler = JavaCompiler.instance(currentContext);
   959             if (procOnly && foundTypeProcessors)
   960                 compiler.shouldStopPolicy = CompileState.FLOW;
   962             if (true) {
   963                 compiler.enterTrees(cleanTrees(roots));
   964             } else {
   965                 List<JavaFileObject> fileObjects = List.nil();
   966                 for (JCCompilationUnit unit : roots)
   967                     fileObjects = fileObjects.prepend(unit.getSourceFile());
   968                 roots = null;
   969                 compiler.enterTrees(compiler.parseFiles(fileObjects.reverse()));
   970             }
   971         }
   973         return compiler;
   974     }
   976     private List<JCCompilationUnit> sourcesToParsedFiles(JavaCompiler compiler)
   977         throws IOException {
   978         List<JavaFileObject> fileObjects = List.nil();
   979         for (JavaFileObject jfo : filer.getGeneratedSourceFileObjects() ) {
   980             fileObjects = fileObjects.prepend(jfo);
   981         }
   983        return compiler.parseFiles(fileObjects);
   984     }
   986     // Call the last round of annotation processing
   987     private List<JCCompilationUnit> runLastRound(PrintWriter xout,
   988                                                  int roundNumber,
   989                                                  boolean errorStatus,
   990                                                  JavaCompiler compiler,
   991                                                  List<JCCompilationUnit> roots,
   992                               TaskListener taskListener) throws IOException {
   993         roundNumber++;
   994         List<ClassSymbol> noTopLevelClasses = List.nil();
   995         Set<TypeElement> noAnnotations =  Collections.emptySet();
   996         printRoundInfo(xout, roundNumber, noTopLevelClasses, noAnnotations, true);
   998         Set<Element> emptyRootElements = Collections.emptySet(); // immutable
   999         RoundEnvironment renv = new JavacRoundEnvironment(true,
  1000                                                           errorStatus,
  1001                                                           emptyRootElements,
  1002                                                           JavacProcessingEnvironment.this);
  1003         if (taskListener != null)
  1004             taskListener.started(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING_ROUND));
  1006         try {
  1007             discoveredProcs.iterator().runContributingProcs(renv);
  1008         } finally {
  1009             if (taskListener != null)
  1010                 taskListener.finished(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING_ROUND));
  1013         // Add any sources generated during the last round to the set
  1014         // of files to be compiled.
  1015         if (moreToDo()) {
  1016             List<JCCompilationUnit> parsedFiles = sourcesToParsedFiles(compiler);
  1017             roots = cleanTrees(roots).appendList(parsedFiles);
  1020         return roots;
  1023     private void updateProcessingState(Context currentContext, boolean lastRound) {
  1024         filer.newRound(currentContext, lastRound);
  1025         messager.newRound(currentContext);
  1027         elementUtils.setContext(currentContext);
  1028         typeUtils.setContext(currentContext);
  1031     private void warnIfUnmatchedOptions() {
  1032         if (!unmatchedProcessorOptions.isEmpty()) {
  1033             log.warning("proc.unmatched.processor.options", unmatchedProcessorOptions.toString());
  1037     private void printRoundInfo(PrintWriter xout,
  1038                                 int roundNumber,
  1039                                 List<ClassSymbol> topLevelClasses,
  1040                                 Set<TypeElement> annotationsPresent,
  1041                                 boolean lastRound) {
  1042         if (printRounds || verbose) {
  1043             xout.println(Log.getLocalizedString("x.print.rounds",
  1044                                                 roundNumber,
  1045                                                 "{" + topLevelClasses.toString(", ") + "}",
  1046                                                 annotationsPresent,
  1047                                                 lastRound));
  1051     private List<ClassSymbol> enterNewClassFiles(Context currentContext) {
  1052         ClassReader reader = ClassReader.instance(currentContext);
  1053         Names names = Names.instance(currentContext);
  1054         List<ClassSymbol> list = List.nil();
  1056         for (Map.Entry<String,JavaFileObject> entry : filer.getGeneratedClasses().entrySet()) {
  1057             Name name = names.fromString(entry.getKey());
  1058             JavaFileObject file = entry.getValue();
  1059             if (file.getKind() != JavaFileObject.Kind.CLASS)
  1060                 throw new AssertionError(file);
  1061             ClassSymbol cs;
  1062             if (isPkgInfo(file, JavaFileObject.Kind.CLASS)) {
  1063                 Name packageName = Convert.packagePart(name);
  1064                 PackageSymbol p = reader.enterPackage(packageName);
  1065                 if (p.package_info == null)
  1066                     p.package_info = reader.enterClass(Convert.shortName(name), p);
  1067                 cs = p.package_info;
  1068                 if (cs.classfile == null)
  1069                     cs.classfile = file;
  1070             } else
  1071                 cs = reader.enterClass(name, file);
  1072             list = list.prepend(cs);
  1074         return list.reverse();
  1077     /**
  1078      * Free resources related to annotation processing.
  1079      */
  1080     public void close() throws IOException {
  1081         filer.close();
  1082         if (discoveredProcs != null) // Make calling close idempotent
  1083             discoveredProcs.close();
  1084         discoveredProcs = null;
  1085         if (processorClassLoader != null && processorClassLoader instanceof Closeable)
  1086             ((Closeable) processorClassLoader).close();
  1089     private List<ClassSymbol> getTopLevelClasses(List<? extends JCCompilationUnit> units) {
  1090         List<ClassSymbol> classes = List.nil();
  1091         for (JCCompilationUnit unit : units) {
  1092             for (JCTree node : unit.defs) {
  1093                 if (node.getTag() == JCTree.CLASSDEF) {
  1094                     classes = classes.prepend(((JCClassDecl) node).sym);
  1098         return classes.reverse();
  1101     private List<ClassSymbol> getTopLevelClassesFromClasses(List<? extends ClassSymbol> syms) {
  1102         List<ClassSymbol> classes = List.nil();
  1103         for (ClassSymbol sym : syms) {
  1104             if (!isPkgInfo(sym)) {
  1105                 classes = classes.prepend(sym);
  1108         return classes.reverse();
  1111     private List<PackageSymbol> getPackageInfoFiles(List<? extends JCCompilationUnit> units) {
  1112         List<PackageSymbol> packages = List.nil();
  1113         for (JCCompilationUnit unit : units) {
  1114             if (isPkgInfo(unit.sourcefile, JavaFileObject.Kind.SOURCE)) {
  1115                 packages = packages.prepend(unit.packge);
  1118         return packages.reverse();
  1121     private List<PackageSymbol> getPackageInfoFilesFromClasses(List<? extends ClassSymbol> syms) {
  1122         List<PackageSymbol> packages = List.nil();
  1123         for (ClassSymbol sym : syms) {
  1124             if (isPkgInfo(sym)) {
  1125                 packages = packages.prepend((PackageSymbol) sym.owner);
  1128         return packages.reverse();
  1131     private boolean isPkgInfo(JavaFileObject fo, JavaFileObject.Kind kind) {
  1132         return fo.isNameCompatible("package-info", kind);
  1135     private boolean isPkgInfo(ClassSymbol sym) {
  1136         return isPkgInfo(sym.classfile, JavaFileObject.Kind.CLASS) && (sym.packge().package_info == sym);
  1139     private Context contextForNextRound(Context context, boolean shareNames)
  1140         throws IOException
  1142         Context next = new Context();
  1144         Options options = Options.instance(context);
  1145         assert options != null;
  1146         next.put(Options.optionsKey, options);
  1148         PrintWriter out = context.get(Log.outKey);
  1149         assert out != null;
  1150         next.put(Log.outKey, out);
  1152         if (shareNames) {
  1153             Names names = Names.instance(context);
  1154             assert names != null;
  1155             next.put(Names.namesKey, names);
  1158         DiagnosticListener<?> dl = context.get(DiagnosticListener.class);
  1159         if (dl != null)
  1160             next.put(DiagnosticListener.class, dl);
  1162         TaskListener tl = context.get(TaskListener.class);
  1163         if (tl != null)
  1164             next.put(TaskListener.class, tl);
  1166         JavaFileManager jfm = context.get(JavaFileManager.class);
  1167         assert jfm != null;
  1168         next.put(JavaFileManager.class, jfm);
  1169         if (jfm instanceof JavacFileManager) {
  1170             ((JavacFileManager)jfm).setContext(next);
  1173         Names names = Names.instance(context);
  1174         assert names != null;
  1175         next.put(Names.namesKey, names);
  1177         Keywords keywords = Keywords.instance(context);
  1178         assert(keywords != null);
  1179         next.put(Keywords.keywordsKey, keywords);
  1181         JavaCompiler oldCompiler = JavaCompiler.instance(context);
  1182         JavaCompiler nextCompiler = JavaCompiler.instance(next);
  1183         nextCompiler.initRound(oldCompiler);
  1185         JavacTaskImpl task = context.get(JavacTaskImpl.class);
  1186         if (task != null) {
  1187             next.put(JavacTaskImpl.class, task);
  1188             task.updateContext(next);
  1191         context.clear();
  1192         return next;
  1195     /*
  1196      * Called retroactively to determine if a class loader was required,
  1197      * after we have failed to create one.
  1198      */
  1199     private boolean needClassLoader(String procNames, Iterable<? extends File> workingpath) {
  1200         if (procNames != null)
  1201             return true;
  1203         String procPath;
  1204         URL[] urls = new URL[1];
  1205         for(File pathElement : workingpath) {
  1206             try {
  1207                 urls[0] = pathElement.toURI().toURL();
  1208                 if (ServiceProxy.hasService(Processor.class, urls))
  1209                     return true;
  1210             } catch (MalformedURLException ex) {
  1211                 throw new AssertionError(ex);
  1213             catch (ServiceProxy.ServiceConfigurationError e) {
  1214                 log.error("proc.bad.config.file", e.getLocalizedMessage());
  1215                 return true;
  1219         return false;
  1222     private class AnnotationCollector extends TreeScanner {
  1223         List<JCTree> path = List.nil();
  1224         static final boolean verbose = false;
  1225         List<JCAnnotation> annotations = List.nil();
  1227         public List<JCAnnotation> findAnnotations(List<? extends JCTree> nodes) {
  1228             annotations = List.nil();
  1229             scan(nodes);
  1230             List<JCAnnotation> found = annotations;
  1231             annotations = List.nil();
  1232             return found.reverse();
  1235         public void scan(JCTree node) {
  1236             if (node == null)
  1237                 return;
  1238             Symbol sym = TreeInfo.symbolFor(node);
  1239             if (sym != null)
  1240                 path = path.prepend(node);
  1241             super.scan(node);
  1242             if (sym != null)
  1243                 path = path.tail;
  1246         public void visitAnnotation(JCAnnotation node) {
  1247             annotations = annotations.prepend(node);
  1248             if (verbose) {
  1249                 StringBuilder sb = new StringBuilder();
  1250                 for (JCTree tree : path.reverse()) {
  1251                     System.err.print(sb);
  1252                     System.err.println(TreeInfo.symbolFor(tree));
  1253                     sb.append("  ");
  1255                 System.err.print(sb);
  1256                 System.err.println(node);
  1261     private static <T extends JCTree> List<T> cleanTrees(List<T> nodes) {
  1262         for (T node : nodes)
  1263             treeCleaner.scan(node);
  1264         return nodes;
  1267     private static TreeScanner treeCleaner = new TreeScanner() {
  1268             public void scan(JCTree node) {
  1269                 super.scan(node);
  1270                 if (node != null)
  1271                     node.type = null;
  1273             public void visitTopLevel(JCCompilationUnit node) {
  1274                 node.packge = null;
  1275                 super.visitTopLevel(node);
  1277             public void visitClassDef(JCClassDecl node) {
  1278                 node.sym = null;
  1279                 super.visitClassDef(node);
  1281             public void visitMethodDef(JCMethodDecl node) {
  1282                 node.sym = null;
  1283                 super.visitMethodDef(node);
  1285             public void visitVarDef(JCVariableDecl node) {
  1286                 node.sym = null;
  1287                 super.visitVarDef(node);
  1289             public void visitNewClass(JCNewClass node) {
  1290                 node.constructor = null;
  1291                 super.visitNewClass(node);
  1293             public void visitAssignop(JCAssignOp node) {
  1294                 node.operator = null;
  1295                 super.visitAssignop(node);
  1297             public void visitUnary(JCUnary node) {
  1298                 node.operator = null;
  1299                 super.visitUnary(node);
  1301             public void visitBinary(JCBinary node) {
  1302                 node.operator = null;
  1303                 super.visitBinary(node);
  1305             public void visitSelect(JCFieldAccess node) {
  1306                 node.sym = null;
  1307                 super.visitSelect(node);
  1309             public void visitIdent(JCIdent node) {
  1310                 node.sym = null;
  1311                 super.visitIdent(node);
  1313         };
  1316     private boolean moreToDo() {
  1317         return filer.newFiles();
  1320     /**
  1321      * {@inheritdoc}
  1323      * Command line options suitable for presenting to annotation
  1324      * processors.  "-Afoo=bar" should be "-Afoo" => "bar".
  1325      */
  1326     public Map<String,String> getOptions() {
  1327         return processorOptions;
  1330     public Messager getMessager() {
  1331         return messager;
  1334     public Filer getFiler() {
  1335         return filer;
  1338     public JavacElements getElementUtils() {
  1339         return elementUtils;
  1342     public JavacTypes getTypeUtils() {
  1343         return typeUtils;
  1346     public SourceVersion getSourceVersion() {
  1347         return Source.toSourceVersion(source);
  1350     public Locale getLocale() {
  1351         return messages.getCurrentLocale();
  1354     public Set<Symbol.PackageSymbol> getSpecifiedPackages() {
  1355         return specifiedPackages;
  1358     // Borrowed from DocletInvoker and apt
  1359     // TODO: remove from apt's Main
  1360     /**
  1361      * Utility method for converting a search path string to an array
  1362      * of directory and JAR file URLs.
  1364      * @param path the search path string
  1365      * @return the resulting array of directory and JAR file URLs
  1366      */
  1367     public static URL[] pathToURLs(String path) {
  1368         StringTokenizer st = new StringTokenizer(path, File.pathSeparator);
  1369         URL[] urls = new URL[st.countTokens()];
  1370         int count = 0;
  1371         while (st.hasMoreTokens()) {
  1372             URL url = fileToURL(new File(st.nextToken()));
  1373             if (url != null) {
  1374                 urls[count++] = url;
  1377         if (urls.length != count) {
  1378             URL[] tmp = new URL[count];
  1379             System.arraycopy(urls, 0, tmp, 0, count);
  1380             urls = tmp;
  1382         return urls;
  1385     /**
  1386      * Returns the directory or JAR file URL corresponding to the specified
  1387      * local file name.
  1389      * @param file the File object
  1390      * @return the resulting directory or JAR file URL, or null if unknown
  1391      */
  1392     private static URL fileToURL(File file) {
  1393         String name;
  1394         try {
  1395             name = file.getCanonicalPath();
  1396         } catch (IOException e) {
  1397             name = file.getAbsolutePath();
  1399         name = name.replace(File.separatorChar, '/');
  1400         if (!name.startsWith("/")) {
  1401             name = "/" + name;
  1403         // If the file does not exist, then assume that it's a directory
  1404         if (!file.isFile()) {
  1405             name = name + "/";
  1407         try {
  1408             return new URL("file", "", name);
  1409         } catch (MalformedURLException e) {
  1410             throw new IllegalArgumentException("file");
  1416     private static final Pattern allMatches = Pattern.compile(".*");
  1418     private static final Pattern noMatches  = Pattern.compile("(\\P{all})+");
  1419     /**
  1420      * Convert import-style string to regex matching that string.  If
  1421      * the string is a valid import-style string, return a regex that
  1422      * won't match anything.
  1423      */
  1424     // TODO: remove version in Apt.java
  1425     public static Pattern importStringToPattern(String s, Processor p, Log log) {
  1426         if (s.equals("*")) {
  1427             return allMatches;
  1428         } else {
  1429             String t = s;
  1430             boolean star = false;
  1432             /*
  1433              * Validate string from factory is legal.  If the string
  1434              * has more than one asterisks or the asterisks does not
  1435              * appear as the last character (preceded by a period),
  1436              * the string is not legal.
  1437              */
  1439             boolean valid = true;
  1440             int index = t.indexOf('*');
  1441             if (index != -1) {
  1442                 // '*' must be last character...
  1443                 if (index == t.length() -1) {
  1444                      // ... and preceeding character must be '.'
  1445                     if ( index-1 >= 0 ) {
  1446                         valid = t.charAt(index-1) == '.';
  1447                         // Strip off ".*$" for identifier checks
  1448                         t = t.substring(0, t.length()-2);
  1450                 } else
  1451                     valid = false;
  1454             // Verify string is off the form (javaId \.)+ or javaId
  1455             if (valid) {
  1456                 String[] javaIds = t.split("\\.", t.length()+2);
  1457                 for(String javaId: javaIds)
  1458                     valid &= SourceVersion.isIdentifier(javaId);
  1461             if (!valid) {
  1462                 log.warning("proc.malformed.supported.string", s, p.getClass().getName());
  1463                 return noMatches; // won't match any valid identifier
  1466             String s_prime = s.replaceAll("\\.", "\\\\.");
  1468             if (s_prime.endsWith("*")) {
  1469                 s_prime =  s_prime.substring(0, s_prime.length() - 1) + ".+";
  1472             return Pattern.compile(s_prime);
  1476     /**
  1477      * For internal use by Sun Microsystems only.  This method will be
  1478      * removed without warning.
  1479      */
  1480     public Context getContext() {
  1481         return context;
  1484     public String toString() {
  1485         return "javac ProcessingEnvironment";
  1488     public static boolean isValidOptionName(String optionName) {
  1489         for(String s : optionName.split("\\.", -1)) {
  1490             if (!SourceVersion.isIdentifier(s))
  1491                 return false;
  1493         return true;

mercurial