Package org.codehaus.aspectwerkz.annotation.expression

Source Code of org.codehaus.aspectwerkz.annotation.expression.AnnotationVisitor

/**************************************************************************************

* Copyright (c) Jonas Bon�r, Alexandre Vasseur. All rights reserved.                 *

* http://aspectwerkz.codehaus.org                                                    *

* ---------------------------------------------------------------------------------- *

* The software in this package is published under the terms of the LGPL license      *

* a copy of which has been included with this distribution in the license.txt file.  *

**************************************************************************************/

package org.codehaus.aspectwerkz.annotation.expression;



import org.codehaus.aspectwerkz.annotation.expression.ast.ASTAnnotation;

import org.codehaus.aspectwerkz.annotation.expression.ast.ASTArray;

import org.codehaus.aspectwerkz.annotation.expression.ast.ASTBoolean;

import org.codehaus.aspectwerkz.annotation.expression.ast.ASTChar;

import org.codehaus.aspectwerkz.annotation.expression.ast.ASTFloat;

import org.codehaus.aspectwerkz.annotation.expression.ast.ASTHex;

import org.codehaus.aspectwerkz.annotation.expression.ast.ASTIdentifier;

import org.codehaus.aspectwerkz.annotation.expression.ast.ASTInteger;

import org.codehaus.aspectwerkz.annotation.expression.ast.ASTKeyValuePair;

import org.codehaus.aspectwerkz.annotation.expression.ast.ASTOct;

import org.codehaus.aspectwerkz.annotation.expression.ast.ASTRoot;

import org.codehaus.aspectwerkz.annotation.expression.ast.ASTString;

import org.codehaus.aspectwerkz.annotation.expression.ast.AnnotationParserVisitor;

import org.codehaus.aspectwerkz.annotation.expression.ast.SimpleNode;

import org.codehaus.aspectwerkz.annotation.expression.ast.AnnotationParser;

import org.codehaus.aspectwerkz.annotation.expression.ast.ParseException;

import org.codehaus.aspectwerkz.annotation.AnnotationElement;

import org.codehaus.aspectwerkz.annotation.AnnotationManager;

import org.codehaus.aspectwerkz.annotation.Annotation;

import org.codehaus.aspectwerkz.exception.WrappedRuntimeException;

import org.objectweb.asm.Type;



import java.lang.reflect.Field;

import java.lang.reflect.Method;

import java.lang.reflect.Array;

import java.util.Map;

import java.util.HashMap;



/**

* Parse a source-like annotation representation to feed a map of AnnotationElement which

* contain holder to actual values. Class and type referenced are holded behind lazy

* wrapper that won't load them unless used.

* <p/>

* Note that this parser will trigger class loading to ensure type consistency

* [change to ASMClassInfo instead of reflect if embedded parsing needed]

* <p/>

* Note: the loader used here is the one from the annotation class and not the one from annotated element

* That does not matter since parse time is a build time operation for now.

*

* @author <a href="mailto:jboner@codehaus.org">Jonas Bon�r </a>

* @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>

*/

public class AnnotationVisitor implements AnnotationParserVisitor {



    /**

     * The one and only annotation parser.

     */

    protected static final AnnotationParser PARSER = new AnnotationParser(System.in);



    protected Map m_annotationElementValueHoldersByName;



    /**

     * We reference class at parse time. We don't need to avoid reflection.

     */

    protected Class m_annotationClass;



    /**

     * Creates a new visitor.

     */

    public AnnotationVisitor(final Map annotationElementValueHoldersByName, final Class annotationClass) {

        m_annotationElementValueHoldersByName = annotationElementValueHoldersByName;

        m_annotationClass = annotationClass;

    }



    /**

     * Parse the given annotationRepresentation (@XXX(...)) to feed the given annotationElements map,

     * based on the annotationClass annotation interface.

     *

     * @param annotationElements

     * @param annotationRepresentation

     * @param annotationClass

     */

    public static void parse(final Map annotationElements, final String annotationRepresentation,

                             final Class annotationClass) {

        try {

            ASTRoot root = PARSER.parse(annotationRepresentation);

            new AnnotationVisitor(annotationElements, annotationClass).visit(root, null);

        } catch (ParseException e) {

            throw new WrappedRuntimeException("cannot parse annotation [" + annotationRepresentation + "]", e);

        }

    }



