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;
8
9 import java.lang.annotation.ElementType;
10 import java.lang.invoke.MethodHandles;
11 import java.lang.reflect.Constructor;
12 import java.lang.reflect.Executable;
13 import java.lang.reflect.Method;
14 import java.lang.reflect.Modifier;
15 import java.security.AccessController;
16 import java.security.PrivilegedAction;
17
18 import org.hibernate.validator.internal.properties.Callable;
19 import org.hibernate.validator.internal.properties.Signature;
20 import org.hibernate.validator.internal.util.classhierarchy.Filters;
21 import org.hibernate.validator.internal.util.logging.Log;
22 import org.hibernate.validator.internal.util.logging.LoggerFactory;
23 import org.hibernate.validator.internal.util.privilegedactions.GetResolvedMemberMethods;
24
25 import com.fasterxml.classmate.Filter;
26 import com.fasterxml.classmate.MemberResolver;
27 import com.fasterxml.classmate.ResolvedType;
28 import com.fasterxml.classmate.ResolvedTypeWithMembers;
29 import com.fasterxml.classmate.TypeResolver;
30 import com.fasterxml.classmate.members.RawMethod;
31 import com.fasterxml.classmate.members.ResolvedMethod;
32
33 /**
34  * Provides shared functionality dealing with executables.
35  *
36  * @author Hardy Ferentschik
37  * @author Gunnar Morling
38  * @author Kevin Pollet &lt;kevin.pollet@serli.com&gt; (C) 2011 SERLI
39  */

40 public final class ExecutableHelper {
41
42     private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() );
43     private final TypeResolver typeResolver;
44
45     public ExecutableHelper(TypeResolutionHelper typeResolutionHelper) {
46         this.typeResolver = typeResolutionHelper.getTypeResolver();
47     }
48
49     public boolean overrides(Callable subTypeMethod, Callable superTypeMethod) {
50         return subTypeMethod.overrides( this, superTypeMethod );
51     }
52
53     /**
54      * Checks, whether {@code subTypeMethod} overrides {@code superTypeMethod}.
55      *
56      * @param subTypeMethod The sub type method (cannot be {@code null}).
57      * @param superTypeMethod The super type method (cannot be {@code null}).
58      *
59      * @return Returns {@code trueif {@code subTypeMethod} overrides {@code superTypeMethod},
60      * {@code false} otherwise.
61      */

62     public boolean overrides(Method subTypeMethod, Method superTypeMethod) {
63         Contracts.assertValueNotNull( subTypeMethod, "subTypeMethod" );
64         Contracts.assertValueNotNull( superTypeMethod, "superTypeMethod" );
65
66         if ( subTypeMethod.equals( superTypeMethod ) ) {
67             return false;
68         }
69
70         if ( !subTypeMethod.getName().equals( superTypeMethod.getName() ) ) {
71             return false;
72         }
73
74         if ( subTypeMethod.getParameterTypes().length != superTypeMethod.getParameterTypes().length ) {
75             return false;
76         }
77
78         if ( !superTypeMethod.getDeclaringClass().isAssignableFrom( subTypeMethod.getDeclaringClass() ) ) {
79             return false;
80         }
81
82         if ( Modifier.isStatic( superTypeMethod.getModifiers() ) || Modifier.isStatic(
83                 subTypeMethod.getModifiers()
84         ) ) {
85             return false;
86         }
87
88         // HV-861 Bridge method should be ignored. Classmates type/member resolution will take care of proper
89         // override detection without considering bridge methods
90         if ( subTypeMethod.isBridge() ) {
91             return false;
92         }
93
94         if ( Modifier.isPrivate( superTypeMethod.getModifiers() ) ) {
95             return false;
96         }
97
98         if ( !isMethodVisibleTo( superTypeMethod, subTypeMethod ) ) {
99             return false;
100         }
101
102         return instanceMethodParametersResolveToSameTypes( subTypeMethod, superTypeMethod );
103     }
104
105     /**
106      * Checks if a pair of given methods ({@code left} and {@code right}) are resolved to the same
107      * method based on the {@code mainSubType} type.
108      *
109      * @param mainSubType a type at the bottom of class hierarchy to be used to lookup the methods.
110      * @param left one of the methods to check
111      * @param right another of the methods to check
112      *
113      * @return {@code trueif a pair of methods are equal {@code left == right}, or one of the methods
114      *         override another one in the class hierarchy with {@code mainSubType} at the bottom,
115      *         {@code false} otherwise.
116      */

