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

Fri, 29 Jan 2010 16:54:52 -0800

author
jjg
date
Fri, 29 Jan 2010 16:54:52 -0800
changeset 483
8e638442522a
parent 382
25f15fdd168a
child 489
4b4e282a3146
permissions
-rw-r--r--

6499119: Created package-info class file modeled improperly
6920317: package-info.java file has to be specified on the javac cmdline, else it will not be avail.
Reviewed-by: darcy

     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                     List<JavaFileObject> fileObjects = List.nil();
   878                     for (JavaFileObject jfo : filer.getGeneratedSourceFileObjects() ) {
   879                         fileObjects = fileObjects.prepend(jfo);
   880                     }
   883                     compiler = JavaCompiler.instance(currentContext);
   884                     List<JCCompilationUnit> parsedFiles = compiler.parseFiles(fileObjects);
   885                     roots = cleanTrees(roots).reverse();
   888                     for (JCCompilationUnit unit : parsedFiles)
   889                         roots = roots.prepend(unit);
   890                     roots = roots.reverse();
   892                     // Check for errors after parsing
   893                     if (compiler.parseErrors()) {
   894                         errorStatus = true;
   895                         break runAround;
   896                     } else {
   897                         List<ClassSymbol> newClasses = enterNewClassFiles(currentContext);
   898                         compiler.enterTrees(roots);
   900                         // annotationsPresentInSource =
   901                         // collector.findAnnotations(parsedFiles);
   902                         ListBuffer<ClassSymbol> tlc = new ListBuffer<ClassSymbol>();
   903                         tlc.appendList(getTopLevelClasses(parsedFiles));
   904                         tlc.appendList(getTopLevelClassesFromClasses(newClasses));
   905                         topLevelClasses  = tlc.toList();
   907                         ListBuffer<PackageSymbol> pif = new ListBuffer<PackageSymbol>();
   908                         pif.appendList(getPackageInfoFiles(parsedFiles));
   909                         pif.appendList(getPackageInfoFilesFromClasses(newClasses));
   910                         packageInfoFiles = pif.toList();
   912                         annotationsPresent = new LinkedHashSet<TypeElement>();
   913                         for (ClassSymbol classSym : topLevelClasses)
   914                             annotationComputer.scan(classSym, annotationsPresent);
   915                         for (PackageSymbol pkgSym : packageInfoFiles)
   916                             annotationComputer.scan(pkgSym, annotationsPresent);
   918                         updateProcessingState(currentContext, false);
   919                     }
   920                 } else
   921                     break runAround; // No new files
   922             }
   923         }
   924         runLastRound(xout, roundNumber, errorStatus, taskListener);
   926         compiler.close(false);
   927         currentContext = contextForNextRound(currentContext, true);
   928         compiler = JavaCompiler.instance(currentContext);
   929         filer.newRound(currentContext, true);
   930         filer.warnIfUnclosedFiles();
   931         warnIfUnmatchedOptions();
   933        /*
   934         * If an annotation processor raises an error in a round,
   935         * that round runs to completion and one last round occurs.
   936         * The last round may also occur because no more source or
   937         * class files have been generated.  Therefore, if an error
   938         * was raised on either of the last *two* rounds, the compile
   939         * should exit with a nonzero exit code.  The current value of
   940         * errorStatus holds whether or not an error was raised on the
   941         * second to last round; errorRaised() gives the error status
   942         * of the last round.
   943         */
   944         errorStatus = errorStatus || messager.errorRaised();
   947         // Free resources
   948         this.close();
   950         if (taskListener != null)
   951             taskListener.finished(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING));
   953         if (errorStatus) {
   954             compiler.log.nerrors += messager.errorCount();
   955             if (compiler.errorCount() == 0)
   956                 compiler.log.nerrors++;
   957         } else if (procOnly && !foundTypeProcessors) {
   958             compiler.todo.clear();
   959         } else { // Final compilation
   960             compiler.close(false);
   961             currentContext = contextForNextRound(currentContext, true);
   962             this.context = currentContext;
   963             updateProcessingState(currentContext, true);
   964             compiler = JavaCompiler.instance(currentContext);
   965             if (procOnly && foundTypeProcessors)
   966                 compiler.shouldStopPolicy = CompileState.FLOW;
   968             if (true) {
   969                 compiler.enterTrees(cleanTrees(roots));
   970             } else {
   971                 List<JavaFileObject> fileObjects = List.nil();
   972                 for (JCCompilationUnit unit : roots)
   973                     fileObjects = fileObjects.prepend(unit.getSourceFile());
   974                 roots = null;
   975                 compiler.enterTrees(compiler.parseFiles(fileObjects.reverse()));
   976             }
   977         }
   979         return compiler;
   980     }
   982     // Call the last round of annotation processing
   983     private void runLastRound(PrintWriter xout,
   984                               int roundNumber,
   985                               boolean errorStatus,
   986                               TaskListener taskListener) throws IOException {
   987         roundNumber++;
   988         List<ClassSymbol> noTopLevelClasses = List.nil();
   989         Set<TypeElement> noAnnotations =  Collections.emptySet();
   990         printRoundInfo(xout, roundNumber, noTopLevelClasses, noAnnotations, true);
   992         Set<Element> emptyRootElements = Collections.emptySet(); // immutable
   993         RoundEnvironment renv = new JavacRoundEnvironment(true,
   994                                                           errorStatus,
   995                                                           emptyRootElements,
   996                                                           JavacProcessingEnvironment.this);
   997         if (taskListener != null)
   998             taskListener.started(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING_ROUND));
  1000         try {
  1001             discoveredProcs.iterator().runContributingProcs(renv);
  1002         } finally {
  1003             if (taskListener != null)
  1004                 taskListener.finished(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING_ROUND));
  1008     private void updateProcessingState(Context currentContext, boolean lastRound) {
  1009         filer.newRound(currentContext, lastRound);
  1010         messager.newRound(currentContext);
  1012         elementUtils.setContext(currentContext);
  1013         typeUtils.setContext(currentContext);
  1016     private void warnIfUnmatchedOptions() {
  1017         if (!unmatchedProcessorOptions.isEmpty()) {
  1018             log.warning("proc.unmatched.processor.options", unmatchedProcessorOptions.toString());
  1022     private void printRoundInfo(PrintWriter xout,
  1023                                 int roundNumber,
  1024                                 List<ClassSymbol> topLevelClasses,
  1025                                 Set<TypeElement> annotationsPresent,
  1026                                 boolean lastRound) {
  1027         if (printRounds || verbose) {
  1028             xout.println(Log.getLocalizedString("x.print.rounds",
  1029                                                 roundNumber,
  1030                                                 "{" + topLevelClasses.toString(", ") + "}",
  1031                                                 annotationsPresent,
  1032                                                 lastRound));
  1036     private List<ClassSymbol> enterNewClassFiles(Context currentContext) {
  1037         ClassReader reader = ClassReader.instance(currentContext);
  1038         Names names = Names.instance(currentContext);
  1039         List<ClassSymbol> list = List.nil();
  1041         for (Map.Entry<String,JavaFileObject> entry : filer.getGeneratedClasses().entrySet()) {
  1042             Name name = names.fromString(entry.getKey());
  1043             JavaFileObject file = entry.getValue();
  1044             if (file.getKind() != JavaFileObject.Kind.CLASS)
  1045                 throw new AssertionError(file);
  1046             ClassSymbol cs;
  1047             if (isPkgInfo(file, JavaFileObject.Kind.CLASS)) {
  1048                 Name packageName = Convert.packagePart(name);
  1049                 PackageSymbol p = reader.enterPackage(packageName);
  1050                 if (p.package_info == null)
  1051                     p.package_info = reader.enterClass(Convert.shortName(name), p);
  1052                 cs = p.package_info;
  1053                 if (cs.classfile == null)
  1054                     cs.classfile = file;
  1055             } else
  1056                 cs = reader.enterClass(name, file);
  1057             list = list.prepend(cs);
  1059         return list.reverse();
  1062     /**
  1063      * Free resources related to annotation processing.
  1064      */
  1065     public void close() throws IOException {
  1066         filer.close();
  1067         if (discoveredProcs != null) // Make calling close idempotent
  1068             discoveredProcs.close();
  1069         discoveredProcs = null;
  1070         if (processorClassLoader != null && processorClassLoader instanceof Closeable)
  1071             ((Closeable) processorClassLoader).close();
  1074     private List<ClassSymbol> getTopLevelClasses(List<? extends JCCompilationUnit> units) {
  1075         List<ClassSymbol> classes = List.nil();
  1076         for (JCCompilationUnit unit : units) {
  1077             for (JCTree node : unit.defs) {
  1078                 if (node.getTag() == JCTree.CLASSDEF) {
  1079                     classes = classes.prepend(((JCClassDecl) node).sym);
  1083         return classes.reverse();
  1086     private List<ClassSymbol> getTopLevelClassesFromClasses(List<? extends ClassSymbol> syms) {
  1087         List<ClassSymbol> classes = List.nil();
  1088         for (ClassSymbol sym : syms) {
  1089             if (!isPkgInfo(sym)) {
  1090                 classes = classes.prepend(sym);
  1093         return classes.reverse();
  1096     private List<PackageSymbol> getPackageInfoFiles(List<? extends JCCompilationUnit> units) {
  1097         List<PackageSymbol> packages = List.nil();
  1098         for (JCCompilationUnit unit : units) {
  1099             if (isPkgInfo(unit.sourcefile, JavaFileObject.Kind.SOURCE)) {
  1100                 packages = packages.prepend(unit.packge);
  1103         return packages.reverse();
  1106     private List<PackageSymbol> getPackageInfoFilesFromClasses(List<? extends ClassSymbol> syms) {
  1107         List<PackageSymbol> packages = List.nil();
  1108         for (ClassSymbol sym : syms) {
  1109             if (isPkgInfo(sym)) {
  1110                 packages = packages.prepend((PackageSymbol) sym.owner);
  1113         return packages.reverse();
  1116     private boolean isPkgInfo(JavaFileObject fo, JavaFileObject.Kind kind) {
  1117         return fo.isNameCompatible("package-info", kind);
  1120     private boolean isPkgInfo(ClassSymbol sym) {
  1121         return isPkgInfo(sym.classfile, JavaFileObject.Kind.CLASS) && (sym.packge().package_info == sym);
  1124     private Context contextForNextRound(Context context, boolean shareNames)
  1125         throws IOException
  1127         Context next = new Context();
  1129         Options options = Options.instance(context);
  1130         assert options != null;
  1131         next.put(Options.optionsKey, options);
  1133         PrintWriter out = context.get(Log.outKey);
  1134         assert out != null;
  1135         next.put(Log.outKey, out);
  1137         if (shareNames) {
  1138             Names names = Names.instance(context);
  1139             assert names != null;
  1140             next.put(Names.namesKey, names);
  1143         DiagnosticListener<?> dl = context.get(DiagnosticListener.class);
  1144         if (dl != null)
  1145             next.put(DiagnosticListener.class, dl);
  1147         TaskListener tl = context.get(TaskListener.class);
  1148         if (tl != null)
  1149             next.put(TaskListener.class, tl);
  1151         JavaFileManager jfm = context.get(JavaFileManager.class);
  1152         assert jfm != null;
  1153         next.put(JavaFileManager.class, jfm);
  1154         if (jfm instanceof JavacFileManager) {
  1155             ((JavacFileManager)jfm).setContext(next);
  1158         Names names = Names.instance(context);
  1159         assert names != null;
  1160         next.put(Names.namesKey, names);
  1162         Keywords keywords = Keywords.instance(context);
  1163         assert(keywords != null);
  1164         next.put(Keywords.keywordsKey, keywords);
  1166         JavaCompiler oldCompiler = JavaCompiler.instance(context);
  1167         JavaCompiler nextCompiler = JavaCompiler.instance(next);
  1168         nextCompiler.initRound(oldCompiler);
  1170         JavacTaskImpl task = context.get(JavacTaskImpl.class);
  1171         if (task != null) {
  1172             next.put(JavacTaskImpl.class, task);
  1173             task.updateContext(next);
  1176         context.clear();
  1177         return next;
  1180     /*
  1181      * Called retroactively to determine if a class loader was required,
  1182      * after we have failed to create one.
  1183      */
  1184     private boolean needClassLoader(String procNames, Iterable<? extends File> workingpath) {
  1185         if (procNames != null)
  1186             return true;
  1188         String procPath;
  1189         URL[] urls = new URL[1];
  1190         for(File pathElement : workingpath) {
  1191             try {
  1192                 urls[0] = pathElement.toURI().toURL();
  1193                 if (ServiceProxy.hasService(Processor.class, urls))
  1194                     return true;
  1195             } catch (MalformedURLException ex) {
  1196                 throw new AssertionError(ex);
  1198             catch (ServiceProxy.ServiceConfigurationError e) {
  1199                 log.error("proc.bad.config.file", e.getLocalizedMessage());
  1200                 return true;
  1204         return false;
  1207     private class AnnotationCollector extends TreeScanner {
  1208         List<JCTree> path = List.nil();
  1209         static final boolean verbose = false;
  1210         List<JCAnnotation> annotations = List.nil();
  1212         public List<JCAnnotation> findAnnotations(List<? extends JCTree> nodes) {
  1213             annotations = List.nil();
  1214             scan(nodes);
  1215             List<JCAnnotation> found = annotations;
  1216             annotations = List.nil();
  1217             return found.reverse();
  1220         public void scan(JCTree node) {
  1221             if (node == null)
  1222                 return;
  1223             Symbol sym = TreeInfo.symbolFor(node);
  1224             if (sym != null)
  1225                 path = path.prepend(node);
  1226             super.scan(node);
  1227             if (sym != null)
  1228                 path = path.tail;
  1231         public void visitAnnotation(JCAnnotation node) {
  1232             annotations = annotations.prepend(node);
  1233             if (verbose) {
  1234                 StringBuilder sb = new StringBuilder();
  1235                 for (JCTree tree : path.reverse()) {
  1236                     System.err.print(sb);
  1237                     System.err.println(TreeInfo.symbolFor(tree));
  1238                     sb.append("  ");
  1240                 System.err.print(sb);
  1241                 System.err.println(node);
  1246     private static <T extends JCTree> List<T> cleanTrees(List<T> nodes) {
  1247         for (T node : nodes)
  1248             treeCleaner.scan(node);
  1249         return nodes;
  1252     private static TreeScanner treeCleaner = new TreeScanner() {
  1253             public void scan(JCTree node) {
  1254                 super.scan(node);
  1255                 if (node != null)
  1256                     node.type = null;
  1258             public void visitTopLevel(JCCompilationUnit node) {
  1259                 node.packge = null;
  1260                 super.visitTopLevel(node);
  1262             public void visitClassDef(JCClassDecl node) {
  1263                 node.sym = null;
  1264                 super.visitClassDef(node);
  1266             public void visitMethodDef(JCMethodDecl node) {
  1267                 node.sym = null;
  1268                 super.visitMethodDef(node);
  1270             public void visitVarDef(JCVariableDecl node) {
  1271                 node.sym = null;
  1272                 super.visitVarDef(node);
  1274             public void visitNewClass(JCNewClass node) {
  1275                 node.constructor = null;
  1276                 super.visitNewClass(node);
  1278             public void visitAssignop(JCAssignOp node) {
  1279                 node.operator = null;
  1280                 super.visitAssignop(node);
  1282             public void visitUnary(JCUnary node) {
  1283                 node.operator = null;
  1284                 super.visitUnary(node);
  1286             public void visitBinary(JCBinary node) {
  1287                 node.operator = null;
  1288                 super.visitBinary(node);
  1290             public void visitSelect(JCFieldAccess node) {
  1291                 node.sym = null;
  1292                 super.visitSelect(node);
  1294             public void visitIdent(JCIdent node) {
  1295                 node.sym = null;
  1296                 super.visitIdent(node);
  1298             public void visitApply(JCMethodInvocation node) {
  1299                 scan(node.typeargs);
  1300                 super.visitApply(node);
  1302         };
  1305     private boolean moreToDo() {
  1306         return filer.newFiles();
  1309     /**
  1310      * {@inheritdoc}
  1312      * Command line options suitable for presenting to annotation
  1313      * processors.  "-Afoo=bar" should be "-Afoo" => "bar".
  1314      */
  1315     public Map<String,String> getOptions() {
  1316         return processorOptions;
  1319     public Messager getMessager() {
  1320         return messager;
  1323     public Filer getFiler() {
  1324         return filer;
  1327     public JavacElements getElementUtils() {
  1328         return elementUtils;
  1331     public JavacTypes getTypeUtils() {
  1332         return typeUtils;
  1335     public SourceVersion getSourceVersion() {
  1336         return Source.toSourceVersion(source);
  1339     public Locale getLocale() {
  1340         return messages.getCurrentLocale();
  1343     public Set<Symbol.PackageSymbol> getSpecifiedPackages() {
  1344         return specifiedPackages;
  1347     // Borrowed from DocletInvoker and apt
  1348     // TODO: remove from apt's Main
  1349     /**
  1350      * Utility method for converting a search path string to an array
  1351      * of directory and JAR file URLs.
  1353      * @param path the search path string
  1354      * @return the resulting array of directory and JAR file URLs
  1355      */
  1356     public static URL[] pathToURLs(String path) {
  1357         StringTokenizer st = new StringTokenizer(path, File.pathSeparator);
  1358         URL[] urls = new URL[st.countTokens()];
  1359         int count = 0;
  1360         while (st.hasMoreTokens()) {
  1361             URL url = fileToURL(new File(st.nextToken()));
  1362             if (url != null) {
  1363                 urls[count++] = url;
  1366         if (urls.length != count) {
  1367             URL[] tmp = new URL[count];
  1368             System.arraycopy(urls, 0, tmp, 0, count);
  1369             urls = tmp;
  1371         return urls;
  1374     /**
  1375      * Returns the directory or JAR file URL corresponding to the specified
  1376      * local file name.
  1378      * @param file the File object
  1379      * @return the resulting directory or JAR file URL, or null if unknown
  1380      */
  1381     private static URL fileToURL(File file) {
  1382         String name;
  1383         try {
  1384             name = file.getCanonicalPath();
  1385         } catch (IOException e) {
  1386             name = file.getAbsolutePath();
  1388         name = name.replace(File.separatorChar, '/');
  1389         if (!name.startsWith("/")) {
  1390             name = "/" + name;
  1392         // If the file does not exist, then assume that it's a directory
  1393         if (!file.isFile()) {
  1394             name = name + "/";
  1396         try {
  1397             return new URL("file", "", name);
  1398         } catch (MalformedURLException e) {
  1399             throw new IllegalArgumentException("file");
  1405     private static final Pattern allMatches = Pattern.compile(".*");
  1407     private static final Pattern noMatches  = Pattern.compile("(\\P{all})+");
  1408     /**
  1409      * Convert import-style string to regex matching that string.  If
  1410      * the string is a valid import-style string, return a regex that
  1411      * won't match anything.
  1412      */
  1413     // TODO: remove version in Apt.java
  1414     public static Pattern importStringToPattern(String s, Processor p, Log log) {
  1415         if (s.equals("*")) {
  1416             return allMatches;
  1417         } else {
  1418             String t = s;
  1419             boolean star = false;
  1421             /*
  1422              * Validate string from factory is legal.  If the string
  1423              * has more than one asterisks or the asterisks does not
  1424              * appear as the last character (preceded by a period),
  1425              * the string is not legal.
  1426              */
  1428             boolean valid = true;
  1429             int index = t.indexOf('*');
  1430             if (index != -1) {
  1431                 // '*' must be last character...
  1432                 if (index == t.length() -1) {
  1433                      // ... and preceeding character must be '.'
  1434                     if ( index-1 >= 0 ) {
  1435                         valid = t.charAt(index-1) == '.';
  1436                         // Strip off ".*$" for identifier checks
  1437                         t = t.substring(0, t.length()-2);
  1439                 } else
  1440                     valid = false;
  1443             // Verify string is off the form (javaId \.)+ or javaId
  1444             if (valid) {
  1445                 String[] javaIds = t.split("\\.", t.length()+2);
  1446                 for(String javaId: javaIds)
  1447                     valid &= SourceVersion.isIdentifier(javaId);
  1450             if (!valid) {
  1451                 log.warning("proc.malformed.supported.string", s, p.getClass().getName());
  1452                 return noMatches; // won't match any valid identifier
  1455             String s_prime = s.replaceAll("\\.", "\\\\.");
  1457             if (s_prime.endsWith("*")) {
  1458                 s_prime =  s_prime.substring(0, s_prime.length() - 1) + ".+";
  1461             return Pattern.compile(s_prime);
  1465     /**
  1466      * For internal use by Sun Microsystems only.  This method will be
  1467      * removed without warning.
  1468      */
  1469     public Context getContext() {
  1470         return context;
  1473     public String toString() {
  1474         return "javac ProcessingEnvironment";
  1477     public static boolean isValidOptionName(String optionName) {
  1478         for(String s : optionName.split("\\.", -1)) {
  1479             if (!SourceVersion.isIdentifier(s))
  1480                 return false;
  1482         return true;

mercurial