    public Object visit(SimpleNode node, Object data) {

        return node.jjtGetChild(0).jjtAccept(this, data);

    }



    public Object visit(ASTRoot node, Object data) {

        return node.jjtGetChild(0).jjtAccept(this, data);

    }



    public Object visit(ASTAnnotation node, Object data) {

        int nr = node.jjtGetNumChildren();



        if (nr == 1 && !(node.jjtGetChild(0) instanceof ASTKeyValuePair)) {

            // single "value" default

            Object value = node.jjtGetChild(0).jjtAccept(this, data);



            if(!(node.jjtGetChild(0) instanceof ASTAnnotation)) { // child already set the value

               m_annotationElementValueHoldersByName.put("value",

                     new AnnotationElement("value", value));

            }

        } else {

            for (int i = 0; i < nr; i++) {

                node.jjtGetChild(i).jjtAccept(this, data);

            }

        }

        return null;

    }



    public Object visit(ASTKeyValuePair node, Object data) {

        String elementName = node.getKey();



        // get the methodInfo for this elementName to access its type from its name

        MethodInfo elementMethod = getMethodInfo(elementName);



        // nested annotation

        if (node.jjtGetChild(0) instanceof ASTAnnotation) {

            Map nestedAnnotationElementValueHoldersByName = new HashMap();

            AnnotationVisitor nestedAnnotationVisitor = new AnnotationVisitor(

                    nestedAnnotationElementValueHoldersByName,

                    elementMethod.elementType

                    );

            nestedAnnotationVisitor.visit((ASTAnnotation)node.jjtGetChild(0), data);

            m_annotationElementValueHoldersByName.put(elementName,

                    new AnnotationElement(elementName,

                            AnnotationManager.instantiateNestedAnnotation(elementMethod.elementType, nestedAnnotationElementValueHoldersByName)));

        } else {

            Object typedValue = node.jjtGetChild(0).jjtAccept(this, elementMethod);

            m_annotationElementValueHoldersByName.put(elementName,

                    new AnnotationElement(elementName, typedValue));

        }

        return null;

    }



    public Object visit(ASTArray node, Object data) {

        MethodInfo methodInfo = (MethodInfo) data;

        Class elementType = methodInfo.elementType;

        if (!elementType.isArray()) {

            throw new RuntimeException(

                    "type for element ["

                    + methodInfo.elementMethod.getName()

                    + "] is not of type array"

            );

        }

        Class componentType = elementType.getComponentType();

        if (componentType.isArray()) {

            throw new UnsupportedOperationException(

                    "multidimensional arrays are not supported for element type, was required method ["

                    + methodInfo.elementMethod.getName()

                    + "]"

            );

        }

        return createTypedArray(node, data, node.jjtGetNumChildren(), componentType);

    }



    public Object visit(ASTIdentifier node, Object data) {

        String identifier = node.getValue();

        if (identifier.endsWith(".class")) {

            return handleClassIdentifier(identifier);

        } else if (isJavaReferenceType(identifier)) {

            return handleReferenceIdentifier(identifier);

        } else {

            throw new RuntimeException("unsupported format for java type or reference [" + identifier + "]");

        }

    }



    public Object visit(ASTBoolean node, Object data) {

        return Boolean.valueOf(node.getValue());

    }



    public Object visit(ASTChar node, Object data) {

        return new Character(node.getValue().charAt(0));

    }



    public Object visit(ASTString node, Object data) {

        // the node contains the  \" string escapes

        if (node.getValue().length() >= 2) {

            return node.getValue().substring(1, node.getValue().length() - 1);

        } else {

            return node.getValue();

        }

    }



    public Object visit(ASTInteger node, Object data) {

        String value = node.getValue();

        char lastChar = value.charAt(value.length() - 1);

        if ((lastChar == 'L') || (lastChar == 'l')) {

            return new Long(value.substring(0, value.length() - 1));

        } else if (value.length() > 9) {

            return new Long(value);

        } else {

            return new Integer(value);

        }

    }



    public Object visit(ASTFloat node, Object data) {

        String value = node.getValue();

        char lastChar = value.charAt(value.length() - 1);

        if ((lastChar == 'D') || (lastChar == 'd')) {

            return new Double(value.substring(0, value.length() - 1));

        } else if ((lastChar == 'F') || (lastChar == 'f')) {

            return new Float(value.substring(0, value.length() - 1));

        } else {

            return new Double(value);

        }

    }



