1
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
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
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
89
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
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
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
140 if ( Modifier.isStatic( right.getModifiers() ) || Modifier.isStatic( left.getModifiers() ) ) {
141 return false;
142 }
143
144
145
146 if ( left.isBridge() || right.isBridge() ) {
147 return false;
148 }
149
150
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
160
161
162
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
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
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
245
246
247 ResolvedMethod[] resolvedMethods = run( GetResolvedMemberMethods.action( typeWithMembers ) );
248
249
250
251
252 if ( resolvedMethods.length == 1 ) {
253 return true;
254 }
255
256
257
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
267
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
286 private <T> T run(PrivilegedAction<T> action) {
287 return System.getSecurityManager() != null ? AccessController.doPrivileged( action ) : action.run();
288 }
289
290
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