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.util.stream.*;
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(proxy, 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()) {
if (type == Class.class)
return toSourceString((Class<?>) value);
else if (type == String.class)
return toSourceString((String) value);
if (type == Character.class)
return toSourceString((char) value);
else if (type == Double.class)
return toSourceString((double) value);
else if (type == Float.class)
return toSourceString((float) value);
else if (type == Long.class)
return toSourceString((long) value);
else
return value.toString();
} else {
Stream<String> stringStream;
if (type == byte[].class)
stringStream = convert((byte[]) value);
else if (type == char[].class)
stringStream = convert((char[]) value);
else if (type == double[].class)
stringStream = DoubleStream.of((double[]) value)
.mapToObj(AnnotationInvocationHandler::toSourceString);
else if (type == float[].class)
stringStream = convert((float[]) value);
else if (type == int[].class)
stringStream = IntStream.of((int[]) value).mapToObj(String::valueOf);
else if (type == long[].class) {
stringStream = LongStream.of((long[]) value)
.mapToObj(AnnotationInvocationHandler::toSourceString);
} else if (type == short[].class)
stringStream = convert((short[]) value);
else if (type == boolean[].class)
stringStream = convert((boolean[]) value);
else if (type == Class[].class)
stringStream =
Arrays.stream((Class<?>[]) value).
map(AnnotationInvocationHandler::toSourceString);
else if (type == String[].class)
stringStream =
Arrays.stream((String[])value).
map(AnnotationInvocationHandler::toSourceString);
else
stringStream = Arrays.stream((Object[])value).map(Objects::toString);
return stringStreamToString(stringStream);
}
}
private static String toSourceString(Class<?> clazz) {
Class<?> finalComponent = clazz;
StringBuilder arrayBackets = new StringBuilder();
while(finalComponent.isArray()) {
finalComponent = finalComponent.getComponentType();
arrayBackets.append("[]");
}
return finalComponent.getName() + arrayBackets.toString() + ".class" ;
}
private static String toSourceString(float f) {
if (Float.isFinite(f))
return Float.toString(f) + "f" ;
else {
if (Float.isInfinite(f)) {
return (f < 0.0f) ? "-1.0f/0.0f": "1.0f/0.0f";
} else
return "0.0f/0.0f";
}
}
private static String toSourceString(double d) {
if (Double.isFinite(d))
return Double.toString(d);
else {
if (Double.isInfinite(d)) {
return (d < 0.0f) ? "-1.0/0.0": "1.0/0.0";
} else
return "0.0/0.0";
}
}
private static String toSourceString(char c) {
StringBuilder sb = new StringBuilder(4);
sb.append('\'');
if (c == '\'')
sb.append("\\'");
else
sb.append(c);
return sb.append('\'')
.toString();
}
private static String toSourceString(long ell) {
String str = String.valueOf(ell);
return (ell < Integer.MIN_VALUE || ell > Integer.MAX_VALUE)
? (str + 'L') : str;
}
private static String toSourceString(String s) {
StringBuilder sb = new StringBuilder();
sb.append('"');
sb.append(s.replace("\"", "\\\""));
sb.append('"');
return sb.toString();
}
private static Stream<String> convert(byte[] values) {
List<String> list = new ArrayList<>(values.length);
for (byte b : values)
list.add(Byte.toString(b));
return list.stream();
}
private static Stream<String> convert(char[] values) {
List<String> list = new ArrayList<>(values.length);
for (char c : values)
list.add(toSourceString(c));
return list.stream();
}
private static Stream<String> convert(float[] values) {
List<String> list = new ArrayList<>(values.length);
for (float f : values) {
list.add(toSourceString(f));
}
return list.stream();
}
private static Stream<String> convert(short[] values) {
List<String> list = new ArrayList<>(values.length);
for (short s : values)
list.add(Short.toString(s));
return list.stream();
}
private static Stream<String> convert(boolean[] values) {
List<String> list = new ArrayList<>(values.length);
for (boolean b : values)
list.add(Boolean.toString(b));
return list.stream();
}
private static String stringStreamToString(Stream<String> stream) {
return stream.collect(Collectors.joining(", ", "{", "}"));
}
private Boolean equalsImpl(Object proxy, Object o) {
if (o == proxy)
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();
if (!type.isArray())
return v1.equals(v2);
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() {
Method[] value = memberMethods;
if (value == null) {
value = computeMemberMethods();
memberMethods = value;
}
return value;
}
private Method[] computeMemberMethods() {
return AccessController.doPrivileged(
new PrivilegedAction<Method[]>() {
public Method[] run() {
final Method[] methods = type.getDeclaredMethods();
validateAnnotationMethods(methods);
AccessibleObject.setAccessible(methods, true);
return methods;
}});
}
private transient volatile Method[] memberMethods;
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);
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<>();
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 jdk.internal.misc.Unsafe unsafe;
private static final long typeOffset;
private static final long memberValuesOffset;
static {
try {
unsafe = jdk.internal.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);
}
}
}