EMMA Coverage Report (generated Thu Dec 06 15:52:10 GMT 2007)
[all classes][com.sun.tools.javac.processing]

COVERAGE SUMMARY FOR SOURCE FILE [JavacProcessingEnvironment.java]

nameclass, %method, %block, %line, %
JavacProcessingEnvironment.java56%  (5/9)19%  (15/80)15%  (389/2653)14%  (85.5/594)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class JavacProcessingEnvironment$AnnotationCollector0%   (0/1)0%   (0/5)0%   (0/63)0%   (0/19)
JavacProcessingEnvironment$AnnotationCollector (JavacProcessingEnvironment): ... 0%   (0/1)0%   (0/12)0%   (0/3)
JavacProcessingEnvironment$AnnotationCollector (JavacProcessingEnvironment, J... 0%   (0/1)0%   (0/4)0%   (0/1)
findAnnotations (List): List 0%   (0/1)0%   (0/15)0%   (0/5)
scan (JCTree): void 0%   (0/1)0%   (0/25)0%   (0/9)
visitAnnotation (JCTree$JCAnnotation): void 0%   (0/1)0%   (0/7)0%   (0/2)
     
class JavacProcessingEnvironment$ComputeAnnotationSet0%   (0/1)0%   (0/3)0%   (0/37)0%   (0/9)
JavacProcessingEnvironment$ComputeAnnotationSet (Elements): void 0%   (0/1)0%   (0/6)0%   (0/3)
scan (Element, Set): Set 0%   (0/1)0%   (0/29)0%   (0/5)
visitPackage (PackageElement, Set): Set 0%   (0/1)0%   (0/2)0%   (0/1)
     
class JavacProcessingEnvironment$NameProcessIterator0%   (0/1)0%   (0/4)0%   (0/114)0%   (0/33)
JavacProcessingEnvironment$NameProcessIterator (String, ClassLoader, Log): void 0%   (0/1)0%   (0/19)0%   (0/6)
hasNext (): boolean 0%   (0/1)0%   (0/76)0%   (0/21)
next (): Processor 0%   (0/1)0%   (0/15)0%   (0/5)
remove (): void 0%   (0/1)0%   (0/4)0%   (0/1)
     
class JavacProcessingEnvironment$ProcessorState0%   (0/1)0%   (0/5)0%   (0/160)0%   (0/30)
JavacProcessingEnvironment$ProcessorState (Processor, Log, Source, Processing... 0%   (0/1)0%   (0/79)0%   (0/16)
annotationSupported (String): boolean 0%   (0/1)0%   (0/21)0%   (0/4)
checkOptionName (String, Log): boolean 0%   (0/1)0%   (0/23)0%   (0/4)
checkSourceVersionCompatibility (Source, Log): void 0%   (0/1)0%   (0/31)0%   (0/4)
removeSupportedOptions (Set): void 0%   (0/1)0%   (0/6)0%   (0/2)
     
class JavacProcessingEnvironment$1100% (1/1)8%   (1/12)4%   (3/82)3%   (1/35)
scan (JCTree): void 0%   (0/1)0%   (0/9)0%   (0/4)
visitAssignop (JCTree$JCAssignOp): void 0%   (0/1)0%   (0/7)0%   (0/3)
visitBinary (JCTree$JCBinary): void 0%   (0/1)0%   (0/7)0%   (0/3)
visitClassDef (JCTree$JCClassDecl): void 0%   (0/1)0%   (0/7)0%   (0/3)
visitIdent (JCTree$JCIdent): void 0%   (0/1)0%   (0/7)0%   (0/3)
visitMethodDef (JCTree$JCMethodDecl): void 0%   (0/1)0%   (0/7)0%   (0/3)
visitNewClass (JCTree$JCNewClass): void 0%   (0/1)0%   (0/7)0%   (0/3)
visitSelect (JCTree$JCFieldAccess): void 0%   (0/1)0%   (0/7)0%   (0/3)
visitTopLevel (JCTree$JCCompilationUnit): void 0%   (0/1)0%   (0/7)0%   (0/3)
visitUnary (JCTree$JCUnary): void 0%   (0/1)0%   (0/7)0%   (0/3)
visitVarDef (JCTree$JCVariableDecl): void 0%   (0/1)0%   (0/7)0%   (0/3)
JavacProcessingEnvironment$1 (): void 100% (1/1)100% (3/3)100% (1/1)
     
class JavacProcessingEnvironment100% (1/1)20%  (8/40)14%  (267/1891)15%  (58.7/401)
access$000 (JavacProcessingEnvironment, String, Exception): Iterator 0%   (0/1)0%   (0/5)0%   (0/1)
access$100 (JavacProcessingEnvironment, Processor, Set, RoundEnvironment): bo... 0%   (0/1)0%   (0/6)0%   (0/1)
callProcessor (Processor, Set, RoundEnvironment): boolean 0%   (0/1)0%   (0/45)0%   (0/8)
cleanTrees (List): List 0%   (0/1)0%   (0/16)0%   (0/3)
contextForNextRound (Context, boolean): Context 0%   (0/1)0%   (0/156)0%   (0/37)
discoverAndRunProcs (Context, Set, List, List): void 0%   (0/1)0%   (0/215)0%   (0/42)
doProcessing (Context, List, List, Iterable): JavaCompiler 0%   (0/1)0%   (0/414)0%   (0/91)
enterNewClassFiles (Context): ListBuffer 0%   (0/1)0%   (0/54)0%   (0/12)
fileToURL (File): URL 0%   (0/1)0%   (0/51)0%   (0/12)
getContext (): Context 0%   (0/1)0%   (0/3)0%   (0/1)
getElementUtils (): JavacElements 0%   (0/1)0%   (0/3)0%   (0/1)
getFiler (): Filer 0%   (0/1)0%   (0/3)0%   (0/1)
getLocale (): Locale 0%   (0/1)0%   (0/2)0%   (0/1)
getMessager (): Messager 0%   (0/1)0%   (0/3)0%   (0/1)
getOptions (): Map 0%   (0/1)0%   (0/3)0%   (0/1)
getPackageInfoFiles (List): List 0%   (0/1)0%   (0/29)0%   (0/7)
getSourceVersion (): SourceVersion 0%   (0/1)0%   (0/4)0%   (0/1)
getSpecifiedPackages (): Set 0%   (0/1)0%   (0/3)0%   (0/1)
getTopLevelClasses (List): List 0%   (0/1)0%   (0/38)0%   (0/6)
getTypeUtils (): JavacTypes 0%   (0/1)0%   (0/3)0%   (0/1)
handleException (String, Exception): void 0%   (0/1)0%   (0/28)0%   (0/5)
handleServiceLoaderUnavailability (String, Exception): Iterator 0%   (0/1)0%   (0/46)0%   (0/10)
importStringToPattern (String, Processor, Log): Pattern 0%   (0/1)0%   (0/128)0%   (0/23)
isValidOptionName (String): boolean 0%   (0/1)0%   (0/26)0%   (0/4)
moreToDo (): boolean 0%   (0/1)0%   (0/4)0%   (0/1)
needClassLoader (String, Iterable): boolean 0%   (0/1)0%   (0/53)0%   (0/14)
pathToURLs (String): URL [] 0%   (0/1)0%   (0/47)0%   (0/13)
printRoundInfo (PrintWriter, int, List, Set, boolean): void 0%   (0/1)0%   (0/42)0%   (0/3)
runLastRound (PrintWriter, int, boolean, TaskListener): void 0%   (0/1)0%   (0/56)0%   (0/12)
toString (): String 0%   (0/1)0%   (0/2)0%   (0/1)
updateProcessingState (Context, boolean): void 0%   (0/1)0%   (0/18)0%   (0/5)
warnIfUnmatchedOptions (): void 0%   (0/1)0%   (0/17)0%   (0/3)
initProcessorOptions (Context): Map 100% (1/1)37%  (28/75)42%  (6.7/16)
initProcessorIterator (Context, Iterable): void 100% (1/1)53%  (49/92)51%  (11.8/23)
JavacProcessingEnvironment (Context, Iterable): void 100% (1/1)92%  (114/124)98%  (20.6/21)
<static initializer> 100% (1/1)94%  (17/18)98%  (3.9/4)
atLeastOneProcessor (): boolean 100% (1/1)100% (5/5)100% (1/1)
close (): void 100% (1/1)100% (7/7)100% (3/3)
initPlatformAnnotations (): Set 100% (1/1)100% (35/35)100% (9/9)
initUnmatchedProcessorOptions (): Set 100% (1/1)100% (12/12)100% (3/3)
     
class JavacProcessingEnvironment$DiscoveredProcessors$ProcessorStateIterator100% (1/1)40%  (2/5)25%  (31/122)26%  (6.8/26)
next (): JavacProcessingEnvironment$ProcessorState 0%   (0/1)0%   (0/52)0%   (0/9)
remove (): void 0%   (0/1)0%   (0/4)0%   (0/1)
runContributingProcs (RoundEnvironment): void 0%   (0/1)0%   (0/28)0%   (0/8)
hasNext (): boolean 100% (1/1)67%  (14/21)61%  (1.8/3)
JavacProcessingEnvironment$DiscoveredProcessors$ProcessorStateIterator (Javac... 100% (1/1)100% (17/17)100% (5/5)
     
class JavacProcessingEnvironment$ServiceIterator100% (1/1)50%  (2/4)41%  (68/164)41%  (15/37)
next (): Processor 0%   (0/1)0%   (0/41)0%   (0/6)
remove (): void 0%   (0/1)0%   (0/4)0%   (0/1)
hasNext (): boolean 100% (1/1)15%  (4/27)20%  (1/5)
JavacProcessingEnvironment$ServiceIterator (JavacProcessingEnvironment, Class... 100% (1/1)70%  (64/92)56%  (14/25)
     
class JavacProcessingEnvironment$DiscoveredProcessors100% (1/1)100% (2/2)100% (20/20)100% (5/5)
JavacProcessingEnvironment$DiscoveredProcessors (JavacProcessingEnvironment, ... 100% (1/1)100% (14/14)100% (4/4)
iterator (): JavacProcessingEnvironment$DiscoveredProcessors$ProcessorStateIt... 100% (1/1)100% (6/6)100% (1/1)

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

[all classes][com.sun.tools.javac.processing]
EMMA 2.0.5312 (C) Vladimir Roubtsov