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