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