360 lines · 12.9 KB · Java
RawDownload
/* * Copyright (c) 2003, 2008, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.reflect.annotation; import java.lang.annotation.*;import java.lang.reflect.*;import java.io.Serializable;import java.util.*;import java.lang.annotation.*;import java.security.AccessController;import java.security.PrivilegedAction; /** * InvocationHandler for dynamic proxy implementation of Annotation. * * @author  Josh Bloch * @since   1.5 */class AnnotationInvocationHandler implements InvocationHandler, Serializable {    private final Class<? extends Annotation> type;    private final Map<String, Object> memberValues;     AnnotationInvocationHandler(Class<? extends Annotation> type, Map<String, Object> memberValues) {        this.type = type;        this.memberValues = memberValues;    }     public Object invoke(Object proxy, Method method, Object[] args) {        String member = method.getName();        Class<?>[] paramTypes = method.getParameterTypes();         // Handle Object and Annotation methods        if (member.equals("equals") && paramTypes.length == 1 &&            paramTypes[0] == Object.class)            return equalsImpl(args[0]);        assert paramTypes.length == 0;        if (member.equals("toString"))            return toStringImpl();        if (member.equals("hashCode"))            return hashCodeImpl();        if (member.equals("annotationType"))            return type;         // Handle annotation member accessors        Object result = memberValues.get(member);         if (result == null)            throw new IncompleteAnnotationException(type, member);         if (result instanceof ExceptionProxy)            throw ((ExceptionProxy) result).generateException();         if (result.getClass().isArray() && Array.getLength(result) != 0)            result = cloneArray(result);         return result;    }     /**     * This method, which clones its array argument, would not be necessary     * if Cloneable had a public clone method.     */    private Object cloneArray(Object array) {        Class<?> type = array.getClass();         if (type == byte[].class) {            byte[] byteArray = (byte[])array;            return byteArray.clone();        }        if (type == char[].class) {            char[] charArray = (char[])array;            return charArray.clone();        }        if (type == double[].class) {            double[] doubleArray = (double[])array;            return doubleArray.clone();        }        if (type == float[].class) {            float[] floatArray = (float[])array;            return floatArray.clone();        }        if (type == int[].class) {            int[] intArray = (int[])array;            return intArray.clone();        }        if (type == long[].class) {            long[] longArray = (long[])array;            return longArray.clone();        }        if (type == short[].class) {            short[] shortArray = (short[])array;            return shortArray.clone();        }        if (type == boolean[].class) {            boolean[] booleanArray = (boolean[])array;            return booleanArray.clone();        }         Object[] objectArray = (Object[])array;        return objectArray.clone();    }      /**     * Implementation of dynamicProxy.toString()     */    private String toStringImpl() {        StringBuffer result = new StringBuffer(128);        result.append('@');        result.append(type.getName());        result.append('(');        boolean firstMember = true;        for (Map.Entry<String, Object> e : memberValues.entrySet()) {            if (firstMember)                firstMember = false;            else                result.append(", ");             result.append(e.getKey());            result.append('=');            result.append(memberValueToString(e.getValue()));        }        result.append(')');        return result.toString();    }     /**     * Translates a member value (in "dynamic proxy return form") into a string     */    private static String memberValueToString(Object value) {        Class<?> type = value.getClass();        if (!type.isArray())    // primitive, string, class, enum const,                                // or annotation            return value.toString();         if (type == byte[].class)            return Arrays.toString((byte[]) value);        if (type == char[].class)            return Arrays.toString((char[]) value);        if (type == double[].class)            return Arrays.toString((double[]) value);        if (type == float[].class)            return Arrays.toString((float[]) value);        if (type == int[].class)            return Arrays.toString((int[]) value);        if (type == long[].class)            return Arrays.toString((long[]) value);        if (type == short[].class)            return Arrays.toString((short[]) value);        if (type == boolean[].class)            return Arrays.toString((boolean[]) value);        return Arrays.toString((Object[]) value);    }     /**     * Implementation of dynamicProxy.equals(Object o)     */    private Boolean equalsImpl(Object o) {        if (o == this)            return true;         if (!type.isInstance(o))            return false;        for (Method memberMethod : getMemberMethods()) {            String member = memberMethod.getName();            Object ourValue = memberValues.get(member);            Object hisValue = null;            AnnotationInvocationHandler hisHandler = asOneOfUs(o);            if (hisHandler != null) {                hisValue = hisHandler.memberValues.get(member);            } else {                try {                    hisValue = memberMethod.invoke(o);                } catch (InvocationTargetException e) {                    return false;                } catch (IllegalAccessException e) {                    throw new AssertionError(e);                }            }            if (!memberValueEquals(ourValue, hisValue))                return false;        }        return true;    }     /**     * Returns an object's invocation handler if that object is a dynamic     * proxy with a handler of type AnnotationInvocationHandler.     * Returns null otherwise.     */    private AnnotationInvocationHandler asOneOfUs(Object o) {        if (Proxy.isProxyClass(o.getClass())) {            InvocationHandler handler = Proxy.getInvocationHandler(o);            if (handler instanceof AnnotationInvocationHandler)                return (AnnotationInvocationHandler) handler;        }        return null;    }     /**     * Returns true iff the two member values in "dynamic proxy return form"     * are equal using the appropriate equality function depending on the     * member type.  The two values will be of the same type unless one of     * the containing annotations is ill-formed.  If one of the containing     * annotations is ill-formed, this method will return false unless the     * two members are identical object references.     */    private static boolean memberValueEquals(Object v1, Object v2) {        Class<?> type = v1.getClass();         // Check for primitive, string, class, enum const, annotation,        // or ExceptionProxy        if (!type.isArray())            return v1.equals(v2);         // Check for array of string, class, enum const, annotation,        // or ExceptionProxy        if (v1 instanceof Object[] && v2 instanceof Object[])            return Arrays.equals((Object[]) v1, (Object[]) v2);         // Check for ill formed annotation(s)        if (v2.getClass() != type)            return false;         // Deal with array of primitives        if (type == byte[].class)            return Arrays.equals((byte[]) v1, (byte[]) v2);        if (type == char[].class)            return Arrays.equals((char[]) v1, (char[]) v2);        if (type == double[].class)            return Arrays.equals((double[]) v1, (double[]) v2);        if (type == float[].class)            return Arrays.equals((float[]) v1, (float[]) v2);        if (type == int[].class)            return Arrays.equals((int[]) v1, (int[]) v2);        if (type == long[].class)            return Arrays.equals((long[]) v1, (long[]) v2);        if (type == short[].class)            return Arrays.equals((short[]) v1, (short[]) v2);        assert type == boolean[].class;        return Arrays.equals((boolean[]) v1, (boolean[]) v2);    }     /**     * Returns the member methods for our annotation type.  These are     * obtained lazily and cached, as they're expensive to obtain     * and we only need them if our equals method is invoked (which should     * be rare).     */    private Method[] getMemberMethods() {        if (memberMethods == null) {            memberMethods = AccessController.doPrivileged(                new PrivilegedAction<Method[]>() {                    public Method[] run() {                        final Method[] mm = type.getDeclaredMethods();                        AccessibleObject.setAccessible(mm, true);                        return mm;                    }                });        }        return memberMethods;    }    private transient volatile Method[] memberMethods = null;     /**     * Implementation of dynamicProxy.hashCode()     */    private int hashCodeImpl() {        int result = 0;        for (Map.Entry<String, Object> e : memberValues.entrySet()) {            result += (127 * e.getKey().hashCode()) ^                memberValueHashCode(e.getValue());        }        return result;    }     /**     * Computes hashCode of a member value (in "dynamic proxy return form")     */    private static int memberValueHashCode(Object value) {        Class<?> type = value.getClass();        if (!type.isArray())    // primitive, string, class, enum const,                                // or annotation            return value.hashCode();         if (type == byte[].class)            return Arrays.hashCode((byte[]) value);        if (type == char[].class)            return Arrays.hashCode((char[]) value);        if (type == double[].class)            return Arrays.hashCode((double[]) value);        if (type == float[].class)            return Arrays.hashCode((float[]) value);        if (type == int[].class)            return Arrays.hashCode((int[]) value);        if (type == long[].class)            return Arrays.hashCode((long[]) value);        if (type == short[].class)            return Arrays.hashCode((short[]) value);        if (type == boolean[].class)            return Arrays.hashCode((boolean[]) value);        return Arrays.hashCode((Object[]) value);    }     private void readObject(java.io.ObjectInputStream s)        throws java.io.IOException, ClassNotFoundException {        s.defaultReadObject();          // Check to make sure that types have not evolved incompatibly         AnnotationType annotationType = null;        try {            annotationType = AnnotationType.getInstance(type);        } catch(IllegalArgumentException e) {            // Class is no longer an annotation type; all bets are off            return;        }         Map<String, Class<?>> memberTypes = annotationType.memberTypes();         for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) {            String name = memberValue.getKey();            Class<?> memberType = memberTypes.get(name);            if (memberType != null) {  // i.e. member still exists                Object value = memberValue.getValue();                if (!(memberType.isInstance(value) ||                      value instanceof ExceptionProxy)) {                    memberValue.setValue(                        new AnnotationTypeMismatchExceptionProxy(                            value.getClass() + "[" + value + "]").setMember(                                annotationType.members().get(name)));                }            }        }    }}