0001: /*
0002: * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved.
0003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004: *
0005: * This code is free software; you can redistribute it and/or modify it
0006: * under the terms of the GNU General Public License version 2 only, as
0007: * published by the Free Software Foundation. Sun designates this
0008: * particular file as subject to the "Classpath" exception as provided
0009: * by Sun in the LICENSE file that accompanied this code.
0010: *
0011: * This code is distributed in the hope that it will be useful, but WITHOUT
0012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0014: * version 2 for more details (a copy is included in the LICENSE file that
0015: * accompanied this code).
0016: *
0017: * You should have received a copy of the GNU General Public License version
0018: * 2 along with this work; if not, write to the Free Software Foundation,
0019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020: *
0021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022: * CA 95054 USA or visit www.sun.com if you need additional information or
0023: * have any questions.
0024: */
0025:
0026: package com.sun.tools.javac.processing;
0027:
0028: import com.sun.source.util.TaskEvent;
0029: import com.sun.source.util.TaskListener;
0030: import com.sun.tools.javac.api.JavacTaskImpl;
0031: import com.sun.tools.javac.util.List;
0032: import com.sun.tools.javac.util.*;
0033: import com.sun.tools.javac.code.*;
0034: import com.sun.tools.javac.code.Symbol.*;
0035: import com.sun.tools.javac.comp.*;
0036: import com.sun.tools.javac.jvm.*;
0037: import com.sun.tools.javac.tree.*;
0038: import com.sun.tools.javac.parser.*;
0039: import com.sun.tools.javac.code.Symbol.*;
0040: import com.sun.tools.javac.model.JavacElements;
0041: import com.sun.tools.javac.model.JavacTypes;
0042: import com.sun.tools.javac.tree.JCTree.*;
0043: import com.sun.tools.javac.main.JavaCompiler;
0044: import java.io.StringWriter;
0045:
0046: import javax.annotation.processing.*;
0047: import javax.lang.model.SourceVersion;
0048: import javax.lang.model.element.AnnotationMirror;
0049: import javax.lang.model.element.Element;
0050: import javax.lang.model.element.TypeElement;
0051: import javax.lang.model.element.PackageElement;
0052: import javax.lang.model.util.*;
0053:
0054: import javax.tools.JavaFileManager;
0055: import javax.tools.StandardJavaFileManager;
0056: import javax.tools.JavaFileObject;
0057: import javax.tools.DiagnosticListener;
0058: import static javax.tools.StandardLocation.*;
0059:
0060: import java.lang.reflect.*;
0061: import java.util.*;
0062: import java.util.regex.*;
0063:
0064: import java.net.URLClassLoader;
0065: import java.net.URL;
0066: import java.io.Closeable;
0067: import java.io.File;
0068: import java.io.PrintWriter;
0069: import java.io.IOException;
0070: import java.net.MalformedURLException;
0071:
0072: /**
0073: * Objects of this class hold and manage the state needed to support
0074: * annotation processing.
0075: *
0076: * <p><b>This is NOT part of any API supported by Sun Microsystems.
0077: * If you write code that depends on this, you do so at your own risk.
0078: * This code and its internal interfaces are subject to change or
0079: * deletion without notice.</b>
0080: */
0081: @Version("@(#)JavacProcessingEnvironment.java 1.36 07/07/13")
0082: public class JavacProcessingEnvironment implements
0083: ProcessingEnvironment, Closeable {
0084: Options options;
0085:
0086: private final boolean printProcessorInfo;
0087: private final boolean printRounds;
0088: private final boolean verbose;
0089: private final boolean lint;
0090: private final boolean procOnly;
0091: private final boolean fatalErrors;
0092:
0093: private final JavacFiler filer;
0094: private final JavacMessager messager;
0095: private final JavacElements elementUtils;
0096: private final JavacTypes typeUtils;
0097:
0098: /**
0099: * Holds relevant state history of which processors have been
0100: * used.
0101: */
0102: private DiscoveredProcessors discoveredProcs;
0103:
0104: /**
0105: * Map of processor-specific options.
0106: */
0107: private final Map<String, String> processorOptions;
0108:
0109: /**
0110: */
0111: private final Set<String> unmatchedProcessorOptions;
0112:
0113: /**
0114: * Annotations implicitly processed and claimed by javac.
0115: */
0116: private final Set<String> platformAnnotations;
0117:
0118: /**
0119: * Set of packages given on command line.
0120: */
0121: private Set<PackageSymbol> specifiedPackages = Collections
0122: .emptySet();
0123:
0124: /** The log to be used for error reporting.
0125: */
0126: Log log;
0127:
0128: /**
0129: * Source level of the compile.
0130: */
0131: Source source;
0132:
0133: private Context context;
0134:
0135: public JavacProcessingEnvironment(Context context,
0136: Iterable<? extends Processor> processors) {
0137: options = Options.instance(context);
0138: this .context = context;
0139: log = Log.instance(context);
0140: source = Source.instance(context);
0141: printProcessorInfo = options.get("-XprintProcessorInfo") != null;
0142: printRounds = options.get("-XprintRounds") != null;
0143: verbose = options.get("-verbose") != null;
0144: lint = options.lint("processing");
0145: procOnly = options.get("-proc:only") != null
0146: || options.get("-Xprint") != null;
0147: fatalErrors = options.get("fatalEnterError") != null;
0148: platformAnnotations = initPlatformAnnotations();
0149:
0150: // Initialize services before any processors are initialzied
0151: // in case processors use them.
0152: filer = new JavacFiler(context);
0153: messager = new JavacMessager(context, this );
0154: elementUtils = new JavacElements(context);
0155: typeUtils = new JavacTypes(context);
0156: processorOptions = initProcessorOptions(context);
0157: unmatchedProcessorOptions = initUnmatchedProcessorOptions();
0158: initProcessorIterator(context, processors);
0159: }
0160:
0161: private Set<String> initPlatformAnnotations() {
0162: Set<String> platformAnnotations = new HashSet<String>();
0163: platformAnnotations.add("java.lang.Deprecated");
0164: platformAnnotations.add("java.lang.Override");
0165: platformAnnotations.add("java.lang.SuppressWarnings");
0166: platformAnnotations.add("java.lang.annotation.Documented");
0167: platformAnnotations.add("java.lang.annotation.Inherited");
0168: platformAnnotations.add("java.lang.annotation.Retention");
0169: platformAnnotations.add("java.lang.annotation.Target");
0170: return Collections.unmodifiableSet(platformAnnotations);
0171: }
0172:
0173: private void initProcessorIterator(Context context,
0174: Iterable<? extends Processor> processors) {
0175: Paths paths = Paths.instance(context);
0176: Log log = Log.instance(context);
0177: Iterator<? extends Processor> processorIterator;
0178:
0179: if (options.get("-Xprint") != null) {
0180: try {
0181: Processor processor = PrintingProcessor.class
0182: .newInstance();
0183: processorIterator = List.of(processor).iterator();
0184: } catch (Throwable t) {
0185: AssertionError assertError = new AssertionError(
0186: "Problem instantiating PrintingProcessor.");
0187: assertError.initCause(t);
0188: throw assertError;
0189: }
0190: } else if (processors != null) {
0191: processorIterator = processors.iterator();
0192: } else {
0193: String processorNames = options.get("-processor");
0194: JavaFileManager fileManager = context
0195: .get(JavaFileManager.class);
0196: try {
0197: // If processorpath is not explicitly set, use the classpath.
0198: ClassLoader processorCL = fileManager
0199: .hasLocation(ANNOTATION_PROCESSOR_PATH) ? fileManager
0200: .getClassLoader(ANNOTATION_PROCESSOR_PATH)
0201: : fileManager.getClassLoader(CLASS_PATH);
0202:
0203: /*
0204: * If the "-processor" option is used, search the appropriate
0205: * path for the named class. Otherwise, use a service
0206: * provider mechanism to create the processor iterator.
0207: */
0208: if (processorNames != null) {
0209: processorIterator = new NameProcessIterator(
0210: processorNames, processorCL, log);
0211: } else {
0212: processorIterator = new ServiceIterator(
0213: processorCL, log);
0214: }
0215: } catch (SecurityException e) {
0216: /*
0217: * A security exception will occur if we can't create a classloader.
0218: * Ignore the exception if, with hindsight, we didn't need it anyway
0219: * (i.e. no processor was specified either explicitly, or implicitly,
0220: * in service configuration file.) Otherwise, we cannot continue.
0221: */
0222: processorIterator = handleServiceLoaderUnavailability(
0223: "proc.cant.create.loader", e);
0224: }
0225: }
0226: discoveredProcs = new DiscoveredProcessors(processorIterator);
0227: }
0228:
0229: /**
0230: * Returns an empty processor iterator if no processors are on the
0231: * relevant path, otherwise if processors are present, logs an
0232: * error. Called when a service loader is unavailable for some
0233: * reason, either because a service loader class cannot be found
0234: * or because a security policy prevents class loaders from being
0235: * created.
0236: *
0237: * @param key The resource key to use to log an error message
0238: * @param e If non-null, pass this exception to Abort
0239: */
0240: private Iterator<Processor> handleServiceLoaderUnavailability(
0241: String key, Exception e) {
0242: JavaFileManager fileManager = context
0243: .get(JavaFileManager.class);
0244:
0245: if (fileManager instanceof JavacFileManager) {
0246: StandardJavaFileManager standardFileManager = (JavacFileManager) fileManager;
0247: Iterable<? extends File> workingPath = fileManager
0248: .hasLocation(ANNOTATION_PROCESSOR_PATH) ? standardFileManager
0249: .getLocation(ANNOTATION_PROCESSOR_PATH)
0250: : standardFileManager.getLocation(CLASS_PATH);
0251:
0252: if (needClassLoader(options.get("-processor"), workingPath))
0253: handleException(key, e);
0254:
0255: } else {
0256: handleException(key, e);
0257: }
0258:
0259: java.util.List<Processor> pl = Collections.emptyList();
0260: return pl.iterator();
0261: }
0262:
0263: /**
0264: * Handle a security exception thrown during initializing the
0265: * Processor iterator.
0266: */
0267: private void handleException(String key, Exception e) {
0268: if (e != null) {
0269: log.error(key, e.getLocalizedMessage());
0270: throw new Abort(e);
0271: } else {
0272: log.error(key);
0273: throw new Abort();
0274: }
0275: }
0276:
0277: /**
0278: * Use a service loader appropriate for the platform to provide an
0279: * iterator over annotations processors. If
0280: * java.util.ServiceLoader is present use it, otherwise, use
0281: * sun.misc.Service, otherwise fail if a loader is needed.
0282: */
0283: private class ServiceIterator implements Iterator<Processor> {
0284: // The to-be-wrapped iterator.
0285: private Iterator<?> iterator;
0286: private Log log;
0287:
0288: ServiceIterator(ClassLoader classLoader, Log log) {
0289: Class<?> loaderClass;
0290: String loadMethodName;
0291: boolean jusl;
0292:
0293: this .log = log;
0294: try {
0295: try {
0296: loaderClass = Class
0297: .forName("java.util.ServiceLoader");
0298: loadMethodName = "load";
0299: jusl = true;
0300: } catch (ClassNotFoundException cnfe) {
0301: try {
0302: loaderClass = Class.forName("sun.misc.Service");
0303: loadMethodName = "providers";
0304: jusl = false;
0305: } catch (ClassNotFoundException cnfe2) {
0306: // Fail softly if a loader is not actually needed.
0307: this .iterator = handleServiceLoaderUnavailability(
0308: "proc.no.service", null);
0309: return;
0310: }
0311: }
0312:
0313: // java.util.ServiceLoader.load or sun.misc.Service.providers
0314: Method loadMethod = loaderClass.getMethod(
0315: loadMethodName, Class.class, ClassLoader.class);
0316:
0317: Object result = loadMethod.invoke(null,
0318: Processor.class, classLoader);
0319:
0320: // For java.util.ServiceLoader, we have to call another
0321: // method to get the iterator.
0322: if (jusl) {
0323: Method m = loaderClass.getMethod("iterator");
0324: result = m.invoke(result); // serviceLoader.iterator();
0325: }
0326:
0327: // The result should now be an iterator.
0328: this .iterator = (Iterator<?>) result;
0329: } catch (Throwable t) {
0330: log.error("proc.service.problem");
0331: throw new Abort(t);
0332: }
0333: }
0334:
0335: public boolean hasNext() {
0336: try {
0337: return iterator.hasNext();
0338: } catch (Throwable t) {
0339: if ("ServiceConfigurationError".equals(t.getClass()
0340: .getSimpleName())) {
0341: log.error("proc.bad.config.file", t
0342: .getLocalizedMessage());
0343: }
0344: throw new Abort(t);
0345: }
0346: }
0347:
0348: public Processor next() {
0349: try {
0350: return (Processor) (iterator.next());
0351: } catch (Throwable t) {
0352: if ("ServiceConfigurationError".equals(t.getClass()
0353: .getSimpleName())) {
0354: log.error("proc.bad.config.file", t
0355: .getLocalizedMessage());
0356: } else {
0357: log.error("proc.processor.constructor.error", t
0358: .getLocalizedMessage());
0359: }
0360: throw new Abort(t);
0361: }
0362: }
0363:
0364: public void remove() {
0365: throw new UnsupportedOperationException();
0366: }
0367: }
0368:
0369: private static class NameProcessIterator implements
0370: Iterator<Processor> {
0371: Processor nextProc = null;
0372: Iterator<String> names;
0373: ClassLoader processorCL;
0374: Log log;
0375:
0376: NameProcessIterator(String names, ClassLoader processorCL,
0377: Log log) {
0378: this .names = Arrays.asList(names.split(",")).iterator();
0379: this .processorCL = processorCL;
0380: this .log = log;
0381: }
0382:
0383: public boolean hasNext() {
0384: if (nextProc != null)
0385: return true;
0386: else {
0387: if (!names.hasNext())
0388: return false;
0389: else {
0390: String processorName = names.next();
0391:
0392: Processor processor;
0393: try {
0394: try {
0395: processor = (Processor) (processorCL
0396: .loadClass(processorName)
0397: .newInstance());
0398: } catch (ClassNotFoundException cnfe) {
0399: log.error("proc.processor.not.found",
0400: processorName);
0401: return false;
0402: } catch (ClassCastException cce) {
0403: log.error("proc.processor.wrong.type",
0404: processorName);
0405: return false;
0406: } catch (Exception e) {
0407: log.error(
0408: "proc.processor.cant.instantiate",
0409: processorName);
0410: return false;
0411: }
0412: } catch (Throwable t) {
0413: throw new AnnotationProcessingError(t);
0414: }
0415: nextProc = processor;
0416: return true;
0417: }
0418:
0419: }
0420: }
0421:
0422: public Processor next() {
0423: if (hasNext()) {
0424: Processor p = nextProc;
0425: nextProc = null;
0426: return p;
0427: } else
0428: throw new NoSuchElementException();
0429: }
0430:
0431: public void remove() {
0432: throw new UnsupportedOperationException();
0433: }
0434: }
0435:
0436: public boolean atLeastOneProcessor() {
0437: return discoveredProcs.iterator().hasNext();
0438: }
0439:
0440: private Map<String, String> initProcessorOptions(Context context) {
0441: Options options = Options.instance(context);
0442: Set<String> keySet = options.keySet();
0443: Map<String, String> tempOptions = new LinkedHashMap<String, String>();
0444:
0445: for (String key : keySet) {
0446: if (key.startsWith("-A") && key.length() > 2) {
0447: int sepIndex = key.indexOf('=');
0448: String candidateKey = null;
0449: String candidateValue = null;
0450:
0451: if (sepIndex == -1)
0452: candidateKey = key.substring(2);
0453: else if (sepIndex >= 3) {
0454: candidateKey = key.substring(2, sepIndex);
0455: candidateValue = (sepIndex < key.length() - 1) ? key
0456: .substring(sepIndex + 1)
0457: : null;
0458: }
0459: tempOptions.put(candidateKey, candidateValue);
0460: }
0461: }
0462:
0463: return Collections.unmodifiableMap(tempOptions);
0464: }
0465:
0466: private Set<String> initUnmatchedProcessorOptions() {
0467: Set<String> unmatchedProcessorOptions = new HashSet<String>();
0468: unmatchedProcessorOptions.addAll(processorOptions.keySet());
0469: return unmatchedProcessorOptions;
0470: }
0471:
0472: /**
0473: * State about how a processor has been used by the tool. If a
0474: * processor has been used on a prior round, its process method is
0475: * called on all subsequent rounds, perhaps with an empty set of
0476: * annotations to process. The {@code annotatedSupported} method
0477: * caches the supported annotation information from the first (and
0478: * only) getSupportedAnnotationTypes call to the processor.
0479: */
0480: static class ProcessorState {
0481: public Processor processor;
0482: public boolean contributed;
0483: private ArrayList<Pattern> supportedAnnotationPatterns;
0484: private ArrayList<String> supportedOptionNames;
0485:
0486: ProcessorState(Processor p, Log log, Source source,
0487: ProcessingEnvironment env) {
0488: processor = p;
0489: contributed = false;
0490:
0491: try {
0492: processor.init(env);
0493:
0494: checkSourceVersionCompatibility(source, log);
0495:
0496: supportedAnnotationPatterns = new ArrayList<Pattern>();
0497: for (String importString : processor
0498: .getSupportedAnnotationTypes()) {
0499: supportedAnnotationPatterns
0500: .add(importStringToPattern(importString,
0501: processor, log));
0502: }
0503:
0504: supportedOptionNames = new ArrayList<String>();
0505: for (String optionName : processor
0506: .getSupportedOptions()) {
0507: if (checkOptionName(optionName, log))
0508: supportedOptionNames.add(optionName);
0509: }
0510:
0511: } catch (Throwable t) {
0512: throw new AnnotationProcessingError(t);
0513: }
0514: }
0515:
0516: /**
0517: * Checks whether or not a processor's source version is
0518: * compatible with the compilation source version. The
0519: * processor's source version needs to be greater than or
0520: * equal to the source version of the compile.
0521: */
0522: private void checkSourceVersionCompatibility(Source source,
0523: Log log) {
0524: SourceVersion procSourceVersion = processor
0525: .getSupportedSourceVersion();
0526:
0527: if (procSourceVersion.compareTo(Source
0528: .toSourceVersion(source)) < 0) {
0529: log.warning(
0530: "proc.processor.incompatible.source.version",
0531: procSourceVersion, processor.getClass()
0532: .getName(), source.name);
0533: }
0534: }
0535:
0536: private boolean checkOptionName(String optionName, Log log) {
0537: boolean valid = isValidOptionName(optionName);
0538: if (!valid)
0539: log.error("proc.processor.bad.option.name", optionName,
0540: processor.getClass().getName());
0541: return valid;
0542: }
0543:
0544: public boolean annotationSupported(String annotationName) {
0545: for (Pattern p : supportedAnnotationPatterns) {
0546: if (p.matcher(annotationName).matches())
0547: return true;
0548: }
0549: return false;
0550: }
0551:
0552: /**
0553: * Remove options that are matched by this processor.
0554: */
0555: public void removeSupportedOptions(
0556: Set<String> unmatchedProcessorOptions) {
0557: unmatchedProcessorOptions.removeAll(supportedOptionNames);
0558: }
0559: }
0560:
0561: // TODO: These two classes can probably be rewritten better...
0562: /**
0563: * This class holds information about the processors that have
0564: * been discoverd so far as well as the means to discover more, if
0565: * necessary. A single iterator should be used per round of
0566: * annotation processing. The iterator first visits already
0567: * discovered processors then fails over to the service provided
0568: * mechanism if additional queries are made.
0569: */
0570: class DiscoveredProcessors implements Iterable<ProcessorState> {
0571:
0572: class ProcessorStateIterator implements
0573: Iterator<ProcessorState> {
0574: DiscoveredProcessors psi;
0575: Iterator<ProcessorState> innerIter;
0576: boolean onProcInterator;
0577:
0578: ProcessorStateIterator(DiscoveredProcessors psi) {
0579: this .psi = psi;
0580: this .innerIter = psi.procStateList.iterator();
0581: this .onProcInterator = false;
0582: }
0583:
0584: public ProcessorState next() {
0585: if (!onProcInterator) {
0586: if (innerIter.hasNext())
0587: return innerIter.next();
0588: else
0589: onProcInterator = true;
0590: }
0591:
0592: if (psi.processorIterator.hasNext()) {
0593: ProcessorState ps = new ProcessorState(
0594: psi.processorIterator.next(), log, source,
0595: JavacProcessingEnvironment.this );
0596: psi.procStateList.add(ps);
0597: return ps;
0598: } else
0599: throw new NoSuchElementException();
0600: }
0601:
0602: public boolean hasNext() {
0603: if (onProcInterator)
0604: return psi.processorIterator.hasNext();
0605: else
0606: return innerIter.hasNext()
0607: || psi.processorIterator.hasNext();
0608: }
0609:
0610: public void remove() {
0611: throw new UnsupportedOperationException();
0612: }
0613:
0614: /**
0615: * Run all remaining processors on the procStateList that
0616: * have not already run this round with an empty set of
0617: * annotations.
0618: */
0619: public void runContributingProcs(RoundEnvironment re) {
0620: if (!onProcInterator) {
0621: Set<TypeElement> emptyTypeElements = Collections
0622: .emptySet();
0623: while (innerIter.hasNext()) {
0624: ProcessorState ps = innerIter.next();
0625: if (ps.contributed)
0626: callProcessor(ps.processor,
0627: emptyTypeElements, re);
0628: }
0629: }
0630: }
0631: }
0632:
0633: Iterator<? extends Processor> processorIterator;
0634: ArrayList<ProcessorState> procStateList;
0635:
0636: public ProcessorStateIterator iterator() {
0637: return new ProcessorStateIterator(this );
0638: }
0639:
0640: DiscoveredProcessors(
0641: Iterator<? extends Processor> processorIterator) {
0642: this .processorIterator = processorIterator;
0643: this .procStateList = new ArrayList<ProcessorState>();
0644: }
0645: }
0646:
0647: private void discoverAndRunProcs(Context context,
0648: Set<TypeElement> annotationsPresent,
0649: List<ClassSymbol> topLevelClasses,
0650: List<PackageSymbol> packageInfoFiles) {
0651: // Writer for -XprintRounds and -XprintProcessorInfo data
0652: PrintWriter xout = context.get(Log.outKey);
0653:
0654: Map<String, TypeElement> unmatchedAnnotations = new HashMap<String, TypeElement>(
0655: annotationsPresent.size());
0656:
0657: for (TypeElement a : annotationsPresent) {
0658: unmatchedAnnotations
0659: .put(a.getQualifiedName().toString(), a);
0660: }
0661:
0662: // Give "*" processors a chance to match
0663: if (unmatchedAnnotations.size() == 0)
0664: unmatchedAnnotations.put("", null);
0665:
0666: DiscoveredProcessors.ProcessorStateIterator psi = discoveredProcs
0667: .iterator();
0668: // TODO: Create proper argument values; need past round
0669: // information to fill in this constructor. Note that the 1
0670: // st round of processing could be the last round if there
0671: // were parse errors on the initial source files; however, we
0672: // are not doing processing in that case.
0673:
0674: Set<Element> rootElements = new LinkedHashSet<Element>();
0675: rootElements.addAll(topLevelClasses);
0676: rootElements.addAll(packageInfoFiles);
0677: rootElements = Collections.unmodifiableSet(rootElements);
0678:
0679: RoundEnvironment renv = new JavacRoundEnvironment(false, false,
0680: rootElements, JavacProcessingEnvironment.this );
0681:
0682: while (unmatchedAnnotations.size() > 0 && psi.hasNext()) {
0683: ProcessorState ps = psi.next();
0684: Set<String> matchedNames = new HashSet<String>();
0685: Set<TypeElement> typeElements = new LinkedHashSet<TypeElement>();
0686: for (String unmatchedAnnotationName : unmatchedAnnotations
0687: .keySet()) {
0688: if (ps.annotationSupported(unmatchedAnnotationName)) {
0689: matchedNames.add(unmatchedAnnotationName);
0690: TypeElement te = unmatchedAnnotations
0691: .get(unmatchedAnnotationName);
0692: if (te != null)
0693: typeElements.add(te);
0694: }
0695: }
0696:
0697: if (matchedNames.size() > 0 || ps.contributed) {
0698: boolean processingResult = callProcessor(ps.processor,
0699: typeElements, renv);
0700: ps.contributed = true;
0701: ps.removeSupportedOptions(unmatchedProcessorOptions);
0702:
0703: if (printProcessorInfo || verbose) {
0704: xout.println(Log.getLocalizedString(
0705: "x.print.processor.info", ps.processor
0706: .getClass().getName(), matchedNames
0707: .toString(), processingResult));
0708: }
0709:
0710: if (processingResult) {
0711: unmatchedAnnotations.keySet().removeAll(
0712: matchedNames);
0713: }
0714:
0715: }
0716: }
0717: unmatchedAnnotations.remove("");
0718:
0719: if (lint && unmatchedAnnotations.size() > 0) {
0720: // Remove annotations processed by javac
0721: unmatchedAnnotations.keySet()
0722: .removeAll(platformAnnotations);
0723: if (unmatchedAnnotations.size() > 0) {
0724: log = Log.instance(context);
0725: log.warning("proc.annotations.without.processors",
0726: unmatchedAnnotations.keySet());
0727: }
0728: }
0729:
0730: // Run contributing processors that haven't run yet
0731: psi.runContributingProcs(renv);
0732:
0733: // Debugging
0734: if (options.get("displayFilerState") != null)
0735: filer.displayState();
0736: }
0737:
0738: /**
0739: * Computes the set of annotations on the symbol in question.
0740: * Leave class public for external testing purposes.
0741: */
0742: public static class ComputeAnnotationSet extends
0743: ElementScanner6<Set<TypeElement>, Set<TypeElement>> {
0744: final Elements elements;
0745:
0746: public ComputeAnnotationSet(Elements elements) {
0747: super ();
0748: this .elements = elements;
0749: }
0750:
0751: @Override
0752: public Set<TypeElement> visitPackage(PackageElement e,
0753: Set<TypeElement> p) {
0754: // Don't scan enclosed elements of a package
0755: return p;
0756: }
0757:
0758: @Override
0759: public Set<TypeElement> scan(Element e, Set<TypeElement> p) {
0760: for (AnnotationMirror annotationMirror : elements
0761: .getAllAnnotationMirrors(e)) {
0762: Element e2 = annotationMirror.getAnnotationType()
0763: .asElement();
0764: p.add((TypeElement) e2);
0765: }
0766: return super .scan(e, p);
0767: }
0768: }
0769:
0770: private boolean callProcessor(Processor proc,
0771: Set<? extends TypeElement> tes, RoundEnvironment renv) {
0772: try {
0773: return proc.process(tes, renv);
0774: } catch (CompletionFailure ex) {
0775: StringWriter out = new StringWriter();
0776: ex.printStackTrace(new PrintWriter(out));
0777: log.error("proc.cant.access", ex.sym, ex.errmsg, out
0778: .toString());
0779: return false;
0780: } catch (Throwable t) {
0781: throw new AnnotationProcessingError(t);
0782: }
0783: }
0784:
0785: // TODO: internal catch clauses?; catch and rethrow an annotation
0786: // processing error
0787: public JavaCompiler doProcessing(Context context,
0788: List<JCCompilationUnit> roots,
0789: List<ClassSymbol> classSymbols,
0790: Iterable<? extends PackageSymbol> pckSymbols)
0791: throws IOException {
0792:
0793: log = Log.instance(context);
0794: // Writer for -XprintRounds and -XprintProcessorInfo data
0795: PrintWriter xout = context.get(Log.outKey);
0796: TaskListener taskListener = context.get(TaskListener.class);
0797:
0798: AnnotationCollector collector = new AnnotationCollector();
0799:
0800: JavaCompiler compiler = JavaCompiler.instance(context);
0801: compiler.todo.clear(); // free the compiler's resources
0802:
0803: int round = 0;
0804:
0805: // List<JCAnnotation> annotationsPresentInSource = collector.findAnnotations(roots);
0806: List<ClassSymbol> topLevelClasses = getTopLevelClasses(roots);
0807:
0808: for (ClassSymbol classSym : classSymbols)
0809: topLevelClasses = topLevelClasses.prepend(classSym);
0810: List<PackageSymbol> packageInfoFiles = getPackageInfoFiles(roots);
0811:
0812: Set<PackageSymbol> specifiedPackages = new LinkedHashSet<PackageSymbol>();
0813: for (PackageSymbol psym : pckSymbols)
0814: specifiedPackages.add(psym);
0815: this .specifiedPackages = Collections
0816: .unmodifiableSet(specifiedPackages);
0817:
0818: // Use annotation processing to compute the set of annotations present
0819: Set<TypeElement> annotationsPresent = new LinkedHashSet<TypeElement>();
0820: ComputeAnnotationSet annotationComputer = new ComputeAnnotationSet(
0821: elementUtils);
0822: for (ClassSymbol classSym : topLevelClasses)
0823: annotationComputer.scan(classSym, annotationsPresent);
0824: for (PackageSymbol pkgSym : packageInfoFiles)
0825: annotationComputer.scan(pkgSym, annotationsPresent);
0826:
0827: Context currentContext = context;
0828:
0829: int roundNumber = 0;
0830: boolean errorStatus = false;
0831:
0832: runAround: while (true) {
0833: if (fatalErrors && compiler.errorCount() != 0) {
0834: errorStatus = true;
0835: break runAround;
0836: }
0837:
0838: this .context = currentContext;
0839: roundNumber++;
0840: printRoundInfo(xout, roundNumber, topLevelClasses,
0841: annotationsPresent, false);
0842:
0843: if (taskListener != null)
0844: taskListener.started(new TaskEvent(
0845: TaskEvent.Kind.ANNOTATION_PROCESSING_ROUND));
0846:
0847: try {
0848: discoverAndRunProcs(currentContext, annotationsPresent,
0849: topLevelClasses, packageInfoFiles);
0850: } finally {
0851: if (taskListener != null)
0852: taskListener
0853: .finished(new TaskEvent(
0854: TaskEvent.Kind.ANNOTATION_PROCESSING_ROUND));
0855: }
0856:
0857: /*
0858: * Processors for round n have run to completion. Prepare
0859: * for round (n+1) by checked for errors raised by
0860: * annotation processors and then checking for syntax
0861: * errors on any generated source files.
0862: */
0863: if (messager.errorRaised()) {
0864: errorStatus = true;
0865: break runAround;
0866: } else {
0867: if (moreToDo()) {
0868: // annotationsPresentInSource = List.nil();
0869: annotationsPresent = new LinkedHashSet<TypeElement>();
0870: topLevelClasses = List.nil();
0871: packageInfoFiles = List.nil();
0872:
0873: compiler.close();
0874: currentContext = contextForNextRound(
0875: currentContext, true);
0876:
0877: JavaFileManager fileManager = currentContext
0878: .get(JavaFileManager.class);
0879:
0880: List<JavaFileObject> fileObjects = List.nil();
0881: for (JavaFileObject jfo : filer
0882: .getGeneratedSourceFileObjects()) {
0883: fileObjects = fileObjects.prepend(jfo);
0884: }
0885:
0886: compiler = JavaCompiler.instance(currentContext);
0887: List<JCCompilationUnit> parsedFiles = compiler
0888: .parseFiles(fileObjects);
0889: roots = cleanTrees(roots).reverse();
0890:
0891: for (JCCompilationUnit unit : parsedFiles)
0892: roots = roots.prepend(unit);
0893: roots = roots.reverse();
0894:
0895: // Check for errors after parsing
0896: if (compiler.parseErrors()) {
0897: errorStatus = true;
0898: break runAround;
0899: } else {
0900: ListBuffer<ClassSymbol> classes = enterNewClassFiles(currentContext);
0901: compiler.enterTrees(roots);
0902:
0903: // annotationsPresentInSource =
0904: // collector.findAnnotations(parsedFiles);
0905: classes
0906: .appendList(getTopLevelClasses(parsedFiles));
0907: topLevelClasses = classes.toList();
0908: packageInfoFiles = getPackageInfoFiles(parsedFiles);
0909:
0910: annotationsPresent = new LinkedHashSet<TypeElement>();
0911: for (ClassSymbol classSym : topLevelClasses)
0912: annotationComputer.scan(classSym,
0913: annotationsPresent);
0914: for (PackageSymbol pkgSym : packageInfoFiles)
0915: annotationComputer.scan(pkgSym,
0916: annotationsPresent);
0917:
0918: updateProcessingState(currentContext, false);
0919: }
0920: } else
0921: break runAround; // No new files
0922: }
0923: }
0924: runLastRound(xout, roundNumber, errorStatus, taskListener);
0925:
0926: compiler.close();
0927: currentContext = contextForNextRound(currentContext, true);
0928: compiler = JavaCompiler.instance(currentContext);
0929: filer.newRound(currentContext, true);
0930: filer.warnIfUnclosedFiles();
0931: warnIfUnmatchedOptions();
0932:
0933: /*
0934: * If an annotation processor raises an error in a round,
0935: * that round runs to completion and one last round occurs.
0936: * The last round may also occur because no more source or
0937: * class files have been generated. Therefore, if an error
0938: * was raised on either of the last *two* rounds, the compile
0939: * should exit with a nonzero exit code. The current value of
0940: * errorStatus holds whether or not an error was raised on the
0941: * second to last round; errorRaised() gives the error status
0942: * of the last round.
0943: */
0944: errorStatus = errorStatus || messager.errorRaised();
0945:
0946: // Free resources
0947: this .close();
0948:
0949: if (taskListener != null)
0950: taskListener.finished(new TaskEvent(
0951: TaskEvent.Kind.ANNOTATION_PROCESSING));
0952:
0953: if (errorStatus) {
0954: compiler.log.nerrors += messager.errorCount();
0955: if (compiler.errorCount() == 0)
0956: compiler.log.nerrors++;
0957: } else if (procOnly) {
0958: compiler.todo.clear();
0959: } else { // Final compilation
0960: compiler.close();
0961: currentContext = contextForNextRound(currentContext, true);
0962: compiler = JavaCompiler.instance(currentContext);
0963:
0964: if (true) {
0965: compiler.enterTrees(cleanTrees(roots));
0966: } else {
0967: List<JavaFileObject> fileObjects = List.nil();
0968: for (JCCompilationUnit unit : roots)
0969: fileObjects = fileObjects.prepend(unit
0970: .getSourceFile());
0971: roots = null;
0972: compiler.enterTrees(compiler.parseFiles(fileObjects
0973: .reverse()));
0974: }
0975: }
0976:
0977: return compiler;
0978: }
0979:
0980: // Call the last round of annotation processing
0981: private void runLastRound(PrintWriter xout, int roundNumber,
0982: boolean errorStatus, TaskListener taskListener)
0983: throws IOException {
0984: roundNumber++;
0985: List<ClassSymbol> noTopLevelClasses = List.nil();
0986: Set<TypeElement> noAnnotations = Collections.emptySet();
0987: printRoundInfo(xout, roundNumber, noTopLevelClasses,
0988: noAnnotations, true);
0989:
0990: Set<Element> emptyRootElements = Collections.emptySet(); // immutable
0991: RoundEnvironment renv = new JavacRoundEnvironment(true,
0992: errorStatus, emptyRootElements,
0993: JavacProcessingEnvironment.this );
0994: if (taskListener != null)
0995: taskListener.started(new TaskEvent(
0996: TaskEvent.Kind.ANNOTATION_PROCESSING_ROUND));
0997:
0998: try {
0999: discoveredProcs.iterator().runContributingProcs(renv);
1000: } finally {
1001: if (taskListener != null)
1002: taskListener.finished(new TaskEvent(
1003: TaskEvent.Kind.ANNOTATION_PROCESSING_ROUND));
1004: }
1005: }
1006:
1007: private void updateProcessingState(Context currentContext,
1008: 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",
1019: unmatchedProcessorOptions.toString());
1020: }
1021: }
1022:
1023: private void printRoundInfo(PrintWriter xout, int roundNumber,
1024: List<ClassSymbol> topLevelClasses,
1025: Set<TypeElement> annotationsPresent, boolean lastRound) {
1026: if (printRounds || verbose) {
1027: xout.println(Log.getLocalizedString("x.print.rounds",
1028: roundNumber, "{" + topLevelClasses.toString(", ")
1029: + "}", annotationsPresent, lastRound));
1030: }
1031: }
1032:
1033: private ListBuffer<ClassSymbol> enterNewClassFiles(
1034: Context currentContext) {
1035: ClassReader reader = ClassReader.instance(currentContext);
1036: Name.Table names = Name.Table.instance(currentContext);
1037: ListBuffer<ClassSymbol> list = new ListBuffer<ClassSymbol>();
1038:
1039: for (Map.Entry<String, JavaFileObject> entry : filer
1040: .getGeneratedClasses().entrySet()) {
1041: Name name = names.fromString(entry.getKey());
1042: JavaFileObject file = entry.getValue();
1043: if (file.getKind() != JavaFileObject.Kind.CLASS)
1044: throw new AssertionError(file);
1045: ClassSymbol cs = reader.enterClass(name, file);
1046: list.append(cs);
1047: }
1048: return list;
1049: }
1050:
1051: /**
1052: * Free resources related to annotation processing.
1053: */
1054: public void close() {
1055: filer.close();
1056: discoveredProcs = null;
1057: }
1058:
1059: private List<ClassSymbol> getTopLevelClasses(
1060: List<? extends JCCompilationUnit> units) {
1061: List<ClassSymbol> classes = List.nil();
1062: for (JCCompilationUnit unit : units) {
1063: for (JCTree node : unit.defs) {
1064: if (node.getTag() == JCTree.CLASSDEF) {
1065: classes = classes.prepend(((JCClassDecl) node).sym);
1066: }
1067: }
1068: }
1069: return classes.reverse();
1070: }
1071:
1072: private List<PackageSymbol> getPackageInfoFiles(
1073: List<? extends JCCompilationUnit> units) {
1074: List<PackageSymbol> packages = List.nil();
1075: for (JCCompilationUnit unit : units) {
1076: boolean isPkgInfo = unit.sourcefile.isNameCompatible(
1077: "package-info", JavaFileObject.Kind.SOURCE);
1078: if (isPkgInfo) {
1079: packages = packages.prepend(unit.packge);
1080: }
1081: }
1082: return packages.reverse();
1083: }
1084:
1085: private Context contextForNextRound(Context context,
1086: boolean shareNames) throws IOException {
1087: Context next = new Context();
1088:
1089: Options options = Options.instance(context);
1090: assert options != null;
1091: next.put(Options.optionsKey, options);
1092:
1093: PrintWriter out = context.get(Log.outKey);
1094: assert out != null;
1095: next.put(Log.outKey, out);
1096:
1097: if (shareNames) {
1098: Name.Table names = Name.Table.instance(context);
1099: assert names != null;
1100: next.put(Name.Table.namesKey, names);
1101: }
1102:
1103: DiagnosticListener dl = context.get(DiagnosticListener.class);
1104: if (dl != null)
1105: next.put(DiagnosticListener.class, dl);
1106:
1107: TaskListener tl = context.get(TaskListener.class);
1108: if (tl != null)
1109: next.put(TaskListener.class, tl);
1110:
1111: JavaFileManager jfm = context.get(JavaFileManager.class);
1112: assert jfm != null;
1113: next.put(JavaFileManager.class, jfm);
1114: if (jfm instanceof JavacFileManager) {
1115: ((JavacFileManager) jfm).setContext(next);
1116: }
1117:
1118: Name.Table names = Name.Table.instance(context);
1119: assert names != null;
1120: next.put(Name.Table.namesKey, names);
1121:
1122: Keywords keywords = Keywords.instance(context);
1123: assert (keywords != null);
1124: next.put(Keywords.keywordsKey, keywords);
1125:
1126: JavaCompiler oldCompiler = JavaCompiler.instance(context);
1127: JavaCompiler nextCompiler = JavaCompiler.instance(next);
1128: nextCompiler.initRound(oldCompiler);
1129:
1130: JavacTaskImpl task = context.get(JavacTaskImpl.class);
1131: if (task != null) {
1132: next.put(JavacTaskImpl.class, task);
1133: task.updateContext(next);
1134: }
1135:
1136: context.clear();
1137: return next;
1138: }
1139:
1140: /*
1141: * Called retroactively to determine if a class loader was required,
1142: * after we have failed to create one.
1143: */
1144: private boolean needClassLoader(String procNames,
1145: Iterable<? extends File> workingpath) {
1146: if (procNames != null)
1147: return true;
1148:
1149: String procPath;
1150: URL[] urls = new URL[1];
1151: for (File pathElement : workingpath) {
1152: try {
1153: urls[0] = pathElement.toURI().toURL();
1154: if (ServiceProxy.hasService(Processor.class, urls))
1155: return true;
1156: } catch (MalformedURLException ex) {
1157: throw new AssertionError(ex);
1158: } catch (ServiceProxy.ServiceConfigurationError e) {
1159: log.error("proc.bad.config.file", e
1160: .getLocalizedMessage());
1161: return true;
1162: }
1163: }
1164:
1165: return false;
1166: }
1167:
1168: private class AnnotationCollector extends TreeScanner {
1169: List<JCTree> path = List.nil();
1170: static final boolean verbose = false;
1171: List<JCAnnotation> annotations = List.nil();
1172:
1173: public List<JCAnnotation> findAnnotations(
1174: List<? extends JCTree> nodes) {
1175: annotations = List.nil();
1176: scan(nodes);
1177: List<JCAnnotation> found = annotations;
1178: annotations = List.nil();
1179: return found.reverse();
1180: }
1181:
1182: public void scan(JCTree node) {
1183: if (node == null)
1184: return;
1185: Symbol sym = TreeInfo.symbolFor(node);
1186: if (sym != null)
1187: path = path.prepend(node);
1188: super .scan(node);
1189: if (sym != null)
1190: path = path.tail;
1191: }
1192:
1193: public void visitAnnotation(JCAnnotation node) {
1194: annotations = annotations.prepend(node);
1195: if (verbose) {
1196: StringBuilder sb = new StringBuilder();
1197: for (JCTree tree : path.reverse()) {
1198: System.err.print(sb);
1199: System.err.println(TreeInfo.symbolFor(tree));
1200: sb.append(" ");
1201: }
1202: System.err.print(sb);
1203: System.err.println(node);
1204: }
1205: }
1206: }
1207:
1208: private static <T extends JCTree> List<T> cleanTrees(List<T> nodes) {
1209: for (T node : nodes)
1210: treeCleaner.scan(node);
1211: return nodes;
1212: }
1213:
1214: private static TreeScanner treeCleaner = new TreeScanner() {
1215: public void scan(JCTree node) {
1216: super .scan(node);
1217: if (node != null)
1218: node.type = null;
1219: }
1220:
1221: public void visitTopLevel(JCCompilationUnit node) {
1222: node.packge = null;
1223: super .visitTopLevel(node);
1224: }
1225:
1226: public void visitClassDef(JCClassDecl node) {
1227: node.sym = null;
1228: super .visitClassDef(node);
1229: }
1230:
1231: public void visitMethodDef(JCMethodDecl node) {
1232: node.sym = null;
1233: super .visitMethodDef(node);
1234: }
1235:
1236: public void visitVarDef(JCVariableDecl node) {
1237: node.sym = null;
1238: super .visitVarDef(node);
1239: }
1240:
1241: public void visitNewClass(JCNewClass node) {
1242: node.constructor = null;
1243: super .visitNewClass(node);
1244: }
1245:
1246: public void visitAssignop(JCAssignOp node) {
1247: node.operator = null;
1248: super .visitAssignop(node);
1249: }
1250:
1251: public void visitUnary(JCUnary node) {
1252: node.operator = null;
1253: super .visitUnary(node);
1254: }
1255:
1256: public void visitBinary(JCBinary node) {
1257: node.operator = null;
1258: super .visitBinary(node);
1259: }
1260:
1261: public void visitSelect(JCFieldAccess node) {
1262: node.sym = null;
1263: super .visitSelect(node);
1264: }
1265:
1266: public void visitIdent(JCIdent node) {
1267: node.sym = null;
1268: super .visitIdent(node);
1269: }
1270: };
1271:
1272: private boolean moreToDo() {
1273: return filer.newFiles();
1274: }
1275:
1276: /**
1277: * {@inheritdoc}
1278: *
1279: * Command line options suitable for presenting to annotation
1280: * processors. "-Afoo=bar" should be "-Afoo" => "bar".
1281: */
1282: public Map<String, String> getOptions() {
1283: return processorOptions;
1284: }
1285:
1286: public Messager getMessager() {
1287: return messager;
1288: }
1289:
1290: public Filer getFiler() {
1291: return filer;
1292: }
1293:
1294: public JavacElements getElementUtils() {
1295: return elementUtils;
1296: }
1297:
1298: public JavacTypes getTypeUtils() {
1299: return typeUtils;
1300: }
1301:
1302: public SourceVersion getSourceVersion() {
1303: return Source.toSourceVersion(source);
1304: }
1305:
1306: public Locale getLocale() {
1307: return Locale.getDefault();
1308: }
1309:
1310: public Set<Symbol.PackageSymbol> getSpecifiedPackages() {
1311: return specifiedPackages;
1312: }
1313:
1314: // Borrowed from DocletInvoker and apt
1315: // TODO: remove from apt's Main
1316: /**
1317: * Utility method for converting a search path string to an array
1318: * of directory and JAR file URLs.
1319: *
1320: * @param path the search path string
1321: * @return the resulting array of directory and JAR file URLs
1322: */
1323: public static URL[] pathToURLs(String path) {
1324: StringTokenizer st = new StringTokenizer(path,
1325: File.pathSeparator);
1326: URL[] urls = new URL[st.countTokens()];
1327: int count = 0;
1328: while (st.hasMoreTokens()) {
1329: URL url = fileToURL(new File(st.nextToken()));
1330: if (url != null) {
1331: urls[count++] = url;
1332: }
1333: }
1334: if (urls.length != count) {
1335: URL[] tmp = new URL[count];
1336: System.arraycopy(urls, 0, tmp, 0, count);
1337: urls = tmp;
1338: }
1339: return urls;
1340: }
1341:
1342: /**
1343: * Returns the directory or JAR file URL corresponding to the specified
1344: * local file name.
1345: *
1346: * @param file the File object
1347: * @return the resulting directory or JAR file URL, or null if unknown
1348: */
1349: private static URL fileToURL(File file) {
1350: String name;
1351: try {
1352: name = file.getCanonicalPath();
1353: } catch (IOException e) {
1354: name = file.getAbsolutePath();
1355: }
1356: name = name.replace(File.separatorChar, '/');
1357: if (!name.startsWith("/")) {
1358: name = "/" + name;
1359: }
1360: // If the file does not exist, then assume that it's a directory
1361: if (!file.isFile()) {
1362: name = name + "/";
1363: }
1364: try {
1365: return new URL("file", "", name);
1366: } catch (MalformedURLException e) {
1367: throw new IllegalArgumentException("file");
1368: }
1369: }
1370:
1371: private static final Pattern allMatches = Pattern.compile(".*");
1372:
1373: private static final Pattern noMatches = Pattern
1374: .compile("(\\P{all})+");
1375:
1376: /**
1377: * Convert import-style string to regex matching that string. If
1378: * the string is a valid import-style string, return a regex that
1379: * won't match anything.
1380: */
1381: // TODO: remove version in Apt.java
1382: public static Pattern importStringToPattern(String s, Processor p,
1383: Log log) {
1384: if (s.equals("*")) {
1385: return allMatches;
1386: } else {
1387: String t = s;
1388: boolean star = false;
1389:
1390: /*
1391: * Validate string from factory is legal. If the string
1392: * has more than one asterisks or the asterisks does not
1393: * appear as the last character (preceded by a period),
1394: * the string is not legal.
1395: */
1396:
1397: boolean valid = true;
1398: int index = t.indexOf('*');
1399: if (index != -1) {
1400: // '*' must be last character...
1401: if (index == t.length() - 1) {
1402: // ... and preceeding character must be '.'
1403: if (index - 1 >= 0) {
1404: valid = t.charAt(index - 1) == '.';
1405: // Strip off ".*$" for identifier checks
1406: t = t.substring(0, t.length() - 2);
1407: }
1408: } else
1409: valid = false;
1410: }
1411:
1412: // Verify string is off the form (javaId \.)+ or javaId
1413: if (valid) {
1414: String[] javaIds = t.split("\\.", t.length() + 2);
1415: for (String javaId : javaIds)
1416: valid &= SourceVersion.isIdentifier(javaId);
1417: }
1418:
1419: if (!valid) {
1420: log.warning("proc.malformed.supported.string", s, p
1421: .getClass().getName());
1422: return noMatches; // won't match any valid identifier
1423: }
1424:
1425: String s_prime = s.replaceAll("\\.", "\\\\.");
1426:
1427: if (s_prime.endsWith("*")) {
1428: s_prime = s_prime.substring(0, s_prime.length() - 1)
1429: + ".+";
1430: }
1431:
1432: return Pattern.compile(s_prime);
1433: }
1434: }
1435:
1436: /**
1437: * For internal use by Sun Microsystems only. This method will be
1438: * removed without warning.
1439: */
1440: public Context getContext() {
1441: return context;
1442: }
1443:
1444: public String toString() {
1445: return "javac ProcessingEnvironment version @(#)JavacProcessingEnvironment.java 1.36 07/07/13";
1446: }
1447:
1448: public static boolean isValidOptionName(String optionName) {
1449: for (String s : optionName.split("\\.", -1)) {
1450: if (!SourceVersion.isIdentifier(s))
1451: return false;
1452: }
1453: return true;
1454: }
1455: }
|