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