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