View Javadoc

    1   package groovy.inspect;
    2
    3   import groovy.lang.GroovyObject;
    4   import groovy.lang.MetaClass;
    5   import groovy.lang.MetaMethod;
    6   import groovy.lang.PropertyValue;
    7
    8   import java.lang.reflect.Modifier;
    9   import java.lang.reflect.Method;
    10  import java.lang.reflect.Field;
    11  import java.lang.reflect.Constructor;
    12  import java.util.*;
    13
    14  import org.codehaus.groovy.runtime.InvokerHelper;
    15  import org.codehaus.groovy.runtime.DefaultGroovyMethods;
    16
    17  /***
    18   * The Inspector provides a unified access to an object's
    19   * information that can be determined by introspection.
    20   *
    21   * @author Dierk Koenig
    22   */
    23  public class Inspector {
    24      protected Object objectUnderInspection = null;
    25
    26      // Indexes to retrieve Class Property information
    27      public static final int CLASS_PACKAGE_IDX       = 0;
    28      public static final int CLASS_CLASS_IDX         = 1;
    29      public static final int CLASS_INTERFACE_IDX     = 2;
    30      public static final int CLASS_SUPERCLASS_IDX    = 3;
    31      public static final int CLASS_OTHER_IDX         = 4;
    32
    33      // Indexes to retrieve field and method information
    34      public static final int MEMBER_ORIGIN_IDX = 0;
    35      public static final int MEMBER_MODIFIER_IDX = 1;
    36      public static final int MEMBER_DECLARER_IDX = 2;
    37      public static final int MEMBER_TYPE_IDX = 3;
    38      public static final int MEMBER_NAME_IDX = 4;
    39      public static final int MEMBER_PARAMS_IDX = 5;
    40      public static final int MEMBER_VALUE_IDX = 5;
    41      public static final int MEMBER_EXCEPTIONS_IDX = 6;
    42
    43      public static final String NOT_APPLICABLE = "n/a";
    44      public static final String GROOVY = "GROOVY";
    45      public static final String JAVA = "JAVA";
    46
    47      /***
    48       * @param objectUnderInspection must not be null
    49       */
    50      public Inspector(Object objectUnderInspection) {
    51          if (null == objectUnderInspection){
    52              throw new IllegalArgumentException("argument must not be null");
    53          }
    54          this.objectUnderInspection = objectUnderInspection;
    55      }
    56
    57      /***
    58       * Get the Class Properties of the object under inspection.
    59       * @return String array to be indexed by the CLASS_xxx_IDX constants
    60       */
    61      public String[] getClassProps() {
    62          String[] result = new String[CLASS_OTHER_IDX+1];
    63          Package pack = getClassUnderInspection().getPackage();
    64          result[CLASS_PACKAGE_IDX] = "package "+ ((pack == null) ? NOT_APPLICABLE : pack.getName());
    65          String modifiers = Modifier.toString(getClassUnderInspection().getModifiers());
    66          String classOrInterface = "class";
    67          if (getClassUnderInspection().isInterface()){
    68              classOrInterface = "interface";
    69          }
    70          result[CLASS_CLASS_IDX] = modifiers + " "+ classOrInterface+" "+ shortName(getClassUnderInspection());
    71          result[CLASS_INTERFACE_IDX] = "implements ";
    72          Class[] interfaces = getClassUnderInspection().getInterfaces();
    73          for (int i = 0; i < interfaces.length; i++) {
    74              result[CLASS_INTERFACE_IDX] += shortName(interfaces[i])+ " ";
    75          }
    76          result[CLASS_SUPERCLASS_IDX] = "extends " + shortName(getClassUnderInspection().getSuperclass());
    77          result[CLASS_OTHER_IDX] = "is Primitive: "+getClassUnderInspection().isPrimitive()
    78                    +", is Array: "   +getClassUnderInspection().isArray()
    79                    +", is Groovy: "  + isGroovy();
    80          return result;
    81      }
    82
    83      public boolean isGroovy() {
    84          return getClassUnderInspection().isAssignableFrom(GroovyObject.class);
    85      }
    86
    87      /***
    88       * Gets the object being inspected.
    89       */
    90      public Object getObject() {
    91      	return objectUnderInspection;
    92      }
    93
    94      /***
    95       * Get info about usual Java instance and class Methods as well as Constructors.
    96       * @return  Array of StringArrays that can be indexed with the MEMBER_xxx_IDX constants
    97       */
    98      public Object[] getMethods(){
    99          Method[] methods = getClassUnderInspection().getMethods();
    100         Constructor[] ctors = getClassUnderInspection().getConstructors();
    101         Object[] result = new Object[methods.length + ctors.length];
    102         int resultIndex = 0;
    103         for (; resultIndex < methods.length; resultIndex++) {
    104             Method method = methods[resultIndex];
    105             result[resultIndex] = methodInfo(method);
    106         }
    107         for (int i = 0; i < ctors.length; i++, resultIndex++) {
    108             Constructor ctor = ctors[i];
    109             result[resultIndex] = methodInfo(ctor);
    110         }
    111         return result;
    112     }
    113      /***
    114      * Get info about instance and class Methods that are dynamically added through Groovy.
    115      * @return  Array of StringArrays that can be indexed with the MEMBER_xxx_IDX constants
    116      */
    117     public Object[] getMetaMethods(){
    118         MetaClass metaClass = InvokerHelper.getMetaClass(objectUnderInspection);
    119         List metaMethods = metaClass.getMetaMethods();
    120         Object[] result = new Object[metaMethods.size()];
    121         int i=0;
    122         for (Iterator iter = metaMethods.iterator(); iter.hasNext(); i++) {
    123             MetaMethod metaMethod = (MetaMethod) iter.next();
    124             result[i] = methodInfo(metaMethod);
    125         }
    126         return result;
    127     }
    128
    129     /***
    130      * Get info about usual Java public fields incl. constants.
    131      * @return  Array of StringArrays that can be indexed with the MEMBER_xxx_IDX constants
    132      */
    133     public Object[] getPublicFields(){
    134         Field[] fields = getClassUnderInspection().getFields();
    135         Object[] result = new Object[fields.length];
    136         for (int i = 0; i < fields.length; i++) {
    137             Field field = fields[i];
    138             result[i] = fieldInfo(field);
    139         }
    140         return result;
    141     }
    142     /***
    143      * Get info about Properties (Java and Groovy alike).
    144      * @return  Array of StringArrays that can be indexed with the MEMBER_xxx_IDX constants
    145      */
    146     public Object[] getPropertyInfo(){
    147         List props = DefaultGroovyMethods.getMetaPropertyValues(objectUnderInspection);
    148         Object[] result = new Object[props.size()];
    149         int i=0;
    150         for (Iterator iter = props.iterator(); iter.hasNext(); i++) {
    151             PropertyValue pv = (PropertyValue) iter.next();
    152             result[i] = fieldInfo(pv);
    153         }
    154         return result;
    155     }
    156
    157     protected String[] fieldInfo(Field field) {
    158         String[] result = new String[MEMBER_VALUE_IDX+1];
    159         result[MEMBER_ORIGIN_IDX] = JAVA;
    160         result[MEMBER_MODIFIER_IDX] = Modifier.toString(field.getModifiers());
    161         result[MEMBER_DECLARER_IDX] = shortName(field.getDeclaringClass());
    162         result[MEMBER_TYPE_IDX] = shortName(field.getType());
    163         result[MEMBER_NAME_IDX] = field.getName();
    164         try {
    165             result[MEMBER_VALUE_IDX] = InvokerHelper.inspect(field.get(objectUnderInspection));
    166         } catch (IllegalAccessException e) {
    167             result[MEMBER_VALUE_IDX] = NOT_APPLICABLE;
    168         }
    169         return withoutNulls(result);
    170     }
    171     protected String[] fieldInfo(PropertyValue pv) {
    172         String[] result = new String[MEMBER_VALUE_IDX+1];
    173         result[MEMBER_ORIGIN_IDX] = GROOVY;
    174         result[MEMBER_MODIFIER_IDX] = "public";
    175         result[MEMBER_DECLARER_IDX] = NOT_APPLICABLE;
    176         result[MEMBER_TYPE_IDX] = shortName(pv.getType());
    177         result[MEMBER_NAME_IDX] = pv.getName();
    178         try {
    179             result[MEMBER_VALUE_IDX] = InvokerHelper.inspect(pv.getValue());
    180         } catch (Exception e) {
    181             result[MEMBER_VALUE_IDX] = NOT_APPLICABLE;
    182         }
    183         return withoutNulls(result);
    184     }
    185
    186     protected Class getClassUnderInspection() {
    187         return objectUnderInspection.getClass();
    188     }
    189
    190     public static String shortName(Class clazz){
    191         if (null == clazz) return NOT_APPLICABLE;
    192         String className = clazz.getName();
    193         if (null == clazz.getPackage()) return className;
    194         String packageName = clazz.getPackage().getName();
    195         int</strong> offset = packageName.length();
    196         if (offset > 0) offset++;
    197         className = className.substring(offset);
    198         return className;
    199     }
    200
    201     protected String[] methodInfo(Method method){
    202         String[] result = new String[MEMBER_EXCEPTIONS_IDX+1];
    203 	    int mod = method.getModifiers();
    204         result[MEMBER_ORIGIN_IDX] = JAVA;
    205         result[MEMBER_MODIFIER_IDX] = Modifier.toString(mod);
    206         result[MEMBER_DECLARER_IDX] = shortName(method.getDeclaringClass());
    207         result[MEMBER_TYPE_IDX] = shortName(method.getReturnType());
    208         result[MEMBER_NAME_IDX] = method.getName();
    209 	    Class[] params = method.getParameterTypes();
    210         StringBuffer sb = new StringBuffer();
    211 	    for (int j = 0; j < params.length; j++) {
    212 		    sb.append(shortName(params[j]));
    213 		    if (j < (params.length - 1)) sb.append(", ");
    214 	    }
    215         result[MEMBER_PARAMS_IDX] = sb.toString();
    216 	    sb.setLength(0);
    217 	    Class[] exceptions = method.getExceptionTypes();
    218 		for (int k = 0; k < exceptions.length; k++) {
    219 		    sb.append(shortName(exceptions[k]));
    220 		    if (k < (exceptions.length - 1)) sb.append(", ");
    221 	    }
    222         result[MEMBER_EXCEPTIONS_IDX] = sb.toString();
    223 	    return withoutNulls(result);
    224     }
    225     protected String[] methodInfo(Constructor ctor){
    226         String[] result = new String[MEMBER_EXCEPTIONS_IDX+1];
    227 	    int mod = ctor.getModifiers();
    228         result[MEMBER_ORIGIN_IDX] = JAVA;
    229         result[MEMBER_MODIFIER_IDX] = Modifier.toString(mod);
    230         result[MEMBER_DECLARER_IDX] = shortName(ctor.getDeclaringClass());
    231         result[MEMBER_TYPE_IDX] = shortName(ctor.getDeclaringClass());
    232         result[MEMBER_NAME_IDX] = ctor.getName();
    233 	    Class[] params = ctor.getParameterTypes();
    234         StringBuffer sb = new StringBuffer();
    235 	    for (int j = 0; j < params.length; j++) {
    236 		    sb.append(shortName(params[j]));
    237 		    if (j < (params.length - 1)) sb.append(", ");
    238 	    }
    239         result[MEMBER_PARAMS_IDX] = sb.toString();
    240 	    sb.setLength(0);
    241 	    Class[] exceptions = ctor.getExceptionTypes();
    242 		for (int k = 0; k < exceptions.length; k++) {
    243 		    sb.append(shortName(exceptions[k]));
    244 		    if (k < (exceptions.length - 1)) sb.append(", ");
    245 	    }
    246         result[MEMBER_EXCEPTIONS_IDX] = sb.toString();
    247 	    return withoutNulls(result);
    248     }
    249     protected String[] methodInfo(MetaMethod method){
    250         String[] result = new String[MEMBER_EXCEPTIONS_IDX+1];
    251 	    int mod = method.getModifiers();
    252         result[MEMBER_ORIGIN_IDX] = GROOVY;
    253         result[MEMBER_MODIFIER_IDX] = Modifier.toString(mod);
    254         result[MEMBER_DECLARER_IDX] = shortName(method.getDeclaringClass());
    255         result[MEMBER_TYPE_IDX] = shortName(method.getReturnType());
    256         result[MEMBER_NAME_IDX] = method.getName();
    257 	    Class[] params = method.getParameterTypes();
    258         StringBuffer sb = new StringBuffer();
    259 	    for (int j = 0; j < params.length; j++) {
    260 		    sb.append(shortName(params[j]));
    261 		    if (j < (params.length - 1)) sb.append(", ");
    262 	    }
    263         result[MEMBER_PARAMS_IDX] = sb.toString();
    264         result[MEMBER_EXCEPTIONS_IDX] = NOT_APPLICABLE; // no exception info for Groovy MetaMethods
    265         return withoutNulls(result);
    266     }
    267
    268     protected String[] withoutNulls(String[] toNormalize){
    269         for (int i = 0; i < toNormalize.length; i++) {
    270             String s = toNormalize[i];
    271             if (null == s) toNormalize[i] = NOT_APPLICABLE;
    272         }
    273         return toNormalize;
    274     }
    275
    276     public static void print(Object[] memberInfo) {
    277         for (int i = 0; i < memberInfo.length; i++) {
    278             String[] metaMethod = (String[]) memberInfo[i];
    279             System.out.print(i+":\t");
    280             for (int j = 0; j < metaMethod.length; j++) {
    281                 String s = metaMethod[j];
    282                 System.out.print(s+" ");
    283             }
    284             System.out.println("");
    285         }
    286     }
    287     public static Collection sort(List memberInfo) {
    288         Collections.sort(memberInfo, new MemberComparator());
    289         return memberInfo;
    290     }
    291
    292     public static class MemberComparator implements Comparator {
    293         public int compare(Object a, Object b) {
    294             String[] aStr = (String[]) a;
    295             String[] bStr = (String[]) b;
    296             int result = aStr[Inspector.MEMBER_NAME_IDX].compareTo(bStr[Inspector.MEMBER_NAME_IDX]);
    297             if (0 != result) return result;
    298             result = aStr[Inspector.MEMBER_TYPE_IDX].compareTo(bStr[Inspector.MEMBER_TYPE_IDX]);
    299             if (0 != result) return result;
    300             result = aStr[Inspector.MEMBER_PARAMS_IDX].compareTo(bStr[Inspector.MEMBER_PARAMS_IDX]);
    301             if (0 != result) return result;
    302             result = aStr[Inspector.MEMBER_DECLARER_IDX].compareTo(bStr[Inspector.MEMBER_DECLARER_IDX]);
    303             if (0 != result) return result;
    304             result = aStr[Inspector.MEMBER_MODIFIER_IDX].compareTo(bStr[Inspector.MEMBER_MODIFIER_IDX]);
    305             if (0 != result) return result;
    306             result = aStr[Inspector.MEMBER_ORIGIN_IDX].compareTo(bStr[Inspector.MEMBER_ORIGIN_IDX]);
    307             return result;
    308         }
    309     }
    310 }