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