jjg@309: /* jjg@309: * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. jjg@309: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. jjg@309: * jjg@309: * This code is free software; you can redistribute it and/or modify it jjg@309: * under the terms of the GNU General Public License version 2 only, as jjg@309: * published by the Free Software Foundation. Sun designates this jjg@309: * particular file as subject to the "Classpath" exception as provided jjg@309: * by Sun in the LICENSE file that accompanied this code. jjg@309: * jjg@309: * This code is distributed in the hope that it will be useful, but WITHOUT jjg@309: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or jjg@309: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License jjg@309: * version 2 for more details (a copy is included in the LICENSE file that jjg@309: * accompanied this code). jjg@309: * jjg@309: * You should have received a copy of the GNU General Public License version jjg@309: * 2 along with this work; if not, write to the Free Software Foundation, jjg@309: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. jjg@309: * jjg@309: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, jjg@309: * CA 95054 USA or visit www.sun.com if you need additional information or jjg@309: * have any questions. jjg@309: */ jjg@309: jjg@309: package com.sun.source.util; jjg@309: jjg@309: import java.util.ArrayList; jjg@309: import java.util.HashSet; jjg@309: import java.util.List; jjg@309: import java.util.Set; jjg@309: jjg@309: import javax.annotation.processing.*; jjg@309: import javax.lang.model.element.Name; jjg@309: import javax.lang.model.element.TypeElement; jjg@309: import javax.lang.model.util.ElementFilter; jjg@309: jjg@309: import com.sun.tools.javac.processing.JavacProcessingEnvironment; jjg@309: import com.sun.tools.javac.util.Context; jjg@309: import com.sun.tools.javac.util.Log; jjg@309: jjg@309: import com.sun.source.tree.ClassTree; jjg@309: jjg@309: /** jjg@309: * This class is an abstract annotation processor designed to be a jjg@309: * convenient superclass for concrete "type processors", processors that jjg@309: * require the type information in the processed source. jjg@309: * jjg@309: *

Type processing occurs in one round after the tool (e.g. java compiler) jjg@309: * analyzes the source (all sources taken as input to the tool and sources jjg@309: * generated by other annotation processors). jjg@309: * jjg@309: *

The tool infrastructure will interact with classes extending this abstract jjg@309: * class as follows: jjg@309: * jjg@309: *

    jjg@309: * [1-3: Identical to {@link Processor} life cycle] jjg@309: * jjg@309: *
  1. If an existing {@code Processor} object is not being used, to jjg@309: * create an instance of a processor the tool calls the no-arg jjg@309: * constructor of the processor class. jjg@309: * jjg@309: *
  2. Next, the tool calls the {@link #init init} method with jjg@309: * an appropriate {@code ProcessingEnvironment}. jjg@309: * jjg@309: *
  3. Afterwards, the tool calls {@link #getSupportedAnnotationTypes jjg@309: * getSupportedAnnotationTypes}, {@link #getSupportedOptions jjg@309: * getSupportedOptions}, and {@link #getSupportedSourceVersion jjg@309: * getSupportedSourceVersion}. These methods are only called once per jjg@309: * run, not on each round. jjg@309: * jjg@309: * [4-5Unique to {@code AbstractTypeProcessor} subclasses] jjg@309: * jjg@309: *
  4. For each class containing a supported annotation, the tool calls jjg@309: * {@link #typeProcess(TypeElement, TreePath) typeProcess} method on the jjg@309: * {@code Processor}. The class is guaranteed to be type-checked Java code jjg@309: * and all the tree type and symbol information is resolved. jjg@309: * jjg@309: *
  5. Finally, the tools calls the jjg@309: * {@link #typeProcessingOver() typeProcessingOver} method jjg@309: * on the {@code Processor}. jjg@309: * jjg@309: *
jjg@309: * jjg@309: *

The tool is permitted to ask type processors to process a class once jjg@309: * it is analyzed before the rest of classes are analyzed. The tool is also jjg@309: * permitted to stop type processing immediately if any errors are raised, jjg@309: * without invoking {@code typeProcessingOver} jjg@309: * jjg@309: *

A subclass may override any of the methods in this class, as long as the jjg@309: * general {@link javax.annotation.processing.Processor Processor} jjg@309: * contract is obeyed, with one notable exception. jjg@309: * {@link #process(Set, RoundEnvironment)} may not be overridden, as it jjg@309: * is called during the regular annotation phase before classes are analyzed. jjg@309: * jjg@309: * @author Mahmood Ali jjg@309: * @since 1.7 jjg@309: */ jjg@309: public abstract class AbstractTypeProcessor extends AbstractProcessor { jjg@309: private final Set elements = new HashSet(); jjg@309: private boolean hasInvokedTypeProcessingOver = false; jjg@309: private JavacProcessingEnvironment env; jjg@309: private final AttributionTaskListener listener = new AttributionTaskListener(); jjg@309: jjg@309: /** jjg@309: * Constructor for subclasses to call. jjg@309: */ jjg@309: protected AbstractTypeProcessor() { } jjg@309: jjg@309: /** jjg@309: * {@inheritDoc} jjg@309: */ jjg@309: @Override jjg@309: public void init(ProcessingEnvironment env) { jjg@309: super.init(env); jjg@309: this.env = (JavacProcessingEnvironment)env; jjg@309: prepareContext(this.env.getContext()); jjg@309: } jjg@309: jjg@309: /** jjg@309: * The use of this method is obsolete in type processors. The method is jjg@309: * called during regular annotation processing phase only. jjg@309: */ jjg@309: @Override jjg@309: public final boolean process(Set annotations, jjg@309: RoundEnvironment roundEnv) { jjg@309: for (TypeElement elem : ElementFilter.typesIn(roundEnv.getRootElements())) { jjg@309: elements.add(elem.getQualifiedName()); jjg@309: } jjg@309: return false; jjg@309: } jjg@309: jjg@309: /** jjg@309: * Processes a fully analyzed class that contains a supported annotation jjg@309: * (look {@link #getSupportedAnnotationTypes()}). jjg@309: * jjg@309: *

The passed class is always a valid type-checked Java code. jjg@309: * jjg@309: * @param element element of the analyzed class jjg@309: * @param tree the tree path to the element, with the leaf being a jjg@309: * {@link ClassTree} jjg@309: */ jjg@309: public abstract void typeProcess(TypeElement element, TreePath tree); jjg@309: jjg@309: /** jjg@309: * A method to be called once all the classes are processed and no error jjg@309: * is reported. jjg@309: * jjg@309: *

Subclasses may override this method to do any aggregate analysis jjg@309: * (e.g. generate report, persistence) or resource deallocation. jjg@309: * jjg@309: *

If an error (a Java error or a processor error) is reported, this jjg@309: * method is not guaranteed to be invoked. jjg@309: */ jjg@309: public void typeProcessingOver() { } jjg@309: jjg@309: /** jjg@309: * adds a listener for attribution. jjg@309: */ jjg@309: private void prepareContext(Context context) { jjg@309: TaskListener otherListener = context.get(TaskListener.class); jjg@309: if (otherListener == null) { jjg@309: context.put(TaskListener.class, listener); jjg@309: } else { jjg@309: // handle cases of multiple listeners jjg@309: context.put(TaskListener.class, (TaskListener)null); jjg@309: TaskListeners listeners = new TaskListeners(); jjg@309: listeners.add(otherListener); jjg@309: listeners.add(listener); jjg@309: context.put(TaskListener.class, listeners); jjg@309: } jjg@309: } jjg@309: jjg@309: /** jjg@309: * A task listener that invokes the processor whenever a class is fully jjg@309: * analyzed. jjg@309: */ jjg@309: private final class AttributionTaskListener implements TaskListener { jjg@309: jjg@309: @Override jjg@309: public void finished(TaskEvent e) { jjg@309: Log log = Log.instance(env.getContext()); jjg@309: jjg@309: if (!hasInvokedTypeProcessingOver && elements.isEmpty() && log.nerrors == 0) { jjg@309: typeProcessingOver(); jjg@309: hasInvokedTypeProcessingOver = true; jjg@309: } jjg@309: jjg@309: if (e.getKind() != TaskEvent.Kind.ANALYZE) jjg@309: return; jjg@309: jjg@309: if (e.getTypeElement() == null) jjg@309: throw new AssertionError("event task without a type element"); jjg@309: if (e.getCompilationUnit() == null) jjg@309: throw new AssertionError("even task without compilation unit"); jjg@309: jjg@309: if (!elements.remove(e.getTypeElement().getQualifiedName())) jjg@309: return; jjg@309: jjg@309: if (log.nerrors != 0) jjg@309: return; jjg@309: jjg@309: TypeElement elem = e.getTypeElement(); jjg@309: TreePath p = Trees.instance(env).getPath(elem); jjg@309: jjg@309: typeProcess(elem, p); jjg@309: jjg@309: if (!hasInvokedTypeProcessingOver && elements.isEmpty() && log.nerrors == 0) { jjg@309: typeProcessingOver(); jjg@309: hasInvokedTypeProcessingOver = true; jjg@309: } jjg@309: } jjg@309: jjg@309: @Override jjg@309: public void started(TaskEvent e) { } jjg@309: jjg@309: } jjg@309: jjg@309: /** jjg@309: * A task listener multiplexer. jjg@309: */ jjg@309: private static class TaskListeners implements TaskListener { jjg@309: private final List listeners = new ArrayList(); jjg@309: jjg@309: public void add(TaskListener listener) { jjg@309: listeners.add(listener); jjg@309: } jjg@309: jjg@309: public void remove(TaskListener listener) { jjg@309: listeners.remove(listener); jjg@309: } jjg@309: jjg@309: @Override jjg@309: public void finished(TaskEvent e) { jjg@309: for (TaskListener listener : listeners) jjg@309: listener.finished(e); jjg@309: } jjg@309: jjg@309: @Override jjg@309: public void started(TaskEvent e) { jjg@309: for (TaskListener listener : listeners) jjg@309: listener.started(e); jjg@309: } jjg@309: } jjg@309: }