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  */
  25 
  26 package com.sun.tools.javac.processing;
  27 
  28 
  29 import java.lang.reflect.*;
  30 import java.util.*;
  31 import java.util.regex.*;
  32 
  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;
  40 
  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;
  52 
  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;
  78 
  79 import static javax.tools.StandardLocation.*;
  80 
  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;
  92 
  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;
 100 
 101     private final JavacFiler filer;
 102     private final JavacMessager messager;
 103     private final JavacElements elementUtils;
 104     private final JavacTypes typeUtils;
 105 
 106     /**
 107      * Holds relevant state history of which processors have been
 108      * used.
 109      */
 110     private DiscoveredProcessors discoveredProcs;
 111 
 112     /**
 113      * Map of processor-specific options.
 114      */
 115     private final Map<String, String> processorOptions;
 116 
 117     /**
 118      */
 119     private final Set<String> unmatchedProcessorOptions;
 120 
 121     /**
 122      * Annotations implicitly processed and claimed by javac.
 123      */
 124     private final Set<String> platformAnnotations;
 125 
 126     /**
 127      * Set of packages given on command line.
 128      */
 129     private Set<PackageSymbol> specifiedPackages = Collections.emptySet();
 130 
 131     /** The log to be used for error reporting.
 132      */
 133     Log log;
 134 
 135     /**
 136      * Source level of the compile.
 137      */
 138     Source source;
 139 
 140     private ClassLoader processorClassLoader;
 141 
 142     /**
 143      * JavacMessages object used for localization
 144      */
 145     private JavacMessages messages;
 146 
 147     private Context context;
 148 
 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;
 163 
 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     }
 175 
 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     }
 187 
 188     private void initProcessorIterator(Context context, Iterable<? extends Processor> processors) {
 189         Log   log   = Log.instance(context);
 190         Iterator<? extends Processor> processorIterator;
 191 
 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);
 212 
 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     }
 235 
 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);
 249 
 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);
 255 
 256             if (needClassLoader(options.get("-processor"), workingPath) )
 257                 handleException(key, e);
 258 
 259         } else {
 260             handleException(key, e);
 261         }
 262 
 263         java.util.List<Processor> pl = Collections.emptyList();
 264         return pl.iterator();
 265     }
 266 
 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     }
 280 
 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;
 294 
 295         ServiceIterator(ClassLoader classLoader, Log log) {
 296             String loadMethodName;
 297 
 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                 }
 316 
 317                 // java.util.ServiceLoader.load or sun.misc.Service.providers
 318                 Method loadMethod = loaderClass.getMethod(loadMethodName,
 319                                                           Class.class,
 320                                                           ClassLoader.class);
 321 
 322                 Object result = loadMethod.invoke(null,
 323                                                   Processor.class,
 324                                                   classLoader);
 325 
 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                 }
 333 
 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         }
 341 
 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         }
 353 
 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         }
 367 
 368         public void remove() {
 369             throw new UnsupportedOperationException();
 370         }
 371 
 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     }
 384 
 385 
 386     private static class NameProcessIterator implements Iterator<Processor> {
 387         Processor nextProc = null;
 388         Iterator<String> names;
 389         ClassLoader processorCL;
 390         Log log;
 391 
 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         }
 397 
 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();
 406 
 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                 }
 428 
 429             }
 430         }
 431 
 432         public Processor next() {
 433             if (hasNext()) {
 434                 Processor p = nextProc;
 435                 nextProc = null;
 436                 return p;
 437             } else
 438                 throw new NoSuchElementException();
 439         }
 440 
 441         public void remove () {
 442             throw new UnsupportedOperationException();
 443         }
 444     }
 445 
 446     public boolean atLeastOneProcessor() {
 447         return discoveredProcs.iterator().hasNext();
 448     }
 449 
 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>();
 454 
 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;
 460 
 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         }
 471 
 472         return Collections.unmodifiableMap(tempOptions);
 473     }
 474 
 475     private Set<String> initUnmatchedProcessorOptions() {
 476         Set<String> unmatchedProcessorOptions = new HashSet<String>();
 477         unmatchedProcessorOptions.addAll(processorOptions.keySet());
 478         return unmatchedProcessorOptions;
 479     }
 480 
 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;
 494 
 495         ProcessorState(Processor p, Log log, Source source, ProcessingEnvironment env) {
 496             processor = p;
 497             contributed = false;
 498 
 499             try {
 500                 processor.init(env);
 501 
 502                 checkSourceVersionCompatibility(source, log);
 503 
 504                 supportedAnnotationPatterns = new ArrayList<Pattern>();
 505                 for (String importString : processor.getSupportedAnnotationTypes()) {
 506                     supportedAnnotationPatterns.add(importStringToPattern(importString,
 507                                                                           processor,
 508                                                                           log));
 509                 }
 510 
 511                 supportedOptionNames = new ArrayList<String>();
 512                 for (String optionName : processor.getSupportedOptions() ) {
 513                     if (checkOptionName(optionName, log))
 514                         supportedOptionNames.add(optionName);
 515                 }
 516 
 517             } catch (Throwable t) {
 518                 throw new AnnotationProcessingError(t);
 519             }
 520         }
 521 
 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();
 530 
 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         }
 538 
 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         }
 547 
 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         }
 555 
 556         /**
 557          * Remove options that are matched by this processor.
 558          */
 559         public void removeSupportedOptions(Set<String> unmatchedProcessorOptions) {
 560             unmatchedProcessorOptions.removeAll(supportedOptionNames);
 561         }
 562     }
 563 
 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> {
 574 
 575         class ProcessorStateIterator implements Iterator<ProcessorState> {
 576             DiscoveredProcessors psi;
 577             Iterator<ProcessorState> innerIter;
 578             boolean onProcInterator;
 579 
 580             ProcessorStateIterator(DiscoveredProcessors psi) {
 581                 this.psi = psi;
 582                 this.innerIter = psi.procStateList.iterator();
 583                 this.onProcInterator = false;
 584             }
 585 
 586             public ProcessorState next() {
 587                 if (!onProcInterator) {
 588                     if (innerIter.hasNext())
 589                         return innerIter.next();
 590                     else
 591                         onProcInterator = true;
 592                 }
 593 
 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             }
 602 
 603             public boolean hasNext() {
 604                 if (onProcInterator)
 605                     return  psi.processorIterator.hasNext();
 606                 else
 607                     return innerIter.hasNext() || psi.processorIterator.hasNext();
 608             }
 609 
 610             public void remove () {
 611                 throw new UnsupportedOperationException();
 612             }
 613 
 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         }
 630 
 631         Iterator<? extends Processor> processorIterator;
 632         ArrayList<ProcessorState>  procStateList;
 633 
 634         public ProcessorStateIterator iterator() {
 635             return new ProcessorStateIterator(this);
 636         }
 637 
 638         DiscoveredProcessors(Iterator<? extends Processor> processorIterator) {
 639             this.processorIterator = processorIterator;
 640             this.procStateList = new ArrayList<ProcessorState>();
 641         }
 642 
 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     }
 653 
 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);
 660 
 661         Map<String, TypeElement> unmatchedAnnotations =
 662             new HashMap<String, TypeElement>(annotationsPresent.size());
 663 
 664         for(TypeElement a  : annotationsPresent) {
 665                 unmatchedAnnotations.put(a.getQualifiedName().toString(),
 666                                          a);
 667         }
 668 
 669         // Give "*" processors a chance to match
 670         if (unmatchedAnnotations.size() == 0)
 671             unmatchedAnnotations.put("", null);
 672 
 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.
 679 
 680         Set<Element> rootElements = new LinkedHashSet<Element>();
 681         rootElements.addAll(topLevelClasses);
 682         rootElements.addAll(packageInfoFiles);
 683         rootElements = Collections.unmodifiableSet(rootElements);
 684 
 685         RoundEnvironment renv = new JavacRoundEnvironment(false,
 686                                                           false,
 687                                                           rootElements,
 688                                                           JavacProcessingEnvironment.this);
 689 
 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             }
 702 
 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);
 708 
 709                 if (printProcessorInfo || verbose) {
 710                     xout.println(Log.getLocalizedString("x.print.processor.info",
 711                                                         ps.processor.getClass().getName(),
 712                                                         matchedNames.toString(),
 713                                                         processingResult));
 714                 }
 715 
 716                 if (processingResult) {
 717                     unmatchedAnnotations.keySet().removeAll(matchedNames);
 718                 }
 719 
 720             }
 721         }
 722         unmatchedAnnotations.remove("");
 723 
 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         }
 733 
 734         // Run contributing processors that haven't run yet
 735         psi.runContributingProcs(renv);
 736 
 737         // Debugging
 738         if (options.get("displayFilerState") != null)
 739             filer.displayState();
 740     }
 741 
 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;
 749 
 750         public ComputeAnnotationSet(Elements elements) {
 751             super();
 752             this.elements = elements;
 753         }
 754 
 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         }
 760 
 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     }
 771 
 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     }
 786 
 787 
 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 {
 795 
 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);
 800 
 801 
 802         AnnotationCollector collector = new AnnotationCollector();
 803 
 804         JavaCompiler compiler = JavaCompiler.instance(context);
 805         compiler.todo.clear(); // free the compiler's resources
 806 
 807         int round = 0;
 808 
 809         // List<JCAnnotation> annotationsPresentInSource = collector.findAnnotations(roots);
 810         List<ClassSymbol> topLevelClasses = getTopLevelClasses(roots);
 811 
 812         for (ClassSymbol classSym : classSymbols)
 813             topLevelClasses = topLevelClasses.prepend(classSym);
 814         List<PackageSymbol> packageInfoFiles =
 815             getPackageInfoFiles(roots);
 816 
 817         Set<PackageSymbol> specifiedPackages = new LinkedHashSet<PackageSymbol>();
 818         for (PackageSymbol psym : pckSymbols)
 819             specifiedPackages.add(psym);
 820         this.specifiedPackages = Collections.unmodifiableSet(specifiedPackages);
 821 
 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);
 829 
 830         Context currentContext = context;
 831 
 832         int roundNumber = 0;
 833         boolean errorStatus = false;
 834 
 835         runAround:
 836         while(true) {
 837             if (fatalErrors && compiler.errorCount() != 0) {
 838                 errorStatus = true;
 839                 break runAround;
 840             }
 841 
 842             this.context = currentContext;
 843             roundNumber++;
 844             printRoundInfo(xout, roundNumber, topLevelClasses, annotationsPresent, false);
 845 
 846             if (taskListener != null)
 847                 taskListener.started(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING_ROUND));
 848 
 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             }
 855 
 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();
 871 
 872                     compiler.close(false);
 873                     currentContext = contextForNextRound(currentContext, true);
 874 
 875                     JavaFileManager fileManager = currentContext.get(JavaFileManager.class);
 876 
 877                     List<JavaFileObject> fileObjects = List.nil();
 878                     for (JavaFileObject jfo : filer.getGeneratedSourceFileObjects() ) {
 879                         fileObjects = fileObjects.prepend(jfo);
 880                     }
 881 
 882 
 883                     compiler = JavaCompiler.instance(currentContext);
 884                     List<JCCompilationUnit> parsedFiles = compiler.parseFiles(fileObjects);
 885                     roots = cleanTrees(roots).reverse();
 886 
 887 
 888                     for (JCCompilationUnit unit : parsedFiles)
 889                         roots = roots.prepend(unit);
 890                     roots = roots.reverse();
 891 
 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);
 899 
 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();
 906 
 907                         ListBuffer<PackageSymbol> pif = new ListBuffer<PackageSymbol>();
 908                         pif.appendList(getPackageInfoFiles(parsedFiles));
 909                         pif.appendList(getPackageInfoFilesFromClasses(newClasses));
 910                         packageInfoFiles = pif.toList();
 911 
 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);
 917 
 918                         updateProcessingState(currentContext, false);
 919                     }
 920                 } else
 921                     break runAround; // No new files
 922             }
 923         }
 924         runLastRound(xout, roundNumber, errorStatus, taskListener);
 925 
 926         compiler.close(false);
 927         currentContext = contextForNextRound(currentContext, true);
 928         compiler = JavaCompiler.instance(currentContext);
 929         filer.newRound(currentContext, true);
 930         filer.warnIfUnclosedFiles();
 931         warnIfUnmatchedOptions();
 932 
 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();
 945 
 946 
 947         // Free resources
 948         this.close();
 949 
 950         if (taskListener != null)
 951             taskListener.finished(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING));
 952 
 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;
 967 
 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         }
 978 
 979         return compiler;
 980     }
 981 
 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);
 991 
 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));
 999 
