001 package echopoint.util;
002
003 /*
004 * This file is part of the Echo Point Project. This project is a collection
005 * of Components that have extended the Echo Web Application Framework.
006 *
007 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
008 *
009 * The contents of this file are subject to the Mozilla Public License Version
010 * 1.1 (the "License"); you may not use this file except in compliance with
011 * the License. You may obtain a copy of the License at
012 * http://www.mozilla.org/MPL/
013 *
014 * Software distributed under the License is distributed on an "AS IS" basis,
015 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
016 * for the specific language governing rights and limitations under the
017 * License.
018 *
019 * Alternatively, the contents of this file may be used under the terms of
020 * either the GNU General Public License Version 2 or later (the "GPL"), or
021 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
022 * in which case the provisions of the GPL or the LGPL are applicable instead
023 * of those above. If you wish to allow use of your version of this file only
024 * under the terms of either the GPL or the LGPL, and not to allow others to
025 * use your version of this file under the terms of the MPL, indicate your
026 * decision by deleting the provisions above and replace them with the notice
027 * and other provisions required by the GPL or the LGPL. If you do not delete
028 * the provisions above, a recipient may use your version of this file under
029 * the terms of any one of the MPL, the GPL or the LGPL.
030 */
031
032 import java.lang.reflect.Constructor;
033 import java.lang.reflect.Field;
034 import java.lang.reflect.InvocationTargetException;
035 import java.lang.reflect.Member;
036 import java.lang.reflect.Method;
037 import java.lang.reflect.Modifier;
038 import java.util.ArrayList;
039 import java.util.Arrays;
040 import java.util.Collections;
041 import java.util.Comparator;
042 import java.util.Iterator;
043 import java.util.List;
044
045 /**
046 * <code>ReflectionKit</code> provides methods that help when using reflection
047 * on Java code.
048 */
049 public class ReflectionKit {
050
051 private static abstract class BaseReflectionKitComparator implements Comparator {
052 /*
053 * Does the standard nullness checks and returns Integer.MAX_VALUE to
054 * indicate that they are both non null objects and hence further checks
055 * may be needed.
056 */
057 int compareForNullness(Object o1, Object o2) {
058 if (o1 == null && o2 == null)
059 return 0;
060 if (o1 == null && o2 != null)
061 return -1;
062 if (o1 != null && o2 == null)
063 return 1;
064 return Integer.MAX_VALUE;
065 }
066
067 /**
068 * Normalises the return code into -1, 0 or 1 as required by {@link Comparator}.
069 * @param rc - the rc in question
070 * @return -1, 0 or 1 as required by {@link Comparator}
071 */
072 protected int normaliseRC(int rc) {
073 if (rc < 0) {
074 return -1;
075 } else if (rc > 0) {
076 return 1;
077 } else {
078 return 0;
079 }
080 }
081
082
083 /**
084 * The compares the two <code>{@link Class}</code> values against
085 * each other in the following order :
086 *
087 * <ol>
088 * <li>c1.equals(c2) return 0</li>
089 * <li>c2 is assignable c1, return -1</li>
090 * <li>c1 is assignable c2, return 1</li>
091 * <li>finally c1.getName() compared to c2.getName()</li>
092 * <li></li>
093 * </ol>
094 */
095 protected int compareByClassDerivation(Class c1, Class c2) {
096 if (c1 == null || c2 == null) {
097 return compareForNullness(c1, c2);
098 }
099 if (c1.equals(c2))
100 return 0;
101 if (c2.isAssignableFrom(c1) || c1.isAssignableFrom(c2)) {
102 if (c2.isAssignableFrom(c1))
103 return -1;
104 else if (c1.isAssignableFrom(c2))
105 return 1;
106 }
107 int rc = c1.getName().compareTo(c2.getName());
108 return normaliseRC(rc);
109
110 }
111 }
112
113 /**
114 * A Comparator that can be used when comparing and sorting Class objects by
115 * class name. NOTE: this is only based on class name nor class derivation.
116 * <p>
117 * In short it sorts in "Class Name" order only.
118 */
119 public static class ClassNameComparator extends BaseReflectionKitComparator implements Comparator {
120 public int compare(Object o1, Object o2) {
121 if (o1 == null || o2 == null) {
122 return compareForNullness(o1, o2);
123 }
124 Class c1 = (Class) o1;
125 Class c2 = (Class) o2;
126 int rc = c1.getName().compareTo(c2.getName());
127 return normaliseRC(rc);
128 }
129 }
130
131 /**
132 * A Comparator that can be used when comparing and sorting Class objects by
133 * most specific class order. If c2 is derived from c1 then it is sorted
134 * before c1. If the classes are unrelated, then its done by class
135 * alphabetic name.
136 * <p>
137 * In short it sorts in "Class Derivation / Class Name" order.
138 */
139 public static class ClassDerivationComparator extends BaseReflectionKitComparator implements Comparator {
140 public int compare(Object o1, Object o2) {
141 if (o1 == null || o2 == null) {
142 return compareForNullness(o1, o2);
143 }
144 return compareByClassDerivation((Class) o1, (Class) o2);
145 }
146 }
147
148 /**
149 * A <code>{@link Comparator}</code> that can be used when comparing and
150 * sorting <code>{@link Member}</code> objects by name, modifier and
151 * finally declaring class order.
152 *
153 * NOTE : <code>{@link java.lang.reflect.Member}</code> is the base class
154 * for {@link Constructor}, {@link Field} and {@link Method} and hence you
155 * can sort any of these types with this <code>Comparator</code>.
156 * <p>
157 * If the <code>Member</code>'s names are the same they are then sorted
158 * by modifier and parameters and finally by declaring class with the most
159 * specific class first.
160 * <p>
161 * In short it sorts in "Member / Modifiers / Parameters / Declaring Class"
162 * order.
163 */
164 public static class MemberClassComparator extends BaseReflectionKitComparator implements Comparator {
165
166 /**
167 * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
168 */
169 public int compare(Object o1, Object o2) {
170 if (o1 == null || o2 == null) {
171 return compareForNullness(o1, o2);
172 }
173 Member m1 = (Member) o1;
174 Member m2 = (Member) o2;
175
176 int rc = compareByModifiers(m1.getModifiers(), m2.getModifiers());
177 if (rc == 0) {
178 if (m1 instanceof Method && m2 instanceof Method) {
179 rc = compareByMethod((Method) m1, (Method) m2);
180 }
181 if (m1 instanceof Field && m2 instanceof Field) {
182 rc = compareByField((Field) m1, (Field) m2);
183 }
184 if (m1 instanceof Constructor && m2 instanceof Constructor) {
185 rc = compareByConstructor((Constructor) m1, (Constructor) m2);
186 }
187 }
188 if (rc == 0) {
189 rc = compareByClassDerivation(m1.getDeclaringClass(), m2.getDeclaringClass());
190 }
191 return normaliseRC(rc);
192 }
193
194 /**
195 * The compares the two <code>{@link Modifier}</code> values against
196 * each other in the following order
197 * <ol>
198 * <li>public</li>
199 * <li>protected</li>
200 * <li>abstract</li>
201 * <li>final</li>
202 * <li>native</li>
203 * <li>interface</li>
204 * <li>static</li>
205 * </ol>
206 *
207 * @param modifier1 -
208 * the modifier of the first <code>Member</code>
209 * @param modifier2 -
210 * the modifier of the second <code>Member</code>
211 * @return -1, 0 or 1 as per {@link Comparator}
212 */
213 protected int compareByModifiers(int modifier1, int modifier2) {
214 int weights[] = new int[2];
215 int modifiers[] = new int[] { modifier1, modifier2 };
216 for (int i = 0; i < modifiers.length; i++) {
217 int mod = modifiers[i];
218 if (Modifier.isPublic(mod))
219 weights[i] |= 0x10000000;
220 if (Modifier.isProtected(mod))
221 weights[i] |= 0x01000000;
222 if (Modifier.isProtected(mod))
223 weights[i] |= 0x00100000;
224 if (Modifier.isAbstract(mod))
225 weights[i] |= 0x00010000;
226 if (Modifier.isFinal(mod))
227 weights[i] |= 0x00001000;
228 if (Modifier.isNative(mod))
229 weights[i] |= 0x00000100;
230 if (Modifier.isInterface(mod))
231 weights[i] |= 0x10000010;
232 if (Modifier.isStatic(mod))
233 weights[i] |= 0x00000001;
234 // dont worry about the rest
235 }
236 int rc = weights[0] - weights[1];
237 return normaliseRC(rc);
238 }
239
240 /**
241 * The compares the two <code>{@link Method}</code> values against
242 * each other in the following order :
243 *
244 * <ol>
245 * <li>Method name</li>
246 * <li>Method return type</li>
247 * <li>Method parameter count</li>
248 * <li>Method parameter types</li>
249 * </ol>
250 *
251 */
252 protected int compareByMethod(Method m1, Method m2) {
253 int rc = m1.getName().compareTo(m2.getName());
254 if (rc == 0) {
255 rc = compareByClassDerivation(m1.getReturnType(), m1.getReturnType());
256 if (rc == 0) {
257 Class[] m1ParamTypes = m1.getParameterTypes();
258 Class[] m2ParamTypes = m2.getParameterTypes();
259 rc = m1ParamTypes.length - m2ParamTypes.length;
260 if (rc == 0) {
261 for (int i = 0; i < m1ParamTypes.length; i++) {
262 rc = compareByClassDerivation(m1ParamTypes[i], m1ParamTypes[i]);
263 if (rc == 0)
264 break;
265 }
266 }
267 }
268 }
269 return normaliseRC(rc);
270 }
271
272 /**
273 * The compares the two <code>{@link Field}</code> values against
274 * each other in the following order :
275 *
276 * <ol>
277 * <li>Field name</li>
278 * <li>Field type</li>
279 * </ol>
280 *
281 */
282 protected int compareByField(Field f1, Field f2) {
283 if (f1 == null || f2 == null) {
284 return compareForNullness(f1, f2);
285 }
286 int rc = f1.getName().compareTo(f2.getName());
287 if (rc == 0) {
288 rc = compareByClassDerivation(f1.getType(), f1.getType());
289 }
290 return normaliseRC(rc);
291 }
292
293 /**
294 * The compares the two <code>{@link Constructor}</code> values against
295 * each other in the following order :
296 *
297 * <ol>
298 * <li>The Constructor parameter count</li>
299 * <li>The Constructor parameter type</li>
300 * </ol>
301 *
302 */
303 protected int compareByConstructor(Constructor c1, Constructor c2) {
304 if (c1 == null || c2 == null) {
305 return compareForNullness(c1, c2);
306 }
307 Class[] c1ParamTypes = c1.getParameterTypes();
308 Class[] c2ParamTypes = c2.getParameterTypes();
309 int rc = c1ParamTypes.length - c2ParamTypes.length;
310 if (rc == 0) {
311 for (int i = 0; i < c1ParamTypes.length; i++) {
312 rc = compareByClassDerivation(c1ParamTypes[i], c1ParamTypes[i]);
313 if (rc == 0)
314 break;
315 }
316 }
317 return normaliseRC(rc);
318 }
319
320 }
321
322 /**
323 * A <code>{@link Comparator}</code> that can be used when comparing and
324 * sorting <code>{@link Member}</code> objects by most specific declaring
325 * class order, then followed by member name.
326 * <p>
327 * In short it sorts in "Class / Member" order.
328 *
329 * @see echopoint.util.ReflectionKit.MemberClassComparator
330 */
331 public static class ClassMemberComparator extends MemberClassComparator {
332 /**
333 * @see echopoint.util.ReflectionKit.MemberClassComparator#compare(java.lang.Object, java.lang.Object)
334 */
335 public int compare(Object o1, Object o2) {
336 if (o1 == null || o2 == null) {
337 return compareForNullness(o1, o2);
338 }
339 Member m1 = (Member) o1;
340 Member m2 = (Member) o2;
341
342 Class c1 = m1.getDeclaringClass();
343 Class c2 = m2.getDeclaringClass();
344 if (c1.equals(c2)) {
345 int rc = compareByModifiers(m1.getModifiers(), m2.getModifiers());
346 if (rc == 0) {
347 if (m1 instanceof Method && m2 instanceof Method) {
348 rc = compareByMethod((Method) m1, (Method) m2);
349 }
350 if (m1 instanceof Field && m2 instanceof Field) {
351 rc = compareByField((Field) m1, (Field) m2);
352 }
353 if (m1 instanceof Constructor && m2 instanceof Constructor) {
354 rc = compareByConstructor((Constructor) m1, (Constructor) m2);
355 }
356 }
357 return normaliseRC(rc);
358 } else {
359 return compareByClassDerivation(c1, c2);
360 }
361 }
362 }
363
364 /**
365 * A <code>{@link Comparator}</code> that can be used when comparing and
366 * sorting <code>{@link Method}</code> objects by name, modifier and class
367 * order.
368 * <p>
369 * If the Methods's names are the same they are sorted in declaring class
370 * order with the most specific class first.
371 * <p>
372 * In short it sorts in "Method / Declaring Class" order.
373 */
374 public static class MethodClassComparator extends MemberClassComparator {
375 }
376
377 /**
378 * A <code>{@link Comparator}</code> that can be used when comparing and
379 * sorting <code>{@link Method}</code> objects by most specific declaring
380 * class order, then followed by method name and parameters.
381 * <p>
382 * In short it sorts in "Class / Method" order.
383 */
384 public static class ClassMethodComparator extends ClassMemberComparator {
385 }
386
387 /**
388 * A <code>{@link Comparator}</code> that can be used when comparing and
389 * sorting <code>{@link Field}</code> objects by name, modifier and class
390 * order.
391 * <p>
392 * If the Field's names are the same they are sorted in declaring class
393 * order with the most specific class first.
394 * <p>
395 * In short it sorts in "Field / Declaring Class" order.
396 */
397 public static class FieldClassComparator extends MemberClassComparator {
398 }
399
400 /**
401 * A <code>{@link Comparator}</code> that can be used when comparing and
402 * sorting <code>{@link Field}</code> objects by most specific declaring
403 * class order, then followed by Field name and parameters.
404 * <p>
405 * In short it sorts in "Class / Field" order.
406 */
407 public static class ClassFieldComparator extends ClassMemberComparator {
408 }
409
410 /**
411 * A <code>{@link Comparator}</code> that can be used when comparing and
412 * sorting <code>{@link Constructor}</code> objects by most specific
413 * declaring class order, then followed by Constructor name and parameters.
414 * <p>
415 * In short it sorts in "Class / Constructor" order.
416 */
417 public static class ClassConstructorComparator extends ClassMemberComparator {
418 }
419
420 /**
421 * <code>MethodSearchCriteria</code> is an interface used to determine if
422 * a method matches some search criteria.
423 */
424 public static interface MethodSearchCriteria {
425 public boolean isMethodOK(Class methodClass, Method method);
426 }
427
428 /** not instantiable */
429 private ReflectionKit() {
430 }
431
432 /**
433 * Returns true if the method is in fact a Java Bean getter method. ie is
434 * starts with 'get' or 'is' and takes no parameters and returns a value and
435 * is not static.
436 * <p>
437 * Note that it does NOT check for public access because its valid to have a
438 * getter that isnt public.
439 *
440 * @param method -
441 * the method to examine
442 * @return true if the method is a Java Bean getter.
443 */
444 public static boolean isGetter(Method method) {
445 if (method == null)
446 return false;
447 if (Modifier.isStatic(method.getModifiers()))
448 return false;
449 if (method.getReturnType().equals(Void.TYPE))
450 return false;
451 if (method.getParameterTypes().length > 0)
452 return false;
453
454 String name = method.getName();
455 if (name.length() > 2 && name.startsWith("is"))
456 return true;
457 if (name.length() > 3 && name.startsWith("get"))
458 return true;
459 return false;
460 }
461
462 /**
463 * Returns true if the method is in fact a Java Bean setter method. ie is
464 * starts with 'set', takes one parameter and has a return value of void and
465 * is not static.
466 * <p>
467 * Note that it does NOT check for public access because its valid to have a
468 * setter that isnt public.
469 *
470 * @param method -
471 * the method to examine
472 * @return true if the method is a Java Bean setter.
473 */
474 public static boolean isSetter(Method method) {
475 if (method == null)
476 return false;
477 if (Modifier.isStatic(method.getModifiers()))
478 return false;
479 if (!method.getReturnType().equals(Void.TYPE))
480 return false;
481 if (method.getParameterTypes().length != 1)
482 return false;
483
484 String name = method.getName();
485 if (name.length() > 3 && name.startsWith("set"))
486 return true;
487 return false;
488 }
489
490 /**
491 * Takes a bean property method name and removes any 'get'/'is'/'set' at the
492 * front and then decapitalizes the rest of the name according to the Java
493 * Bean Spec.
494 *
495 * @param name -
496 * the name of the method or field name to change
497 * @return - the decapitalized name
498 */
499 public static String decapitalize(String name) {
500 if (name.startsWith("is"))
501 name = name.substring(2);
502 else if (name.startsWith("get"))
503 name = name.substring(3);
504 else if (name.startsWith("set"))
505 name = name.substring(3);
506 if (name == null || name.length() == 0) {
507 return name;
508 }
509 if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) && Character.isUpperCase(name.charAt(0))) {
510 return name;
511 }
512 char chars[] = name.toCharArray();
513 chars[0] = Character.toLowerCase(chars[0]);
514 return new String(chars);
515 }
516
517 /**
518 * This method will returns member methods of the targetClass that meet a
519 * specified search criteria. The targetClass will be search up to and
520 * including the stopClass.
521 * <p>
522 * The results are sorted by method name within derived class. The most
523 * specific class methods will be returned first.
524 * <p>
525 * If stopClass is not a superclass or superinterface of targetClass, then
526 * Object.class is used.
527 *
528 *
529 * @param targetClass -
530 * the class to check for methods
531 * @param stopClass -
532 * the supertype to stop at.
533 * @param methodSearchCriteria -
534 * the MethodSearchCirteria to use
535 * @return and array of Methods or Method[0] if there are none that match
536 * the criteria
537 *
538 * @see MethodSearchCriteria
539 */
540 public static Method[] getMethods(Class targetClass, Class stopClass, MethodSearchCriteria methodSearchCriteria) {
541 if (targetClass == null || stopClass == null)
542 return new Method[0];
543
544 if (!stopClass.isAssignableFrom(targetClass))
545 stopClass = Object.class;
546
547 int methodCount = 0;
548 int index = 0;
549 Method[] methods;
550 Class currentClass = targetClass;
551 List methodList = new ArrayList();
552 do {
553 methods = currentClass.getDeclaredMethods();
554 List subMethodList = new ArrayList();
555 if (methods.length > 0) {
556 for (int i = 0; i < methods.length; i++) {
557 Method m = methods[i];
558 // does it meet our matching criteria
559 if (methodSearchCriteria.isMethodOK(currentClass, m)) {
560 subMethodList.add(m);
561 methodCount++;
562 }
563 }
564 // sort the methods and add to the master list
565 Collections.sort(subMethodList, new ClassMethodComparator());
566 for (Iterator iter = subMethodList.iterator(); iter.hasNext();) {
567 methodList.add(iter.next());
568 }
569 }
570 if (currentClass == stopClass)
571 break;
572 currentClass = currentClass.getSuperclass();
573 } while (currentClass != null);
574
575 methods = new Method[methodCount];
576 for (Iterator iter = methodList.iterator(); iter.hasNext();) {
577 Method method = (Method) iter.next();
578 methods[index++] = method;
579 }
580 Arrays.sort(methods,new ClassMethodComparator());
581 return methods;
582 }
583
584 /**
585 * Returns an array containing Method objects reflecting all the member
586 * methods of the class or interface represented by the targetClass object,
587 * including those declared by the class or interface and and those
588 * inherited from superclasses and superinterfaces up until stopClass.
589 * <p>
590 * All public, protected, default (package) access, and private methods are
591 * returned.
592 * <p>
593 * If stopClass is not a superclass or superinterface of targetClass, then
594 * Object.class is used.
595 *
596 * @param targetClass -
597 * the class to check for methods
598 * @param stopClass -
599 * the supertype to stop at.
600 * @return and array of Methods or Method[0] if there are none
601 */
602 public static Method[] getAllDeclaredMethods(Class targetClass, Class stopClass) {
603 return getMethods(targetClass, stopClass, new MethodSearchCriteria() {
604 public boolean isMethodOK(Class methodClass, Method method) {
605 return true;
606 }
607 });
608 }
609
610 /**
611 * Shorthand method for
612 * ReflectionKit.getAllMethods(targetClass,Object.class);
613 *
614 * @see ReflectionKit#getAllDeclaredMethods(Class, Class)
615 */
616 public static Method[] getAllDeclaredMethods(Class targetClass) {
617 return getAllDeclaredMethods(targetClass, Object.class);
618 }
619
620 /**
621 * Returns an array containing Method objects reflecting all the member
622 * methods of the class or interface represented by the targetClass object,
623 * including those declared by the class or interface and and those
624 * inherited from superclasses and superinterfaces up until stopClass.
625 * <p>
626 * Only public methods are returned.
627 * <p>
628 * If stopClass is not a superclass or superinterface of targetClass, then
629 * Object.class is used.
630 *
631 * @param targetClass -
632 * the class to check for methods
633 * @param stopClass -
634 * the supertype to stop at.
635 * @return and array of Methods or Method[0] if there are none
636 */
637 public static Method[] getAllPublicMethods(Class targetClass, Class stopClass) {
638 return getMethods(targetClass, stopClass, new MethodSearchCriteria() {
639 public boolean isMethodOK(Class methodClass, Method method) {
640 return Modifier.isPublic(method.getModifiers());
641 }
642 });
643 }
644
645 /**
646 * Returns an array containing getter Method objects reflecting all the
647 * member methods of the class or interface represented by the targetClass
648 * object, including those declared by the class or interface and and those
649 * inherited from superclasses and superinterfaces up until stopClass.
650 * <p>
651 * Only methods matching the Java Bean specifiction for a getter method are
652 * returned.
653 * <p>
654 * If stopClass is not a superclass or superinterface of targetClass, then
655 * Object.class is used.
656 * <p>
657 * The methods are returned in method name order using the MethodComparator
658 * comparator.
659 *
660 * @param targetClass -
661 * the class to check for methods
662 * @param stopClass -
663 * the supertype to stop at.
664 * @return and array of Methods or Method[0] if there are none
665 */
666 public static Method[] getAllBeanGetterMethods(Class targetClass, Class stopClass) {
667 Method[] methods = getMethods(targetClass, stopClass, new MethodSearchCriteria() {
668 public boolean isMethodOK(Class methodClass, Method method) {
669 return isGetter(method);
670 }
671 });
672 Arrays.sort(methods, new ClassMethodComparator());
673 return methods;
674 }
675
676 /**
677 * Returns an array containing setter Method objects reflecting all the
678 * member methods of the class or interface represented by the targetClass
679 * object, including those declared by the class or interface and and those
680 * inherited from superclasses and superinterfaces up until stopClass.
681 * <p>
682 * Only methods matching the Java Bean specifiction for a setter method are
683 * returned.
684 * <p>
685 * If stopClass is not a superclass or superinterface of targetClass, then
686 * Object.class is used.
687 *
688 * @param targetClass -
689 * the class to check for methods
690 * @param stopClass -
691 * the supertype to stop at.
692 * @return and array of Methods or Method[0] if there are none
693 */
694 public static Method[] getAllBeanSetterMethods(Class targetClass, Class stopClass) {
695 Method[] methods = getMethods(targetClass, stopClass, new MethodSearchCriteria() {
696 public boolean isMethodOK(Class methodClass, Method method) {
697 return isSetter(method);
698 }
699 });
700 Arrays.sort(methods, new ClassMethodComparator());
701 return methods;
702 }
703
704 /**
705 * Returns an array containing getter and setter Method objects reflecting
706 * all the member methods of the class or interface represented by the
707 * targetClass object, including those declared by the class or interface
708 * and and those inherited from superclasses and superinterfaces up until
709 * stopClass.
710 * <p>
711 * Only methods matching the Java Bean specifiction for a getter or setter
712 * method are returned.
713 * <p>
714 * If stopClass is not a superclass or superinterface of targetClass, then
715 * Object.class is used.
716 *
717 * @param targetClass -
718 * the class to check for methods
719 * @param stopClass -
720 * the supertype to stop at.
721 * @return and array of Methods or Method[0] if there are none
722 */
723 public static Method[] getAllBeanMethods(Class targetClass, Class stopClass) {
724 Method[] methods = getMethods(targetClass, stopClass, new MethodSearchCriteria() {
725 public boolean isMethodOK(Class methodClass, Method method) {
726 return isSetter(method) || isGetter(method);
727 }
728 });
729 Arrays.sort(methods, new MethodClassComparator());
730 return methods;
731 }
732
733 /**
734 * Returns the getter method for a given setter method. It is determined by
735 * getting the best matching property name as well as matching return type
736 * to setter parameter type.
737 *
738 * @param beanSetter -
739 * a bean setter method of class in question
740 * @return the bean getter method or null if it cant be found
741 * @throws IllegalArgumentException -
742 * if the method passed in is null
743 */
744 public static Method getBeanGetter(Method beanSetter) {
745 if (beanSetter == null)
746 throw new IllegalArgumentException("beanSetter must be non null");
747 String setterName = decapitalize(beanSetter.getName());
748 Class setterType = beanSetter.getParameterTypes()[0];
749
750 Method getters[] = getAllBeanGetterMethods(beanSetter.getDeclaringClass(), Object.class);
751 for (int i = 0; i < getters.length; i++) {
752 Method getter = getters[i];
753 String getterName = decapitalize(getter.getName());
754 if (getterName.equals(setterName)) {
755 if (getter.getReturnType().equals(setterType))
756 return getter;
757 }
758 }
759 return null;
760 }
761
762 /**
763 * @see ReflectionKit#getClassHierarchy(Class, Class)
764 */
765 public static Class[] getClassHierarchy(Class targetClass) {
766 return getClassHierarchy(targetClass, Object.class);
767 }
768
769 /**
770 * Returns an array containing their hierarchy of class objects for the
771 * given class object. The array is sorted in most specific class order, ie
772 * the first class is the class object itself, followed by its super class,
773 * all the way down to stopClass.
774 * <p>
775 * If stopClass is not a superclass or superinterface of targetClass, then
776 * Object.class is used.
777 *
778 * @param targetClass -
779 * the class to start the hierarchial search from
780 * @param stopClass -
781 * the supertype to stop at.
782 * @return and array of Class or Class[0] if there are none
783 */
784 public static Class[] getClassHierarchy(Class targetClass, Class stopClass) {
785 if (targetClass == null || stopClass == null)
786 return new Class[0];
787
788 if (targetClass == stopClass)
789 return new Class[] { targetClass };
790
791 if (!stopClass.isAssignableFrom(targetClass))
792 stopClass = Object.class;
793
794 List list = new ArrayList();
795 list.add(targetClass);
796 do {
797 targetClass = targetClass.getSuperclass();
798 if (targetClass != null)
799 list.add(targetClass);
800 if (targetClass.equals(stopClass))
801 break;
802 } while (targetClass != null);
803 return (Class[]) list.toArray(new Class[list.size()]);
804 }
805
806 /**
807 * This method can be called to determine whether an object has the specific
808 * named method. This is useful in allowing you to conditionally call a
809 * method that may be present in a future version of a class. For example
810 * when you code is built to Java 1.3, you could call a Java 1.4 API
811 * conditionally if its present.
812 *
813 * @param methodName -
814 * the name of the method to invoke
815 * @param paramTypes -
816 * the types of the methods parameters if this is null then it is
817 * deemed Class[0]
818 * @param returnType -
819 * the methods return type, if this is null then it is deemed
820 * Void.TYPE
821 * @param targetObj -
822 * the object to invoke the method on
823 * @return - the return value of the method if any or null.
824 *
825 */
826 public static boolean hasMethod(String methodName, Class[] paramTypes, Class returnType, Object targetObj) {
827 if (targetObj == null)
828 throw new IllegalArgumentException("You must provide a target Object!");
829 if (paramTypes == null)
830 paramTypes = new Class[0];
831 if (returnType == null)
832 returnType = Void.TYPE;
833
834 final String testMethodName = methodName;
835 final Class[] testParamTypes = paramTypes;
836 final Class testReturnType = returnType;
837 Method[] methods = getMethods(targetObj.getClass(), Object.class, new MethodSearchCriteria() {
838 /**
839 * @see echopoint.util.ReflectionKit.MethodSearchCriteria#isMethodOK(java.lang.Class,
840 * java.lang.reflect.Method)
841 */
842 public boolean isMethodOK(Class methodClass, Method method) {
843 if (method.getName().equals(testMethodName)) {
844 if (method.getReturnType().equals(testReturnType)) {
845 Class[] paramTypes = method.getParameterTypes();
846 if (paramTypes.length == testParamTypes.length) {
847 for (int i = 0; i < paramTypes.length; i++) {
848 if (!testParamTypes[i].equals(paramTypes[i]))
849 return false;
850 }
851 return true;
852 }
853 }
854 }
855 return false;
856 }
857 });
858 if (methods.length > 0) {
859 return true;
860 }
861 return false;
862 }
863
864 /**
865 * This method can be called to invoke a specific named method of an object.
866 * This is useful in allowing you to conditionally call a method that may be
867 * present in a future version of a class. For example when you code is
868 * built to Java 1.3, you could call a Java 1.4 API conditionally if its
869 * present.
870 *
871 * @param methodName -
872 * the name of the method to invoke
873 * @param paramTypes -
874 * the types of the methods parameters if this is null then it is
875 * deemed Class[0]
876 * @param returnType -
877 * the methods return type, if this is null then it is deemed
878 * Void.TYPE
879 * @param targetObj -
880 * the object to invoke the method on
881 * @param params -
882 * the parameters for the method if this is null then it is
883 * deemed Object[0]
884 * @return - the return value of the method if any or null.
885 *
886 */
887 public static Object invokeIfPresent(String methodName, Class[] paramTypes, Class returnType, Object targetObj, Object[] params) {
888 if (targetObj == null)
889 throw new IllegalArgumentException("You must provide a target Object!");
890 if (paramTypes == null)
891 paramTypes = new Class[0];
892 if (params == null)
893 params = new Object[0];
894 if (returnType == null)
895 returnType = Void.TYPE;
896
897 final String testMethodName = methodName;
898 final Class[] testParamTypes = paramTypes;
899 final Class testReturnType = returnType;
900 Method[] methods = getMethods(targetObj.getClass(), Object.class, new MethodSearchCriteria() {
901 /**
902 * @see echopoint.util.ReflectionKit.MethodSearchCriteria#isMethodOK(java.lang.Class,
903 * java.lang.reflect.Method)
904 */
905 public boolean isMethodOK(Class methodClass, Method method) {
906 if (method.getName().equals(testMethodName)) {
907 if (method.getReturnType().equals(testReturnType)) {
908 Class[] paramTypes = method.getParameterTypes();
909 if (paramTypes.length == testParamTypes.length) {
910 for (int i = 0; i < paramTypes.length; i++) {
911 if (!testParamTypes[i].equals(paramTypes[i]))
912 return false;
913 }
914 return true;
915 }
916 }
917 }
918 return false;
919 }
920 });
921 if (methods.length > 0) {
922 try {
923 return methods[0].invoke(targetObj, params);
924 } catch (IllegalArgumentException e) {
925 throw new RuntimeException(e);
926 } catch (IllegalAccessException e) {
927 throw new RuntimeException(e);
928 } catch (InvocationTargetException e) {
929 throw new RuntimeException(e);
930 }
931 }
932 return null;
933 }
934 }