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