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