Package org.codehaus.aspectwerkz.annotation

Source Code of org.codehaus.aspectwerkz.annotation.Java5AnnotationInvocationHandler

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

* 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;



import org.codehaus.aspectwerkz.reflect.MethodInfo;

import org.codehaus.aspectwerkz.reflect.ClassInfo;

import org.codehaus.aspectwerkz.reflect.impl.asm.AsmClassInfo;

import org.codehaus.aspectwerkz.exception.WrappedRuntimeException;

import org.objectweb.asm.attrs.*;

import org.objectweb.asm.attrs.Annotation;

import org.objectweb.asm.Type;



import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;

import java.lang.reflect.Proxy;

import java.lang.reflect.Field;

import java.lang.reflect.Array;

import java.util.List;

import java.util.Collection;

import java.util.ArrayList;

import java.util.Iterator;

import java.util.Map;

import java.util.HashMap;

import java.util.Arrays;

import java.io.Serializable;



/**

* Dynamic proxy handler for ASM Annotations we extract

* The handler resolve the LazyClass to a concrete Class so that the proxy creation does not trigger

* any class loading.

* <p/>

*

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

*/

public class Java5AnnotationInvocationHandler implements InvocationHandler {



    /**

     * The annotation class name

     */

    private final String m_annotationClassName;



    /**

     * A list of AnnotationElement containing the annotation instance element values

     * (including the defaulted value)

     */

    private final List m_annotationElements;



    /**

     * private ctor - see getAnnotationProxy()

     *

     * @param annotationClassName

     * @param annotationElements

     */

    private Java5AnnotationInvocationHandler(String annotationClassName, Collection annotationElements) {

        m_annotationClassName = annotationClassName;

        m_annotationElements = new ArrayList(annotationElements.size());

        for (Iterator iterator = annotationElements.iterator(); iterator.hasNext();) {

            m_annotationElements.add(iterator.next());

        }

    }



    /**

     * Dynamic proxy based implementation

     * toString(), annotationType() and value() have a specific behavior

     *

     * @param proxy

     * @param method

     * @param args

     * @return

     * @throws Throwable

     */

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        String name = method.getName();

