001: package junitx.ddtunit.util;
002:
003: import java.lang.reflect.Array;
004: import java.lang.reflect.Constructor;
005: import java.lang.reflect.Field;
006: import java.lang.reflect.Method;
007: import java.util.HashSet;
008: import java.util.Iterator;
009: import java.util.Set;
010: import java.util.Vector;
011:
012: import org.apache.log4j.Logger;
013:
014: /**
015: * This example is taken from Thinking in Java secd. ed. (p.680f) This class
016: * used reflection to show all the methods of a class, even if the methods are
017: * defined in the base class.
018: *
019: * @author jgellien
020: */
021: public class ClassAnalyser {
022: /**
023: * Constant to define constructor select in method
024: * {@link #findMethodByParams(String, String, Class[])}
025: */
026: public final static String CLASS_CONSTRUCTOR = "class$";
027:
028: private final static String usage = "usage: \n"
029: + "ShowMethods qualified.class.name\n"
030: + "To show all methods in class or: \n"
031: + "ShowMethods qualified.class.name word\n"
032: + "To search for methods involving 'word'";
033:
034: private static Logger log = Logger.getLogger(ClassAnalyser.class);
035:
036: /**
037: * Constructor for the ClassAnalyser object
038: */
039: private ClassAnalyser() {
040: // no special init
041: }
042:
043: /**
044: * The main program for the ShowMethods class
045: *
046: * @param args The command line arguments
047: */
048: public static void main(String[] args) {
049: if (args.length < 1) {
050: System.out.println(ClassAnalyser.usage);
051: System.exit(0);
052: }
053:
054: System.out.println("ClassAnalyser started with class "
055: + args[0]);
056:
057: if (args.length == 1) {
058: ClassAnalyser.showAllMethods(args[0]);
059: ClassAnalyser.showAllFields(args[0]);
060: } else {
061: ClassAnalyser.showSelectedMethods(args[0], args[1]);
062: }
063:
064: System.out.println("ClassAnalyser end.");
065: }
066:
067: /**
068: * Extract simple class name without package information
069: *
070: * @param obj object to analyse
071: *
072: * @return class name
073: */
074: public static String getShortName(Object obj) {
075: String className = obj.getClass().getName();
076:
077: return getShortName(className);
078: }
079:
080: /**
081: * Extract simple class name without package information
082: *
083: * @param className to analyse
084: *
085: * @return name without package extension
086: */
087: public static String getShortName(Class className) {
088: String localName = className.getName();
089:
090: return getShortName(localName);
091: }
092:
093: /**
094: * Extract simple class name without package information
095: *
096: * @param className qualified class name to analyse
097: *
098: * @return class name
099: */
100: public static String getShortName(String className) {
101: String shortName = className.substring(className
102: .lastIndexOf(".") + 1, className.length());
103:
104: return shortName;
105: }
106:
107: /**
108: * Display all attributes/fields of class quaifiedClassName to the
109: * configured appender specified by Log4j
110: *
111: * @param qualifiedClassName name of class under analysis
112: *
113: * @throws RuntimeException ClassAnalyserException if an error occures
114: */
115: public static void showAllFields(String qualifiedClassName) {
116: try {
117: Class c = Class.forName(qualifiedClassName);
118: Field[] fields = c.getDeclaredFields();
119: log.debug("===Class " + qualifiedClassName + " fields:");
120:
121: for (int i = 0; i < fields.length; i++) {
122: log.debug(fields[i].toString());
123: }
124: } catch (ClassNotFoundException ex) {
125: log.error("No such class: " + qualifiedClassName, ex);
126: throw new ClassAnalyserException("No class found of type "
127: + qualifiedClassName, ex);
128: }
129: }
130:
131: /**
132: * Display all attributes/fields of class quaifiedClassName matching
133: * searchTerm to the configured appender specified by Log4j
134: *
135: * @param qualifiedClassName name of class under analysis
136: * @param searchTerm match string to check against
137: *
138: * @throws RuntimeException ClassAnalyserException if an error occures
139: */
140: public static void showSelectedFields(String qualifiedClassName,
141: String searchTerm) {
142: try {
143: Class c = Class.forName(qualifiedClassName);
144: Field[] fields = c.getDeclaredFields();
145: log.debug("===Class " + qualifiedClassName
146: + " fields selected by '" + searchTerm + "':");
147:
148: for (int i = 0; i < fields.length; i++) {
149: if (fields[i].toString().indexOf(searchTerm) != -1) {
150: log.debug(fields[i].toString());
151: }
152: }
153: } catch (ClassNotFoundException ex) {
154: log.error("No such class: " + qualifiedClassName, ex);
155: throw new ClassAnalyserException("No class found of type "
156: + qualifiedClassName, ex);
157: }
158: }
159:
160: /**
161: * Search for exact match of searchTerm in the list of declared fields of
162: * the class qualifiedClassName.
163: *
164: * @param qualifiedClassName Description of the Parameter
165: * @param searchTerm Description of the Parameter
166: *
167: * @return Field that was found or null if field does not exists
168: *
169: * @throws RuntimeException ClassAnalyserException if an error occures
170: */
171: public static Field getSelectedField(String qualifiedClassName,
172: String searchTerm) {
173: Field localField = null;
174: if (qualifiedClassName != null && searchTerm != null) {
175: try {
176: Class c = Class.forName(qualifiedClassName);
177:
178: while (c != null) {
179: // this gets you only not inherited fiels ==> you have to
180: // iterate over
181: // all parent classes
182: Field[] fields = c.getDeclaredFields();
183: String className = c.getName();
184: log.debug("===Class " + className
185: + " fields selected by '" + searchTerm
186: + "':");
187:
188: boolean found = false;
189:
190: for (int i = 0; i < fields.length; i++) {
191: // use Field.getName() instead of Field.toString()
192: // if field does not exists getName() returns a string
193: // starting with
194: // "class$<full qualified class name>"
195: String fieldName = fields[i].getName();
196: log.debug("check search term <" + searchTerm
197: + "> in field <" + fieldName + ">");
198:
199: if (!fieldName.startsWith("class$")
200: && fieldName.equals(searchTerm)) {
201: // first hit
202: if (!found) {
203: localField = fields[i];
204: found = true;
205: log.debug("First hit.");
206:
207: break;
208: } else {
209: throw new IllegalArgumentException(
210: "double count of field "
211: + searchTerm);
212: }
213: }
214: }
215:
216: if (!found) {
217: c = c.getSuperclass();
218: log.debug("=== No Hit");
219: } else {
220: c = null;
221: log.debug("=== End of Check");
222: }
223: }
224: } catch (ClassNotFoundException ex) {
225: log.error("No such class: " + qualifiedClassName, ex);
226: throw new ClassAnalyserException(
227: "No class found of type " + qualifiedClassName,
228: ex);
229: }
230: }
231: return localField;
232: }
233:
234: /**
235: * Display all methods of class quaifiedClassName to the configured appender
236: * specified by Log4j
237: *
238: * @param qualifiedClassName name of class under analysis
239: *
240: * @throws RuntimeException ClassAnalyserException if an error occures
241: */
242: public static void showAllMethods(String qualifiedClassName) {
243: try {
244: Class c = Class.forName(qualifiedClassName);
245: Method[] m = c.getMethods();
246: Constructor[] ctor = c.getConstructors();
247: log.debug("===Class " + qualifiedClassName + " methods:");
248:
249: for (int i = 0; i < m.length; i++) {
250: log.debug(m[i].toString());
251: }
252:
253: log.debug("===Class " + qualifiedClassName
254: + " constructors:");
255:
256: for (int i = 0; i < ctor.length; i++) {
257: log.debug(ctor[i].toString());
258: }
259: } catch (ClassNotFoundException ex) {
260: log.error("No such class: " + qualifiedClassName, ex);
261: throw new ClassAnalyserException("No class found of type "
262: + qualifiedClassName, ex);
263: }
264: }
265:
266: /**
267: * Put all method names of the class qualifiedClassName into a String array
268: * and return it.
269: *
270: * @param qualifiedClassName Description of the Parameter
271: *
272: * @return array of all method names of the class to analyse
273: *
274: * @throws RuntimeException ClassAnalyserException if an error occures
275: */
276: public static String[] getAllMethods(String qualifiedClassName) {
277: Class c;
278: Method[] m;
279:
280: try {
281: c = Class.forName(qualifiedClassName);
282: m = c.getMethods();
283: log.debug("===Class " + qualifiedClassName + " methods:");
284:
285: String[] methods = new String[m.length];
286:
287: for (int i = 0; i < m.length; i++) {
288: log.debug(m[i].toString());
289: methods[i] = m[i].getName();
290: }
291:
292: return methods;
293: } catch (ClassNotFoundException ex) {
294: log.error("No such class: " + qualifiedClassName, ex);
295: throw new ClassAnalyserException("No class found of type "
296: + qualifiedClassName, ex);
297: }
298: }
299:
300: /**
301: * Display all constructors and methods of qualifiedClassName class which
302: * match with searchTerm. <br/>Output is set to Log4J Info level of this
303: * classes Logger.
304: *
305: * @param qualifiedClassName Name of class to analyse
306: * @param searchTerm Match term for methods to display
307: *
308: * @throws RuntimeException ClassAnalyserException if an error occures
309: */
310: public static void showSelectedMethods(String qualifiedClassName,
311: String searchTerm) {
312: try {
313: Class c = Class.forName(qualifiedClassName);
314: Method[] m = c.getMethods();
315: Constructor[] ctor = c.getConstructors();
316:
317: for (int i = 0; i < m.length; i++) {
318: if (m[i].toString().indexOf(searchTerm) != -1) {
319: log.info(m[i].toString());
320: }
321: }
322:
323: for (int i = 0; i < ctor.length; i++) {
324: if (ctor[i].toString().indexOf(searchTerm) != -1) {
325: log.info(ctor[i].toString());
326: }
327: }
328: } catch (ClassNotFoundException ex) {
329: log.error("No such class: " + qualifiedClassName, ex);
330: throw new ClassAnalyserException("No class found of type "
331: + qualifiedClassName, ex);
332: }
333: }
334:
335: /**
336: * Find a Constructor/Method of class className by using the argument list
337: * args and try to vary arguments which could be of primitive type. <br/>
338: * The args list only contains classes.
339: *
340: * @param className
341: * @param methodName
342: * @param args
343: * @return Method/Constructor object
344: */
345: public static Object findMethodByParams(String className,
346: String methodName, Class[] args) {
347: Object method = null;
348:
349: try {
350: Object[] methods;
351: Class myClass = Class.forName(className);
352: if (CLASS_CONSTRUCTOR.compareTo(methodName) == 0) {
353: methods = myClass.getDeclaredConstructors();
354: } else {
355: methods = myClass.getDeclaredMethods();
356: }
357:
358: Vector searchMethods = filterByNameAndParamCount(methods,
359: methodName, args.length);
360: boolean found = false;
361: for (Iterator iter = searchMethods.iterator(); iter
362: .hasNext();) {
363: Object myMethod = iter.next();
364:
365: found = filterByParam(myMethod, args, 0);
366:
367: if (found) {
368: // exit with first valid method
369: method = myMethod;
370:
371: break;
372: }
373: }
374: // if method not found in active clazz search in superclazzes
375: if (!found) {
376: Set super Clazzes = getSuperElements(myClass);
377: for (Iterator iter = super Clazzes.iterator(); iter
378: .hasNext();) {
379: Class clazz = (Class) iter.next();
380: method = findMethodByParams(clazz.getName(),
381: methodName, args);
382: if (method != null) {
383: found = true;
384: break;
385: }
386: }
387: }
388: } catch (Exception e) {
389: throw new ClassAnalyserException(
390: "Could not find constructor of class " + className,
391: e);
392: }
393:
394: return method;
395: }
396:
397: /**
398: * Check if parameter type on position pos of constructor method and args
399: * are the same. <br/>If the actual position arguments are the same the
400: * method is called with (pos+1).
401: *
402: * @param constructor
403: * @param list of class arguments to match
404: * @param pos of argument to match
405: * @return true if parameter matches
406: */
407: private static boolean filterByParam(Object method, Class[] args,
408: int pos) {
409: boolean valid = false;
410: Class arg;
411: Class argOnPos;
412:
413: // exit criterion for recursion
414: if (pos >= args.length) {
415: valid = true;
416: } else {
417: arg = args[pos];
418:
419: if (java.lang.reflect.Constructor.class.isInstance(method)) {
420: argOnPos = ((Constructor) method).getParameterTypes()[pos];
421: } else {
422: argOnPos = ((Method) method).getParameterTypes()[pos];
423: }
424:
425: // check if argument class is a valid argument substitution of
426: // constructor signature argument class. E.g. check if argument
427: // is perhaps derived from a superclass of selected
428: // constructor/method argument
429: if (arg.equals(argOnPos)
430: || getSuperElements(arg).contains(argOnPos)) {
431: valid = filterByParam(method, args, pos + 1);
432:
433: // This path does not work, go the primitive way
434: if (!valid) {
435: valid = filterByPrimitiveParam(method, args,
436: pos + 1);
437: }
438: } else {
439: valid = filterByPrimitiveParam(method, args, pos);
440: }
441: }
442:
443: return valid;
444: }
445:
446: public static Set getSuperElements(Class clazz) {
447: Set clazzList = new HashSet();
448: Class[] interfaces = clazz.getInterfaces();
449: for (int count = 0; count < interfaces.length; count++) {
450: clazzList.addAll(getSuperElements(interfaces[count]));
451: clazzList.add(interfaces[count]);
452: }
453: Class super Clazz = clazz.getSuperclass();
454: if (super Clazz == null) {
455: return clazzList;
456: } else {
457: Set result = getSuperElements(super Clazz);
458: result.add(super Clazz);
459: result.addAll(clazzList);
460: return result;
461: }
462: }
463:
464: private static boolean filterByPrimitiveParam(Object method,
465: Class[] args, int pos) {
466: boolean valid = false;
467: Class arg;
468: Class argOnPos;
469:
470: // exit criterion for recursion
471: if (pos >= args.length) {
472: valid = true;
473: } else {
474: arg = args[pos];
475:
476: if (java.lang.reflect.Constructor.class.isInstance(method)) {
477: argOnPos = ((Constructor) method).getParameterTypes()[pos];
478: } else {
479: argOnPos = ((Method) method).getParameterTypes()[pos];
480: }
481:
482: // check if primitiv type of argOnPos exists
483: if (hasPrimitive(arg)
484: && argOnPos.equals(getPrimitiveClass(arg))) {
485: valid = filterByParam(method, args, pos + 1);
486: }
487: }
488:
489: return valid;
490: }
491:
492: /**
493: * Retrieve primitove type of specified clazz
494: *
495: * @param checkClass to retrieve associated primitive type from
496: *
497: * @return associated primitive type or null if non exists
498: */
499: public static Class getPrimitiveClass(Class checkClass) {
500: Class primitive = null;
501: boolean arrayFound = false;
502: Class verifyClazz = checkClass;
503: if (hasPrimitive(checkClass)) {
504: // check for array class
505: String name = checkClass.getName();
506: if (name.startsWith("[L")) {
507: arrayFound = true;
508: try {
509: verifyClazz = Class.forName(name.substring(2, name
510: .length() - 1));
511: } catch (ClassNotFoundException ex) {
512: throw new ClassAnalyserException(
513: "Could not construct base class from array");
514: }
515: }
516: if (verifyClazz.equals(java.lang.Integer.class)) {
517: if (arrayFound) {
518: primitive = getPrimitiveArrayClass("[I");
519: } else {
520: primitive = Integer.TYPE;
521: }
522: } else if (verifyClazz.equals(java.lang.Long.class)) {
523: if (arrayFound) {
524: primitive = getPrimitiveArrayClass("[J");
525: } else {
526: primitive = Long.TYPE;
527: }
528: } else if (verifyClazz.equals(java.lang.Short.class)) {
529: if (arrayFound) {
530: primitive = getPrimitiveArrayClass("[S");
531: } else {
532: primitive = Short.TYPE;
533: }
534: } else if (verifyClazz.equals(java.lang.Double.class)) {
535: if (arrayFound) {
536: primitive = getPrimitiveArrayClass("[D");
537: } else {
538: primitive = Double.TYPE;
539: }
540: } else if (verifyClazz.equals(java.lang.Float.class)) {
541: if (arrayFound) {
542: primitive = getPrimitiveArrayClass("[F");
543: } else {
544: primitive = Float.TYPE;
545: }
546: } else if (verifyClazz.equals(Character.class)) {
547: if (arrayFound) {
548: primitive = getPrimitiveArrayClass("[C");
549: } else {
550: primitive = Character.TYPE;
551: }
552: } else if (verifyClazz.equals(Byte.class)) {
553: if (arrayFound) {
554: primitive = getPrimitiveArrayClass("[B");
555: } else {
556: primitive = Byte.TYPE;
557: }
558: } else if (verifyClazz.equals(Boolean.class)) {
559: if (arrayFound) {
560: primitive = getPrimitiveArrayClass("[Z");
561: } else {
562: primitive = Boolean.TYPE;
563: }
564: }
565: }
566:
567: return primitive;
568: }
569:
570: /**
571: * Convert an array of object to its primitive counterpart. Values of null
572: * will be replaced by the primitive default value.
573: *
574: * @param value - array of Object to convert
575: * @return array of primitive counterpart
576: */
577: public static Object createPrimitiveArray(Object value) {
578: int valueLength = Array.getLength(value);
579: Object valueArray;
580: valueArray = Array.newInstance(ClassAnalyser
581: .getPrimitiveArrayBaseType(value.getClass()),
582: valueLength);
583: Object obj = null;
584: for (int count = 0; count < valueLength; count++) {
585: obj = Array.get(value, count);
586: if (obj != null) {
587: Array.set(valueArray, count, obj);
588: }
589: }
590: return valueArray;
591: }
592:
593: /**
594: *
595: * @param clazz
596: * @return primitive clazz
597: */
598: static public Class getPrimitiveArrayBaseType(Class clazz) {
599: String clazzName = clazz.getName();
600: Class baseClazz = null;
601: if (clazzName != null && clazzName.startsWith("[L")) {
602: clazzName = clazzName.substring(2, clazzName.length() - 1);
603: try {
604: baseClazz = Class.forName(clazzName);
605: } catch (ClassNotFoundException ex) {
606: throw new ClassAnalyserException(
607: "Could not create base class of array");
608: }
609: }
610: Class primitiveClazz = getPrimitiveClass(baseClazz);
611: return primitiveClazz;
612: }
613:
614: /**
615: * @param primitive array string presentation
616: * @return array class of primitives
617: */
618: static private Class getPrimitiveArrayClass(String primitive) {
619: Class primitiveArrayClazz = null;
620: try {
621: primitiveArrayClazz = Class.forName(primitive);
622: } catch (ClassNotFoundException ex) {
623: throw new ClassAnalyserException("Could not create "
624: + primitive + " array");
625: }
626: return primitiveArrayClazz;
627: }
628:
629: /**
630: * Verify if provided clazz has primitive type
631: *
632: * @param checkClass to look for associated primitive type
633: *
634: * @return true if primitive type is found
635: */
636: public static boolean hasPrimitive(Class checkClass) {
637: boolean check = false;
638: Class verifyClazz = checkClass;
639: // check for array class and process with content class
640: String name = checkClass.getName();
641: if (name.startsWith("[L")) {
642: try {
643: verifyClazz = Class.forName(name.substring(2, name
644: .length() - 1));
645: } catch (ClassNotFoundException ex) {
646: throw new ClassAnalyserException(
647: "Could not construct base class from array");
648: }
649: }
650: if (verifyClazz.equals(java.lang.Integer.class)
651: || verifyClazz.equals(java.lang.Short.class)
652: || verifyClazz.equals(java.lang.Long.class)
653: || verifyClazz.equals(java.lang.Float.class)
654: || verifyClazz.equals(java.lang.Double.class)
655: || verifyClazz.equals(java.lang.Character.class)
656: || verifyClazz.equals(java.lang.Byte.class)
657: || verifyClazz.equals(java.lang.Boolean.class)) {
658: check = true;
659: }
660:
661: return check;
662: }
663:
664: /**
665: * Take array and return all constructors that have the signature count
666: * defined by parameter count.
667: *
668: * @param set of constructors to filer
669: * @param name of method to process
670: * @param count of signature to filter
671: * @return Vector of valid constructors
672: */
673: private static Vector filterByNameAndParamCount(Object[] methods,
674: String methodName, int count) {
675: Vector selected = new Vector();
676:
677: for (int i = 0; i < methods.length; i++) {
678: int paramCount;
679: String myName;
680:
681: if (java.lang.reflect.Constructor.class
682: .isInstance(methods[i])) {
683: Constructor myConstr = (Constructor) methods[i];
684: myName = CLASS_CONSTRUCTOR;
685: paramCount = myConstr.getParameterTypes().length;
686: } else {
687: Method myMethod = (Method) methods[i];
688: myName = myMethod.getName();
689: paramCount = myMethod.getParameterTypes().length;
690: }
691:
692: if ((methodName.compareTo(myName) == 0)
693: && (paramCount == count)) {
694: selected.add(methods[i]);
695: }
696: }
697:
698: return selected;
699: }
700:
701: /**
702: * Extract package from object instance
703: *
704: * @return package name of object instance
705: */
706: public static String classPackage(Object obj) {
707: String packageName = "";
708: String pathSeparator = "/";
709: if (obj != null) {
710: if (obj.getClass().getPackage() != null) {
711: packageName = obj.getClass().getPackage().getName()
712: .replaceAll("\\.", pathSeparator);
713: }
714: }
715: return packageName;
716: }
717: }
|