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