16
16
*/
17
17
package org .hibernate .validator .internal .util .annotationfactory ;
18
18
19
+ import static org .hibernate .validator .internal .util .CollectionHelper .newHashMap ;
20
+
19
21
import java .io .Serializable ;
20
22
import java .lang .annotation .Annotation ;
21
23
import java .lang .reflect .InvocationHandler ;
24
+ import java .lang .reflect .InvocationTargetException ;
22
25
import java .lang .reflect .Method ;
23
- import java .util .HashMap ;
26
+ import java .util .Arrays ;
27
+ import java .util .Collections ;
24
28
import java .util .Map ;
29
+ import java .util .Map .Entry ;
25
30
import java .util .Set ;
26
31
import java .util .SortedSet ;
27
32
import java .util .TreeSet ;
42
47
* default value for that element from the annotation interface, if it exists.
43
48
* If no default exists, it will throw an exception.
44
49
* <p/>
45
- * Warning: this class does not implement <code>hashCode()</code> and
46
- * <code>equals()</code> - it just uses the ones it inherits from <code>Object</code>.
47
- * This means that an <code>AnnotationProxy</code> does <i>not</i> follow the
48
- * recommendations of the <code>Annotation</code> javadoc about these two
49
- * methods. That's why you should never mix <code>AnnotationProxies</code>
50
- * with "real" annotations. For example, don't put them into the same
51
- * <code>Collection</code>.
52
50
*
53
51
* @author Paolo Perrotta
54
52
* @author Davide Marchignoli
53
+ * @author Gunnar Morling
54
+ *
55
55
* @see java.lang.annotation.Annotation
56
56
*/
57
57
public class AnnotationProxy implements Annotation , InvocationHandler , Serializable {
@@ -61,14 +61,16 @@ public class AnnotationProxy implements Annotation, InvocationHandler, Serializa
61
61
62
62
private final Class <? extends Annotation > annotationType ;
63
63
private final Map <String , Object > values ;
64
+ private final int hashCode ;
64
65
65
66
public AnnotationProxy (AnnotationDescriptor <?> descriptor ) {
66
67
this .annotationType = descriptor .type ();
67
- values = getAnnotationValues ( descriptor );
68
+ values = Collections .unmodifiableMap ( getAnnotationValues ( descriptor ) );
69
+ this .hashCode = calculateHashCode ();
68
70
}
69
71
70
72
private Map <String , Object > getAnnotationValues (AnnotationDescriptor <?> descriptor ) {
71
- Map <String , Object > result = new HashMap < String , Object > ();
73
+ Map <String , Object > result = newHashMap ();
72
74
int processedValuesFromDescriptor = 0 ;
73
75
final Method [] declaredMethods = ReflectionHelper .getDeclaredMethods ( annotationType );
74
76
for ( Method m : declaredMethods ) {
@@ -99,17 +101,113 @@ else if ( m.getDefaultValue() != null ) {
99
101
return result ;
100
102
}
101
103
104
+ private Object getAnnotationMemberValue (Annotation annotation , String name ) {
105
+ try {
106
+ return ReflectionHelper .getDeclaredMethod ( annotation .annotationType (), name )
107
+ .invoke ( annotation );
108
+ }
109
+ catch ( IllegalAccessException e ) {
110
+ throw log .getUnableToRetrieveAnnotationParameterValueException ( e );
111
+ }
112
+ catch ( IllegalArgumentException e ) {
113
+ throw log .getUnableToRetrieveAnnotationParameterValueException ( e );
114
+ }
115
+ catch ( InvocationTargetException e ) {
116
+ throw log .getUnableToRetrieveAnnotationParameterValueException ( e );
117
+ }
118
+ }
119
+
120
+ @ Override
102
121
public Object invoke (Object proxy , Method method , Object [] args ) throws Throwable {
103
122
if ( values .containsKey ( method .getName () ) ) {
104
123
return values .get ( method .getName () );
105
124
}
106
125
return method .invoke ( this , args );
107
126
}
108
127
128
+ @ Override
109
129
public Class <? extends Annotation > annotationType () {
110
130
return annotationType ;
111
131
}
112
132
133
+ /**
134
+ * Performs an equality check as described in {@link Annotation#equals(Object)}.
135
+ *
136
+ * @param obj The object to compare
137
+ *
138
+ * @return Whether the given object is equal to this annotation proxy or not
139
+ *
140
+ * @see Annotation#equals(Object)
141
+ */
142
+ @ Override
143
+ public boolean equals (Object obj ) {
144
+ if ( this == obj ) {
145
+ return true ;
146
+ }
147
+ if ( obj == null ) {
148
+ return false ;
149
+ }
150
+ if ( !annotationType .isInstance ( obj ) ) {
151
+ return false ;
152
+ }
153
+
154
+ Annotation other = annotationType .cast ( obj );
155
+
156
+ //compare annotation member values
157
+ for ( Entry <String , Object > member : values .entrySet () ) {
158
+
159
+ Object value = member .getValue ();
160
+ Object otherValue = getAnnotationMemberValue ( other , member .getKey () );
161
+
162
+ if ( !areEqual ( value , otherValue ) ) {
163
+ return false ;
164
+ }
165
+ }
166
+
167
+ return true ;
168
+ }
169
+
170
+ /**
171
+ * Calculates the hash code of this annotation proxy as described in
172
+ * {@link Annotation#hashCode()}.
173
+ *
174
+ * @return The hash code of this proxy.
175
+ *
176
+ * @see Annotation#hashCode()
177
+ */
178
+ @ Override
179
+ public int hashCode () {
180
+ return hashCode ;
181
+ }
182
+
183
+ private int calculateHashCode () {
184
+
185
+ int hashCode = 0 ;
186
+
187
+ for ( Entry <String , Object > member : values .entrySet () ) {
188
+ Object value = member .getValue ();
189
+
190
+ int nameHashCode = member .getKey ().hashCode ();
191
+
192
+ int valueHashCode =
193
+ !value .getClass ().isArray () ? value .hashCode () :
194
+ value .getClass () == boolean [].class ? Arrays .hashCode ( (boolean []) value ) :
195
+ value .getClass () == byte [].class ? Arrays .hashCode ( (byte []) value ) :
196
+ value .getClass () == char [].class ? Arrays .hashCode ( (char []) value ) :
197
+ value .getClass () == double [].class ? Arrays .hashCode ( (double []) value ) :
198
+ value .getClass () == float [].class ? Arrays .hashCode ( (float []) value ) :
199
+ value .getClass () == int [].class ? Arrays .hashCode ( (int []) value ) :
200
+ value .getClass () == long [].class ? Arrays .hashCode ( (long []) value ) :
201
+ value .getClass () == short [].class ? Arrays .hashCode ( (short []) value ) :
202
+ Arrays .hashCode ( (Object []) value );
203
+
204
+ hashCode += 127 * nameHashCode ^ valueHashCode ;
205
+ }
206
+
207
+ return hashCode ;
208
+ }
209
+
210
+ @ Override
113
211
public String toString () {
114
212
StringBuilder result = new StringBuilder ();
115
213
result .append ( '@' ).append ( annotationType .getName () ).append ( '(' );
@@ -133,4 +231,18 @@ private SortedSet<String> getRegisteredMethodsInAlphabeticalOrder() {
133
231
result .addAll ( values .keySet () );
134
232
return result ;
135
233
}
234
+
235
+ private boolean areEqual (Object o1 , Object o2 ) {
236
+ return
237
+ !o1 .getClass ().isArray () ? o1 .equals ( o2 ) :
238
+ o1 .getClass () == boolean [].class ? Arrays .equals ( (boolean []) o1 , (boolean []) o2 ) :
239
+ o1 .getClass () == byte [].class ? Arrays .equals ( (byte []) o1 , (byte []) o2 ) :
240
+ o1 .getClass () == char [].class ? Arrays .equals ( (char []) o1 , (char []) o2 ) :
241
+ o1 .getClass () == double [].class ? Arrays .equals ( (double []) o1 , (double []) o2 ) :
242
+ o1 .getClass () == float [].class ? Arrays .equals ( (float []) o1 , (float []) o2 ) :
243
+ o1 .getClass () == int [].class ? Arrays .equals ( (int []) o1 , (int []) o2 ) :
244
+ o1 .getClass () == long [].class ? Arrays .equals ( (long []) o1 , (long []) o2 ) :
245
+ o1 .getClass () == short [].class ? Arrays .equals ( (short []) o1 , (short []) o2 ) :
246
+ Arrays .equals ( (Object []) o1 , (Object []) o2 );
247
+ }
136
248
}
0 commit comments