        if ("toString".equals(name)) {

            StringBuffer sb = new StringBuffer();

            sb.append('@').append(m_annotationClassName);

            sb.append("(");

            String sep = "";

            for (Iterator iterator = m_annotationElements.iterator(); iterator.hasNext();) {

                AnnotationElement annotationElement = (AnnotationElement) iterator.next();

                sb.append(sep).append(annotationElement.name + "=" + annotationElement.toString());

                sep = ", ";

            }

            sb.append(")");

            return sb.toString();

        } else if ("annotationType".equals(name)) {

            // funny, may explain why 1.5 Annotation intf has annotationType + getClass

            // since a dynamic proxy handler cannot hijack getClass() ..

            return Class.forName(m_annotationClassName, false, proxy.getClass().getClassLoader());

        } else if ("value".equals(name)) {

            if (m_annotationElements.isEmpty()) {

                return null;

            } else {

                //FIXME !!value can be there with other elements !

                // we could check that we don't have more than one element

                return ((AnnotationElement) m_annotationElements.get(0)).resolveValueHolderFrom(

                        proxy.getClass().getClassLoader()

                );

            }

        } else {

            for (Iterator iterator = m_annotationElements.iterator(); iterator.hasNext();) {

                AnnotationElement annotationElement = (AnnotationElement) iterator.next();

                if (name.equals(annotationElement.name)) {

                    return annotationElement.resolveValueHolderFrom(proxy.getClass().getClassLoader());

                }

            }

            // element not found for such a name

            throw new RuntimeException("No such element on Annotation @" + m_annotationClassName + " : " + name);

        }

    }



    /**

     * Build and return a dynamic proxy representing the given ASM Annotation.

     * The proxy implements the AspectWerkz Annotation interface, as well as the user type Annotation.

     * Each elements of the annotation is proxied if needed or agressively created unless Class types to not trigger

     * any nested loading.

     *

     * Note: JSR-175 does not support Annotation value of N-dimensional array. At most 1 dimension is supported and

     * only for a subset of Java types.

     *

     * @param annotation

     * @param loader the classloader of the annotatED component (can be different from the one of the annotation class)

     * @return

     */

    public static org.codehaus.aspectwerkz.annotation.Annotation getAnnotationProxy(org.objectweb.asm.attrs.Annotation annotation, ClassLoader loader) {

        String annotationClassName = Type.getType(annotation.type).getClassName();



        // get the ClassInfo for the annoation class to populate the assigned element values

        // with lazy value holders from the setted value or the default value if defaulted element

        // has been used in the annotation

        ClassInfo annotationClassInfo = AsmClassInfo.getClassInfo(annotationClassName, loader);

        Map annotationElementValueHoldersByName = new HashMap();



        // populate with the default values (might be then overriden by setted values)

        MethodInfo[] annotationMethods = annotationClassInfo.getMethods();

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

            MethodInfo annotationMethod = annotationMethods[i];

            for (Iterator iterator = annotationMethod.getAnnotations().iterator(); iterator.hasNext();) {

                AnnotationInfo annotationInfo = (AnnotationInfo) iterator.next();

                // handles AnnotationDefault attribute that we have wrapped. See AnnotationDefault.

                if (annotationInfo.getName().equals(AnnotationDefault.NAME)) {

                    Object value = ((AnnotationDefault)annotationInfo.getAnnotation()).value();

                    Object valueHolder = getAnnotationValueHolder(value, loader);

                    annotationElementValueHoldersByName.put(annotationMethod.getName(),

                                                            new AnnotationElement(annotationMethod.getName(),

                                                                                  valueHolder)

                    );

                }

            }

        }



        // override and populate with the setted values

        List settedElementValues = annotation.elementValues;

        for (int i = 0; i < settedElementValues.size(); i++) {

            Object[] element = (Object[]) settedElementValues.get(i);

            String name = (String) element[0];

            Object valueHolder = getAnnotationValueHolder(element[1], loader);

            annotationElementValueHoldersByName.put(name, new AnnotationElement(name, valueHolder));

        }



        // create a dynamic proxy to embody the annotation instance

        try {

            Class typeClass = Class.forName(annotationClassName, false, loader);

            Object proxy = Proxy.newProxyInstance(

                    loader,

                    new Class[]{org.codehaus.aspectwerkz.annotation.Annotation.class, typeClass},

                    new Java5AnnotationInvocationHandler(annotationClassName,

                                                         annotationElementValueHoldersByName.values()

                    )

            );

            return (org.codehaus.aspectwerkz.annotation.Annotation) proxy;

        } catch (ClassNotFoundException e) {

            throw new WrappedRuntimeException(e);

        }

    }



    /**

     * Turn an ASM Annotation value into a concrete Java value holder, unless the value is of type

     * Class, in which case we wrap it behind a LazyClass() object so that actual loading of the class

     * will be done lazily

     *

     * @param value

     * @param loader

     * @return

     */

    private static Object getAnnotationValueHolder(Object value, ClassLoader loader) {

        if (value instanceof Annotation.EnumConstValue) {

            Annotation.EnumConstValue enumAsmValue = (Annotation.EnumConstValue) value;

            try {

                Class enumClass = Class.forName(Type.getType(enumAsmValue.typeName).getClassName(), false, loader);

                Field enumConstValue = enumClass.getField(enumAsmValue.constName);

                return enumConstValue.get(null);

            } catch (Exception e) {

                throw new WrappedRuntimeException(e);

            }

        } else if (value instanceof Type) {

            // TODO may require additional filtering ?

            return new AnnotationElement.LazyClass(((Type) value).getClassName());

        } else if (value instanceof Annotation) {

            return getAnnotationProxy(((Annotation) value), loader);

        } else if (value instanceof Object[]) {

            Object[] values = (Object[]) value;

            Object[] holders = new Object[values.length];

            boolean isLazyClass = false;

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

                holders[i] = getAnnotationValueHolder(values[i], loader);

                if (!isLazyClass && holders[i] instanceof AnnotationElement.LazyClass) {

                    isLazyClass = true;

                }

            }

            if (isLazyClass) {

                // retype the array

                AnnotationElement.LazyClass[] typedHolders = (AnnotationElement.LazyClass[]) Array.newInstance(AnnotationElement.LazyClass.class, values.length);

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

                    typedHolders[i] = (AnnotationElement.LazyClass) holders[i];

                }

                return typedHolders;

            } else {

                return holders;

            }

        }

        return value;

    }



}



TOP

Related Classes of org.codehaus.aspectwerkz.annotation.Java5AnnotationInvocationHandler

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.