Package org.glassfish.apf.impl

Source Code of org.glassfish.apf.impl.AnnotationProcessorImpl

/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License").  You
* may not use this file except in compliance with the License.  You can
* obtain a copy of the License at
* https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt.  See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license."  If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above.  However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/

package org.glassfish.apf.impl;

import java.util.EmptyStackException;
import java.util.Map;
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;
import java.util.Set;
import java.util.HashSet;
import java.util.Stack;
import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Constructor;
import java.util.logging.Logger;

import org.glassfish.apf.ProcessingContext;
import org.glassfish.apf.AnnotationProcessor;
import org.glassfish.apf.AnnotationInfo;
import org.glassfish.apf.AnnotationProcessorException;
import org.glassfish.apf.AnnotationHandler;
import org.glassfish.apf.AnnotatedElementHandler;
import org.glassfish.apf.ComponentInfo;
import org.glassfish.apf.ResultType;
import org.glassfish.apf.HandlerProcessingResult;
import org.glassfish.apf.ProcessingResult;
import org.glassfish.apf.Scanner;
import java.util.logging.Level;


/**
*
* @author dochez
*/
public class AnnotationProcessorImpl implements AnnotationProcessor {
   
    AnnotationProcessorImpl delegate;
    Map<Class<? extends Annotation>, List<AnnotationHandler>> handlers =
            new HashMap<Class<? extends Annotation>, List<AnnotationHandler>>();
   
    int errorCount;
    Logger logger;
    Stack<StackElement> annotatedElements = new Stack<StackElement>();
    Set<Package> visitedPackages = new HashSet<Package>();
   
    /** Creates a new instance of AnnotationProcessorImpl */
    public AnnotationProcessorImpl() {
        logger = AnnotationUtils.getLogger();
    }
   
    public void setDelegate(AnnotationProcessorImpl delegate) {
        this.delegate = delegate;
    }   
    public ProcessingContext createContext() {
        ProcessingContext ctx = new ProcessingContextImpl(this);
        ctx.setErrorHandler(new DefaultErrorHandler());
        return ctx;
    }
   
    /**
     * Log a message on the default logger
     */
    public void log(Level level, AnnotationInfo locator, String localizedMessage){
        if (logger!=null && logger.isLoggable(level)){
            if (locator!=null){
                logger.log(level, AnnotationUtils.getLocalString(
                    "enterprise.deployment.annotation.error",
                    "{2}\n symbol: {0}\n location: {1}",
                    new Object[] { locator.getAnnotation().annotationType().getName(), locator.getAnnotatedElement(), localizedMessage}));
            } else{
                logger.log(level, localizedMessage);
            }
        }
    }
   
    /**
     * Starts the annotation processing tool passing the processing context which
     * encapuslate all information necessary for the configuration of the tool.
     * @param ctx is the initialized processing context
     * @return the result of the annoations processing
     */   
    public ProcessingResult process(ProcessingContext ctx)
        throws AnnotationProcessorException
    {
       
        Scanner<Object> scanner = ctx.getProcessingInput();
        ProcessingResultImpl result = new ProcessingResultImpl();
        errorCount=0;
       
        for (Class c : scanner.getElements()) {
           
            result.add(process(ctx, c));         
        }
        return result;
    }
   
    /**
     * Process a set of classes from the parameter list rather than from the
     * processing context. This allow the annotation handlers to call be the
     * annotation processing tool when classes need to be processed in a
     * particular context rather than when they are picked up by the scanner.
     *
     * @param the processing context
     * @param the list of classes to process
     * @return the processing result for such classes
     * @throws AnnotationProcessorException if handlers fail to process
     * an annotation
     */   
    public ProcessingResult process(ProcessingContext ctx, Class[] classes)
        throws AnnotationProcessorException {
       
        ProcessingResultImpl result = new ProcessingResultImpl();
        for (Class c : classes) {
            result.add(process(ctx, c));
        }
        return result;
    }
   
