1 /*
2  * Hibernate Validator, declare and validate application constraints
3  *
4  * License: Apache License, Version 2.0
5  * See the license.txt file in the root directory or <http://www.apache.org/licenses/LICENSE-2.0>.
6  */

7 package org.hibernate.validator.internal.util.annotation;
8
9 import java.io.Serializable;
10 import java.lang.annotation.Annotation;
11 import java.lang.reflect.InvocationHandler;
12 import java.lang.reflect.Method;
13 import java.lang.reflect.Proxy;
14 import java.security.AccessController;
15 import java.security.PrivilegedAction;
16 import java.util.Arrays;
17 import java.util.Map;
18 import java.util.Map.Entry;
19
20 import org.hibernate.validator.internal.util.privilegedactions.GetAnnotationAttributes;
21
22 /**
23  * A concrete implementation of {@code Annotation} that pretends it is a
24  * "real" source code annotation. It's also an {@code InvocationHandler}.
25  * <p>
26  * When you create an {@code AnnotationProxy}, you must initialize it
27  * with an {@code AnnotationDescriptor}.
28  * The adapter checks that the provided elements are the same elements defined
29  * in the annotation interface. However, it does <i>not</i> check that their
30  * values are the right type. If you omit an element, the adapter will use the
31  * default value for that element from the annotation interfaceif it exists.
32  * If no default exists, it will throw an exception.
33  * </p>
34  *
35  * @author Paolo Perrotta
36  * @author Davide Marchignoli
37  * @author Gunnar Morling
38  * @author Guillaume Smet
39  * @see java.lang.annotation.Annotation
40  */

41 class AnnotationProxy implements Annotation, InvocationHandler, Serializable {
42
43     private static final long serialVersionUID = 6907601010599429454L;
44
45     private final AnnotationDescriptor<? extends Annotation> descriptor;
46
47     AnnotationProxy(AnnotationDescriptor<? extends Annotation> descriptor) {
48         this.descriptor = descriptor;
49     }
50
51     @Override
52     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
53         Object value = descriptor.getAttribute( method.getName() );
54         if ( value != null ) {
55             return value;
56         }
57         return method.invoke( this, args );
58     }
59
60     @Override
61     public Class<? extends Annotation> annotationType() {
62         return descriptor.getType();
63     }
64
65     /**
66      * Performs an equality check as described in {@link Annotation#equals(Object)}.
67      *
68      * @param obj The object to compare
69      *
70      * @return Whether the given object is equal to this annotation proxy or not
71      *
72      * @see Annotation#equals(Object)
73      */

74     @Override
75     public boolean equals(Object obj) {
76         if ( this == obj ) {
77             return true;
78         }
79         if ( obj == null ) {
80             return false;
81         }
82         if ( !descriptor.getType().isInstance( obj ) ) {
83             return false;
84         }
85
86         Annotation other = descriptor.getType().cast( obj );
87
88         Map<String, Object> otherAttributes = getAnnotationAttributes( other );
89
90         if ( descriptor.getAttributes().size() != otherAttributes.size() ) {
91             return false;
92         }
93
94         // compare annotation member values
95         for ( Entry<String, Object> member : descriptor.getAttributes().entrySet() ) {
96             Object value = member.getValue();
97             Object otherValue = otherAttributes.get( member.getKey() );
98
99             if ( !areEqual( value, otherValue ) ) {
100                 return false;
101             }
102         }
103
104         return true;
105     }
106
107     /**
108      * Calculates the hash code of this annotation proxy as described in
109      * {@link Annotation#hashCode()}.
110      *
111      * @return The hash code of this proxy.
112      *
113      * @see Annotation#hashCode()
114      */

115     @Override
116     public int hashCode() {
117         return descriptor.hashCode();
118     }
119
120     @Override
121     public String toString() {
122         return descriptor.toString();
123     }
124
125     private boolean areEqual(Object o1, Object o2) {
126         return
127                 !o1.getClass().isArray() ? o1.equals( o2 )
128                         : o1.getClass() == boolean[].class ? Arrays.equals( (boolean[]) o1, (boolean[]) o2 )
129                         : o1.getClass() == byte[].class ? Arrays.equals( (byte[]) o1, (byte[]) o2 )
130                         : o1.getClass() == char[].class ? Arrays.equals( (char[]) o1, (char[]) o2 )
131                         : o1.getClass() == double[].class ? Arrays.equals( (double[]) o1, (double[]) o2 )
132                         : o1.getClass() == float[].class ? Arrays.equals( (float[]) o1, (float[]) o2 )
133                         : o1.getClass() == int[].class ? Arrays.equals( (int[]) o1, (int[]) o2 )
134                         : o1.getClass() == long[].class ? Arrays.equals( (long[]) o1, (long[]) o2 )
135                         : o1.getClass() == short[].class ? Arrays.equals( (short[]) o1, (short[]) o2 )
136                         : Arrays.equals( (Object[]) o1, (Object[]) o2 );
137     }
138
139     private Map<String, Object> getAnnotationAttributes(Annotation annotation) {
140         // We only enable this optimization if the security manager is not enabled. Otherwise,
141         // we would have to add every package containing constraints to the security policy.
142         if ( Proxy.isProxyClass( annotation.getClass() ) && System.getSecurityManager() == null ) {
143             InvocationHandler invocationHandler = Proxy.getInvocationHandler( annotation );
144             if ( invocationHandler instanceof AnnotationProxy ) {
145                 return ( (AnnotationProxy) invocationHandler ).descriptor.getAttributes();
146             }
147         }
148
149         return run( GetAnnotationAttributes.action( annotation ) );
150     }
151
152     /**
153      * Runs the given privileged action, using a privileged block if required.
154      * <p>
155      * <b>NOTE:</b> This must never be changed into a publicly available method to avoid execution of arbitrary
156      * privileged actions within HV's protection domain.
157      */

158     private <T> T run(PrivilegedAction<T> action) {
159         return System.getSecurityManager() != null ? AccessController.doPrivileged( action ) : action.run();
160     }
161 }
162