Package org.hotswap.agent.javassist.bytecode.annotation

Source Code of org.hotswap.agent.javassist.bytecode.annotation.AnnotationImpl

/*
* Javassist, a Java-bytecode translator toolkit.
* Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License.  Alternatively, the contents of this file may be used under
* the terms of the GNU Lesser General Public License Version 2.1 or later,
* or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*/

package org.hotswap.agent.javassist.bytecode.annotation;

import org.hotswap.agent.javassist.bytecode.AnnotationDefaultAttribute;
import org.hotswap.agent.javassist.bytecode.ClassFile;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
* Internal-use only.  This is a helper class internally used for implementing
* <code>toAnnotationType()</code> in <code>Annotation</code>.
*
* @author Shigeru Chiba
* @author <a href="mailto:bill@jboss.org">Bill Burke</a>
* @author <a href="mailto:adrian@jboss.org">Adrian Brock</a>
*/
public class AnnotationImpl implements InvocationHandler {
    private static final String JDK_ANNOTATION_CLASS_NAME = "java.lang.annotation.Annotation";
    private static Method JDK_ANNOTATION_TYPE_METHOD = null;

    private Annotation annotation;
    private org.hotswap.agent.javassist.ClassPool pool;
    private ClassLoader classLoader;
    private transient Class annotationType;
    private transient int cachedHashCode = Integer.MIN_VALUE;

    static {
        // Try to resolve the JDK annotation type method
        try {
            Class clazz = Class.forName(JDK_ANNOTATION_CLASS_NAME);
            JDK_ANNOTATION_TYPE_METHOD = clazz.getMethod("annotationType", (Class[]) null);
        } catch (Exception ignored) {
            // Probably not JDK5+
        }
    }

    /**
     * Constructs an annotation object.
     *
     * @param cl    class loader for obtaining annotation types.
     * @param clazz the annotation type.
     * @param cp    class pool for containing an annotation
     *              type (or null).
     * @param anon  the annotation.
     * @return the annotation
     */
    public static Object make(ClassLoader cl, Class clazz, org.hotswap.agent.javassist.ClassPool cp,
                              Annotation anon) {
        AnnotationImpl handler = new AnnotationImpl(anon, cp, cl);
        return Proxy.newProxyInstance(cl, new Class[]{clazz}, handler);
    }

    private AnnotationImpl(Annotation a, org.hotswap.agent.javassist.ClassPool cp, ClassLoader loader) {
        annotation = a;
        pool = cp;
        classLoader = loader;
    }

    /**
     * Obtains the name of the annotation type.
     *
     * @return the type name
     */
    public String getTypeName() {
        return annotation.getTypeName();
    }

    /**
     * Get the annotation type
     *
     * @return the annotation class
     * @throws NoClassDefFoundError when the class could not loaded
     */
    private Class getAnnotationType() {
        if (annotationType == null) {
            String typeName = annotation.getTypeName();
            try {
                annotationType = classLoader.loadClass(typeName);
            } catch (ClassNotFoundException e) {
                NoClassDefFoundError error = new NoClassDefFoundError("Error loading annotation class: " + typeName);
                error.setStackTrace(e.getStackTrace());
                throw error;
            }
        }
        return annotationType;
    }

    /**
     * Obtains the internal data structure representing the annotation.
     *
     * @return the annotation
     */
    public Annotation getAnnotation() {
        return annotation;
    }

    /**
     * Executes a method invocation on a proxy instance.
     * The implementations of <code>toString()</code>, <code>equals()</code>,
     * and <code>hashCode()</code> are directly supplied by the
     * <code>AnnotationImpl</code>.  The <code>annotationType()</code> method
     * is also available on the proxy instance.
     */
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        String name = method.getName();
        if (Object.class == method.getDeclaringClass()) {
            if ("equals".equals(name)) {
                Object obj = args[0];
                return new Boolean(checkEquals(obj));
            } else if ("toString".equals(name))
                return annotation.toString();
            else if ("hashCode".equals(name))
                return new Integer(hashCode());
        } else if ("annotationType".equals(name)
                && method.getParameterTypes().length == 0)
            return getAnnotationType();