1000         try {
1001             discoveredProcs.iterator().runContributingProcs(renv);
1002         } finally {
1003             if (taskListener != null)
1004                 taskListener.finished(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING_ROUND));
1005         }
1006     }
1007 
1008     private void updateProcessingState(Context currentContext, boolean lastRound) {
1009         filer.newRound(currentContext, lastRound);
1010         messager.newRound(currentContext);
1011 
1012         elementUtils.setContext(currentContext);
1013         typeUtils.setContext(currentContext);
1014     }
1015 
1016     private void warnIfUnmatchedOptions() {
1017         if (!unmatchedProcessorOptions.isEmpty()) {
1018             log.warning("proc.unmatched.processor.options", unmatchedProcessorOptions.toString());
1019         }
1020     }
1021 
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));
1033         }
1034     }
1035 
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();
1040 
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);
1058         }
1059         return list.reverse();
1060     }
1061 
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();
1072     }
1073 
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);
1080                 }
1081             }
1082         }
1083         return classes.reverse();
1084     }
1085 
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);
1091             }
1092         }
1093         return classes.reverse();
1094     }
1095 
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);
1101             }
1102         }
1103         return packages.reverse();
1104     }
1105 
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);
1111             }
1112         }
1113         return packages.reverse();
1114     }
1115 
1116     private boolean isPkgInfo(JavaFileObject fo, JavaFileObject.Kind kind) {
1117         return fo.isNameCompatible("package-info", kind);
1118     }
1119 
1120     private boolean isPkgInfo(ClassSymbol sym) {
1121         return isPkgInfo(sym.classfile, JavaFileObject.Kind.CLASS) && (sym.packge().package_info == sym);
1122     }
1123 
1124     private Context contextForNextRound(Context context, boolean shareNames)
1125         throws IOException
1126     {
1127         Context next = new Context();
1128 
1129         Options options = Options.instance(context);
1130         assert options != null;
1131         next.put(Options.optionsKey, options);
1132 
1133         PrintWriter out = context.get(Log.outKey);
1134         assert out != null;
1135         next.put(Log.outKey, out);
1136 
1137         if (shareNames) {
1138             Names names = Names.instance(context);
1139             assert names != null;
1140             next.put(Names.namesKey, names);
1141         }
1142 
1143         DiagnosticListener<?> dl = context.get(DiagnosticListener.class);
1144         if (dl != null)
1145             next.put(DiagnosticListener.class, dl);
1146 
1147         TaskListener tl = context.get(TaskListener.class);
1148         if (tl != null)
1149             next.put(TaskListener.class, tl);
1150 
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);
1156         }
1157 
1158         Names names = Names.instance(context);
1159         assert names != null;
1160         next.put(Names.namesKey, names);
1161 
1162         Keywords keywords = Keywords.instance(context);
1163         assert(keywords != null);
1164         next.put(Keywords.keywordsKey, keywords);
1165 
1166         JavaCompiler oldCompiler = JavaCompiler.instance(context);
1167         JavaCompiler nextCompiler = JavaCompiler.instance(next);
1168         nextCompiler.initRound(oldCompiler);
1169 
1170         JavacTaskImpl task = context.get(JavacTaskImpl.class);
1171         if (task != null) {
1172             next.put(JavacTaskImpl.class, task);
1173             task.updateContext(next);
1174         }
1175 
1176         context.clear();
1177         return next;
1178     }
1179 
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;
1187 
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);
1197             }
1198             catch (ServiceProxy.ServiceConfigurationError e) {
1199                 log.error("proc.bad.config.file", e.getLocalizedMessage());
1200                 return true;
1201             }
1202         }
1203 
1204         return false;
1205     }
1206 
1207     private class AnnotationCollector extends TreeScanner {
1208         List<JCTree> path = List.nil();
1209         static final boolean verbose = false;
1210         List<JCAnnotation> annotations = List.nil();
1211 
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();
1218         }
1219 
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;
1229         }
1230 
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("  ");
1239                 }
1240                 System.err.print(sb);
1241                 System.err.println(node);
1242             }
1243         }
1244     }
1245 
1246     private static <T extends JCTree> List<T> cleanTrees(List<T> nodes) {
1247         for (T node : nodes)
1248             treeCleaner.scan(node);
1249         return nodes;
1250     }
1251 
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;
1257             }
1258             public void visitTopLevel(JCCompilationUnit node) {
1259                 node.packge = null;
1260                 super.visitTopLevel(node);
1261             }
1262             public void visitClassDef(JCClassDecl node) {
1263                 node.sym = null;
1264                 super.visitClassDef(node);
1265             }
1266             public void visitMethodDef(JCMethodDecl node) {
1267                 node.sym = null;
1268                 super.visitMethodDef(node);
1269             }
1270             public void visitVarDef(JCVariableDecl node) {
1271                 node.sym = null;
1272                 super.visitVarDef(node);
1273             }
1274             public void visitNewClass(JCNewClass node) {
1275                 node.constructor = null;
1276                 super.visitNewClass(node);
1277             }
1278             public void visitAssignop(JCAssignOp node) {
1279                 node.operator = null;
1280                 super.visitAssignop(node);
1281             }
1282             public void visitUnary(JCUnary node) {
1283                 node.operator = null;
1284                 super.visitUnary(node);
1285             }
1286             public void visitBinary(JCBinary node) {
1287                 node.operator = null;
1288                 super.visitBinary(node);
1289             }
1290             public void visitSelect(JCFieldAccess node) {
1291                 node.sym = null;
1292                 super.visitSelect(node);
1293             }
1294             public void visitIdent(JCIdent node) {
1295                 node.sym = null;
1296                 super.visitIdent(node);
1297             }
1298         };
1299 
1300 
1301     private boolean moreToDo() {
1302         return filer.newFiles();
1303     }
1304 
1305     /**
1306      * {@inheritdoc}
1307      *
1308      * Command line options suitable for presenting to annotation
1309      * processors.  "-Afoo=bar" should be "-Afoo" => "bar".
1310      */
1311     public Map<String,String> getOptions() {
1312         return processorOptions;
1313     }
1314 
1315     public Messager getMessager() {
1316         return messager;
1317     }
1318 
1319     public Filer getFiler() {
1320         return filer;
1321     }
1322 
1323     public JavacElements getElementUtils() {
1324         return elementUtils;
1325     }
1326 
1327     public JavacTypes getTypeUtils() {
1328         return typeUtils;
1329     }
1330 
1331     public SourceVersion getSourceVersion() {
1332         return Source.toSourceVersion(source);
1333     }
1334 
1335     public Locale getLocale() {
1336         return messages.getCurrentLocale();
1337     }
1338 
1339     public Set<Symbol.PackageSymbol> getSpecifiedPackages() {
1340         return specifiedPackages;
1341     }
1342 
1343     // Borrowed from DocletInvoker and apt
1344     // TODO: remove from apt's Main
1345     /**
1346      * Utility method for converting a search path string to an array
1347      * of directory and JAR file URLs.
1348      *
1349      * @param path the search path string
1350      * @return the resulting array of directory and JAR file URLs
1351      */
1352     public static URL[] pathToURLs(String path) {
1353         StringTokenizer st = new StringTokenizer(path, File.pathSeparator);
1354         URL[] urls = new URL[st.countTokens()];
1355         int count = 0;
1356         while (st.hasMoreTokens()) {
1357             URL url = fileToURL(new File(st.nextToken()));
1358             if (url != null) {
1359                 urls[count++] = url;
1360             }
1361         }
1362         if (urls.length != count) {
1363             URL[] tmp = new URL[count];
1364             System.arraycopy(urls, 0, tmp, 0, count);
1365             urls = tmp;
1366         }
1367         return urls;
1368     }
1369 
1370     /**
1371      * Returns the directory or JAR file URL corresponding to the specified
1372      * local file name.
1373      *
1374      * @param file the File object
1375      * @return the resulting directory or JAR file URL, or null if unknown
1376      */
1377     private static URL fileToURL(File file) {
1378         String name;
1379         try {
1380             name = file.getCanonicalPath();
1381         } catch (IOException e) {
1382             name = file.getAbsolutePath();
1383         }
1384         name = name.replace(File.separatorChar, '/');
1385         if (!name.startsWith("/")) {
1386             name = "/" + name;
1387         }
1388         // If the file does not exist, then assume that it's a directory
1389         if (!file.isFile()) {
1390             name = name + "/";
1391         }
1392         try {
1393             return new URL("file", "", name);
1394         } catch (MalformedURLException e) {
1395             throw new IllegalArgumentException("file");
1396         }
1397     }
1398 
1399 
1400 
1401     private static final Pattern allMatches = Pattern.compile(".*");
1402 
1403     private static final Pattern noMatches  = Pattern.compile("(\\P{all})+");
1404     /**
1405      * Convert import-style string to regex matching that string.  If
1406      * the string is a valid import-style string, return a regex that
1407      * won't match anything.
1408      */
1409     // TODO: remove version in Apt.java
1410     public static Pattern importStringToPattern(String s, Processor p, Log log) {
1411         if (s.equals("*")) {
1412             return allMatches;
1413         } else {
1414             String t = s;
1415             boolean star = false;
1416 
1417             /*
1418              * Validate string from factory is legal.  If the string
1419              * has more than one asterisks or the asterisks does not
1420              * appear as the last character (preceded by a period),
1421              * the string is not legal.
1422              */
1423 
1424             boolean valid = true;
1425             int index = t.indexOf('*');
1426             if (index != -1) {
1427                 // '*' must be last character...
1428                 if (index == t.length() -1) {
1429                      // ... and preceeding character must be '.'
1430                     if ( index-1 >= 0 ) {
1431                         valid = t.charAt(index-1) == '.';
1432                         // Strip off ".*$" for identifier checks
1433                         t = t.substring(0, t.length()-2);
1434                     }
1435                 } else
1436                     valid = false;
1437             }
1438 
1439             // Verify string is off the form (javaId \.)+ or javaId
1440             if (valid) {
1441                 String[] javaIds = t.split("\\.", t.length()+2);
1442                 for(String javaId: javaIds)
1443                     valid &= SourceVersion.isIdentifier(javaId);
1444             }
1445 
1446             if (!valid) {
1447                 log.warning("proc.malformed.supported.string", s, p.getClass().getName());
1448                 return noMatches; // won't match any valid identifier
1449             }
1450 
1451             String s_prime = s.replaceAll("\\.", "\\\\.");
1452 
1453             if (s_prime.endsWith("*")) {
1454                 s_prime =  s_prime.substring(0, s_prime.length() - 1) + ".+";
1455             }
1456 
1457             return Pattern.compile(s_prime);
1458         }
1459     }
1460 
1461     /**
1462      * For internal use by Sun Microsystems only.  This method will be
1463      * removed without warning.
1464      */
1465     public Context getContext() {
1466         return context;
1467     }
1468 
1469     public String toString() {
1470         return "javac ProcessingEnvironment";
1471     }
1472 
1473     public static boolean isValidOptionName(String optionName) {
1474         for(String s : optionName.split("\\.", -1)) {
1475             if (!SourceVersion.isIdentifier(s))
1476                 return false;
1477         }
1478         return true;
1479     }
1480 }