    private ProcessingResult process(ProcessingContext ctx, Class c)
        throws AnnotationProcessorException {
       
        Scanner scanner = ctx.getProcessingInput();
        ProcessingResultImpl result = new ProcessingResultImpl();
       
        // let's see first if this package is new to us and annotated.
        Package classPackage = c.getPackage();
        if (classPackage != null && visitedPackages.add(classPackage)) {
            // new package
            result.add(classPackage,
                    processAnnotations(ctx, ElementType.PACKAGE, classPackage));
        }

        ComponentInfo info = null;
        try {
            info = scanner.getComponentInfo(c);
        } catch (NoClassDefFoundError err) {
            // issue 456: allow verifier to report this issue
            AnnotationProcessorException ape =
                    new AnnotationProcessorException(
                            AnnotationUtils.getLocalString(
                                    "enterprise.deployment.annotation.classnotfounderror",
                                    "Class [ {0} ] not found. Error while loading [ {1} ]",
                                    new Object[]{err.getMessage(), c}));
            ctx.getErrorHandler().error(ape);
            // let's continue to the next class instead of aborting the whole
            // annotation processing
            return result;
        }

        // process the class itself.
        AnnotatedElementHandler handler= ctx.getHandler();
        logStart(handler, ElementType.TYPE,c);
        result.add(c, processAnnotations(ctx, c));
       
        // now dive into the fields.
        for (Field field : info.getFields()) {
            result.add(field,processAnnotations(ctx, ElementType.FIELD, field));
        }
       
        // constructors...
        for (Constructor constructor : info.getConstructors()) {
            logStart(ctx.getHandler(), ElementType.CONSTRUCTOR, constructor);
            result.add(constructor, processAnnotations(ctx, constructor));
           
            // parameters
            processParameters(ctx, constructor.getParameterAnnotations());
           
            logEnd(ctx.getHandler(), ElementType.CONSTRUCTOR, constructor);
           
        }
       
        // methods...
        for (Method method : info.getMethods()) {
            logStart(ctx.getHandler(), ElementType.METHOD, method);
            result.add(method, processAnnotations(ctx, method));
           
            // parameters
            processParameters(ctx, method.getParameterAnnotations());
           
            logEnd(ctx.getHandler(), ElementType.METHOD, method);
        }
       
        // Because of annotation inheritance, we need to to travel to
        // the superclasses to ensure that annotations defined at the
        // TYPE level are processed at this component level.
        // Note : so far, I am ignoring the implemented interfaces
        Class currentClass = c.getSuperclass();
        while (currentClass!=null && !currentClass.equals(Object.class)) {
            // the trick is to add the results for this class, not
            // for the ones they are defined in...
            result.add(c, processAnnotations(ctx, currentClass));
            currentClass = currentClass.getSuperclass();
        }
       
        // end of class processing, we need to get the top handler
        // since it may have changed during the annotation processing
        logEnd(ctx.getHandler(), ElementType.TYPE, c)
       
        return result;
    }
   
    private HandlerProcessingResult processParameters(ProcessingContext ctx, Annotation[][] parametersAnnotations)
    throws AnnotationProcessorException
    {

        HandlerProcessingResultImpl result = new HandlerProcessingResultImpl();
       
        // process the method parameters...
        for (Annotation[] parameterAnnotations : parametersAnnotations) {
            logStart(ctx.getHandler(), ElementType.PARAMETER, null);
            if (parameterAnnotations!=null) {
                for (Annotation annotation : parameterAnnotations) {
                    AnnotationInfo info = new AnnotationInfo(ctx, null, annotation, ElementType.PARAMETER);
                    process(ctx, info, result);
                    dumpProcessingResult(result);
                }
            }
            logEnd(ctx.getHandler(), ElementType.PARAMETER, null);
        }
        return result;       
    }
   
    private HandlerProcessingResult processAnnotations(ProcessingContext ctx, ElementType type, AnnotatedElement element)
        throws AnnotationProcessorException
    {
       
        AnnotatedElementHandler handler = ctx.getHandler();
        logStart(handler, type, element);
        HandlerProcessingResult result = processAnnotations(ctx, element);
        logEnd(handler, type, element);

        dumpProcessingResult(result);                   
       
        return result;
    }
   
    private HandlerProcessingResult processAnnotations(ProcessingContext ctx, AnnotatedElement element)
        throws AnnotationProcessorException
    {
   
        HandlerProcessingResultImpl result= new HandlerProcessingResultImpl();
       
        for (Annotation annotation : element.getAnnotations()) {
            // initialize the result...
            AnnotationInfo subElement = new AnnotationInfo(ctx, element, annotation, getTopElementType());
            if (!result.processedAnnotations().containsKey(annotation.annotationType())) {
                process(ctx, subElement, result);
            } else {
                if (AnnotationUtils.shouldLog("annotation")) {
                    logger.finer("Annotation " + annotation.annotationType() + " already processed");
                }
            }      
        }
        return result;
    }
   