        MemberValue mv = annotation.getMemberValue(name);
        if (mv == null)
            return getDefault(name, method);
        else
            return mv.getValue(classLoader, pool, method);
    }

    private Object getDefault(String name, Method method)
            throws ClassNotFoundException, RuntimeException {
        String classname = annotation.getTypeName();
        if (pool != null) {
            try {
                org.hotswap.agent.javassist.CtClass cc = pool.get(classname);
                ClassFile cf = cc.getClassFile2();
                org.hotswap.agent.javassist.bytecode.MethodInfo minfo = cf.getMethod(name);
                if (minfo != null) {
                    AnnotationDefaultAttribute ainfo
                            = (AnnotationDefaultAttribute)
                            minfo.getAttribute(AnnotationDefaultAttribute.tag);
                    if (ainfo != null) {
                        MemberValue mv = ainfo.getDefaultValue();
                        return mv.getValue(classLoader, pool, method);
                    }
                }
            } catch (org.hotswap.agent.javassist.NotFoundException e) {
                throw new RuntimeException("cannot find a class file: "
                        + classname);
            }
        }

        throw new RuntimeException("no default value: " + classname + "."
                + name + "()");
    }

    /**
     * Returns a hash code value for this object.
     */
    public int hashCode() {
        if (cachedHashCode == Integer.MIN_VALUE) {
            int hashCode = 0;

            // Load the annotation class
            getAnnotationType();

            Method[] methods = annotationType.getDeclaredMethods();
            for (int i = 0; i < methods.length; ++i) {
                String name = methods[i].getName();
                int valueHashCode = 0;

                // Get the value
                MemberValue mv = annotation.getMemberValue(name);
                Object value = null;
                try {
                    if (mv != null)
                        value = mv.getValue(classLoader, pool, methods[i]);
                    if (value == null)
                        value = getDefault(name, methods[i]);
                } catch (RuntimeException e) {
                    throw e;
                } catch (Exception e) {
                    throw new RuntimeException("Error retrieving value " + name + " for annotation " + annotation.getTypeName(), e);
                }

                // Calculate the hash code
                if (value != null) {
                    if (value.getClass().isArray())
                        valueHashCode = arrayHashCode(value);
                    else
                        valueHashCode = value.hashCode();
                }
                hashCode += 127 * name.hashCode() ^ valueHashCode;
            }

            cachedHashCode = hashCode;
        }
        return cachedHashCode;
    }

    /**
     * Check that another annotation equals ourselves.
     *
     * @param obj the other annotation
     * @return the true when equals false otherwise
     * @throws Exception for any problem
     */
    private boolean checkEquals(Object obj) throws Exception {
        if (obj == null)
            return false;

        // Optimization when the other is one of ourselves
        if (obj instanceof Proxy) {
            InvocationHandler ih = Proxy.getInvocationHandler(obj);
            if (ih instanceof AnnotationImpl) {
                AnnotationImpl other = (AnnotationImpl) ih;
                return annotation.equals(other.annotation);
            }
        }

        Class otherAnnotationType = (Class) JDK_ANNOTATION_TYPE_METHOD.invoke(obj, (Object[]) null);
        if (getAnnotationType().equals(otherAnnotationType) == false)
            return false;

        Method[] methods = annotationType.getDeclaredMethods();
        for (int i = 0; i < methods.length; ++i) {
            String name = methods[i].getName();

            // Get the value
            MemberValue mv = annotation.getMemberValue(name);
            Object value = null;
            Object otherValue = null;
            try {
                if (mv != null)
                    value = mv.getValue(classLoader, pool, methods[i]);
                if (value == null)
                    value = getDefault(name, methods[i]);
                otherValue = methods[i].invoke(obj, (Object[]) null);
            } catch (RuntimeException e) {
                throw e;
            } catch (Exception e) {
                throw new RuntimeException("Error retrieving value " + name + " for annotation " + annotation.getTypeName(), e);
            }

            if (value == null && otherValue != null)
                return false;
            if (value != null && value.equals(otherValue) == false)
                return false;
        }

        return true;
    }

    /**
     * Calculates the hashCode of an array using the same
     * algorithm as java.util.Arrays.hashCode()
     *
     * @param object the object
     * @return the hashCode
     */
    private static int arrayHashCode(Object object) {
        if (object == null)
            return 0;

        int result = 1;

        Object[] array = (Object[]) object;
        for (int i = 0; i < array.length; ++i) {
            int elementHashCode = 0;
            if (array[i] != null)
                elementHashCode = array[i].hashCode();
            result = 31 * result + elementHashCode;
        }
        return result;
    }
}
TOP

Related Classes of org.hotswap.agent.javassist.bytecode.annotation.AnnotationImpl

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.