1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.grouplens.grapht.annotation;
21
22 import com.google.common.collect.ImmutableMap;
23 import org.apache.commons.lang3.AnnotationUtils;
24 import org.apache.commons.lang3.ClassUtils;
25 import org.grouplens.grapht.util.ClassProxy;
26
27 import java.io.*;
28 import java.lang.annotation.Annotation;
29 import java.lang.reflect.InvocationHandler;
30 import java.lang.reflect.Method;
31 import java.util.Arrays;
32 import java.util.Map;
33
34
35
36
37
38
39
40
41 class AnnotationProxy<T extends Annotation> implements InvocationHandler, Serializable {
42 private static final long serialVersionUID = 1L;
43 private final ClassProxy annotationType;
44 private final ImmutableMap<String, Object> attributes;
45 private transient Class<T> cachedType;
46
47 public AnnotationProxy(Class<T> type, Map<String, Object> attrs) {
48 annotationType = ClassProxy.of(type);
49 cachedType = type;
50 attributes = ImmutableMap.copyOf(attrs);
51 }
52
53
54
55
56
57
58
59 @SuppressWarnings("unchecked")
60 private void readObject(ObjectInputStream in) throws ObjectStreamException {
61 try {
62 in.defaultReadObject();
63 cachedType = (Class<T>) annotationType.resolve();
64 } catch (IOException e) {
65 ObjectStreamException ex = new StreamCorruptedException("IO exception");
66 ex.initCause(e);
67 throw ex;
68 } catch (ClassNotFoundException e) {
69 ObjectStreamException ex = new InvalidObjectException("IO exception");
70 ex.initCause(e);
71 throw ex;
72 }
73 }
74
75 @Override
76 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
77 if (isHashCode(method)) {
78 return proxyHashCode(proxy);
79 } else if (isEquals(method)) {
80 return proxyEquals(proxy, args[0]);
81 } else if (isAnnotationType(method)) {
82 return proxyAnnotationType();
83 } else if (isToString(method)) {
84 return proxyToString(proxy);
85 } else if (attributes.containsKey(method.getName()) && method.getParameterTypes().length == 0) {
86 return copyAnnotationValue(attributes.get(method.getName()));
87 } else {
88
89 return copyAnnotationValue(method.getDefaultValue());
90 }
91
92
93 }
94
95 private boolean isEquals(Method m) {
96 return m.getName().equals("equals") && m.getReturnType().equals(boolean.class)
97 && m.getParameterTypes().length == 1 && m.getParameterTypes()[0].equals(Object.class);
98 }
99
100 private boolean isHashCode(Method m) {
101 return m.getName().equals("hashCode") && m.getReturnType().equals(int.class)
102 && m.getParameterTypes().length == 0;
103 }
104
105 private boolean isAnnotationType(Method m) {
106 return m.getName().equals("annotationType") && m.getReturnType().equals(Class.class)
107 && m.getParameterTypes().length == 0;
108 }
109
110 private boolean isToString(Method m) {
111 return m.getName().equals("toString") && m.getReturnType().equals(String.class)
112 && m.getParameterTypes().length == 0;
113 }
114
115 private Class<? extends Annotation> proxyAnnotationType() {
116 return cachedType;
117 }
118
119 private String proxyToString(Object o) {
120 return AnnotationUtils.toString((Annotation) o);
121 }
122
123 private int proxyHashCode(Object proxy) {
124 return AnnotationUtils.hashCode((Annotation) proxy);
125 }
126
127 private boolean proxyEquals(Object o1, Object o2) {
128 return AnnotationUtils.equals((Annotation) o1, (Annotation) o2);
129 }
130
131
132
133
134
135
136
137
138 @SuppressWarnings("unchecked")
139 static Object copyAnnotationValue(Object o) {
140 if (o.getClass().isArray()) {
141
142 if (o instanceof boolean[]) {
143 boolean[] a = (boolean[]) o;
144 return Arrays.copyOf(a, a.length);
145 } else if (o instanceof byte[]) {
146 byte[] a = (byte[]) o;
147 return Arrays.copyOf(a, a.length);
148 } else if (o instanceof short[]) {
149 short[] a = (short[]) o;
150 return Arrays.copyOf(a, a.length);
151 } else if (o instanceof int[]) {
152 int[] a = (int[]) o;
153 return Arrays.copyOf(a, a.length);
154 } else if (o instanceof long[]) {
155 long[] a = (long[]) o;
156 return Arrays.copyOf(a, a.length);
157 } else if (o instanceof char[]) {
158 char[] a = (char[]) o;
159 return Arrays.copyOf(a, a.length);
160 } else if (o instanceof float[]) {
161 float[] a = (float[]) o;
162 return Arrays.copyOf(a, a.length);
163 } else if (o instanceof double[]) {
164 double[] a = (double[]) o;
165 return Arrays.copyOf(a, a.length);
166 } else {
167 Object[] a = (Object[]) o;
168 return Arrays.copyOf(a, a.length, (Class<? extends Object[]>) o.getClass());
169 }
170 } else if (o instanceof String
171 || o instanceof Annotation
172 || ClassUtils.isPrimitiveOrWrapper(o.getClass())
173 || o instanceof Enum
174 || o instanceof Class) {
175
176 return o;
177 } else {
178 throw new IllegalArgumentException("not an annotation value");
179 }
180 }
181 }