117     public boolean isResolvedToSameMethodInHierarchy(Class<?> mainSubType, Method left, Method right) {
118         Contracts.assertValueNotNull( mainSubType, "mainSubType" );
119         Contracts.assertValueNotNull( left, "left" );
120         Contracts.assertValueNotNull( right, "right" );
121
122         if ( left.equals( right ) ) {
123             return true;
124         }
125
126         if ( !left.getName().equals( right.getName() ) ) {
127             return false;
128         }
129
130         // methods with same name in the same class should be different
131         if ( left.getDeclaringClass().equals( right.getDeclaringClass() ) ) {
132             return false;
133         }
134
135         if ( left.getParameterTypes().length != right.getParameterTypes().length ) {
136             return false;
137         }
138
139         // if at least one method from a pair is static - they are different methods
140         if ( Modifier.isStatic( right.getModifiers() ) || Modifier.isStatic( left.getModifiers() ) ) {
141             return false;
142         }
143
144         // HV-861 Bridge method should be ignored. Classmates type/member resolution will take care of proper
145         // override detection without considering bridge methods
146         if ( left.isBridge() || right.isBridge() ) {
147             return false;
148         }
149
150         // if one of the methods is private - methods are different
151         if ( Modifier.isPrivate( left.getModifiers() ) || Modifier.isPrivate( right.getModifiers() ) ) {
152             return false;
153         }
154
155         if ( !isMethodVisibleTo( right, left ) || !isMethodVisibleTo( left, right ) ) {
156             return false;
157         }
158
159         // We need to check if the passed mainSubType is not a Weld proxy. In case of proxy we need to get
160         // a class that was proxied otherwise we can use the class itself. This is due to the issue that
161         // call to Class#getGenericInterfaces() on a Weld proxy returns raw types instead of parametrized
162         // generics and methods will not be resolved correctly.
163         return instanceMethodParametersResolveToSameTypes(
164                 Filters.excludeProxies().accepts( mainSubType ) ? mainSubType : mainSubType.getSuperclass(),
165                 left,
166                 right
167         );
168     }
169
170     private static boolean isMethodVisibleTo(Method visibleMethod, Method otherMethod) {
171         return Modifier.isPublic( visibleMethod.getModifiers() ) || Modifier.isProtected( visibleMethod.getModifiers() )
172                 || visibleMethod.getDeclaringClass().getPackage().equals( otherMethod.getDeclaringClass().getPackage() );
173     }
174
175     public static String getSimpleName(Executable executable) {
176         return executable instanceof Constructor ? executable.getDeclaringClass().getSimpleName() : executable.getName();
177     }
178
179     public static Signature getSignature(Executable executable) {
180         return getSignature( getSimpleName( executable ), executable.getParameterTypes() );
181     }
182
183     public static Signature getSignature(String name, Class<?>[] parameterTypes) {
184         return new Signature( name, parameterTypes );
185     }
186
187     /**
188      * Returns a string representation of an executable with the given name and parameter types in the form
189      * {@code <name>(<parameterType 0> ...  <parameterType n>)}, e.g. for logging purposes.
190      *
191      * @param name the name of the executable
192      * @param parameterTypes the types of the executable's parameters
193      *
194      * @return A string representation of the given executable.
195      */

