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.logging.Messages.MESSAGES;
10
11 import java.lang.reflect.Constructor;
12 import java.lang.reflect.Executable;
13 import java.lang.reflect.Field;
14 import java.lang.reflect.Method;
15 import java.security.AccessController;
16 import java.security.PrivilegedAction;
17 import java.util.Optional;
18
19 import org.hibernate.validator.internal.properties.Constrainable;
20 import org.hibernate.validator.internal.util.Contracts;
21 import org.hibernate.validator.internal.util.privilegedactions.GetDeclaredConstructor;
22 import org.hibernate.validator.internal.util.privilegedactions.GetDeclaredField;
23 import org.hibernate.validator.internal.util.privilegedactions.GetDeclaredMethod;
24 import org.hibernate.validator.internal.util.privilegedactions.GetMethodFromGetterNameCandidates;
25 import org.hibernate.validator.spi.nodenameprovider.JavaBeanProperty;
26 import org.hibernate.validator.spi.nodenameprovider.PropertyNodeNameProvider;
27 import org.hibernate.validator.spi.properties.ConstrainableExecutable;
28 import org.hibernate.validator.spi.properties.GetterPropertySelectionStrategy;
29
30 /**
31  * Helper class that gives ability to find {@link Constrainable} versions
32  * of JavaBean's fields, getters, constructors and methods.
33  *
34  * @author Marko Bekhta
35  */

36 public class JavaBeanHelper {
37
38     private final GetterPropertySelectionStrategy getterPropertySelectionStrategy;
39     private final PropertyNodeNameProvider propertyNodeNameProvider;
40
41     public JavaBeanHelper(GetterPropertySelectionStrategy getterPropertySelectionStrategy, PropertyNodeNameProvider propertyNodeNameProvider) {
42         this.getterPropertySelectionStrategy = getterPropertySelectionStrategy;
43         this.propertyNodeNameProvider = propertyNodeNameProvider;
44     }
45
46     public GetterPropertySelectionStrategy getGetterPropertySelectionStrategy() {
47         return getterPropertySelectionStrategy;
48     }
49
50     public Optional<JavaBeanField> findDeclaredField(Class<?> declaringClass, String property) {
51         Contracts.assertNotNull( declaringClass, MESSAGES.classCannotBeNull() );
52
53         Field field = run( GetDeclaredField.action( declaringClass, property ) );
54
55         return Optional.ofNullable( field ).map( this::field );
56     }
57
58     public Optional<JavaBeanGetter> findDeclaredGetter(Class<?> declaringClass, String property) {
59         Contracts.assertNotNull( declaringClass, MESSAGES.classCannotBeNull() );
60
61         return findGetter( declaringClass, property, false );
62     }
63
64     public Optional<JavaBeanGetter> findGetter(Class<?> declaringClass, String property) {
65         Contracts.assertNotNull( declaringClass, MESSAGES.classCannotBeNull() );
66
67         return findGetter( declaringClass, property, true );
68     }
69
70     private Optional<JavaBeanGetter> findGetter(Class<?> declaringClass, String property, boolean lookForMethodsOnSuperClass) {
71         Method getter = run(
72                 GetMethodFromGetterNameCandidates.action(
73                         declaringClass,
74                         getterPropertySelectionStrategy.getGetterMethodNameCandidates( property ),
75                         lookForMethodsOnSuperClass
76                 )
77         );
78         if ( getter == null ) {
79             return Optional.empty();
80         }
81         else {
82             return Optional.of( new JavaBeanGetter( declaringClass, getter, property, propertyNodeNameProvider.getName(
83                     new JavaBeanPropertyImpl( declaringClass, property ) ) ) );
84         }
85     }
86
87     public Optional<JavaBeanMethod> findDeclaredMethod(Class<?> declaringClass, String methodName, Class<?>... parameterTypes) {
88         Method method = run( GetDeclaredMethod.action( declaringClass, methodName, parameterTypes ) );
89         if ( method == null ) {
90             return Optional.empty();
91         }
92         else {
93             return Optional.of( executable( declaringClass, method ) );
94         }
95     }
96
97     public <T> Optional<JavaBeanConstructor> findDeclaredConstructor(Class<T> declaringClass, Class<?>... parameterTypes) {
98         Constructor<T> constructor = run( GetDeclaredConstructor.action( declaringClass, parameterTypes ) );
99         if ( constructor == null ) {
100             return Optional.empty();
101         }
102         else {
103             return Optional.of( new JavaBeanConstructor( constructor ) );
104         }
105     }
106
107     public JavaBeanExecutable<?> executable(Executable executable) {
108         return executable( executable.getDeclaringClass(), executable );
109     }
110
111     public JavaBeanExecutable<?> executable(Class<?> declaringClass, Executable executable) {
112         if ( executable instanceof Constructor ) {
113             return new JavaBeanConstructor( (Constructor<?>) executable );
114         }
115
116         return executable( declaringClass, ( (Method) executable ) );
117     }
118
119     public JavaBeanMethod executable(Class<?> declaringClass, Method method) {
120         JavaBeanConstrainableExecutable executable = new JavaBeanConstrainableExecutable( method );
121
122         Optional<String> correspondingProperty = getterPropertySelectionStrategy.getProperty( executable );
123         if ( correspondingProperty.isPresent() ) {
124             return new JavaBeanGetter( declaringClass, method, correspondingProperty.get(), propertyNodeNameProvider.getName(
125                     new JavaBeanPropertyImpl( declaringClass, correspondingProperty.get() ) ) );
126         }
127
128         return new JavaBeanMethod( method );
129     }
130
131     public JavaBeanField field(Field field) {
132         return new JavaBeanField( field, propertyNodeNameProvider.getName( new JavaBeanPropertyImpl( field.getDeclaringClass(), field.getName() ) ) );
133     }
134
135     /**
136      * Runs the given privileged action, using a privileged block if required.
137      *
138      * <b>NOTE:</b> This must never be changed into a publicly available method to avoid execution of arbitrary
139      * privileged actions within HV's protection domain.
140      */

141     private <T> T run(PrivilegedAction<T> action) {
142         return System.getSecurityManager() != null ? AccessController.doPrivileged( action ) : action.run();
143     }
144
145     private static class JavaBeanConstrainableExecutable implements ConstrainableExecutable {
146
147         private final Method method;
148
149         private JavaBeanConstrainableExecutable(Method method) {
150             this.method = method;
151         }
152
153         @Override
154         public Class<?> getReturnType() {
155             return method.getReturnType();
156         }
157
158         @Override
159         public String getName() {
160             return method.getName();
161         }
162
163         @Override
164         public Class<?>[] getParameterTypes() {
165             return method.getParameterTypes();
166         }
167     }
168
169     private static class JavaBeanPropertyImpl implements JavaBeanProperty {
170         private final Class<?> declaringClass;
171         private final String name;
172
173         private JavaBeanPropertyImpl(Class<?> declaringClass, String name) {
174             this.declaringClass = declaringClass;
175             this.name = name;
176         }
177
178         @Override
179         public Class<?> getDeclaringClass() {
180             return declaringClass;
181         }
182
183         @Override
184         public String getName() {
185             return name;
186         }
187     }
188 }
189