1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 package org.modeshape.common.util;
25
26 import java.lang.reflect.Array;
27 import java.lang.reflect.InvocationTargetException;
28 import java.lang.reflect.Method;
29 import java.util.ArrayList;
30 import java.util.Collections;
31 import java.util.HashMap;
32 import java.util.HashSet;
33 import java.util.Iterator;
34 import java.util.LinkedList;
35 import java.util.List;
36 import java.util.Map;
37 import java.util.Set;
38 import java.util.regex.Pattern;
39 import net.jcip.annotations.Immutable;
40
41
42
43
44 @Immutable
45 public class Reflection {
46
47
48
49
50
51
52
53
54 public static Class<?>[] buildArgumentClasses( Object... arguments ) {
55 if (arguments == null || arguments.length == 0) return EMPTY_CLASS_ARRAY;
56 Class<?>[] result = new Class<?>[arguments.length];
57 int i = 0;
58 for (Object argument : arguments) {
59 if (argument != null) {
60 result[i] = argument.getClass();
61 } else {
62 result[i] = null;
63 }
64 }
65 return result;
66 }
67
68
69
70
71
72
73
74
75 public static List<Class<?>> buildArgumentClassList( Object... arguments ) {
76 if (arguments == null || arguments.length == 0) return Collections.emptyList();
77 List<Class<?>> result = new ArrayList<Class<?>>(arguments.length);
78 for (Object argument : arguments) {
79 if (argument != null) {
80 result.add(argument.getClass());
81 } else {
82 result.add(null);
83 }
84 }
85 return result;
86 }
87
88
89
90
91
92
93
94
95 public static List<Class<?>> convertArgumentClassesToPrimitives( Class<?>... arguments ) {
96 if (arguments == null || arguments.length == 0) return Collections.emptyList();
97 List<Class<?>> result = new ArrayList<Class<?>>(arguments.length);
98 for (Class<?> clazz : arguments) {
99 if (clazz == Boolean.class) clazz = Boolean.TYPE;
100 else if (clazz == Character.class) clazz = Character.TYPE;
101 else if (clazz == Byte.class) clazz = Byte.TYPE;
102 else if (clazz == Short.class) clazz = Short.TYPE;
103 else if (clazz == Integer.class) clazz = Integer.TYPE;
104 else if (clazz == Long.class) clazz = Long.TYPE;
105 else if (clazz == Float.class) clazz = Float.TYPE;
106 else if (clazz == Double.class) clazz = Double.TYPE;
107 else if (clazz == Void.class) clazz = Void.TYPE;
108 result.add(clazz);
109 }
110
111 return result;
112 }
113
114
115
116
117
118
119
120
121 public static String getClassName( final Class<?> clazz ) {
122 final String fullName = clazz.getName();
123 final int fullNameLength = fullName.length();
124
125
126 int numArrayDimensions = 0;
127 while (numArrayDimensions < fullNameLength) {
128 final char c = fullName.charAt(numArrayDimensions);
129 if (c != '[') {
130 String name = null;
131
132 switch (c) {
133 case 'L': {
134 name = fullName.subSequence(numArrayDimensions + 1, fullNameLength).toString();
135 break;
136 }
137 case 'B': {
138 name = "byte";
139 break;
140 }
141 case 'C': {
142 name = "char";
143 break;
144 }
145 case 'D': {
146 name = "double";
147 break;
148 }
149 case 'F': {
150 name = "float";
151 break;
152 }
153 case 'I': {
154 name = "int";
155 break;
156 }
157 case 'J': {
158 name = "long";
159 break;
160 }
161 case 'S': {
162 name = "short";
163 break;
164 }
165 case 'Z': {
166 name = "boolean";
167 break;
168 }
169 case 'V': {
170 name = "void";
171 break;
172 }
173 default: {
174 name = fullName.subSequence(numArrayDimensions, fullNameLength).toString();
175 }
176 }
177 if (numArrayDimensions == 0) {
178
179 return name;
180 }
181
182 if (numArrayDimensions < BRACKETS_PAIR.length) {
183 name = name + BRACKETS_PAIR[numArrayDimensions];
184 } else {
185 for (int i = 0; i < numArrayDimensions; i++) {
186 name = name + BRACKETS_PAIR[1];
187 }
188 }
189 return name;
190 }
191 ++numArrayDimensions;
192 }
193
194 return fullName;
195 }
196
197 private static final Class<?>[] EMPTY_CLASS_ARRAY = new Class[] {};
198 private static final String[] BRACKETS_PAIR = new String[] {"", "[]", "[][]", "[][][]", "[][][][]", "[][][][][]"};
199
200 private final Class<?> targetClass;
201 private Map<String, LinkedList<Method>> methodMap = null;
202
203
204
205
206
207
208
209
210 public Reflection( Class<?> targetClass ) {
211 CheckArg.isNotNull(targetClass, "targetClass");
212 this.targetClass = targetClass;
213 }
214
215
216
217
218
219
220 public Class<?> getTargetClass() {
221 return this.targetClass;
222 }
223
224
225
226
227
228
229
230
231 public Method[] findMethods( String methodName,
232 boolean caseSensitive ) {
233 Pattern pattern = caseSensitive ? Pattern.compile(methodName) : Pattern.compile(methodName, Pattern.CASE_INSENSITIVE);
234 return findMethods(pattern);
235 }
236
237
238
239
240
241
242
243 public Method[] findMethods( Pattern methodNamePattern ) {
244 final Method[] allMethods = this.targetClass.getMethods();
245 final List<Method> result = new ArrayList<Method>();
246 for (int i = 0; i < allMethods.length; i++) {
247 final Method m = allMethods[i];
248 if (methodNamePattern.matcher(m.getName()).matches()) {
249 result.add(m);
250 }
251 }
252 return result.toArray(new Method[result.size()]);
253 }
254
255
256
257
258
259
260
261 public Method[] findGetterMethods() {
262 final Method[] allMethods = this.targetClass.getMethods();
263 final List<Method> result = new ArrayList<Method>();
264 for (int i = 0; i < allMethods.length; i++) {
265 final Method m = allMethods[i];
266 int numParams = m.getParameterTypes().length;
267 if (numParams != 0) continue;
268 String name = m.getName();
269 if (name.equals("getClass")) continue;
270 if (m.getReturnType() == Void.TYPE) continue;
271 if (name.startsWith("get") || name.startsWith("is")) {
272 result.add(m);
273 }
274 }
275 return result.toArray(new Method[result.size()]);
276 }
277
278
279
280
281
282
283
284 public String[] findGetterPropertyNames() {
285 final Method[] getters = findGetterMethods();
286 final List<String> result = new ArrayList<String>();
287 for (int i = 0; i < getters.length; i++) {
288 final Method m = getters[i];
289 String name = m.getName();
290 if (name.startsWith("get") && name.length() > 3) {
291 result.add(name.substring(3));
292 } else if (name.startsWith("is") && name.length() > 2) {
293 result.add(name.substring(2));
294 }
295 }
296 return result.toArray(new String[result.size()]);
297 }
298
299
300
301
302
303
304
305
306 public Method findFirstMethod( String methodName,
307 boolean caseSensitive ) {
308 Pattern pattern = caseSensitive ? Pattern.compile(methodName) : Pattern.compile(methodName, Pattern.CASE_INSENSITIVE);
309 return findFirstMethod(pattern);
310 }
311
312
313
314
315
316
317
318 public Method findFirstMethod( Pattern methodNamePattern ) {
319 final Method[] allMethods = this.targetClass.getMethods();
320 for (int i = 0; i < allMethods.length; i++) {
321 final Method m = allMethods[i];
322 if (methodNamePattern.matcher(m.getName()).matches()) {
323 return m;
324 }
325 }
326 return null;
327 }
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348 public Object invokeBestMethodOnTarget( String[] methodNames,
349 Object target,
350 Object... arguments )
351 throws NoSuchMethodException, SecurityException, IllegalArgumentException, IllegalAccessException,
352 InvocationTargetException {
353 Class<?>[] argumentClasses = buildArgumentClasses(arguments);
354 int remaining = methodNames.length;
355 Object result = null;
356 for (String methodName : methodNames) {
357 --remaining;
358 try {
359 Method method = findBestMethodWithSignature(methodName, argumentClasses);
360 result = method.invoke(target, arguments);
361 break;
362 } catch (NoSuchMethodException e) {
363 if (remaining == 0) throw e;
364 }
365 }
366 return result;
367 }
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387 public Object invokeSetterMethodOnTarget( String javaPropertyName,
388 Object target,
389 Object argument )
390 throws NoSuchMethodException, SecurityException, IllegalArgumentException, IllegalAccessException,
391 InvocationTargetException {
392 String[] methodNamesArray = findMethodNames("set" + javaPropertyName);
393 try {
394 return invokeBestMethodOnTarget(methodNamesArray, target, argument);
395 } catch (NoSuchMethodException e) {
396
397 if (argument instanceof Object[]) {
398 Object[] arrayArg = (Object[])argument;
399 for (Object arrayValue : arrayArg) {
400 if (arrayValue == null) continue;
401 Class<?> arrayValueType = arrayValue.getClass();
402
403 Object typedArray = Array.newInstance(arrayValueType, arrayArg.length);
404 Object[] newArray = (Object[])typedArray;
405 for (int i = 0; i != arrayArg.length; ++i) {
406 newArray[i] = arrayArg[i];
407 }
408
409 try {
410 return invokeBestMethodOnTarget(methodNamesArray, target, typedArray);
411 } catch (NoSuchMethodException e2) {
412
413 throw e;
414 }
415 }
416 }
417 throw e;
418 }
419 }
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434 public Object invokeGetterMethodOnTarget( String javaPropertyName,
435 Object target )
436 throws NoSuchMethodException, SecurityException, IllegalArgumentException, IllegalAccessException,
437 InvocationTargetException {
438 String[] methodNamesArray = findMethodNames("get" + javaPropertyName);
439 return invokeBestMethodOnTarget(methodNamesArray, target);
440 }
441
442 protected String[] findMethodNames( String methodName ) {
443 Method[] methods = findMethods(methodName, false);
444 Set<String> methodNames = new HashSet<String>();
445 for (Method method : methods) {
446 String actualMethodName = method.getName();
447 methodNames.add(actualMethodName);
448 }
449 return methodNames.toArray(new String[methodNames.size()]);
450 }
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467 public Method findBestMethodOnTarget( String methodName,
468 Object... arguments ) throws NoSuchMethodException, SecurityException {
469 Class<?>[] argumentClasses = buildArgumentClasses(arguments);
470 return findBestMethodWithSignature(methodName, argumentClasses);
471 }
472
473
474
475
476
477
478
479
480
481
482
483
484
485 public Method findBestMethodWithSignature( String methodName,
486 Class<?>... argumentsClasses ) throws NoSuchMethodException, SecurityException {
487
488
489 Method result;
490
491
492
493
494 Class<?>[] classArgs = null;
495 try {
496 classArgs = argumentsClasses != null ? argumentsClasses : new Class[] {};
497 result = this.targetClass.getMethod(methodName, classArgs);
498 return result;
499 } catch (NoSuchMethodException e) {
500
501 }
502
503
504
505
506 List<Class<?>> argumentsClassList = convertArgumentClassesToPrimitives(argumentsClasses);
507 try {
508 classArgs = argumentsClassList.toArray(new Class[argumentsClassList.size()]);
509 result = this.targetClass.getMethod(methodName, classArgs);
510 return result;
511 } catch (NoSuchMethodException e) {
512
513 }
514
515
516
517
518
519
520
521
522
523 Method method;
524 LinkedList<Method> methodsWithSameName;
525 if (this.methodMap == null) {
526
527 this.methodMap = new HashMap<String, LinkedList<Method>>();
528 Method[] methods = this.targetClass.getMethods();
529 for (int i = 0; i != methods.length; ++i) {
530 method = methods[i];
531 methodsWithSameName = this.methodMap.get(method.getName());
532 if (methodsWithSameName == null) {
533 methodsWithSameName = new LinkedList<Method>();
534 this.methodMap.put(method.getName(), methodsWithSameName);
535 }
536 methodsWithSameName.addFirst(method);
537 }
538 }
539
540
541
542
543
544
545 for (int j = 0; j != 2; ++j) {
546 methodsWithSameName = this.methodMap.get(methodName);
547 if (methodsWithSameName == null) {
548 throw new NoSuchMethodException(methodName);
549 }
550 Iterator<Method> iter = methodsWithSameName.iterator();
551 Class<?>[] args;
552 Class<?> clazz;
553 boolean allMatch;
554 while (iter.hasNext()) {
555 method = iter.next();
556 args = method.getParameterTypes();
557 if (args.length == argumentsClassList.size()) {
558 allMatch = true;
559 for (int i = 0; i < args.length; ++i) {
560 clazz = argumentsClassList.get(i);
561 if (clazz != null) {
562 Class<?> argClass = args[i];
563 if (argClass.isAssignableFrom(clazz)) {
564
565 } else if (argClass.isArray() && clazz.isArray()
566 && argClass.getComponentType().isAssignableFrom(clazz.getComponentType())) {
567
568 } else {
569 allMatch = false;
570 i = args.length;
571 }
572 } else {
573
574 if (args[i].isPrimitive()) {
575 allMatch = false;
576 i = args.length;
577 }
578 }
579 }
580 if (allMatch) {
581 return method;
582 }
583 }
584 }
585 }
586
587 throw new NoSuchMethodException(methodName);
588 }
589
590 }