    public Object visit(ASTHex node, Object data) {

        throw new UnsupportedOperationException("hex numbers not yet supported");

    }



    public Object visit(ASTOct node, Object data) {

        throw new UnsupportedOperationException("octal numbers not yet supported");

    }



    /**

     * For a typed annotation, there should be

     * - a setter method setx or setX

     * - a getter method x or getx or getX

     *

     * @param elementName

     * @return

     */

    private MethodInfo getMethodInfo(final String elementName) {

        StringBuffer javaBeanMethodPostfix = new StringBuffer();

        javaBeanMethodPostfix.append(elementName.substring(0, 1).toUpperCase());

        if (elementName.length() > 1) {

            javaBeanMethodPostfix.append(elementName.substring(1));

        }



        MethodInfo methodInfo = new MethodInfo();

        Method[] methods = m_annotationClass.getDeclaredMethods();

        // look for element methods

        for (int i = 0; i < methods.length; i++) {

            Method elementMethod = methods[i];

            if (elementMethod.getName().equals(elementName)) {

                methodInfo.elementMethod = elementMethod;

                methodInfo.elementType = elementMethod.getReturnType();

                break;

            }

        }

        if (methodInfo.elementMethod == null) {

            throw new RuntimeException(

                    "method for the annotation element ["

                    + elementName

                    + "] can not be found in annotation interface ["

                    + m_annotationClass.getName()

                    + "]"

            );

        }

        return methodInfo;

    }



    private boolean isJavaReferenceType(final String valueAsString) {

        int first = valueAsString.indexOf('.');

        int last = valueAsString.lastIndexOf('.');

        int comma = valueAsString.indexOf(',');

        if ((first > 0) && (last > 0) && (first != last) && (comma < 0)) {

            return true;

        } else {

            return false;

        }

    }



    private Object createTypedArray(final ASTArray node,

                                    final Object data,

                                    final int nrOfElements,

                                    final Class componentType) {

        if (componentType.equals(String.class)) {

            String[] array = new String[nrOfElements];

            for (int i = 0; i < nrOfElements; i++) {

                String value = (String) node.jjtGetChild(i).jjtAccept(this, data);

                array[i] = value;

            }

            return array;

        } else if (componentType.equals(long.class)) {

            long[] array = new long[nrOfElements];

            for (int i = 0; i < nrOfElements; i++) {

                array[i] = ((Long) node.jjtGetChild(i).jjtAccept(this, data)).longValue();

            }

            return array;

        } else if (componentType.equals(int.class)) {

            int[] array = new int[nrOfElements];

            for (int i = 0; i < nrOfElements; i++) {

                array[i] = ((Integer) node.jjtGetChild(i).jjtAccept(this, data)).intValue();

            }

            return array;

        } else if (componentType.equals(short.class)) {

            short[] array = new short[nrOfElements];

            for (int i = 0; i < nrOfElements; i++) {

                array[i] = ((Short) node.jjtGetChild(i).jjtAccept(this, data)).shortValue();

            }

            return array;

        } else if (componentType.equals(double.class)) {

            double[] array = new double[nrOfElements];

            for (int i = 0; i < nrOfElements; i++) {

                array[i] = ((Double) node.jjtGetChild(i).jjtAccept(this, data)).doubleValue();

            }

            return array;

        } else if (componentType.equals(float.class)) {

            float[] array = new float[nrOfElements];

            for (int i = 0; i < nrOfElements; i++) {

                array[i] = ((Float) node.jjtGetChild(i).jjtAccept(this, data)).floatValue();

            }

            return array;

        } else if (componentType.equals(byte.class)) {

            byte[] array = new byte[nrOfElements];

            for (int i = 0; i < nrOfElements; i++) {

                array[i] = ((Byte) node.jjtGetChild(i).jjtAccept(this, data)).byteValue();

            }

            return array;

        } else if (componentType.equals(char.class)) {

            char[] array = new char[nrOfElements];

            for (int i = 0; i < nrOfElements; i++) {

                array[i] = ((Character) node.jjtGetChild(i).jjtAccept(this, data)).charValue();

            }

            return array;

        } else if (componentType.equals(boolean.class)) {

            boolean[] array = new boolean[nrOfElements];

            for (int i = 0; i < nrOfElements; i++) {

                array[i] = ((Boolean) node.jjtGetChild(i).jjtAccept(this, data)).booleanValue();

            }

            return array;

        } else if (componentType.equals(Class.class)) {

            AnnotationElement.LazyClass[] array = new AnnotationElement.LazyClass[nrOfElements];

            for (int i = 0; i < nrOfElements; i++) {

                array[i] = (AnnotationElement.LazyClass) node.jjtGetChild(i).jjtAccept(this, data);

            }

            return array;

        } else {

            if (nrOfElements > 1 && node.jjtGetChild(0) instanceof ASTAnnotation) {

                // nested array of annotation

                Object[] nestedTyped = (Object[])Array.newInstance(componentType, nrOfElements);

                for (int i = 0; i < nrOfElements; i++) {

                    Map nestedAnnotationElementValueHoldersByName = new HashMap();

                    AnnotationVisitor nestedAnnotationVisitor = new AnnotationVisitor(

                            nestedAnnotationElementValueHoldersByName,

                            componentType

                            );

                    nestedAnnotationVisitor.visit((ASTAnnotation)node.jjtGetChild(i), data);

                    nestedTyped[i] = AnnotationManager.instantiateNestedAnnotation(componentType, nestedAnnotationElementValueHoldersByName);

                }

                return nestedTyped;

            } else {

                // reference type

                Object[] array = new Object[nrOfElements];

                for (int i = 0; i < nrOfElements; i++) {

                    array[i] = node.jjtGetChild(i).jjtAccept(this, data);

                }

                return array;

            }

        }

    }



