1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.lang3;
18
19 import java.lang.annotation.Annotation;
20 import java.lang.reflect.Method;
21 import java.util.Arrays;
22
23 import org.apache.commons.lang3.builder.AbstractReflection;
24 import org.apache.commons.lang3.builder.ToStringBuilder;
25 import org.apache.commons.lang3.builder.ToStringStyle;
26 import org.apache.commons.lang3.exception.UncheckedException;
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52 public class AnnotationUtils {
53
54
55
56
57 private static final ToStringStyle TO_STRING_STYLE = new ToStringStyle() {
58
59
60 private static final long serialVersionUID = 1L;
61
62 {
63 setDefaultFullDetail(true);
64 setArrayContentDetail(true);
65 setUseClassName(true);
66 setUseShortClassName(true);
67 setUseIdentityHashCode(false);
68 setContentStart("(");
69 setContentEnd(")");
70 setFieldSeparator(", ");
71 setArrayStart("[");
72 setArrayEnd("]");
73 }
74
75
76
77
78 @Override
79 protected void appendDetail(final StringBuffer buffer, final String fieldName, Object value) {
80 if (value instanceof Annotation) {
81 value = AnnotationUtils.toString((Annotation) value);
82 }
83 super.appendDetail(buffer, fieldName, value);
84 }
85
86
87
88
89 @Override
90 protected String getShortClassName(final Class<?> cls) {
91
92 return ClassUtils.getAllInterfaces(cls).stream().filter(Annotation.class::isAssignableFrom).findFirst()
93 .map(iface -> "@" + iface.getName())
94 .orElse(StringUtils.EMPTY);
95
96 }
97
98 };
99
100
101
102
103
104
105
106
107 private static boolean annotationArrayMemberEquals(final Annotation[] a1, final Annotation[] a2) {
108 if (a1.length != a2.length) {
109 return false;
110 }
111 for (int i = 0; i < a1.length; i++) {
112 if (!equals(a1[i], a2[i])) {
113 return false;
114 }
115 }
116 return true;
117 }
118
119
120
121
122
123
124
125
126
127 private static boolean arrayMemberEquals(final Class<?> componentType, final Object o1, final Object o2) {
128 if (componentType.isAnnotation()) {
129 return annotationArrayMemberEquals((Annotation[]) o1, (Annotation[]) o2);
130 }
131 if (componentType.equals(Byte.TYPE)) {
132 return Arrays.equals((byte[]) o1, (byte[]) o2);
133 }
134 if (componentType.equals(Short.TYPE)) {
135 return Arrays.equals((short[]) o1, (short[]) o2);
136 }
137 if (componentType.equals(Integer.TYPE)) {
138 return Arrays.equals((int[]) o1, (int[]) o2);
139 }
140 if (componentType.equals(Character.TYPE)) {
141 return Arrays.equals((char[]) o1, (char[]) o2);
142 }
143 if (componentType.equals(Long.TYPE)) {
144 return Arrays.equals((long[]) o1, (long[]) o2);
145 }
146 if (componentType.equals(Float.TYPE)) {
147 return Arrays.equals((float[]) o1, (float[]) o2);
148 }
149 if (componentType.equals(Double.TYPE)) {
150 return Arrays.equals((double[]) o1, (double[]) o2);
151 }
152 if (componentType.equals(Boolean.TYPE)) {
153 return Arrays.equals((boolean[]) o1, (boolean[]) o2);
154 }
155 return Arrays.equals((Object[]) o1, (Object[]) o2);
156 }
157
158
159
160
161
162
163
164
165 private static int arrayMemberHash(final Class<?> componentType, final Object o) {
166 if (componentType.equals(Byte.TYPE)) {
167 return Arrays.hashCode((byte[]) o);
168 }
169 if (componentType.equals(Short.TYPE)) {
170 return Arrays.hashCode((short[]) o);
171 }
172 if (componentType.equals(Integer.TYPE)) {
173 return Arrays.hashCode((int[]) o);
174 }
175 if (componentType.equals(Character.TYPE)) {
176 return Arrays.hashCode((char[]) o);
177 }
178 if (componentType.equals(Long.TYPE)) {
179 return Arrays.hashCode((long[]) o);
180 }
181 if (componentType.equals(Float.TYPE)) {
182 return Arrays.hashCode((float[]) o);
183 }
184 if (componentType.equals(Double.TYPE)) {
185 return Arrays.hashCode((double[]) o);
186 }
187 if (componentType.equals(Boolean.TYPE)) {
188 return Arrays.hashCode((boolean[]) o);
189 }
190 return Arrays.hashCode((Object[]) o);
191 }
192
193
194
195
196
197
198
199
200
201
202
203
204 public static boolean equals(final Annotation a1, final Annotation a2) {
205 if (a1 == a2) {
206 return true;
207 }
208 if (a1 == null || a2 == null) {
209 return false;
210 }
211 final Class<? extends Annotation> type1 = a1.annotationType();
212 final Class<? extends Annotation> type2 = a2.annotationType();
213 Validate.notNull(type1, "Annotation %s with null annotationType()", a1);
214 Validate.notNull(type2, "Annotation %s with null annotationType()", a2);
215 if (!type1.equals(type2)) {
216 return false;
217 }
218 try {
219 for (final Method m : type1.getDeclaredMethods()) {
220 if (m.getParameterTypes().length == 0
221 && isValidAnnotationMemberType(m.getReturnType())) {
222 AbstractReflection.setAccessible(AbstractReflection.getForceAccessible(), m);
223 final Object v1 = m.invoke(a1);
224 final Object v2 = m.invoke(a2);
225 if (!memberEquals(m.getReturnType(), v1, v2)) {
226 return false;
227 }
228 }
229 }
230 } catch (final ReflectiveOperationException ex) {
231 throw new IllegalStateException(ex);
232 }
233 return true;
234 }
235
236
237
238
239
240
241
242
243
244
245
246 public static int hashCode(final Annotation a) {
247 int result = 0;
248 final Class<? extends Annotation> type = a.annotationType();
249 for (final Method m : type.getDeclaredMethods()) {
250 try {
251 AbstractReflection.setAccessible(AbstractReflection.getForceAccessible(), m);
252 final Object value = m.invoke(a);
253 if (value == null) {
254 throw new IllegalStateException(String.format("Annotation method %s returned null", m));
255 }
256 result += hashMember(m.getName(), value);
257 } catch (final ReflectiveOperationException ex) {
258 throw new UncheckedException(ex);
259 }
260 }
261 return result;
262 }
263
264
265
266
267
268
269
270
271
272 private static int hashMember(final String name, final Object value) {
273 final int part1 = name.hashCode() * 127;
274 if (ObjectUtils.isArray(value)) {
275 return part1 ^ arrayMemberHash(value.getClass().getComponentType(), value);
276 }
277 if (value instanceof Annotation) {
278 return part1 ^ hashCode((Annotation) value);
279 }
280 return part1 ^ value.hashCode();
281 }
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296 public static boolean isValidAnnotationMemberType(Class<?> type) {
297 if (type == null) {
298 return false;
299 }
300 if (type.isArray()) {
301 type = type.getComponentType();
302 }
303 return type.isPrimitive() || type.isEnum() || type.isAnnotation()
304 || String.class.equals(type) || Class.class.equals(type);
305 }
306
307
308
309
310
311
312
313
314
315
316
317 private static boolean memberEquals(final Class<?> type, final Object o1, final Object o2) {
318 if (o1 == o2) {
319 return true;
320 }
321 if (o1 == null || o2 == null) {
322 return false;
323 }
324 if (type.isArray()) {
325 return arrayMemberEquals(type.getComponentType(), o1, o2);
326 }
327 if (type.isAnnotation()) {
328 return equals((Annotation) o1, (Annotation) o2);
329 }
330 return o1.equals(o2);
331 }
332
333
334
335
336
337
338
339
340
341 public static String toString(final Annotation a) {
342 final ToStringBuilder builder = new ToStringBuilder(a, TO_STRING_STYLE);
343 for (final Method m : a.annotationType().getDeclaredMethods()) {
344 if (m.getParameterTypes().length > 0) {
345 continue;
346 }
347 try {
348 AbstractReflection.setAccessible(AbstractReflection.getForceAccessible(), m);
349 builder.append(m.getName(), m.invoke(a));
350 } catch (final ReflectiveOperationException ex) {
351 throw new UncheckedException(ex);
352 }
353 }
354 return builder.build();
355 }
356
357
358
359
360
361
362
363
364
365
366
367
368 @Deprecated
369 public AnnotationUtils() {
370
371 }
372 }