|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package sun.reflect.annotation; |
|
|
|
import java.io.ObjectInputStream; |
|
import java.lang.annotation.*; |
|
import java.lang.reflect.*; |
|
import java.io.Serializable; |
|
import java.util.*; |
|
import java.security.AccessController; |
|
import java.security.PrivilegedAction; |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
class AnnotationInvocationHandler implements InvocationHandler, Serializable { |
|
private static final long serialVersionUID = 6182022883658399397L; |
|
private final Class<? extends Annotation> type; |
|
private final Map<String, Object> memberValues; |
|
|
|
AnnotationInvocationHandler(Class<? extends Annotation> type, Map<String, Object> memberValues) { |
|
Class<?>[] superInterfaces = type.getInterfaces(); |
|
if (!type.isAnnotation() || |
|
superInterfaces.length != 1 || |
|
superInterfaces[0] != java.lang.annotation.Annotation.class) |
|
throw new AnnotationFormatError("Attempt to create proxy for a non-annotation type."); |
|
this.type = type; |
|
this.memberValues = memberValues; |
|
} |
|
|
|
public Object invoke(Object proxy, Method method, Object[] args) { |
|
String member = method.getName(); |
|
Class<?>[] paramTypes = method.getParameterTypes(); |
|
|
|
|
|
if (member.equals("equals") && paramTypes.length == 1 && |
|
paramTypes[0] == Object.class) |
|
return equalsImpl(args[0]); |
|
if (paramTypes.length != 0) |
|
throw new AssertionError("Too many parameters for an annotation method"); |
|
|
|
switch(member) { |
|
case "toString": |
|
return toStringImpl(); |
|
case "hashCode": |
|
return hashCodeImpl(); |
|
case "annotationType": |
|
return type; |
|
} |
|
|
|
|
|
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; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
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(); |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private String toStringImpl() { |
|
StringBuilder result = new StringBuilder(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(); |
|
} |
|
|
|
|
|
|
|
*/ |
|
private static String memberValueToString(Object value) { |
|
Class<?> type = value.getClass(); |
|
if (!type.isArray()) |
|
|
|
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); |
|
} |
|
|
|
|
|
|
|
*/ |
|
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; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private AnnotationInvocationHandler asOneOfUs(Object o) { |
|
if (Proxy.isProxyClass(o.getClass())) { |
|
InvocationHandler handler = Proxy.getInvocationHandler(o); |
|
if (handler instanceof AnnotationInvocationHandler) |
|
return (AnnotationInvocationHandler) handler; |
|
} |
|
return null; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private static boolean memberValueEquals(Object v1, Object v2) { |
|
Class<?> type = v1.getClass(); |
|
|
|
// Check for primitive, string, class, enum const, annotation, |
|
|
|
if (!type.isArray()) |
|
return v1.equals(v2); |
|
|
|
// Check for array of string, class, enum const, annotation, |
|
|
|
if (v1 instanceof Object[] && v2 instanceof Object[]) |
|
return Arrays.equals((Object[]) v1, (Object[]) v2); |
|
|
|
|
|
if (v2.getClass() != type) |
|
return false; |
|
|
|
|
|
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); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private Method[] getMemberMethods() { |
|
if (memberMethods == null) { |
|
memberMethods = AccessController.doPrivileged( |
|
new PrivilegedAction<Method[]>() { |
|
public Method[] run() { |
|
final Method[] mm = type.getDeclaredMethods(); |
|
validateAnnotationMethods(mm); |
|
AccessibleObject.setAccessible(mm, true); |
|
return mm; |
|
} |
|
}); |
|
} |
|
return memberMethods; |
|
} |
|
private transient volatile Method[] memberMethods = null; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private void validateAnnotationMethods(Method[] memberMethods) { |
|
|
|
|
|
|
|
*/ |
|
boolean valid = true; |
|
for(Method method : memberMethods) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
if (method.getModifiers() != (Modifier.PUBLIC | Modifier.ABSTRACT) || |
|
method.isDefault() || |
|
method.getParameterCount() != 0 || |
|
method.getExceptionTypes().length != 0) { |
|
valid = false; |
|
break; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
Class<?> returnType = method.getReturnType(); |
|
if (returnType.isArray()) { |
|
returnType = returnType.getComponentType(); |
|
if (returnType.isArray()) { |
|
valid = false; |
|
break; |
|
} |
|
} |
|
|
|
if (!((returnType.isPrimitive() && returnType != void.class) || |
|
returnType == java.lang.String.class || |
|
returnType == java.lang.Class.class || |
|
returnType.isEnum() || |
|
returnType.isAnnotation())) { |
|
valid = false; |
|
break; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
String methodName = method.getName(); |
|
if ((methodName.equals("toString") && returnType == java.lang.String.class) || |
|
(methodName.equals("hashCode") && returnType == int.class) || |
|
(methodName.equals("annotationType") && returnType == java.lang.Class.class)) { |
|
valid = false; |
|
break; |
|
} |
|
} |
|
if (valid) |
|
return; |
|
else |
|
throw new AnnotationFormatError("Malformed method on an annotation type"); |
|
} |
|
|
|
|
|
|
|
*/ |
|
private int hashCodeImpl() { |
|
int result = 0; |
|
for (Map.Entry<String, Object> e : memberValues.entrySet()) { |
|
result += (127 * e.getKey().hashCode()) ^ |
|
memberValueHashCode(e.getValue()); |
|
} |
|
return result; |
|
} |
|
|
|
|
|
|
|
*/ |
|
private static int memberValueHashCode(Object value) { |
|
Class<?> type = value.getClass(); |
|
if (!type.isArray()) |
|
|
|
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 { |
|
ObjectInputStream.GetField fields = s.readFields(); |
|
|
|
@SuppressWarnings("unchecked") |
|
Class<? extends Annotation> t = (Class<? extends Annotation>)fields.get("type", null); |
|
@SuppressWarnings("unchecked") |
|
Map<String, Object> streamVals = (Map<String, Object>)fields.get("memberValues", null); |
|
|
|
// Check to make sure that types have not evolved incompatibly |
|
|
|
AnnotationType annotationType = null; |
|
try { |
|
annotationType = AnnotationType.getInstance(t); |
|
} catch(IllegalArgumentException e) { |
|
|
|
throw new java.io.InvalidObjectException("Non-annotation type in annotation serial stream"); |
|
} |
|
|
|
Map<String, Class<?>> memberTypes = annotationType.memberTypes(); |
|
|
|
Map<String, Object> mv = new LinkedHashMap<>(); |
|
|
|
// If there are annotation members without values, that |
|
|
|
for (Map.Entry<String, Object> memberValue : streamVals.entrySet()) { |
|
String name = memberValue.getKey(); |
|
Object value = null; |
|
Class<?> memberType = memberTypes.get(name); |
|
if (memberType != null) { |
|
value = memberValue.getValue(); |
|
if (!(memberType.isInstance(value) || |
|
value instanceof ExceptionProxy)) { |
|
value = new AnnotationTypeMismatchExceptionProxy( |
|
value.getClass() + "[" + value + "]").setMember( |
|
annotationType.members().get(name)); |
|
} |
|
} |
|
mv.put(name, value); |
|
} |
|
|
|
UnsafeAccessor.setType(this, t); |
|
UnsafeAccessor.setMemberValues(this, mv); |
|
} |
|
|
|
private static class UnsafeAccessor { |
|
private static final sun.misc.Unsafe unsafe; |
|
private static final long typeOffset; |
|
private static final long memberValuesOffset; |
|
static { |
|
try { |
|
unsafe = sun.misc.Unsafe.getUnsafe(); |
|
typeOffset = unsafe.objectFieldOffset |
|
(AnnotationInvocationHandler.class.getDeclaredField("type")); |
|
memberValuesOffset = unsafe.objectFieldOffset |
|
(AnnotationInvocationHandler.class.getDeclaredField("memberValues")); |
|
} catch (Exception ex) { |
|
throw new ExceptionInInitializerError(ex); |
|
} |
|
} |
|
static void setType(AnnotationInvocationHandler o, |
|
Class<? extends Annotation> type) { |
|
unsafe.putObject(o, typeOffset, type); |
|
} |
|
|
|
static void setMemberValues(AnnotationInvocationHandler o, |
|
Map<String, Object> memberValues) { |
|
unsafe.putObject(o, memberValuesOffset, memberValues); |
|
} |
|
} |
|
} |