    private Object handleClassIdentifier(String identifier) {

        int index = identifier.lastIndexOf('.');

        String className = identifier.substring(0, index);



        int dimension = 0;

        String componentClassName = className;

        while (componentClassName.endsWith("[]")) {

            dimension++;

            componentClassName = componentClassName.substring(0, componentClassName.length()-2);

        }



        Class componentClass = null;

        boolean isComponentPrimitive = true;

        if (componentClassName.equals("long")) {

            componentClass = long.class;

        } else if (componentClassName.equals("int")) {

            componentClass = int.class;

        } else if (componentClassName.equals("short")) {

            componentClass = short.class;

        } else if (componentClassName.equals("double")) {

            componentClass = double.class;

        } else if (componentClassName.equals("float")) {

            componentClass = float.class;

        } else if (componentClassName.equals("byte")) {

            componentClass = byte.class;

        } else if (componentClassName.equals("char")) {

            componentClass = char.class;

        } else if (componentClassName.equals("boolean")) {

            componentClass = boolean.class;

        } else {

            isComponentPrimitive = false;

            try {

                componentClass = Class.forName(componentClassName, false, m_annotationClass.getClassLoader());

            } catch (ClassNotFoundException e) {

                throw new RuntimeException("could not load class [" + className + "] due to: " + e.toString());

            }

        }



        // primitive types are not wrapped in a LazyClass

        if (isComponentPrimitive) {

            if (dimension <= 0) {

                return componentClass;

            } else {

                return Array.newInstance(componentClass, dimension);

            }

        } else {

            String componentType = Type.getType(componentClass).getDescriptor();

            for (int i = 0; i < dimension; i++) {

                componentType = "[" + componentType;

            }

            Type type = Type.getType(componentType);

            return new AnnotationElement.LazyClass(type.getClassName());

        }

    }



    private Object handleReferenceIdentifier(String identifier) {

        int index = identifier.lastIndexOf('.');

        String className = identifier.substring(0, index);

        String fieldName = identifier.substring(index + 1, identifier.length());

        try {

            // TODO m_annotationClass might be higher in the CL than a referenced identifier

            Class clazz = Class.forName(className, false, m_annotationClass.getClassLoader());

            Field field = clazz.getDeclaredField(fieldName);

            return field.get(null);

        } catch (Exception e) {

            throw new RuntimeException(

                    "could not access reference field [" + identifier + "] due to: " + e.toString()

            );

        }

    }



    /**

     * Holds the element method and type.

     */

    private static class MethodInfo {



        public Method elementMethod;



        public Class elementType;

    }

}
TOP

Related Classes of org.codehaus.aspectwerkz.annotation.expression.AnnotationVisitor

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.