1 package org.codehaus.plexus.util.reflection;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 import java.lang.reflect.Constructor;
20 import java.lang.reflect.Field;
21 import java.lang.reflect.InvocationTargetException;
22 import java.lang.reflect.Method;
23
24 import java.util.HashMap;
25 import java.util.Map;
26
27
28
29
30
31
32
33 public final class Reflector
34 {
35 private static final String CONSTRUCTOR_METHOD_NAME = "$$CONSTRUCTOR$$";
36
37 private static final String GET_INSTANCE_METHOD_NAME = "getInstance";
38
39 private Map<String, Map<String, Map<String, Method>>> classMaps =
40 new HashMap<String, Map<String, Map<String, Method>>>();
41
42
43 public Reflector()
44 {
45 }
46
47
48
49
50
51
52
53
54
55
56
57 @SuppressWarnings( { "UnusedDeclaration" } )
58 public <T> T newInstance( Class<T> theClass, Object[] params )
59 throws ReflectorException
60 {
61 if ( params == null )
62 {
63 params = new Object[0];
64 }
65
66 Class[] paramTypes = new Class[params.length];
67
68 for ( int i = 0, len = params.length; i < len; i++ )
69 {
70 paramTypes[i] = params[i].getClass();
71 }
72
73 try
74 {
75 Constructor<T> con = getConstructor( theClass, paramTypes );
76
77 if ( con == null )
78 {
79 StringBuilder buffer = new StringBuilder();
80
81 buffer.append( "Constructor not found for class: " );
82 buffer.append( theClass.getName() );
83 buffer.append( " with specified or ancestor parameter classes: " );
84
85 for ( Class paramType : paramTypes )
86 {
87 buffer.append( paramType.getName() );
88 buffer.append( ',' );
89 }
90
91 buffer.setLength( buffer.length() - 1 );
92
93 throw new ReflectorException( buffer.toString() );
94 }
95
96 return con.newInstance( params );
97 }
98 catch ( InstantiationException | InvocationTargetException | IllegalAccessException ex )
99 {
100 throw new ReflectorException( ex );
101 }
102 }
103
104
105
106
107
108
109
110
111
112
113
114 @SuppressWarnings( { "UnusedDeclaration" } )
115 public <T> T getSingleton( Class<T> theClass, Object[] initParams )
116 throws ReflectorException
117 {
118 Class[] paramTypes = new Class[initParams.length];
119
120 for ( int i = 0, len = initParams.length; i < len; i++ )
121 {
122 paramTypes[i] = initParams[i].getClass();
123 }
124
125 try
126 {
127 Method method = getMethod( theClass, GET_INSTANCE_METHOD_NAME, paramTypes );
128
129
130 return (T) method.invoke( null, initParams );
131 }
132 catch ( InvocationTargetException | IllegalAccessException ex )
133 {
134 throw new ReflectorException( ex );
135 }
136 }
137
138
139
140
141
142
143
144
145
146
147 @SuppressWarnings( { "UnusedDeclaration" } )
148 public Object invoke( Object target, String methodName, Object[] params )
149 throws ReflectorException
150 {
151 if ( params == null )
152 {
153 params = new Object[0];
154 }
155
156 Class[] paramTypes = new Class[params.length];
157
158 for ( int i = 0, len = params.length; i < len; i++ )
159 {
160 paramTypes[i] = params[i].getClass();
161 }
162
163 try
164 {
165 Method method = getMethod( target.getClass(), methodName, paramTypes );
166
167 if ( method == null )
168 {
169 StringBuilder buffer = new StringBuilder();
170
171 buffer.append( "Singleton-producing method named '" ).append( methodName ).append( "' not found with specified parameter classes: " );
172
173 for ( Class paramType : paramTypes )
174 {
175 buffer.append( paramType.getName() );
176 buffer.append( ',' );
177 }
178
179 buffer.setLength( buffer.length() - 1 );
180
181 throw new ReflectorException( buffer.toString() );
182 }
183
184 return method.invoke( target, params );
185 }
186 catch ( InvocationTargetException | IllegalAccessException ex )
187 {
188 throw new ReflectorException( ex );
189 }
190 }
191
192 @SuppressWarnings( { "UnusedDeclaration" } )
193 public Object getStaticField( Class targetClass, String fieldName )
194 throws ReflectorException
195 {
196 try
197 {
198 Field field = targetClass.getField( fieldName );
199
200 return field.get( null );
201 }
202 catch ( SecurityException | NoSuchFieldException | IllegalArgumentException | IllegalAccessException e )
203 {
204 throw new ReflectorException( e );
205 }
206 }
207
208 @SuppressWarnings( { "UnusedDeclaration" } )
209 public Object getField( Object target, String fieldName )
210 throws ReflectorException
211 {
212 return getField( target, fieldName, false );
213 }
214
215 public Object getField( Object target, String fieldName, boolean breakAccessibility )
216 throws ReflectorException
217 {
218 Class targetClass = target.getClass();
219 while ( targetClass != null )
220 {
221 try
222 {
223 Field field = targetClass.getDeclaredField( fieldName );
224
225 boolean accessibilityBroken = false;
226 if ( !field.isAccessible() && breakAccessibility )
227 {
228 field.setAccessible( true );
229 accessibilityBroken = true;
230 }
231
232 Object result = field.get( target );
233
234 if ( accessibilityBroken )
235 {
236 field.setAccessible( false );
237 }
238
239 return result;
240 }
241 catch ( SecurityException e )
242 {
243 throw new ReflectorException( e );
244 }
245 catch ( NoSuchFieldException e )
246 {
247 if ( targetClass == Object.class )
248 throw new ReflectorException( e );
249 targetClass = targetClass.getSuperclass();
250 }
251 catch ( IllegalAccessException e )
252 {
253 throw new ReflectorException( e );
254 }
255 }
256
257 return null;
258 }
259
260
261
262
263
264
265
266
267
268
269 @SuppressWarnings( { "UnusedDeclaration" } )
270 public Object invokeStatic( Class targetClass, String methodName, Object[] params )
271 throws ReflectorException
272 {
273 if ( params == null )
274 {
275 params = new Object[0];
276 }
277
278 Class[] paramTypes = new Class[params.length];
279
280 for ( int i = 0, len = params.length; i < len; i++ )
281 {
282 paramTypes[i] = params[i].getClass();
283 }
284
285 try
286 {
287 Method method = getMethod( targetClass, methodName, paramTypes );
288
289 if ( method == null )
290 {
291 StringBuilder buffer = new StringBuilder();
292
293 buffer.append( "Singleton-producing method named \'" ).append( methodName ).append( "\' not found with specified parameter classes: " );
294
295 for ( Class paramType : paramTypes )
296 {
297 buffer.append( paramType.getName() );
298 buffer.append( ',' );
299 }
300
301 buffer.setLength( buffer.length() - 1 );
302
303 throw new ReflectorException( buffer.toString() );
304 }
305
306 return method.invoke( null, params );
307 }
308 catch ( InvocationTargetException | IllegalAccessException ex )
309 {
310 throw new ReflectorException( ex );
311 }
312 }
313
314
315
316
317
318
319
320
321
322
323 public <T> Constructor<T> getConstructor( Class<T> targetClass, Class[] params )
324 throws ReflectorException
325 {
326 Map<String, Constructor<T>> constructorMap = getConstructorMap( targetClass );
327
328 StringBuilder key = new StringBuilder( 200 );
329
330 key.append( "(" );
331
332 for ( Class param : params )
333 {
334 key.append( param.getName() );
335 key.append( "," );
336 }
337
338 if ( params.length > 0 )
339 {
340 key.setLength( key.length() - 1 );
341 }
342
343 key.append( ")" );
344
345 Constructor<T> constructor;
346
347 String paramKey = key.toString();
348
349 synchronized ( paramKey.intern() )
350 {
351 constructor = constructorMap.get( paramKey );
352
353 if ( constructor == null )
354 {
355 @SuppressWarnings( { "unchecked" } )
356 Constructor<T>[] cands = (Constructor<T>[]) targetClass.getConstructors();
357
358 for ( Constructor<T> cand : cands )
359 {
360 Class[] types = cand.getParameterTypes();
361
362 if ( params.length != types.length )
363 {
364 continue;
365 }
366
367 for ( int j = 0, len2 = params.length; j < len2; j++ )
368 {
369 if ( !types[j].isAssignableFrom( params[j] ) )
370 {
371 continue;
372 }
373 }
374
375
376 constructor = cand;
377 constructorMap.put( paramKey, constructor );
378 }
379 }
380 }
381
382 if ( constructor == null )
383 {
384 throw new ReflectorException( "Error retrieving constructor object for: " + targetClass.getName()
385 + paramKey );
386 }
387
388 return constructor;
389 }
390
391 public Object getObjectProperty( Object target, String propertyName )
392 throws ReflectorException
393 {
394 Object returnValue;
395
396 if ( propertyName == null || propertyName.trim().length() < 1 )
397 {
398 throw new ReflectorException( "Cannot retrieve value for empty property." );
399 }
400
401 String beanAccessor = "get" + Character.toUpperCase( propertyName.charAt( 0 ) );
402 if ( propertyName.trim().length() > 1 )
403 {
404 beanAccessor += propertyName.substring( 1 ).trim();
405 }
406
407 Class targetClass = target.getClass();
408 Class[] emptyParams = {};
409
410 Method method = _getMethod( targetClass, beanAccessor, emptyParams );
411 if ( method == null )
412 {
413 method = _getMethod( targetClass, propertyName, emptyParams );
414 }
415 if ( method != null )
416 {
417 try
418 {
419 returnValue = method.invoke( target, new Object[] {} );
420 }
421 catch ( IllegalAccessException e )
422 {
423 throw new ReflectorException( "Error retrieving property \'" + propertyName + "\' from \'" + targetClass
424 + "\'", e );
425 }
426 catch ( InvocationTargetException e )
427 {
428 throw new ReflectorException( "Error retrieving property \'" + propertyName + "\' from \'" + targetClass
429 + "\'", e );
430 }
431 }
432
433 if ( method != null )
434 {
435 try
436 {
437 returnValue = method.invoke( target, new Object[] {} );
438 }
439 catch ( IllegalAccessException e )
440 {
441 throw new ReflectorException( "Error retrieving property \'" + propertyName + "\' from \'" + targetClass
442 + "\'", e );
443 }
444 catch ( InvocationTargetException e )
445 {
446 throw new ReflectorException( "Error retrieving property \'" + propertyName + "\' from \'" + targetClass
447 + "\'", e );
448 }
449 }
450 else
451 {
452 returnValue = getField( target, propertyName, true );
453 if ( returnValue == null )
454 {
455
456 throw new ReflectorException( "Neither method: \'" + propertyName + "\' nor bean accessor: \'"
457 + beanAccessor + "\' can be found for class: \'" + targetClass + "\', and retrieval of field: \'"
458 + propertyName + "\' returned null as value." );
459 }
460 }
461
462 return returnValue;
463 }
464
465
466
467
468
469
470
471
472
473
474 public Method getMethod( Class targetClass, String methodName, Class[] params )
475 throws ReflectorException
476 {
477 Method method = _getMethod( targetClass, methodName, params );
478
479 if ( method == null )
480 {
481 throw new ReflectorException( "Method: \'" + methodName + "\' not found in class: \'" + targetClass
482 + "\'" );
483 }
484
485 return method;
486 }
487
488 private Method _getMethod( Class targetClass, String methodName, Class[] params )
489 throws ReflectorException
490 {
491 Map<String, Method> methodMap = (Map<String, Method>) getMethodMap( targetClass, methodName );
492
493 StringBuilder key = new StringBuilder( 200 );
494
495 key.append( "(" );
496
497 for ( Class param : params )
498 {
499 key.append( param.getName() );
500 key.append( "," );
501 }
502
503 key.append( ")" );
504
505 Method method;
506
507 String paramKey = key.toString();
508
509 synchronized ( paramKey.intern() )
510 {
511 method = methodMap.get( paramKey );
512
513 if ( method == null )
514 {
515 Method[] cands = targetClass.getMethods();
516
517 for ( Method cand : cands )
518 {
519 String name = cand.getName();
520
521 if ( !methodName.equals( name ) )
522 {
523 continue;
524 }
525
526 Class[] types = cand.getParameterTypes();
527
528 if ( params.length != types.length )
529 {
530 continue;
531 }
532
533 for ( int j = 0, len2 = params.length; j < len2; j++ )
534 {
535 if ( !types[j].isAssignableFrom( params[j] ) )
536 {
537 continue;
538 }
539 }
540
541
542 method = cand;
543 methodMap.put( paramKey, method );
544 }
545 }
546 }
547
548 return method;
549 }
550
551
552
553
554
555
556
557
558 private <T> Map<String, Constructor<T>> getConstructorMap( Class<T> theClass )
559 throws ReflectorException
560 {
561 return (Map<String, Constructor<T>>) getMethodMap( theClass, CONSTRUCTOR_METHOD_NAME );
562 }
563
564
565
566
567
568
569
570
571
572 private Map<String, ?> getMethodMap( Class theClass, String methodName )
573 throws ReflectorException
574 {
575 Map<String, Method> methodMap;
576
577 if ( theClass == null )
578 {
579 return null;
580 }
581
582 String className = theClass.getName();
583
584 synchronized ( className.intern() )
585 {
586 Map<String, Map<String, Method>> classMethods = classMaps.get( className );
587
588 if ( classMethods == null )
589 {
590 classMethods = new HashMap<>();
591 methodMap = new HashMap<>();
592 classMethods.put( methodName, methodMap );
593 classMaps.put( className, classMethods );
594 }
595 else
596 {
597 String key = className + "::" + methodName;
598
599 synchronized ( key.intern() )
600 {
601 methodMap = classMethods.get( methodName );
602
603 if ( methodMap == null )
604 {
605 methodMap = new HashMap<>();
606 classMethods.put( methodName, methodMap );
607 }
608 }
609 }
610 }
611
612 return methodMap;
613 }
614 }