1
2 package eu.simuline.testhelpers;
3
4 import eu.simuline.util.BasicTypesCompatibilityChecker;
5
6 import java.lang.reflect.Modifier;
7 import java.lang.reflect.Constructor;
8 import java.lang.reflect.Method;
9 import java.lang.reflect.Field;
10 import java.lang.reflect.InvocationTargetException;
11
12 /**
13 * Provides access even to private fields, methods, constructors
14 * and inner classes, static or not, via reflection.
15 * This is necessary for white-box-tests.
16 * Note that class objects for instances <code>i</code>
17 * are given by <code>i.getClass()</code>
18 *(except for instances of primitive types)
19 * but also if no instance is available,
20 * the class object is given by
21 * <ul>
22 * <li>
23 * <code><classname>.class</code> for all types even the primitive ones
24 * and also for arrays
25 * (<code>int.class</code> and <code>int[].class</code> are valid)
26 * but not for hidden inner classes, e.g. <code>private</code> ones,
27 * <li>
28 * <code>Boolean.TYPE</code>,
29 * <code>Character.TYPE</code> and so on for the primitive types
30 * (which is superfluous because <code>boolean.class</code>
31 * works as well as <code>Boolean.TYPE</code>,
32 * except for <code>java.lang.Void.TYPE</code> which I never used so far),
33 * <li>
34 * methods {@link #getInnerClass(Class,String)} and
35 * {@link #getInnerClass(Class,String[])} for hidden inner classes.
36 * Note that formally this even works for anonymous classes
37 * but since names of anonymous classes may change
38 * if another anonymous inner class is inserted,
39 * this feature should be used with caution.
40 * I personally feel it is best not to use it at all.
41 * </ul>
42 * Avoid using <code>Class.forName(String className)</code>.
43 * <p>
44 * The method {@link #getField(Class,String)}
45 * returns the value of the specified static field.
46 * Note that if the field has primitive type,
47 * the wrapper of its content is returned.
48 * Correspondingly, {@link #getField(Object,String)}
49 * returns the specified instant field.
50 * Note that since the class <code>Class</code> is final,
51 * and has no (non-public) fields, there should be no reason,
52 * to access a <code>Class</code>-object.
53 * In case of a very special application,
54 * casts like <code>getField((Object)ls,name)</code>
55 * should resolve the ambiguity.
56 * The two methods described above, should cover the typical situations.
57 * In some special cases however,
58 * including overwritten fields and fields of inner classes,
59 * {@link #getField(Class,Object,String)}
60 * may provide the only way to access a field.
61 * <p>
62 * What can be said about the methods <code>getField</code>
63 * applies correspondingly to methods <code>setField</code>.
64 * Note that there is no way to change fields
65 * which are declared <code>final</code>.
66 * To be more precise, the pointer in a final field
67 * <p>
68 * Similarly for invoking methods:
69 * Except for very special cases,
70 * {@link #invokeStatic(Class,String,Object...)} for static methods and
71 * {@link #invoke(Object,String,Object...)} for instance methods
72 * will be sufficient, but for some special cases
73 * including overwriting and inner classes
74 * {@link #invoke(Class,Object,String,Object...)}
75 * may be the only way to invoke a method.
76 * Note that these methods apply to parameters with primitive types as well
77 * by passing the appropriate wrapper object:
78 * For example <code>Integer.toString(int i)</code> may be invoked by
79 * <code>invoke(Integer.class,"toString",new Integer(i))</code>.
80 * <p>
81 * Still there is a problem:
82 * if wrapping parameters makes signatures ambiguous,
83 * the parameter types must be specified explicitly
84 * using method {@link #invoke(Class,Object,String,Class[],Object[])}.
85 * For this case, which seems to be quite rare,
86 * there are no convenience methods.
87 * <p>
88 * Note that the return type is always <code>Object</code>.
89 * Of course, the return value may be casted
90 * to the return type of the specified method,
91 * provided the return type is an object and
92 * neither void nor a primitive type.
93 * In the latter case, the return value is wrapped in the corresponding
94 * wrapper class unlike the parameters.
95 * <p>
96 * For the last case, creating objects using constructors,
97 * in most cases {@link #create(Class,Object[])} is sufficient;
98 * to avoid ambiguities, one has to specify the types of the parameters
99 * and use {@link #create(Class,Class[],Object[])} instead.
100 *
101 * @param <T>
102 * parameter representing the class to be accessed.
103 *
104 * @author <a href="mailto:ernst.reissner@simuline.eu">Ernst Reissner</a>
105 * @version 1.0
106 */
107 public final class Accessor<T> {
108
109 /* -------------------------------------------------------------------- *
110 * constants. *
111 * -------------------------------------------------------------------- */
112
113 /**
114 * The separator between a class and its enclosing class
115 * for inner classes.
116 */
117 private static final String INNER_SEPARATOR = "$";
118
119 /**
120 * String denoting an unspecified class.
121 * This is needed for producing output only.
122 *
123 * @see #paramsToString
124 */
125 private static final String UNSPECIFIED_CLASS = "<unspecified class>";
126
127 // several string literals occurring more than once.
128 private static final String STR_DNE = " does not exist. ";
129 private static final String STR_IN_CLS = "' in class '";
130 private static final String STR_SPEC_NULL_CLS = "Specified null-class. ";
131
132 /* -------------------------------------------------------------------- *
133 * private constructor. *
134 * -------------------------------------------------------------------- */
135
136 /**
137 * Formally to create a new <code>Accessor</code> instance
138 * but intended in contrary
139 * to prevent an <code>Accessor</code> from being instantiated.
140 * Note that the modifiers <code>final</code> and <code>abstract</code>
141 * are mutually exclusive
142 * and so this trick is the only remaining possibility.
143 */
144 private Accessor() {
145 }
146
147 /* -------------------------------------------------------------------- *
148 * private methods. *
149 * -------------------------------------------------------------------- */
150
151 /**
152 * Converts a list of classes into their string-representation.
153 *
154 * @param paramCls
155 * an array of <code>Class</code>es.
156 * The entries in the array may well be <code>null</code>.
157 * @return
158 * a comma separated sequence of the classes given.
159 */
160 private static String paramsToString(Class<?>... paramCls) {
161 StringBuffer ret = new StringBuffer();
162 String clsString;
163 ret.append('(');
164 if (paramCls.length != 0) {
165 clsString = paramCls[0] == null
166 ? UNSPECIFIED_CLASS
167 : paramCls[0].getName();
168 ret.append(clsString);
169 for (int i = 1; i < paramCls.length; i++) {
170 ret.append(", ");
171 clsString = paramCls[i] == null
172 ? UNSPECIFIED_CLASS
173 : paramCls[i].getName();
174 ret.append(clsString);
175 }
176 }
177 ret.append(')');
178 return ret.toString();
179 }
180
181 /**
182 * Invokes the specified method with the given parameters
183 * and returns the value (which may be void of course. )
184 *
185 * @param method
186 * a <code>Method</code>.
187 * @param target
188 * the target to which the specified method is to be applied.
189 * the target <em>must</em> be <code>null</code>
190 * if and only if the method specified is static.
191 * @param parameters
192 * the list of parameters used when invoking <code>method</code>.
193 * Note that parameters of elementary types
194 * have to be wrapped in an object
195 * (e.g. write <code>new Integer(8)</code>
196 * instead of just <code>8</code>).
197 * @return
198 * The result of invoking the specified method
199 * with the given parameters.
200 * If the method has no return value, <code>null</code> is returned.
201 * @throws IllegalArgumentException
202 * <ul>
203 * <li>
204 * if the specified method is static but <code>target != null</code>.
205 * <li>
206 * if the specified method is a member method
207 * but <code>target == null</code>.
208 * </ul>
209 * @throws IllegalStateException
210 * with message "Method should be accessible; still is not. "
211 * if the method is not accessible.
212 * @throws InvocationTargetException
213 * to wrap an exception thrown by the method invoked.
214 * Unwrap it using {@link Throwable#getCause}.
215 */
216 private static Object invoke(Method method,
217 Object target,
218 Object... parameters)
219 throws InvocationTargetException {
220
221 if (Modifier.isStatic(method.getModifiers()) !=
222 (target == null)) {
223 if (Modifier.isStatic(method.getModifiers())) {
224 throw new IllegalArgumentException
225 ("For static method " + method.getName() +
226 " no target has to be provided (i.e. null). ");
227 } else {
228 throw new IllegalArgumentException
229 ("For member method " + method.getName() +
230 " a target has to be provided (not null). ");
231 }
232 }
233
234 try {
235 return method.invoke(target, parameters);
236 } catch (IllegalAccessException ie) {
237 throw new IllegalStateException// NOPMD
238 ("Method should be accessible; still is not. ");
239 }
240 }
241
242 /**
243 * Returns the classes of the given parameters.
244 *
245 * @param parameters
246 * a parameter list represented as an array of <code>Object</code>s.
247 * @return
248 * the classes of the given parameters.
249 * For a <code>null</code>-parameter,
250 * the <code>null</code>-class is returned.
251 */
252 private static Class<?>[] getParamCls(Object... parameters) {
253 Class<?>[] paramCls = new Class<?>[parameters.length];
254 for (int i = 0; i < parameters.length; i++) {
255 paramCls[i] = parameters[i] == null
256 ? null
257 : parameters[i].getClass();
258 }
259 return paramCls;
260 }
261
262 /*----------------------------------------------------------------------*
263 * methods for public access. *
264 *----------------------------------------------------------------------*/
265
266 /*----------------------------------------------------------------------*
267 * getField methods *
268 *----------------------------------------------------------------------*/
269
270 /**
271 * Returns the value of the specified member field resp. its wrapper.
272 *
273 * @param target
274 * the instance for which a field is to be accessed.
275 * @param fieldName
276 * the name of the field.
277 * @return
278 * If the type of the specified field is <code>Object</code>,
279 * this object is returned directly;
280 * otherwise the value of the field is wrapped
281 * in an object of the appropriate class.
282 * E.g. <code>new Integer(5)</code> is returned
283 * instead of the <code>int</code>-value <code>5</code>.
284 * @throws NoSuchFieldException
285 * if the specified object
286 * does not contain a field with the given name,
287 * e.g. because <code>fieldName == null</code>.
288 * @throws IllegalArgumentException
289 * if the target is <code>null</code>
290 * or if the specified field is static.
291 * @see #setField(Object, String, Object)
292 */
293 public static Object getField(Object target,
294 String fieldName)
295 throws NoSuchFieldException {
296
297 if (target == null) {
298 throw new IllegalArgumentException
299 ("Specified null-target. ");
300 }
301
302 return getField(target.getClass(), target, fieldName);
303 }
304
305 /**
306 * Returns the value of the specified static field resp. its wrapper.
307 *
308 * @param aClass
309 * the class for which a static field is to be accessed.
310 * Typically one will use the expression
311 * <code><classname>.class</code> to determine the class-object.
312 * @param fieldName
313 * the name of the field.
314 * @return
315 * If the value of the specified field is an Object,
316 * this object is returned directly;
317 * otherwise the value of the field is wrapped
318 * in an object of the appropriate class.
319 * E.g. <code>new Integer(5)</code> is returned
320 * instead of the <code>int</code>-value <code>5</code>.
321 * @throws NoSuchFieldException
322 * if the specified class
323 * does not contain a field with the given name,
324 * e.g. because <code>fieldName == null</code>.
325 * @throws IllegalArgumentException
326 * if the class-parameter is <code>null</code>
327 * or if the specified field is not static.
328 * @see #setField(Class, String, Object)
329 */
330 public static Object getField(Class<?> aClass,
331 String fieldName)
332 throws NoSuchFieldException {
333
334 return getField(aClass, null, fieldName);
335 }
336
337 /**
338 * Returns the specified <code>Field</code> object if possible.
339 * This method is commonly used by the methods
340 * named <code>getField</code> and <code>setField</code>.
341 *
342 * @param aClass
343 * a <code>Class</code> object.
344 * Typically one will use the expression
345 * <code><classname>.class</code>
346 * to determine the class-object.
347 * @param fieldName
348 * the name of a field to look for in the specified class
349 * and its superclasses.
350 * @param shouldBeStatic
351 * whether the specified field should static.
352 * @return
353 * the specified <code>Field</code> object
354 * (Should never return <code>null</code>)
355 * made accessible if it exists.
356 * @throws IllegalArgumentException
357 * if the "<code>null</code>-class" is specified.
358 * @throws NoSuchFieldException
359 * if the specified class and none of its superclasses
360 * containd a field with the given name,
361 * e.g. because <code>fieldName == null</code>.
362 */
363 private static Field getFieldObj(Class<?> aClass,
364 String fieldName,
365 boolean shouldBeStatic)
366 throws NoSuchFieldException {
367
368 if (aClass == null) {
369 throw new IllegalArgumentException(STR_SPEC_NULL_CLS);
370 }
371
372 Field[] cands;
373 Class<?> candClass = aClass;
374
375 do {
376 // look for the specified field in candClass.
377 cands = candClass.getDeclaredFields();
378 for (int i = 0; i < cands.length; i++) {
379 if (cands[i].getName().equals(fieldName)) {
380 cands[i].setAccessible(true);
381 // Here, aField is not null
382 // (if no field: Exception thrown).
383
384 if (shouldBeStatic !=
385 Modifier.isStatic(cands[i].getModifiers())) {
386 throw new IllegalArgumentException
387 ("The specified field '" + fieldName +
388 "' should " +
389 (shouldBeStatic ? "" : "not ") +
390 "be static. ");
391 }
392
393 return cands[i];
394 }
395 }
396 // Here, no such field is found.
397
398 // prepare search in superclass.
399 candClass = candClass.getSuperclass();
400 } while (candClass != null);
401 // Here, the specified field is not found.
402
403 // throws a NoSuchFieldException
404 aClass.getDeclaredField(fieldName);
405 throw new IllegalStateException
406 ("Should throw a NoSuchFieldException. ");
407 }
408
409 /**
410 * Returns the value of the specified static field
411 * or member field resp. its wrapper.
412 * Use {@link #getField(Class, String)} or
413 * {@link #getField(Object, String)} if possible.
414 *
415 * @param aClass
416 * Some class object.
417 * Either <code>target == null</code>
418 * or <code>target instanceof aClass</code>.
419 * @param target
420 * the object for which a field is to be accessed.
421 * For static fields, this <em>must</em> be <code>null</code>;
422 * whereas for memeber fields
423 * this has to be the corresponding instance.
424 * @param fieldName
425 * the name of the field.
426 * @return
427 * If the value of the specified field is an Object,
428 * this object is returned directly;
429 * otherwise the value of the field is wrapped
430 * in an object of the appropriate class.
431 * E.g. <code>new Integer(5)</code> is returned
432 * instead of the <code>int</code>-value <code>5</code>.
433 * @throws NoSuchFieldException
434 * if the specified class
435 * does not contain a field with the given name,
436 * e.g. because <code>fieldName == null</code>.
437 * @throws IllegalArgumentException
438 * <ul>
439 * <li>
440 * if the <code>null</code>-class is specified
441 * <li>
442 * if the target is <code>null</code>
443 * whereas the specified field is a member field.
444 * <li>
445 * if the target is not <code>null</code>
446 * whereas the specified field is static.
447 * </ul>
448 * @see #setField(Class, Object, String, Object)
449 */
450 public static Object getField(Class<?> aClass,
451 Object target,
452 String fieldName)
453 throws NoSuchFieldException {
454
455 Field aField = getFieldObj(aClass, fieldName, target == null);
456
457 try {
458 return aField.get(target);
459 } catch (IllegalAccessException e) {
460 throw new IllegalStateException// NOPMD
461 ("Field '" + fieldName + STR_IN_CLS +
462 (aClass == null ? target.getClass() : aClass).getName() +
463 "is not accessible although it should. ");
464 }
465 }
466
467 /*----------------------------------------------------------------------*
468 * setField methods *
469 *----------------------------------------------------------------------*/
470
471 /**
472 * If the type of the specified field extends <code>Object</code>,
473 * invoking this method acts like <code>target.fieldName = value</code>;
474 * otherwise the argument <code>value</code> is unwrapped first.
475 * Note that there is no way to assign a value
476 * to a field which is declared <code>final</code>.
477 *
478 * @param target
479 * the object for which a field is to be accessed.
480 * @param fieldName
481 * the name of the field.
482 * @param value
483 * If the type of the specified field extends <code>Object</code>,
484 * the type of <code>value</code> must be a supertype;
485 * otherwise it must be the corresponding wrapper.
486 * E.g. the field declared by <code>int intField;</code>
487 * is set by <code>setField(target, "intField", new Integer(5))</code>
488 * instead of <code>setField(target, "intField", 5)</code>.
489 * @throws NoSuchFieldException
490 * if the specified class
491 * does not contain a field with the given name.
492 * @throws IllegalArgumentException
493 * if the target is <code>null</code>
494 * or if the specified field is static
495 * or if the specified field is declared <code>final</code>.
496 * @see #getField(Object, String)
497 */
498 public static void setField(Object target,
499 String fieldName,
500 Object value)
501 throws NoSuchFieldException {
502
503 if (target == null) {
504 throw new IllegalArgumentException
505 ("Specified null-target. ");
506 }
507 setField(target.getClass(), target, fieldName, value);
508 }
509
510 /**
511 * If the type of the specified field extends <code>Object</code>,
512 * invoking this method acts like <code>aClass.fieldName = value</code>;
513 * otherwise the argument <code>value</code> is unwrapped first.
514 * Note that there is no way to assign a value
515 * to a field which is declared <code>final</code>.
516 *
517 * @param aClass
518 * the class for which a static field is to be accessed.
519 * Typically one will use the expression
520 * <code><classname>.class</code>
521 * to determine the class-object.
522 * @param fieldName
523 * the name of the field.
524 * @param value
525 * If the type of the specified field extends <code>Object</code>,
526 * the type of <code>value</code> must be a supertype;
527 * otherwise it must be the corresponding wrapper.
528 * E.g. the field declared by <code>static int intField;</code>
529 * is set by <code>setField(aClass, "intField", new Integer(5))</code>
530 * instead of <code>setField(aClass, "intField", 5)</code>.
531 * @throws NoSuchFieldException
532 * if the specified class
533 * does not contain a field with the given name.
534 * @throws IllegalArgumentException
535 * if <code>aClass == null</code>
536 * or if the specified field is not static
537 * or if the specified field is declared <code>final</code>.
538 * @see #getField(Class, String)
539 */
540 public static void setField(Class<?> aClass,
541 String fieldName,
542 Object value)
543 throws NoSuchFieldException {
544
545 setField(aClass, null, fieldName, value);
546 }
547
548 /**
549 * If the type of the specified field extends <code>Object</code>,
550 * invoking this method acts like <code>target.field = value</code>;
551 * otherwise the argument <code>value</code> is unwrapped first.
552 * Note that there is no way to assign a value
553 * to a field which is declared <code>final</code>.
554 *
555 * @param aClass
556 * Some class object.
557 * Either <code>target == null</code>
558 * or <code>target instanceof aClass</code>.
559 * @param target
560 * the object for which a field is to be accessed.
561 * @param fieldName
562 * the name of the field.
563 * @param value
564 * If the type of the specified field extends <code>Object</code>,
565 * the type of <code>value</code> must be the same;
566 * otherwise it must be the corresponding wrapper.
567 * @throws NoSuchFieldException
568 * if the specified class
569 * does not contain a field with the given name.
570 * @throws IllegalArgumentException
571 * <ul>
572 * <li>
573 * if the <code>null</code>-class is specified
574 * <li>
575 * if the target is <code>null</code>
576 * whereas the specified field is a member field.
577 * <li>
578 * if the target is not <code>null</code>
579 * whereas the specified field is static.
580 * <li>
581 * if the specified field is declared <code>final</code>.
582 * <li>
583 * if the specified field is primitive
584 * but a <code>null</code>-value is tried to be assigned.
585 * </ul>
586 * @see #getField(Class, Object, String)
587 */
588 public static void setField(Class<?> aClass,
589 Object target,
590 String fieldName,
591 Object value)
592 throws NoSuchFieldException {
593
594 Field aField = getFieldObj(aClass, fieldName, target == null);
595
596 if (aField.getType().isPrimitive() && value == null) {
597 throw new IllegalArgumentException
598 ("Tried to assign null-value to field '" + fieldName +
599 STR_IN_CLS +
600 (aClass == null ? target.getClass() : aClass).getName() +
601 "' although its type '" + aField.getType() +
602 "' is primitive. ");
603 }
604
605 try {
606 aField.set(target, value);
607 } catch (IllegalAccessException e) {
608 if (aClass == null) {
609 aClass = target.getClass();
610 }
611 String clsName = aClass.getName();
612 if (Modifier.isFinal(aField.getModifiers())) {
613 throw new IllegalArgumentException// NOPMD
614 ("Field '" + fieldName + STR_IN_CLS + clsName +
615 "' is declared final and is hence not accessible. ");
616 }
617 throw new IllegalStateException// NOPMD
618 ("Field '" + fieldName + STR_IN_CLS + clsName +
619 "' is not accessible although it should. ");
620 }
621 }
622
623
624 /*----------------------------------------------------------------------*
625 * invoke methods with implicitly specified parameter types *
626 *----------------------------------------------------------------------*/
627
628 /**
629 * For invoking static methods.
630 *
631 * @param aClass
632 * The class of the static method to be invoked.
633 * The method is looked up recursively
634 * in the superclasses of <code>aClass</code>.
635 * Typically one will use the expression
636 * <code><classname>.class</code>
637 * to determine the class-object.
638 * @param methodName
639 * the short name of a static method.
640 * Short means without package or class.
641 * @param parameters
642 * the list of parameters used when invoking <code>methodName</code>.
643 * Note that parameters of elementary types
644 * have to be wrapped in an object
645 * (e.g. write <code>new Integer(8)</code>
646 * instead of just <code>8</code>).
647 * @return
648 * <ul>
649 * <li>
650 * If the return type of the specified method
651 * is <code>Object</code> or a subclass,
652 * the return value is returned directly;
653 * <li>
654 * if it is a primitive type, it is wrapped
655 * in an object of the appropriate class.
656 * E.g. <code>new Integer(5)</code> is returned
657 * instead of the <code>int</code>-value <code>5</code>.
658 * <li>
659 * If the return type is void,
660 * i.e. <code>java.lang.Void.TYPE</code>,
661 * <code>null</code> is returned.
662 * </ul>
663 * @throws IllegalArgumentException
664 * <ul>
665 * <li>
666 * if <code>aClass == null</code>.
667 * <li>
668 * if the specified method does not exist or is not unique.
669 * </ul>
670 * @throws InvocationTargetException
671 * to wrap an exception thrown by the method invoked.
672 * Unwrap it using {@link Throwable#getCause}.
673 * @see #invoke(Class, Object, String, Object...)
674 */
675 public static Object invokeStatic(Class<?> aClass,
676 String methodName,
677 Object... parameters)
678 throws InvocationTargetException {
679
680 return invoke(aClass, null, methodName, parameters);
681 }
682
683 /**
684 * For invoking member methods.
685 *
686 * @param target
687 * the target on which the specified member method is to be applied.
688 * @param methodName
689 * the short name of a static method.
690 * Short means without package or class.
691 * @param parameters
692 * the list of parameters used when invoking <code>methodName</code>.
693 * Note that parameters of elementary types
694 * have to be wrapped in an object
695 * (e.g. write <code>new Integer(8)</code>
696 * instead of just <code>8</code>).
697 * @return
698 * <ul>
699 * <li>
700 * If the return type of the specified method
701 * is <code>Object</code> or a subclass,
702 * the return value is returned directly;
703 * <li>
704 * if it is a primitive type, it is wrapped
705 * in an object of the appropriate class.
706 * E.g. <code>new Integer(5)</code> is returned
707 * instead of the <code>int</code>-value <code>5</code>.
708 * <li>
709 * If the return type is void,
710 * i.e. <code>java.lang.Void.TYPE</code>,
711 * <code>null</code> is returned.
712 * </ul>
713 * @throws IllegalArgumentException
714 * <ul>
715 * <li>
716 * if <code>target == null</code>.
717 * <li>
718 * if the specified method does not exist or is not unique.
719 * </ul>
720 * @throws InvocationTargetException
721 * to wrap an exception thrown by the method invoked.
722 * Unwrap it using {@link Throwable#getCause}.
723 * @see #invoke(Class, Object, String, Object...)
724 */
725 public static Object invoke(Object target,
726 String methodName,
727 Object... parameters)
728 throws InvocationTargetException {
729
730 return invoke(target.getClass(), target, methodName, parameters);
731 }
732
733 /**
734 * Invokes the specified method with the given parameters
735 * and returns the value (which may be void of course. )
736 * <p>
737 * CAUTION: This may cause an exception although the runtime system
738 * finds out which method is ment.
739 * This is the case if for example
740 * methods <code>exa(int)</code> and <code>exa(Integer)</code>
741 * are present:
742 * both are invoked with
743 * <code>invoke(cls, target, "exa", new Integer(0))}</code>.
744 * In this case
745 * use {@link #invoke(Class, Object, String, Class[], Object[])} instead.
746 *
747 * @param aClass
748 * The class of the method to be invoked.
749 * The method is looked up recursively
750 * in the superclasses of <code>aClass</code>.
751 * Typically one will use the expression
752 * <code><classname>.class</code>
753 * to determine the class-object.
754 * @param target
755 * the target to which the specified method is to be applied.
756 * the target <em>must</em> be <code>null</code>
757 * if and only if the method specified is static.
758 * @param methodName
759 * the short name of a method.
760 * Short means without package or class.
761 * @param parameters
762 * the list of parameters used when invoking <code>methodName</code>.
763 * Note that parameters of elementary types
764 * have to be wrapped in an object
765 * (e.g. write <code>new Integer(8)</code>
766 * instead of just <code>8</code>).
767 * @return
768 * <ul>
769 * <li>
770 * If the return type of the specified method
771 * is <code>Object</code> or a subclass,
772 * the return value is returned directly;
773 * <li>
774 * if it is a primitive type, it is wrapped
775 * in an object of the appropriate class.
776 * E.g. <code>new Integer(5)</code> is returned
777 * instead of the <code>int</code>-value <code>5</code>.
778 * <li>
779 * If the return type is void,
780 * i.e. <code>java.lang.Void.TYPE</code>,
781 * <code>null</code> is returned.
782 * </ul>
783 * @throws InvocationTargetException
784 * to wrap an exception thrown by the method invoked.
785 * Unwrap it using {@link Throwable#getCause}.
786 * @throws IllegalArgumentException
787 * <ul>
788 * <li>
789 * if <code>aClass == null</code>.
790 * <li>
791 * if the specified method is static but <code>target != null</code>.
792 * <li>
793 * if the specified method is a member method
794 * but <code>target == null</code>.
795 * <li>
796 * if the specified method does not exist or is not unique.
797 * </ul>
798 */
799 public static Object invoke(Class<?> aClass,
800 Object target,
801 String methodName,
802 Object... parameters)
803 throws InvocationTargetException {
804
805 if (aClass == null) {
806 throw new IllegalArgumentException(STR_SPEC_NULL_CLS);
807 }
808
809 Method[] cands;
810 Class<?> candClass = aClass;
811 Method toBeInvoked;
812
813 // Find out the methods matching the signature
814 // and collect them in "cands".
815 do {
816 cands = candClass.getDeclaredMethods();
817
818 toBeInvoked = getMethod(aClass,
819 methodName,
820 cands,
821 parameters);
822
823 if (toBeInvoked != null) {
824 return invoke(toBeInvoked, target, parameters);
825 }
826 // prepare search in superclass.
827 candClass = candClass.getSuperclass();
828 } while (candClass != null);
829 // Here, the desired method is not found.
830
831 throw new IllegalArgumentException
832 ("Method " + aClass.getName() + "." + methodName +
833 paramsToString(getParamCls(parameters)) + STR_DNE);
834 }
835
836 /**
837 * If more than one method with the same parameter types
838 * is declared in a class,
839 * and one of these methods has a return type
840 * that is more specific than any of the others,
841 * that method is returned;
842 * otherwise one of the methods is chosen arbitrarily.
843 *
844 * @param aClass
845 * The class of the method to be invoked.
846 * The method is looked up recursively
847 * in the superclasses of <code>aClass</code>.
848 * Typically one will use the expression
849 * <code><classname>.class</code>
850 * to determine the class-object.
851 * @param target
852 * the target to which the specified method is to be applied.
853 * If the method specified is static, the target is ignored.
854 * Convention: should be <code>null</code> in this case.
855 * @param methodName
856 * the short name of a method.
857 * Short means without package or class.
858 * @param paramCls
859 * the types specifying the parameter list of the desired method.
860 * Typically one will use the expression
861 * <code><classname>.class</code>
862 * to denote a class, even a primitive one.
863 * For primitive types, the alternatives
864 * java.lang.Boolean.TYPE
865 * java.lang.Character.TYPE
866 * java.lang.Byte.TYPE
867 * java.lang.Short.TYPE
868 * java.lang.Integer.TYPE
869 * java.lang.Long.TYPE
870 * java.lang.Float.TYPE
871 * java.lang.Double.TYPE
872 * (java.lang.Void.TYPE)
873 * are available.
874 * For hidden inner classes, e.g. <code>private</code> ones,
875 * {@link #getInnerClass(Class, String[])} and
876 * {@link #getInnerClass(Class, String)}
877 * may be used.
878 * If an object <code>i</code> of a desired class is present
879 * and if the class of is <code>i</code> not primitive,
880 * <code>i.getClass()</code> returns the desired class object as well.
881 * @param parameters
882 * the list of parameters used when invoking <code>methodName</code>.
883 * Note that parameters of elementary types
884 * have to be wrapped in an object
885 * (e.g. write <code>new Integer(8)</code>
886 * instead of just <code>8</code>).
887 * @return
888 * <ul>
889 * <li>
890 * If the return type of the specified method
891 * is <code>Object</code> or a subclass,
892 * the return value is returned directly;
893 * <li>
894 * if it is a primitive type, it is wrapped
895 * in an object of the appropriate class.
896 * E.g. <code>new Integer(5)</code> is returned
897 * instead of the <code>int</code>-value <code>5</code>.
898 * <li>
899 * If the return type is void,
900 * i.e. <code>java.lang.Void.TYPE</code>,
901 * <code>null</code> is returned.
902 * </ul>
903 * @throws IllegalArgumentException
904 * if the specified method does not exist.
905 * @throws InvocationTargetException
906 * to wrap an exception thrown by the method invoked.
907 * Unwrap it using {@link Throwable#getCause}.
908 * @see #invoke(Class, Object, String, Class[], Object[])
909 */
910 public static Object invoke(Class<?> aClass,
911 Object target,
912 String methodName,
913 Class<?>[] paramCls,
914 Object[] parameters)
915 throws InvocationTargetException {
916
917 if (aClass == null) {
918 throw new IllegalArgumentException(STR_SPEC_NULL_CLS);
919 }
920 Method toBeInvoked = getToBeInvoked(aClass, methodName, paramCls);
921 if (toBeInvoked != null) {
922 return invoke(toBeInvoked, target, parameters);
923 }
924 // Here, the desired method is not found.
925
926 throw new IllegalArgumentException
927 ("Method " + aClass.getName() + "." + methodName +
928 paramsToString(getParamCls(parameters)) + STR_DNE);
929 }
930
931 /**
932 * Returns the specified method if it exists;
933 * otherwise <code>null</code>.
934 * Note that it is searched in the superclasses as well.
935 *
936 * @param aClass
937 * the <code>Class</code> to start searching the method.
938 * Typically one will use the expression
939 * <code><classname>.class</code>
940 * to determine the class-object.
941 * @param methodName
942 * the name of the method to be returned.
943 * @param paramCls
944 * the types specifying the parameter list of the desired method.
945 * @return
946 * the method with the specified name and parameter list.
947 * The search is started in <code>candClass</code>
948 * and descends recursively until a method is found.
949 * If no method is found, <code>null</code> is returned.
950 */
951 static Method getToBeInvoked(Class<?> aClass,
952 String methodName,
953 Class<?>... paramCls) {
954
955 Method toBeInvoked;
956 for (Class<?> candClass = aClass;
957 candClass != null;
958 candClass = candClass.getSuperclass()) {
959 try {
960 toBeInvoked = candClass.getDeclaredMethod(methodName, paramCls);
961 } catch (NoSuchMethodException e) {
962 // method is not found: look it up in subclass.
963 continue;
964 }
965 if (Modifier.isAbstract(toBeInvoked.getModifiers())) {
966 return null;
967 }
968 toBeInvoked.setAccessible(true);
969 return toBeInvoked; // NOPMD
970 }
971 // Here, the desired method is not found.
972
973 return null;
974 }
975
976
977 /*----------------------------------------------------------------------*
978 * create methods *
979 *----------------------------------------------------------------------*/
980
981 /**
982 * Returns an object created by the specified constructor.
983 * Note that this method is not applicable to
984 * interfaces, primitive types, array classes, or void.
985 * In the context of this package, these restrictions are not severe.
986 *
987 * @param aClass
988 * the class of the instance to be created.
989 * Typically one will use the expression
990 * <code><classname>.class</code>
991 * to determine the class-object.
992 * @param parameters
993 * the list of parameters of the specified constructor.
994 * Note that parameters of elementary types
995 * have to be wrapped in an object
996 * (e.g. write <code>new Integer(8)</code>
997 * instead of just <code>8</code>).
998 * <p>
999 * Note also that for static inner classes,
1000 * formally the surrounding instance is prefixed as a parameter.
1001 * ****** i think this does not apply to methods,
1002 * but actually i did not try yet.
1003 * @return
1004 * an <code>Object</code> created by the specified constructor.
1005 * @throws InstantiationException
1006 * if the instantiation with the specified constructor failed.
1007 * @throws IllegalArgumentException
1008 * if the specified constructor does not exist or is not unique.
1009 * @throws InvocationTargetException
1010 * to wrap an exception thrown by the constructor invoked.
1011 * Unwrap it using {@link Throwable#getCause}.
1012 */
1013 public static <T> T create(Class<T> aClass,
1014 Object... parameters)
1015 throws InstantiationException, InvocationTargetException {
1016
1017
1018 Constructor<T> toBeInvoked = getConstructor(aClass, parameters);
1019 // toBeInvoked may also be the default constructor,
1020 // although this is not EXPLICITLY declared, of course.
1021
1022 if (toBeInvoked == null) {
1023 throw new IllegalArgumentException
1024 ("Constructor " + aClass.getName() +
1025 paramsToString(getParamCls(parameters)) +
1026 STR_DNE);
1027 }
1028 return create(toBeInvoked, parameters);
1029 }
1030
1031 /*
1032 * Returns an instance of the class given by <code>aClass</code>
1033 * generated by the default constructor.
1034 *
1035 * @param aClass
1036 * a <code>Class</code> object.
1037 * Typically one will use the expression
1038 * <code><classname>.class</code>
1039 * to determine the class-object.
1040 * @return
1041 * an <code>Object</code> created by the default constructor
1042 * of the specified class.
1043 * @exception InstantiationException
1044 * if an error occurs in the default constructor.
1045 */
1046 /*
1047 private static Object createDefault(Class aClass)
1048 throws InstantiationException {
1049 try {
1050 return aClass.newInstance();
1051 } catch (IllegalAccessException e) {
1052 throw new IllegalStateException
1053 ("Found unexpected exception " + e +
1054 " while trying to invoke default constructor " +
1055 "which is not declared. ");
1056 }
1057 }
1058 */
1059
1060 /**
1061 * Returns an object created by the specified constructor.
1062 * Note that this method is not applicable to
1063 * interfaces, primitive types, array classes, or void.
1064 * In the context of this package, these restrictions are not severe.
1065 *
1066 * @param aClass
1067 * the class of the instance to be created
1068 * Typically one will use the expression
1069 * <code><classname>.class</code>
1070 * to determine the class-object.
1071 * @param paramCls
1072 * the types specifying the parameter list of the desired method.
1073 * Typically one will use the expression
1074 * <code><classname>.class</code>
1075 * to denote a class, even a primitive one.
1076 * For primitive types, the alternatives
1077 * java.lang.Boolean.TYPE
1078 * java.lang.Character.TYPE
1079 * java.lang.Byte.TYPE
1080 * java.lang.Short.TYPE
1081 * java.lang.Integer.TYPE
1082 * java.lang.Long.TYPE
1083 * java.lang.Float.TYPE
1084 * java.lang.Double.TYPE
1085 * (java.lang.Void.TYPE)
1086 * are available.
1087 * For hidden inner classes, e.g. <code>private</code> ones,
1088 * {@link #getInnerClass(Class, String[])} and
1089 * {@link #getInnerClass(Class, String)}
1090 * may be used.
1091 * If an object <code>i</code> of a desired class is present
1092 * and if the class of is <code>i</code> not primitive,
1093 * <code>i.getClass()</code> returns the desired class object as well.
1094 * <p>
1095 * Note also that for static inner classes,
1096 * formally the surrounding class is prefixed as a parameter.
1097 * ****** i think this does not apply to methods,
1098 * but actually i did not try yet.
1099 * @param parameters
1100 * the list of parameters of the specified constructor.
1101 * Note that parameters of elementary types
1102 * have to be wrapped in an object
1103 * (e.g. write <code>new Integer(8)</code>
1104 * instead of just <code>8</code>).
1105 * <p>
1106 * Note also that for static inner classes,
1107 * formally the surrounding instance is prefixed as a parameter.
1108 * ****** i think this does not apply to methods,
1109 * but actually i did not try yet.
1110 * @return
1111 * an <code>Object</code> created by the specified constructor.
1112 * @throws InstantiationException
1113 * if the instantiation with the specified constructor failed.
1114 * @throws NoSuchMethodException
1115 * if the specified constructor does not exist.
1116 * @throws InvocationTargetException
1117 * to wrap an exception thrown by the constructor invoked.
1118 * Unwrap it using {@link Throwable#getCause}.
1119 */
1120 public static <T> T create(Class<T> aClass,
1121 Class<?>[] paramCls,
1122 Object... parameters)
1123 throws NoSuchMethodException,
1124 InstantiationException,
1125 InvocationTargetException {
1126
1127 // includes the default constructor?!? -- not specified!
1128 Constructor<T> toBeInvoked = aClass
1129 .getDeclaredConstructor(paramCls);
1130 if (toBeInvoked == null) {
1131 throw new IllegalArgumentException
1132 ("Constructor " + aClass.getName() +
1133 paramsToString(paramCls) + STR_DNE);
1134 }
1135 toBeInvoked.setAccessible(true);
1136 return create(toBeInvoked, parameters);
1137 }
1138
1139 /**
1140 * Invoke the specified constructor with the given arguments.
1141 *
1142 * @param toBeInvoked
1143 * some <code>Constructor</code>.
1144 * @param parameters
1145 * parameters fitting the given <code>Constructor</code>.
1146 * @throws InstantiationException
1147 * if the instantiation with the specified constructor failed.
1148 * @throws InvocationTargetException
1149 * to wrap an exception thrown by the constructor invoked.
1150 * Unwrap it using {@link Throwable#getCause}.
1151 */
1152 private static <T> T create(Constructor<T> toBeInvoked,
1153 Object... parameters)
1154 throws InstantiationException, InvocationTargetException {
1155
1156 try {
1157 return toBeInvoked.newInstance(parameters);
1158 } catch (IllegalAccessException ie) {
1159 throw new IllegalStateException// NOPMD
1160 ("Constructor should be accessible; still is not. ");
1161 }
1162 }
1163
1164 /**
1165 * Returns whether the given method
1166 * matches the name and the parameter list.
1167 *
1168 * @param cand
1169 * either a <code>Constructor</code> or a <code>Method</code>.
1170 * @param methodName
1171 * the required name of a <code>Method</code>.
1172 * If <code>cand</code> is a constructor, this field is ignored;
1173 * if it is a method the name of which does not match,
1174 * <code>null</code> is returned.
1175 * @param parameters
1176 * the list of parameters.
1177 * @return
1178 * <code>true</code> if and only if
1179 * all of the following conditions are satisfied:
1180 * <ul>
1181 * <li>
1182 * <code>cand</code> is either a non-abstract method
1183 * named <code>methodName</code>.
1184 * <li>
1185 * the parameter list of <code>cand</code>
1186 * matches <code>parameters</code>
1187 * as specified for {@link #paramsMatch}.
1188 * </ul>
1189 * @throws IllegalStateException
1190 * if <code>cand</code> is neither a method nor a constructor.
1191 */
1192 private static boolean methodMatches(Method cand,
1193 String methodName,
1194 Object... parameters) {
1195 // Check name.
1196 if (!cand.getName().equals(methodName)) {
1197 return false;
1198 }
1199 // Here, cand has the right name.
1200
1201 // Check not abstract.
1202 if (Modifier.isAbstract(cand.getModifiers())) {
1203 return false;
1204 }
1205 // Here, cand is not abstract.
1206
1207 // Here, paramTypes contains the parameter types of cand.
1208
1209 return paramsMatch(cand.getParameterTypes(), parameters);
1210 }
1211
1212 /**
1213 * Returns whether the given constructor matches the parameter list.
1214 *
1215 * @param cand
1216 * either a <code>Constructor</code> or a <code>Method</code>.
1217 * @param parameters
1218 * the list of parameters.
1219 * @return a
1220 * <code>true</code> if and only if
1221 * the parameter list of <code>cand</code>
1222 * matches <code>parameters</code>
1223 * as specified for {@link #paramsMatch}.
1224 * @throws IllegalStateException
1225 * if <code>cand</code> is neither a method nor a constructor.
1226 */
1227 private static <T> boolean constructorMatches(Constructor<T> cand,
1228 Object... parameters) {
1229 return paramsMatch(cand.getParameterTypes(), parameters);
1230 }
1231
1232 /**
1233 * Returns whether the given the parameter list matches the classes.
1234 *
1235 * @param paramTypes
1236 * the list of classes of parameters.
1237 * @param parameters
1238 * the list of parameters.
1239 * @return a
1240 * <code>true</code> if and only if
1241 * the parameter list of <code>cand</code>
1242 * matches <code>parameters</code>
1243 * up to equivalence of primitive types and their wrappers.
1244 * This includes that the two arrays have the same length.
1245 * @throws IllegalStateException
1246 * if <code>cand</code> is neither a method nor a constructor.
1247 */
1248 private static boolean paramsMatch(Class<?>[] paramTypes,
1249 Object... parameters) {
1250 if (paramTypes.length != parameters.length) {
1251 return false;
1252 }
1253 // Here, the name and the number of parameters match.
1254
1255 for (int j = 0; j < parameters.length; j++) {
1256 if (!BasicTypesCompatibilityChecker
1257 .areCompatible(paramTypes[j], parameters[j])) {
1258 return false;
1259 }
1260 }
1261 // Here, the complete signature matches
1262 // (except for the return type).
1263 return true;
1264 }
1265
1266 /**
1267 * Returns the specified method
1268 * if <code>cands</code> offers exactly one possibility.
1269 *
1270 * @param aClass
1271 * the <code>Class</code>
1272 * in which to search a <code>Method</code>.
1273 * For error/exception messages only.
1274 * @param methodName
1275 * the name of the desired <code>Method</code>.
1276 * @param cands
1277 * an array of <code>Method</code>s
1278 * the return value of this method is choosen from.
1279 * @param parameters
1280 * the list of parameters.
1281 * @return
1282 * a <code>Method</code>; possibly <code>null</code>
1283 * if the specified <code>Method</code>
1284 * does not exist in the specified class.
1285 * @throws IllegalArgumentException
1286 * if the specified constructor or method is not unique.
1287 */
1288 private static Method getMethod(Class<?> aClass,
1289 String methodName,
1290 Method[] cands,
1291 Object... parameters) {
1292
1293 // "null" means that no method or constructor has been found so far.
1294 Method result = null;
1295
1296 for (int i = 0; i < cands.length; i++) {
1297 if (!methodMatches(cands[i], methodName, parameters)) {
1298 continue;
1299 }
1300
1301 if (result != null) {
1302 // Found more than one method/constructor.
1303 throw new IllegalArgumentException
1304 ("Method " + aClass.getName() + "." + methodName +
1305 paramsToString(getParamCls(parameters)) +
1306 " is not unique: cannot distinguish " + result +
1307 " from " + cands[i] + ". ");
1308 }
1309 // cands[i] is the first method that matches.
1310 result = cands[i];
1311 } // for all cands
1312
1313 if (result != null) {
1314 result.setAccessible(true);
1315 }
1316 return result;
1317 }
1318
1319 /**
1320 * Returns the specified constructor
1321 * if <code>aClas</code> offers exactly one possibility
1322 * with the given parameters.
1323 *
1324 * @param aClass
1325 * the <code>Class</code>
1326 * in which to search a <code>Constructor</code>.
1327 * For error/exception messages only.
1328 * @param parameters
1329 * the list of parameters.
1330 * @return
1331 * a <code>Constructor</code> or <code>null</code>
1332 * if the specified constructor
1333 * does not exist in the specified class.
1334 * @throws IllegalArgumentException
1335 * if the specified constructor or method is not unique.
1336 */
1337 private static <T> Constructor<T> getConstructor(Class<T> aClass,
1338 Object... parameters) {
1339
1340 Constructor<?>[] cands = //(Constructor<T>[])
1341 aClass.getDeclaredConstructors();
1342
1343 // "null" means that no method or constructor has been found so far.
1344 Constructor<T> result = null;
1345
1346 for (int i = 0; i < cands.length; i++) {
1347 if (!constructorMatches(cands[i], parameters)) {
1348 continue;
1349 }
1350
1351 if (result != null) {
1352 // Found more than one method/constructor.
1353 throw new IllegalArgumentException
1354 ("Constructor " + aClass.getName() +
1355 paramsToString(getParamCls(parameters)) +
1356 " is not unique: cannot distinguish " + result +
1357 " from " + cands[i] + ". ");
1358 }
1359 // cands[i] is the first constructor that matches.
1360
1361 try {
1362 // one would just write result = (Constructor<T>)cands[i];
1363 result = aClass
1364 .getDeclaredConstructor(cands[i].getParameterTypes());
1365 // not assert result == cands[i]
1366 assert result.equals(cands[i]);
1367 // **** the cast is a weakness in the jdk5 (reported)
1368 // because cands is an array and these cannot be generified
1369 // **** in jdk6 hardly any improvement (to be rep.)
1370 // possible solution is: use a List but with other disadvantages
1371 } catch (NoSuchMethodException e) {
1372 throw new IllegalStateException("****"); // NOPMD
1373 }
1374
1375 } // for all cands
1376
1377 if (result != null) {
1378 result.setAccessible(true);
1379 }
1380 return result;
1381 }
1382
1383 /*----------------------------------------------------------------------*
1384 * getInnerClass methods *
1385 *----------------------------------------------------------------------*/
1386
1387
1388 /**
1389 * Returns the inner class of <code>enclosingCls</code>
1390 * with the specified name <code>innerClsName</code>.
1391 * By inner classes we mean both static inner classes and member classes.
1392 * Also inherited classes are included.
1393 *
1394 * @param enclosingCls
1395 * a <code>Class</code> object which may also be an inner class,
1396 * static or not.
1397 * @param pathToInner
1398 * a short path of a class enclosed by <code>enclosingCls</code>.
1399 * If the name of the enclosing class is <tt>enclosing</tt>
1400 * then the name of the inner class has the form
1401 * <tt>enclosing$shortName</tt>.
1402 * Here, "$" is as specified in {@link #INNER_SEPARATOR}.
1403 * This remains also true for nested inner classes.
1404 * In this case, <tt>shortName</tt> itself has the form
1405 * <tt>cls1$...$clsN</tt>.
1406 * The the corresponding short path is
1407 * <code>new String[] {cls1, ..., clsN}</code>.
1408 * For paths with length <code>1</code>
1409 * one may use {@link #getInnerClass(Class, String)} instead.
1410 * @return
1411 * the <code>Class</code> object represented by the parameters.
1412 * @throws IllegalArgumentException
1413 * if either of the parameters is <code>null</code>.
1414 * @throws IllegalArgumentException
1415 * if the specified class does not exist.
1416 * @see #getInnerClass(Class, String)
1417 */
1418 public static Class<?> getInnerClass(Class<?> enclosingCls,
1419 String[] pathToInner) {
1420 Class<?> result = enclosingCls;
1421 for (int i = 0; i < pathToInner.length; i++) {
1422 result = getInnerClass(result, pathToInner[i]);
1423 }
1424 return result;
1425 }
1426
1427 /**
1428 * Returns the inner class of <code>enclosingCls</code>
1429 * with the specified name <code>innerClsName</code>.
1430 * By inner classes we mean both static inner classes and member classes.
1431 * Also inherited classes are included.
1432 *
1433 * @param enclosingCls
1434 * a <code>Class</code> object which may also be an inner class,
1435 * static or not.
1436 * @param innerClsName
1437 * a short name of a class enclosed by <code>enclosingCls</code>.
1438 * If the name of the enclosing class is <tt>enclosing</tt>
1439 * then the name of the inner class has the form
1440 * <tt>enclosing$shortName</tt>.
1441 * Here, "$" is as specified in {@link #INNER_SEPARATOR}.
1442 * This remains also true for nested inner classes.
1443 * In this case, <tt>shortName</tt> itself has the form
1444 * <tt>cls1$...$clsN</tt>.
1445 * <em>CAUTION</em>
1446 * In this case apply method {@link #getInnerClass(Class, String[])}
1447 * instead.
1448 * @return
1449 * the <code>Class</code> object represented by the parameters.
1450 * @throws IllegalArgumentException
1451 * if either of the parameters is <code>null</code> or
1452 * if the specified class does not exist.
1453 * @see #getInnerClass(Class, String[])
1454 */
1455 public static Class<?> getInnerClass(Class<?> enclosingCls,
1456 String innerClsName) {
1457
1458 if (enclosingCls == null) {
1459 throw new IllegalArgumentException(STR_SPEC_NULL_CLS);
1460 }
1461 if (innerClsName == null) {
1462 throw new IllegalArgumentException
1463 ("Specified null-class-name. ");
1464 }
1465
1466 Class<?>[] cands;
1467 Class<?> candCls = enclosingCls;
1468 String candClsName;
1469
1470 do {
1471 // look for the specified inner class in candClass.
1472
1473 cands = candCls.getDeclaredClasses();
1474 candClsName = candCls.getName() + INNER_SEPARATOR;
1475
1476 for (int i = 0; i < cands.length; i++) {
1477 if (cands[i].getName().equals(candClsName + innerClsName)) {
1478 return cands[i];
1479 }
1480 }
1481 // prepare search in superclass.
1482 candCls = candCls.getSuperclass();
1483 } while (candCls != null);
1484 // Here, the specified inner class is not found.
1485
1486 throw new IllegalArgumentException
1487 ("Class '" + enclosingCls.getName() +
1488 "' has no inner class named '" + innerClsName + "'. ");
1489 }
1490 }
1491
1492
1493
1494 // Accessor.java:157: warning: [rawtypes] found raw type: Class
1495 // private static String paramsToString(Class... paramCls) {
1496 // ^
1497 // missing type arguments for generic class Class<T>
1498 // where T is a type-variable:
1499 // T extends Object declared in class Class
1500 // Accessor.java:910: warning: [rawtypes] found raw type: Class
1501 // Class[] paramCls,
1502 // ^
1503 // missing type arguments for generic class Class<T>
1504 // where T is a type-variable:
1505 // T extends Object declared in class Class
1506 // Accessor.java:1019: warning: [unchecked] unchecked cast
1507 // (Constructor<T>[])aClass.getDeclaredConstructors(),
1508 // ^
1509 // required: Constructor<T>[]
1510 // found: Constructor<?>[]
1511 // where T is a type-variable:
1512 // T extends Object declared in method <T>create(Class<T>,Object...)
1513 // Accessor.java:1123: warning: [rawtypes] found raw type: Class
1514 // Class[] paramCls,
1515 // ^
1516 // missing type arguments for generic class Class<T>
1517 // where T is a type-variable:
1518 // T extends Object declared in class Class
1519 // Accessor.java:1250: warning: [rawtypes] found raw type: Class
1520 // private static boolean paramsMatch(Class[] paramTypes,
1521 // ^
1522 // missing type arguments for generic class Class<T>
1523 // where T is a type-variable:
1524 // T extends Object declared in class Class
1525 // Accessor.java:1453: warning: [rawtypes] found raw type: Class
1526 // Class[] cands;
1527 // ^
1528 // missing type arguments for generic class Class<T>
1529 // where T is a type-variable:
1530 // T extends Object declared in class Class
1531 // 6 warnings
1532
1533 // Compilation finished at Mon Oct 23 00:05:00