1 package org.apache.commons.beanutils2;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import static java.lang.System.getSecurityManager;
23 import static java.lang.reflect.Modifier.isPublic;
24 import static java.security.AccessController.doPrivileged;
25 import static org.apache.commons.beanutils2.Assertions.checkArgument;
26 import static org.apache.commons.beanutils2.TypeUtils.checkTypesCompatible;
27 import static org.apache.commons.beanutils2.TypeUtils.getPrimitiveWrapper;
28 import static org.apache.commons.beanutils2.TypeUtils.isAssignmentCompatible;
29
30 import java.lang.reflect.AccessibleObject;
31 import java.lang.reflect.Constructor;
32 import java.lang.reflect.Member;
33 import java.lang.reflect.Method;
34 import java.security.PrivilegedAction;
35 import java.util.Arrays;
36 import java.util.Map;
37 import java.util.WeakHashMap;
38 import java.util.concurrent.locks.Lock;
39 import java.util.concurrent.locks.ReentrantLock;
40
41 abstract class AccessibleObjectsRegistry<AO extends AccessibleObject & Member>
42 {
43
44
45 private static final float NON_FOUND_COST = 1.5f;
46
47
48 private static final float SUPERCLASS_COST = 1.0f;
49
50
51 private static final float INTERFACE_COST = 0.25f;
52
53
54 private static final float WRAPPER_COST = 0.25f;
55
56 private static final AccessibleObjectsRegistry<Constructor<?>> CONSTRUCTORS_REGISTRY = new ConstructorsRegistry();
57
58 private static final AccessibleObjectsRegistry<Method> METHODS_REGISTRY = new MethodsRegistry();
59
60 public static AccessibleObjectsRegistry<Constructor<?>> getConstructorsRegistry()
61 {
62 return CONSTRUCTORS_REGISTRY;
63 }
64
65 public static AccessibleObjectsRegistry<Method> getMethodsRegistry()
66 {
67 return METHODS_REGISTRY;
68 }
69
70 private final Map<AccessibleObjectDescriptor, AO> cache = new WeakHashMap<AccessibleObjectDescriptor, AO>();
71
72 private AccessibleObjectsRegistry()
73 {
74
75 }
76
77 public final AO get( boolean exact, Class<?> type, Class<?>... parameterTypes )
78 {
79 return get( exact, type, null, parameterTypes );
80 }
81
82 public final AO get( boolean exact, Class<?> type, String name, Class<?>... parameterTypes )
83 {
84 AccessibleObjectDescriptor key = new AccessibleObjectDescriptor( exact, type, name, parameterTypes );
85
86 final Lock lock = new ReentrantLock();
87 lock.lock();
88 try
89 {
90 AO accessibleObject = cache.get( key );
91 if ( accessibleObject != null )
92 {
93 return accessibleObject;
94 }
95
96
97
98 try
99 {
100 accessibleObject = resolveDirectly( type, name, parameterTypes );
101 if ( exact || accessibleObject != null )
102 {
103 return makeAccessible( accessibleObject );
104 }
105 }
106 catch ( NoSuchMethodException e )
107 {
108
109 if ( exact )
110 {
111 return null;
112 }
113 }
114
115
116
117 int paramSize = parameterTypes.length;
118 AO bestMatch = null;
119 AO[] accessibleObjectsArray = getAccessibleObjectsArray( type );
120 float bestMatchCost = Float.MAX_VALUE;
121 float myCost = Float.MAX_VALUE;
122 for ( int i = 0, size = accessibleObjectsArray.length; i < size; i++ )
123 {
124 if ( matches( accessibleObjectsArray[i], name ) )
125 {
126
127 Class<?>[] methodsParams = getParameterTypes( accessibleObjectsArray[i] );
128 int methodParamSize = methodsParams.length;
129 if ( methodParamSize == paramSize )
130 {
131 boolean match = checkTypesCompatible( methodsParams, parameterTypes );
132
133 if ( match )
134 {
135
136 AO current = resolveAccessible( type, accessibleObjectsArray[i] );
137 if ( current != null )
138 {
139
140 myCost = getTotalTransformationCost( parameterTypes, getParameterTypes( current ) );
141 if ( myCost < bestMatchCost )
142 {
143 bestMatch = current;
144 bestMatchCost = myCost;
145 }
146 }
147 }
148 }
149 }
150 }
151
152 if ( bestMatch != null )
153 {
154 bestMatch = makeAccessible( bestMatch );
155 }
156 cache.put( key, bestMatch );
157 return bestMatch;
158 }
159 finally
160 {
161 lock.unlock();
162 }
163 }
164
165 protected abstract AO resolveDirectly( Class<?> type, String name, Class<?>... parameterTypes )
166 throws NoSuchMethodException;
167
168 protected abstract AO[] getAccessibleObjectsArray( Class<?> type );
169
170 protected abstract boolean matches( AO accessibleObject, String name );
171
172 protected abstract Class<?>[] getParameterTypes( AO accessibleObject );
173
174 protected abstract AO resolveAccessible( Class<?> type, AO accessibleObject );
175
176
177
178
179
180
181
182
183 private static float getTotalTransformationCost( Class<?>[] srcArgs, Class<?>[] destArgs )
184 {
185 float totalCost = 0.0f;
186 for ( int i = 0; i < srcArgs.length; i++ )
187 {
188 Class<?> srcClass, destClass;
189 srcClass = srcArgs[i];
190 destClass = destArgs[i];
191 totalCost += getObjectTransformationCost( srcClass, destClass );
192 }
193 return totalCost;
194 }
195
196
197
198
199
200
201
202
203
204 private static float getObjectTransformationCost( Class<?> srcClass, Class<?> destClass )
205 {
206 float cost = 0.0f;
207 while ( srcClass != null && !destClass.equals( srcClass ) )
208 {
209 if ( destClass.isPrimitive() )
210 {
211 Class<?> destClassWrapperClazz = getPrimitiveWrapper( destClass );
212 if ( destClassWrapperClazz != null && destClassWrapperClazz.equals( srcClass ) )
213 {
214 cost += WRAPPER_COST;
215 break;
216 }
217 }
218 if ( destClass.isInterface() && isAssignmentCompatible( destClass, srcClass ) )
219 {
220
221
222
223
224 cost += INTERFACE_COST;
225 break;
226 }
227 cost += SUPERCLASS_COST;
228 srcClass = srcClass.getSuperclass();
229 }
230
231
232
233 if ( srcClass == null )
234 {
235 cost += NON_FOUND_COST;
236 }
237
238 return cost;
239 }
240
241 private AO makeAccessible( AO accessibleObject )
242 {
243 PrivilegedAction<AO> action = new MakeAccessiblePrivilegedAction<AO>( accessibleObject );
244 if ( getSecurityManager() != null )
245 {
246 return doPrivileged( action );
247 }
248 return action.run();
249 }
250
251
252
253
254 private static class ConstructorsRegistry extends AccessibleObjectsRegistry<Constructor<?>>
255 {
256
257 @Override
258 protected Constructor<?> resolveDirectly( Class<?> type, String name, Class<?>...parameterTypes )
259 throws NoSuchMethodException
260 {
261 return type.getConstructor( parameterTypes );
262 }
263
264 @Override
265 protected Constructor<?>[] getAccessibleObjectsArray( Class<?> type )
266 {
267 return type.getConstructors();
268 }
269
270 @Override
271 protected boolean matches( Constructor<?> accessibleObject, String name )
272 {
273 return true;
274 }
275
276 @Override
277 protected Class<?>[] getParameterTypes( Constructor<?> accessibleObject )
278 {
279 return accessibleObject.getParameterTypes();
280 }
281
282 @Override
283 protected Constructor<?> resolveAccessible( Class<?> type, Constructor<?> accessibleObject )
284 {
285 if ( accessibleObject == null )
286 {
287 return null;
288 }
289
290 if ( !isPublic( accessibleObject.getModifiers() ) )
291 {
292 return null;
293 }
294
295
296 Class<?> beanClass = accessibleObject.getDeclaringClass();
297 if ( isPublic( beanClass.getModifiers() ) )
298 {
299 return accessibleObject;
300 }
301
302
303 return null;
304 }
305
306 }
307
308
309
310
311 private static final class MethodsRegistry extends AccessibleObjectsRegistry<Method>
312 {
313
314 @Override
315 protected Method resolveDirectly( Class<?> type, String name, Class<?>...parameterTypes )
316 throws NoSuchMethodException
317 {
318 return type.getMethod( name, parameterTypes );
319 }
320
321 @Override
322 protected Method[] getAccessibleObjectsArray( Class<?> type )
323 {
324 return type.getMethods();
325 }
326
327 @Override
328 protected boolean matches( Method accessibleObject, String name )
329 {
330 return name.equals( accessibleObject.getName() );
331 }
332
333 @Override
334 protected Class<?>[] getParameterTypes( Method accessibleObject )
335 {
336 return accessibleObject.getParameterTypes();
337 }
338
339 @Override
340 protected Method resolveAccessible( Class<?> type, Method method )
341 {
342
343 if ( method == null )
344 {
345 return null;
346 }
347
348
349 if ( !isPublic( method.getModifiers() ) )
350 {
351 return null;
352 }
353
354 if ( type == null )
355 {
356 type = method.getDeclaringClass();
357 }
358 else
359 {
360 checkArgument( method.getDeclaringClass().isAssignableFrom( type ),
361 "%s is not assignable from ", type.getName(), method.getDeclaringClass().getName() );
362 }
363
364
365 if ( isPublic( type.getModifiers() ) )
366 {
367 return method;
368 }
369
370 String methodName = method.getName();
371 Class<?>[] parameterTypes = method.getParameterTypes();
372
373
374 method = getAccessibleMethodFromInterfaceNest( type, methodName, parameterTypes );
375
376
377 if ( method == null )
378 {
379 method = getAccessibleMethodFromSuperclass( type, methodName, parameterTypes );
380 }
381
382 return method;
383 }
384
385
386
387
388
389
390
391
392
393
394 private static Method getAccessibleMethodFromSuperclass( Class<?> clazz, String methodName,
395 Class<?>... parameterTypes )
396 {
397 Class<?> parentClazz = clazz.getSuperclass();
398 while ( parentClazz != null )
399 {
400 if ( isPublic( parentClazz.getModifiers() ) )
401 {
402 try
403 {
404 return parentClazz.getMethod( methodName, parameterTypes );
405 }
406 catch ( NoSuchMethodException e )
407 {
408
409 }
410 }
411 parentClazz = parentClazz.getSuperclass();
412 }
413 return null;
414 }
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430 private static Method getAccessibleMethodFromInterfaceNest( Class<?> clazz, String methodName,
431 Class<?>... parameterTypes )
432 {
433 Method method = null;
434
435
436 while ( clazz != null )
437 {
438
439 for ( Class<?> interfaceType : clazz.getInterfaces() )
440 {
441
442 if ( !isPublic( interfaceType.getModifiers() ) )
443 {
444 continue;
445 }
446
447
448 try
449 {
450 method = interfaceType.getDeclaredMethod( methodName, parameterTypes );
451 }
452 catch ( NoSuchMethodException e )
453 {
454
455
456
457 }
458 if ( method != null )
459 {
460 return method;
461 }
462
463
464 method = getAccessibleMethodFromInterfaceNest( interfaceType, methodName, parameterTypes );
465 if ( method != null )
466 {
467 return method;
468 }
469 }
470
471 clazz = clazz.getSuperclass();
472 }
473
474
475 return null;
476 }
477
478 }
479
480
481
482
483 private static final class AccessibleObjectDescriptor
484 {
485
486 private final Class<?> type;
487
488 private final String methodName;
489
490 private final Class<?>[] parameterTypes;
491
492 private final boolean exact;
493
494 private final int hashCode;
495
496
497
498
499
500
501
502
503
504 public AccessibleObjectDescriptor( boolean exact, Class<?> type, String methodName, Class<?>... parameterTypes )
505 {
506 final int prime = 31;
507 int hashCode = 1;
508
509 this.exact = exact;
510 hashCode = prime * hashCode + ( exact ? 1231 : 1237 );
511
512 this.type = type;
513 hashCode = prime * hashCode + ( ( type == null ) ? 0 : type.getName().hashCode() );
514
515 this.methodName = methodName;
516 hashCode = prime * hashCode + ( ( methodName == null ) ? 0 : methodName.hashCode() );
517
518 this.parameterTypes = parameterTypes;
519 hashCode = prime * hashCode + Arrays.hashCode( parameterTypes );
520
521 this.hashCode = hashCode;
522 }
523
524 @Override
525 public int hashCode()
526 {
527 return hashCode;
528 }
529
530 @Override
531 public boolean equals( Object obj )
532 {
533 if ( this == obj )
534 {
535 return true;
536 }
537 if ( obj == null )
538 {
539 return false;
540 }
541
542 AccessibleObjectDescriptor other = (AccessibleObjectDescriptor) obj;
543
544 if ( type != other.type )
545 {
546 return false;
547 }
548
549 if ( exact != other.exact )
550 {
551 return false;
552 }
553
554
555 if ( methodName == null )
556 {
557 if ( other.methodName != null )
558 {
559 return false;
560 }
561 }
562 else if ( !methodName.equals( other.methodName ) )
563 {
564 return false;
565 }
566
567 if ( !Arrays.equals( parameterTypes, other.parameterTypes ) )
568 {
569 return false;
570 }
571
572 return true;
573 }
574
575 }
576
577 private static class MakeAccessiblePrivilegedAction<T extends AccessibleObject>
578 implements PrivilegedAction<T>
579 {
580
581 private final T accessibleObject;
582
583
584
585
586 private MakeAccessiblePrivilegedAction( T accessibleObject )
587 {
588 this.accessibleObject = accessibleObject;
589 }
590
591
592
593
594 public T run()
595 {
596 try
597 {
598 accessibleObject.setAccessible( true );
599 }
600 catch ( SecurityException se )
601 {
602
603
604
605 }
606 return accessibleObject;
607 }
608
609 }
610
611 }