196     public static String getExecutableAsString(String name, Class<?>... parameterTypes) {
197         StringBuilder signature = new StringBuilder( name.length() + 2 + parameterTypes.length * 25 );
198         signature.append( name ).append( '(' );
199         boolean separator = false;
200         for ( Class<?> parameterType : parameterTypes ) {
201             if ( separator ) {
202                 signature.append( ", " );
203             }
204             else {
205                 separator = true;
206             }
207             signature.append( parameterType.getSimpleName() );
208         }
209         signature.append( ')' );
210         return signature.toString();
211     }
212
213     public static ElementType getElementType(Executable executable) {
214         return executable instanceof Constructor ? ElementType.CONSTRUCTOR : ElementType.METHOD;
215     }
216
217     /**
218      * Whether the parameters of the two given instance methods resolve to the same types or not. Takes type parameters into account.
219      *
220      * @param subTypeMethod a method on a supertype
221      * @param superTypeMethod a method on a subtype
222      *
223      * @return {@code trueif the parameters of the two methods resolve to the same types, {@code false otherwise}.
224      */

225     private boolean instanceMethodParametersResolveToSameTypes(Method subTypeMethod, Method superTypeMethod) {
226         return instanceMethodParametersResolveToSameTypes( subTypeMethod.getDeclaringClass(), subTypeMethod, superTypeMethod );
227     }
228
229     private boolean instanceMethodParametersResolveToSameTypes(Class<?> mainSubType, Method left, Method right) {
230         if ( left.getParameterTypes().length == 0 ) {
231             return true;
232         }
233
234         ResolvedType resolvedSubType = typeResolver.resolve( mainSubType );
235
236         MemberResolver memberResolver = new MemberResolver( typeResolver );
237         memberResolver.setMethodFilter( new SimpleMethodFilter( left, right ) );
238         ResolvedTypeWithMembers typeWithMembers = memberResolver.resolve(
239                 resolvedSubType,
240                 null,
241                 null
242         );
243
244         // ClassMate itself doesn't require any special permissions, but it invokes reflection APIs which do.
245         // Wrapping the call into a privileged action to avoid that all calling code bases need to have the required
246         // permission
247         ResolvedMethod[] resolvedMethods = run( GetResolvedMemberMethods.action( typeWithMembers ) );
248
249         // The ClassMate doc says that overridden methods are flattened to one
250         // resolved method. But that is the case only for methods without any
251         // generic parameters.
252         if ( resolvedMethods.length == 1 ) {
253             return true;
254         }
255
256         // For methods with generic parameters I have to compare the argument
257         // types (which are resolved) of the two filtered member methods.
258         try {
259             for ( int i = 0; i < resolvedMethods[0].getArgumentCount(); i++ ) {
260                 if ( !resolvedMethods[0].getArgumentType( i )
261                         .equals( resolvedMethods[1].getArgumentType( i ) ) ) {
262                     return false;
263                 }
264             }
265         }
266         // Putting this in as a safe guard for HV-861. In case the issue occurs again we will have some
267         // better information
268         catch (ArrayIndexOutOfBoundsException e) {
269             LOG.debug(
270                     "Error in ExecutableHelper#instanceMethodParametersResolveToSameTypes comparing "
271                             + left
272                             + " with "
273                             + right
274             );
275         }
276
277         return true;
278     }
279
280     /**
281      * Runs the given privileged action, using a privileged block if required.
282      * <p>
283      * <b>NOTE:</b> This must never be changed into a publicly available method to avoid execution of arbitrary
284      * privileged actions within HV's protection domain.
285      */

286     private <T> T run(PrivilegedAction<T> action) {
287         return System.getSecurityManager() != null ? AccessController.doPrivileged( action ) : action.run();
288     }
289
290     /**
291      * A filter implementation filtering methods matching given methods.
292      *
293      * @author Gunnar Morling
294      */

295     private static class SimpleMethodFilter implements Filter<RawMethod> {
296         private final Method method1;
297         private final Method method2;
298
299         private SimpleMethodFilter(Method method1, Method method2) {
300             this.method1 = method1;
301             this.method2 = method2;
302         }
303
304         @Override
305         public boolean include(RawMethod element) {
306             return element.getRawMember().equals( method1 ) || element.getRawMember()
307                     .equals( method2 );
308         }
309     }
310 }
311