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