1
7 package org.hibernate.validator.internal.util.annotation;
8
9 import static org.hibernate.validator.internal.util.CollectionHelper.newHashMap;
10
11 import java.io.Serializable;
12 import java.lang.annotation.Annotation;
13 import java.lang.invoke.MethodHandles;
14 import java.lang.reflect.Method;
15 import java.security.AccessController;
16 import java.security.PrivilegedAction;
17 import java.util.Arrays;
18 import java.util.HashMap;
19 import java.util.Map;
20 import java.util.Map.Entry;
21 import java.util.Set;
22 import java.util.SortedSet;
23 import java.util.TreeSet;
24
25 import org.hibernate.validator.internal.util.CollectionHelper;
26 import org.hibernate.validator.internal.util.StringHelper;
27 import org.hibernate.validator.internal.util.logging.Log;
28 import org.hibernate.validator.internal.util.logging.LoggerFactory;
29 import org.hibernate.validator.internal.util.privilegedactions.GetAnnotationAttributes;
30 import org.hibernate.validator.internal.util.privilegedactions.GetDeclaredMethods;
31
32
46 public class AnnotationDescriptor<A extends Annotation> implements Serializable {
47
48 private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() );
49
50 private final Class<A> type;
51
52 private final Map<String, Object> attributes;
53
54 private final int hashCode;
55
56 private final A annotation;
57
58 @SuppressWarnings("unchecked")
59 public AnnotationDescriptor(A annotation) {
60 this.type = (Class<A>) annotation.annotationType();
61 this.attributes = run( GetAnnotationAttributes.action( annotation ) );
62 this.annotation = annotation;
63 this.hashCode = buildHashCode();
64 }
65
66 public AnnotationDescriptor(AnnotationDescriptor<A> descriptor) {
67 this.type = descriptor.type;
68 this.attributes = descriptor.attributes;
69 this.hashCode = descriptor.hashCode;
70 this.annotation = descriptor.annotation;
71 }
72
73 private AnnotationDescriptor(Class<A> annotationType, Map<String, Object> attributes) {
74 this.type = annotationType;
75 this.attributes = CollectionHelper.toImmutableMap( attributes );
76 this.hashCode = buildHashCode();
77 this.annotation = AnnotationFactory.create( this );
78 }
79
80 public Class<A> getType() {
81 return type;
82 }
83
84 public Map<String, Object> getAttributes() {
85 return attributes;
86 }
87
88 @SuppressWarnings("unchecked")
89 public <T> T getMandatoryAttribute(String attributeName, Class<T> attributeType) {
90 Object attribute = attributes.get( attributeName );
91
92 if ( attribute == null ) {
93 throw LOG.getUnableToFindAnnotationAttributeException( type, attributeName, null );
94 }
95
96 if ( !attributeType.isAssignableFrom( attribute.getClass() ) ) {
97 throw LOG.getWrongAnnotationAttributeTypeException( type, attributeName, attributeType, attribute.getClass() );
98 }
99
100 return (T) attribute;
101 }
102
103 @SuppressWarnings("unchecked")
104 public <T> T getAttribute(String attributeName, Class<T> attributeType) {
105 Object attribute = attributes.get( attributeName );
106
107 if ( attribute == null ) {
108 return null;
109 }
110
111 if ( !attributeType.isAssignableFrom( attribute.getClass() ) ) {
112 throw LOG.getWrongAnnotationAttributeTypeException( type, attributeName, attributeType, attribute.getClass() );
113 }
114
115 return (T) attribute;
116 }
117
118 public Object getAttribute(String attributeName) {
119 return attributes.get( attributeName );
120 }
121
122 public A getAnnotation() {
123 return annotation;
124 }
125
126 @Override
127 public boolean equals(Object obj) {
128 if ( this == obj ) {
129 return true;
130 }
131 if ( obj == null || !( obj instanceof AnnotationDescriptor ) ) {
132 return false;
133 }
134
135 AnnotationDescriptor<?> other = (AnnotationDescriptor<?>) obj;
136
137 if ( !type.equals( other.type ) ) {
138 return false;
139 }
140
141 if ( attributes.size() != other.attributes.size() ) {
142 return false;
143 }
144
145 for ( Entry<String, Object> member : attributes.entrySet() ) {
146 Object value = member.getValue();
147 Object otherValue = other.attributes.get( member.getKey() );
148
149 if ( !areEqual( value, otherValue ) ) {
150 return false;
151 }
152 }
153
154 return true;
155 }
156
157
165 @Override
166 public int hashCode() {
167 return hashCode;
168 }
169
170 @Override
171 public String toString() {
172 StringBuilder result = new StringBuilder();
173 result.append( '@' ).append( StringHelper.toShortString( type ) ).append( '(' );
174 for ( String s : getRegisteredAttributesInAlphabeticalOrder() ) {
175 result.append( s ).append( '=' ).append( attributes.get( s ) ).append( ", " );
176 }
177
178 if ( attributes.size() > 0 ) {
179 result.delete( result.length() - 2, result.length() );
180 result.append( ")" );
181 }
182 else {
183 result.delete( result.length() - 1, result.length() );
184 }
185
186 return result.toString();
187 }
188
189 private SortedSet<String> getRegisteredAttributesInAlphabeticalOrder() {
190 return new TreeSet<String>( attributes.keySet() );
191 }
192
193 private int buildHashCode() {
194 int hashCode = 0;
195
196 for ( Entry<String, Object> member : attributes.entrySet() ) {
197 Object value = member.getValue();
198
199 int nameHashCode = member.getKey().hashCode();
200
201 int valueHashCode = !value.getClass().isArray() ? value.hashCode()
202 : value.getClass() == boolean[].class ? Arrays.hashCode( (boolean[]) value )
203 : value.getClass() == byte[].class ? Arrays.hashCode( (byte[]) value )
204 : value.getClass() == char[].class ? Arrays.hashCode( (char[]) value )
205 : value.getClass() == double[].class ? Arrays.hashCode( (double[]) value )
206 : value.getClass() == float[].class ? Arrays.hashCode( (float[]) value )
207 : value.getClass() == int[].class ? Arrays.hashCode( (int[]) value )
208 : value.getClass() == long[].class ? Arrays.hashCode( (long[]) value )
209 : value.getClass() == short[].class ? Arrays.hashCode( (short[]) value )
210 : Arrays.hashCode( (Object[]) value );
211
212 hashCode += 127 * nameHashCode ^ valueHashCode;
213 }
214
215 return hashCode;
216 }
217
218 private boolean areEqual(Object o1, Object o2) {
219 return !o1.getClass().isArray() ? o1.equals( o2 )
220 : o1.getClass() == boolean[].class ? Arrays.equals( (boolean[]) o1, (boolean[]) o2 )
221 : o1.getClass() == byte[].class ? Arrays.equals( (byte[]) o1, (byte[]) o2 )
222 : o1.getClass() == char[].class ? Arrays.equals( (char[]) o1, (char[]) o2 )
223 : o1.getClass() == double[].class ? Arrays.equals( (double[]) o1, (double[]) o2 )
224 : o1.getClass() == float[].class ? Arrays.equals( (float[]) o1, (float[]) o2 )
225 : o1.getClass() == int[].class ? Arrays.equals( (int[]) o1, (int[]) o2 )
226 : o1.getClass() == long[].class ? Arrays.equals( (long[]) o1, (long[]) o2 )
227 : o1.getClass() == short[].class ? Arrays.equals( (short[]) o1, (short[]) o2 )
228 : Arrays.equals( (Object[]) o1, (Object[]) o2 );
229 }
230
231 public static class Builder<S extends Annotation> {
232
233 private final Class<S> type;
234
235 private final Map<String, Object> attributes;
236
237 public Builder(Class<S> type) {
238 this.type = type;
239 this.attributes = new HashMap<String, Object>();
240 }
241
242 public Builder(Class<S> type, Map<String, Object> attributes) {
243 this.type = type;
244 this.attributes = new HashMap<String, Object>( attributes );
245 }
246
247 @SuppressWarnings("unchecked")
248 public Builder(S annotation) {
249 this.type = (Class<S>) annotation.annotationType();
250 this.attributes = new HashMap<String, Object>( run( GetAnnotationAttributes.action( annotation ) ) );
251 }
252
253 public void setAttribute(String attributeName, Object value) {
254 attributes.put( attributeName, value );
255 }
256
257 public boolean hasAttribute(String key) {
258 return attributes.containsKey( key );
259 }
260
261 public Class<S> getType() {
262 return type;
263 }
264
265 public AnnotationDescriptor<S> build() {
266 return new AnnotationDescriptor<>( type, getAnnotationAttributes() );
267 }
268
269 private Map<String, Object> getAnnotationAttributes() {
270 Map<String, Object> result = newHashMap( attributes.size() );
271 int processedValuesFromDescriptor = 0;
272 final Method[] declaredMethods = run( GetDeclaredMethods.action( type ) );
273 for ( Method m : declaredMethods ) {
274 Object elementValue = attributes.get( m.getName() );
275 if ( elementValue != null ) {
276 result.put( m.getName(), elementValue );
277 processedValuesFromDescriptor++;
278 }
279 else if ( m.getDefaultValue() != null ) {
280 result.put( m.getName(), m.getDefaultValue() );
281 }
282 else {
283 throw LOG.getNoValueProvidedForAnnotationAttributeException(
284 m.getName(),
285 type
286 );
287 }
288 }
289 if ( processedValuesFromDescriptor != attributes.size() ) {
290 Set<String> unknownAttributes = attributes.keySet();
291 unknownAttributes.removeAll( result.keySet() );
292
293 throw LOG.getTryingToInstantiateAnnotationWithUnknownAttributesException(
294 type,
295 unknownAttributes
296 );
297 }
298 return result;
299 }
300
301 @Override
302 public String toString() {
303 StringBuilder result = new StringBuilder();
304 result.append( '@' ).append( StringHelper.toShortString( type ) ).append( '(' );
305 for ( String s : getRegisteredAttributesInAlphabeticalOrder() ) {
306 result.append( s ).append( '=' ).append( attributes.get( s ) ).append( ", " );
307 }
308
309 if ( attributes.size() > 0 ) {
310 result.delete( result.length() - 2, result.length() );
311 result.append( ")" );
312 }
313 else {
314 result.delete( result.length() - 1, result.length() );
315 }
316
317 return result.toString();
318 }
319
320 private SortedSet<String> getRegisteredAttributesInAlphabeticalOrder() {
321 return new TreeSet<String>( attributes.keySet() );
322 }
323 }
324
325
330 private static <V> V run(PrivilegedAction<V> action) {
331 return System.getSecurityManager() != null ? AccessController.doPrivileged( action ) : action.run();
332 }
333 }
334