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