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