    private void process(ProcessingContext ctx, AnnotationInfo element, HandlerProcessingResultImpl result)
        throws AnnotationProcessorException
    {
       
       
        Annotation annotation = element.getAnnotation();
        if (AnnotationUtils.shouldLog("annotation")) {
            logger.finer("Annotation : " + annotation.annotationType().getName() + " delegate = " + delegate);
        }
        result.addResult(annotation.annotationType(), ResultType.UNPROCESSED);
       
        // we ignore all java.* annotations
        Package annPackage = annotation.annotationType().getPackage();
        if (annPackage != null && annPackage.getName().startsWith("java.lang"))
            return;
       
        List<AnnotationHandler> annotationHandlers = handlers.get(annotation.annotationType());
        if (annotationHandlers!=null) {
            for (AnnotationHandler handler : annotationHandlers) {
               
                // here we need to be careful, we are ready to invoke a handler
                // to process a particular annotation type. However, this handler
                // may have defined a list of annotations that should be processed
                // (if present on the annotated element) before itself.
                // do this check and process those annotations first.
                Class<? extends Annotation>[] dependencies = handler.getTypeDependencies();
                if (dependencies!=null) {
                    AnnotatedElement ae = element.getAnnotatedElement();
                    for (Class<? extends Annotation> annotationType : dependencies) {
                        Annotation depAnnotation = ae.getAnnotation(annotationType);
                        if (depAnnotation!=null) {                       
                            ResultType resultType = result.processedAnnotations().get(annotationType);
                            if (resultType==null || resultType==ResultType.UNPROCESSED){
                                // annotation is present, process it.
                                AnnotationInfo info = new AnnotationInfo(ctx, ae, depAnnotation, getTopElementType());
                                process(ctx, info, result);
                            }
                        }
                    }
                }
               
                // at this point, all annotation that I declared depending on
                // are processed
                HandlerProcessingResult processingResult = null;
                try {
                    processingResult = handler.processAnnotation(element);
                } catch(AnnotationProcessorException ape) {
                    // I am logging this exception
                    log(Level.SEVERE, ape.getLocator(), ape.getMessage());
                   
                    // I am not throwing the exception unless it is fatal so annotation
                    // processing can continue and we have a chance to report all
                    // errors.
                    if (ape.isFatal()) {
                        throw ape;
                    }
                   
                    if (++errorCount>100){
                        throw new AnnotationProcessorException(
                                AnnotationUtils.getLocalString(
                                    "enterprise.deployment.annotation.toomanyerror",
                                    "Too many errors, annotation processing abandoned."));
                    }
                   
                    processingResult =
                        HandlerProcessingResultImpl.getDefaultResult(
                        annotation.annotationType(), ResultType.FAILED);
                } catch(Throwable e){
                    AnnotationProcessorException ape = new AnnotationProcessorException(e.getMessage(), element);
                    ape.initCause(e);
                    throw ape;
                }
                result.addAll(processingResult);
            }
        } else {
            if (delegate!=null) {
                delegate.process(ctx, element, result);
            } else {          
                ctx.getErrorHandler().fine(
                        new AnnotationProcessorException("No handler defined for "
                            + annotation.annotationType()));
            }
        }
    }
   
    private void dumpProcessingResult(HandlerProcessingResult result) {

        if (result==null || !AnnotationUtils.shouldLog("annotation")) {
            return;
        }
  
        Map<Class<? extends Annotation>, ResultType> annotationResults =
                result.processedAnnotations();
        for (Class annotationType : annotationResults.keySet()) {           
            logger.finer("Annotation " + annotationType + " : " +
                    annotationResults.get(annotationType));
        }
    }
    public void pushAnnotationHandler(AnnotationHandler handler) {
       
        Class<? extends Annotation> type = handler.getAnnotationType();
        List<AnnotationHandler> currentHandlers = handlers.get(type);
        if (currentHandlers==null) {
            currentHandlers = new ArrayList<AnnotationHandler>();
            handlers.put(type, currentHandlers);
        }
        currentHandlers.add(handler);
    }
   
    public void popAnnotationHandler(Class<? extends Annotation> type) {
        List<AnnotationHandler> currentHandlers = handlers.get(type);
        if (currentHandlers!=null) {
            currentHandlers.remove(currentHandlers.size());
        }       
    }   

    public AnnotationHandler getAnnotationHandler(Class<? extends Annotation> type) {
        List<AnnotationHandler> currentHandlers = handlers.get(type);
        if (currentHandlers!=null && currentHandlers.size()>0) {
            return currentHandlers.get(0);
        }       
        return null;
    }     
   
    /**
     * @return the last element pushed on the stack which ElementType was
     * the one passed or null if no stack element is of the given type.
     */
    public AnnotatedElement getLastAnnotatedElement(ElementType type) {
        for (int i=annotatedElements.size();i!=0;i--) {
            StackElement e = annotatedElements.get(i - 1);
            if (e.getElementType().equals(type))
                return e.getAnnotatedElement();
        }
        return null;
    }   
   
    public Stack<StackElement> getStack() {
        return annotatedElements;
    }
   
    private void logStart(AnnotatedElementHandler handler, ElementType type, AnnotatedElement c) throws AnnotationProcessorException {
       
        if (AnnotationUtils.shouldLog("types")) {
            AnnotationUtils.getLogger().finer(type + " START : " + c);
        }
       
        // push it to our annotated element stack
        annotatedElements.push(new StackElement(type, c));
        if(delegate!=null) {
            delegate.getStack().push(new StackElement(type, c));
        }
       
        if (handler!=null) {
            handler.startElement(type, c);
        }  
    }
   
    private void logEnd(AnnotatedElementHandler handler, ElementType type, AnnotatedElement c) throws AnnotationProcessorException {
       
        if (AnnotationUtils.shouldLog("types")) {
            AnnotationUtils.getLogger().finer(type + " END : " + c);
        }
       
        // pop it from our annotated element stack
        annotatedElements.pop();
        if(delegate!=null) {
            delegate.getStack().pop();
        }
       
        if (handler!=null) {
            handler.endElement(type, c);
        }  
    }  
   
    /**
     * @return the top annotated elements stack element type
     */
    private ElementType getTopElementType() {
        try {
            StackElement top = annotatedElements.peek();
            return top.getElementType();
        } catch(EmptyStackException ex) {
            return null;
        }
    }
}
TOP

Related Classes of org.glassfish.apf.impl.AnnotationProcessorImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.