langtools/src/share/classes/com/sun/source/util/AbstractTypeProcessor.java
changeset 7083 f6bd6c4fcf54
parent 7070 4fd2b103e010
parent 7082 b36c199d8de8
child 7084 ea14efe08312
equal deleted inserted replaced
7070:4fd2b103e010 7083:f6bd6c4fcf54
     1 /*
       
     2  * Copyright (c) 2009, Oracle and/or its affiliates. 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.  Oracle designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    22  * or visit www.oracle.com if you need additional information or have any
       
    23  * questions.
       
    24  */
       
    25 
       
    26 package com.sun.source.util;
       
    27 
       
    28 import java.util.ArrayList;
       
    29 import java.util.HashSet;
       
    30 import java.util.List;
       
    31 import java.util.Set;
       
    32 
       
    33 import javax.annotation.processing.*;
       
    34 import javax.lang.model.element.Name;
       
    35 import javax.lang.model.element.TypeElement;
       
    36 import javax.lang.model.util.ElementFilter;
       
    37 
       
    38 import com.sun.tools.javac.processing.JavacProcessingEnvironment;
       
    39 import com.sun.tools.javac.util.Context;
       
    40 import com.sun.tools.javac.util.Log;
       
    41 
       
    42 import com.sun.source.tree.ClassTree;
       
    43 
       
    44 /**
       
    45  * This class is an abstract annotation processor designed to be a
       
    46  * convenient superclass for concrete "type processors", processors that
       
    47  * require the type information in the processed source.
       
    48  *
       
    49  * <p>Type processing occurs in one round after the tool (e.g. java compiler)
       
    50  * analyzes the source (all sources taken as input to the tool and sources
       
    51  * generated by other annotation processors).
       
    52  *
       
    53  * <p>The tool infrastructure will interact with classes extending this abstract
       
    54  * class as follows:
       
    55  *
       
    56  * <ol>
       
    57  * [1-3: Identical to {@link Processor} life cycle]
       
    58  *
       
    59  * <li>If an existing {@code Processor} object is not being used, to
       
    60  * create an instance of a processor the tool calls the no-arg
       
    61  * constructor of the processor class.
       
    62  *
       
    63  * <li>Next, the tool calls the {@link #init init} method with
       
    64  * an appropriate {@code ProcessingEnvironment}.
       
    65  *
       
    66  * <li>Afterwards, the tool calls {@link #getSupportedAnnotationTypes
       
    67  * getSupportedAnnotationTypes}, {@link #getSupportedOptions
       
    68  * getSupportedOptions}, and {@link #getSupportedSourceVersion
       
    69  * getSupportedSourceVersion}.  These methods are only called once per
       
    70  * run, not on each round.
       
    71  *
       
    72  * [4-5Unique to {@code AbstractTypeProcessor} subclasses]
       
    73  *
       
    74  * <li>For each class containing a supported annotation, the tool calls
       
    75  * {@link #typeProcess(TypeElement, TreePath) typeProcess} method on the
       
    76  * {@code Processor}.  The class is guaranteed to be type-checked Java code
       
    77  * and all the tree type and symbol information is resolved.
       
    78  *
       
    79  * <li>Finally, the tools calls the
       
    80  * {@link #typeProcessingOver() typeProcessingOver} method
       
    81  * on the {@code Processor}.
       
    82  *
       
    83  * </ol>
       
    84  *
       
    85  * <p>The tool is permitted to ask type processors to process a class once
       
    86  * it is analyzed before the rest of classes are analyzed.  The tool is also
       
    87  * permitted to stop type processing immediately if any errors are raised,
       
    88  * without invoking {@code typeProcessingOver}
       
    89  *
       
    90  * <p>A subclass may override any of the methods in this class, as long as the
       
    91  * general {@link javax.annotation.processing.Processor Processor}
       
    92  * contract is obeyed, with one notable exception.
       
    93  * {@link #process(Set, RoundEnvironment)} may not be overridden, as it
       
    94  * is called during the regular annotation phase before classes are analyzed.
       
    95  *
       
    96  * @author Mahmood Ali
       
    97  * @since 1.7
       
    98  */
       
    99 public abstract class AbstractTypeProcessor extends AbstractProcessor {
       
   100     private final Set<Name> elements = new HashSet<Name>();
       
   101     private boolean hasInvokedTypeProcessingOver = false;
       
   102     private JavacProcessingEnvironment env;
       
   103     private final AttributionTaskListener listener = new AttributionTaskListener();
       
   104 
       
   105     /**
       
   106      * Constructor for subclasses to call.
       
   107      */
       
   108     protected AbstractTypeProcessor() { }
       
   109 
       
   110     /**
       
   111      * {@inheritDoc}
       
   112      */
       
   113     @Override
       
   114     public void init(ProcessingEnvironment env) {
       
   115         super.init(env);
       
   116         this.env = (JavacProcessingEnvironment)env;
       
   117         prepareContext(this.env.getContext());
       
   118     }
       
   119 
       
   120     /**
       
   121      * The use of this method is obsolete in type processors.  The method is
       
   122      * called during regular annotation processing phase only.
       
   123      */
       
   124     @Override
       
   125     public final boolean process(Set<? extends TypeElement> annotations,
       
   126             RoundEnvironment roundEnv) {
       
   127         for (TypeElement elem : ElementFilter.typesIn(roundEnv.getRootElements())) {
       
   128             elements.add(elem.getQualifiedName());
       
   129         }
       
   130         return false;
       
   131     }
       
   132 
       
   133     /**
       
   134      * Processes a fully analyzed class that contains a supported annotation
       
   135      * (look {@link #getSupportedAnnotationTypes()}).
       
   136      *
       
   137      * <p>The passed class is always a valid type-checked Java code.
       
   138      *
       
   139      * @param element       element of the analyzed class
       
   140      * @param tree  the tree path to the element, with the leaf being a
       
   141      *              {@link ClassTree}
       
   142      */
       
   143     public abstract void typeProcess(TypeElement element, TreePath tree);
       
   144 
       
   145     /**
       
   146      * A method to be called once all the classes are processed and no error
       
   147      * is reported.
       
   148      *
       
   149      * <p>Subclasses may override this method to do any aggregate analysis
       
   150      * (e.g. generate report, persistence) or resource deallocation.
       
   151      *
       
   152      * <p>If an error (a Java error or a processor error) is reported, this
       
   153      * method is not guaranteed to be invoked.
       
   154      */
       
   155     public void typeProcessingOver() { }
       
   156 
       
   157     /**
       
   158      * adds a listener for attribution.
       
   159      */
       
   160     private void prepareContext(Context context) {
       
   161         TaskListener otherListener = context.get(TaskListener.class);
       
   162         if (otherListener == null) {
       
   163             context.put(TaskListener.class, listener);
       
   164         } else {
       
   165             // handle cases of multiple listeners
       
   166             context.put(TaskListener.class, (TaskListener)null);
       
   167             TaskListeners listeners = new TaskListeners();
       
   168             listeners.add(otherListener);
       
   169             listeners.add(listener);
       
   170             context.put(TaskListener.class, listeners);
       
   171         }
       
   172     }
       
   173 
       
   174     /**
       
   175      * A task listener that invokes the processor whenever a class is fully
       
   176      * analyzed.
       
   177      */
       
   178     private final class AttributionTaskListener implements TaskListener {
       
   179 
       
   180         @Override
       
   181         public void finished(TaskEvent e) {
       
   182             Log log = Log.instance(env.getContext());
       
   183 
       
   184             if (!hasInvokedTypeProcessingOver && elements.isEmpty() && log.nerrors == 0) {
       
   185                 typeProcessingOver();
       
   186                 hasInvokedTypeProcessingOver = true;
       
   187             }
       
   188 
       
   189             if (e.getKind() != TaskEvent.Kind.ANALYZE)
       
   190                 return;
       
   191 
       
   192             if (e.getTypeElement() == null)
       
   193                 throw new AssertionError("event task without a type element");
       
   194             if (e.getCompilationUnit() == null)
       
   195                 throw new AssertionError("even task without compilation unit");
       
   196 
       
   197             if (!elements.remove(e.getTypeElement().getQualifiedName()))
       
   198                 return;
       
   199 
       
   200             if (log.nerrors != 0)
       
   201                 return;
       
   202 
       
   203             TypeElement elem = e.getTypeElement();
       
   204             TreePath p = Trees.instance(env).getPath(elem);
       
   205 
       
   206             typeProcess(elem, p);
       
   207 
       
   208             if (!hasInvokedTypeProcessingOver && elements.isEmpty() && log.nerrors == 0) {
       
   209                 typeProcessingOver();
       
   210                 hasInvokedTypeProcessingOver = true;
       
   211             }
       
   212         }
       
   213 
       
   214         @Override
       
   215         public void started(TaskEvent e) { }
       
   216 
       
   217     }
       
   218 
       
   219     /**
       
   220      * A task listener multiplexer.
       
   221      */
       
   222     private static class TaskListeners implements TaskListener {
       
   223         private final List<TaskListener> listeners = new ArrayList<TaskListener>();
       
   224 
       
   225         public void add(TaskListener listener) {
       
   226             listeners.add(listener);
       
   227         }
       
   228 
       
   229         public void remove(TaskListener listener) {
       
   230             listeners.remove(listener);
       
   231         }
       
   232 
       
   233         @Override
       
   234         public void finished(TaskEvent e) {
       
   235             for (TaskListener listener : listeners)
       
   236                 listener.finished(e);
       
   237         }
       
   238 
       
   239         @Override
       
   240         public void started(TaskEvent e) {
       
   241             for (TaskListener listener : listeners)
       
   242                 listener.started(e);
       
   243         }
       
   244     }
       
   245 }