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.properties.javabean;
8
9 import static org.hibernate.validator.internal.util.TypeHelper.isHibernateValidatorEnhancedBean;
10
11 import java.lang.annotation.Annotation;
12 import java.lang.reflect.AnnotatedType;
13 import java.lang.reflect.Field;
14 import java.lang.reflect.Type;
15 import java.lang.reflect.TypeVariable;
16 import java.security.AccessController;
17 import java.security.PrivilegedAction;
18
19 import org.hibernate.validator.HibernateValidatorPermission;
20 import org.hibernate.validator.engine.HibernateValidatorEnhancedBean;
21 import org.hibernate.validator.internal.properties.PropertyAccessor;
22 import org.hibernate.validator.internal.util.ReflectionHelper;
23 import org.hibernate.validator.internal.util.privilegedactions.GetDeclaredField;
24
25 /**
26  * @author Marko Bekhta
27  */

28 public class JavaBeanField implements org.hibernate.validator.internal.properties.Field, JavaBeanAnnotatedConstrainable {
29
30     private final Field field;
31     private final String resolvedPropertyName;
32     private final Type typeForValidatorResolution;
33     private final Type type;
34
35     public JavaBeanField(Field field, String resolvedPropertyName) {
36         this.field = field;
37         this.type = ReflectionHelper.typeOf( field );
38         this.typeForValidatorResolution = ReflectionHelper.boxedType( this.type );
39         this.resolvedPropertyName = resolvedPropertyName;
40     }
41
42     @Override
43     public String getName() {
44         return field.getName();
45     }
46
47     @Override
48     public Class<?> getDeclaringClass() {
49         return field.getDeclaringClass();
50     }
51
52     @Override
53     public Type getType() {
54         return type;
55     }
56
57     @Override
58     public Type getTypeForValidatorResolution() {
59         return typeForValidatorResolution;
60     }
61
62     @Override
63     public String getPropertyName() {
64         return getName();
65     }
66
67     @Override
68     public String getResolvedPropertyName() {
69         return resolvedPropertyName;
70     }
71
72     @Override
73     public AnnotatedType getAnnotatedType() {
74         return field.getAnnotatedType();
75     }
76
77     @Override
78     public Annotation[] getDeclaredAnnotations() {
79         return field.getDeclaredAnnotations();
80     }
81
82     @Override
83     public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {
84         return field.getAnnotation( annotationClass );
85     }
86
87     @Override
88     public Type getGenericType() {
89         return ReflectionHelper.typeOf( field );
90     }
91
92     @Override
93     public TypeVariable<?>[] getTypeParameters() {
94         return field.getType().getTypeParameters();
95     }
96
97     @Override
98     public PropertyAccessor createAccessor() {
99         if ( isHibernateValidatorEnhancedBean( field.getDeclaringClass() ) ) {
100             return new EnhancedBeanFieldAccessor( field.getName() );
101         }
102         else {
103             return new FieldAccessor( field );
104         }
105     }
106
107     @Override
108     public boolean equals(Object o) {
109         if ( this == o ) {
110             return true;
111         }
112         if ( o == null || this.getClass() != o.getClass() ) {
113             return false;
114         }
115
116         JavaBeanField that = (JavaBeanField) o;
117
118         if ( !this.field.equals( that.field ) ) {
119             return false;
120         }
121         if ( !this.typeForValidatorResolution.equals( that.typeForValidatorResolution ) ) {
122             return false;
123         }
124         return this.type.equals( that.type );
125     }
126
127     @Override
128     public int hashCode() {
129         int result = this.field.hashCode();
130         result = 31 * result + this.typeForValidatorResolution.hashCode();
131         result = 31 * result + this.type.hashCode();
132         return result;
133     }
134
135     @Override
136     public String toString() {
137         return getName();
138     }
139
140     private static class EnhancedBeanFieldAccessor implements PropertyAccessor {
141
142         private final String name;
143
144         private EnhancedBeanFieldAccessor(final String name) {
145             this.name = name;
146         }
147
148         @Override
149         public Object getValueFrom(Object bean) {
150             // we don't do an instanceof check here as it should already be applied when the accessor was created.
151             return ( (HibernateValidatorEnhancedBean) bean ).$$_hibernateValidator_getFieldValue( name );
152         }
153     }
154
155     private static class FieldAccessor implements PropertyAccessor {
156
157         private Field accessibleField;
158
159         private FieldAccessor(Field field) {
160             this.accessibleField = getAccessible( field );
161         }
162
163         @Override
164         public Object getValueFrom(Object bean) {
165             return ReflectionHelper.getValue( accessibleField, bean );
166         }
167     }
168
169     /**
170      * Returns an accessible copy of the given member.
171      */

172     private static Field getAccessible(Field original) {
173         SecurityManager sm = System.getSecurityManager();
174         if ( sm != null ) {
175             sm.checkPermission( HibernateValidatorPermission.ACCESS_PRIVATE_MEMBERS );
176         }
177
178         Class<?> clazz = original.getDeclaringClass();
179
180         return run( GetDeclaredField.andMakeAccessible( clazz, original.getName() ) );
181     }
182
183     /**
184      * Runs the given privileged action, using a privileged block if required.
185      * <p>
186      * <b>NOTE:</b> This must never be changed into a publicly available method to avoid execution of arbitrary
187      * privileged actions within HV's protection domain.
188      */

189     private static <T> T run(PrivilegedAction<T> action) {
190         return System.getSecurityManager() != null ? AccessController.doPrivileged( action ) : action.run();
191     }
192 }
193