001 package com.mockrunner.util.common; 002 003 import java.lang.reflect.Method; 004 import java.lang.reflect.Modifier; 005 import java.util.ArrayList; 006 import java.util.Arrays; 007 import java.util.HashSet; 008 import java.util.List; 009 import java.util.Set; 010 011 import com.mockrunner.base.NestedApplicationException; 012 013 public class MethodUtil 014 { 015 /** 016 * Invokes the method with the specified name on the specified object 017 * and throws a {@link com.mockrunner.base.NestedApplicationException}, 018 * if the invocation fails. The method must be public and must not 019 * have any parameters. 020 * @param object the object the method is invoked from 021 * @param methodName the name of the method 022 * @return the result of the method invocation 023 */ 024 public static Object invoke(Object object, String methodName) 025 { 026 try 027 { 028 Method method = object.getClass().getMethod(methodName, null); 029 return method.invoke(object, null); 030 } 031 catch(Exception exc) 032 { 033 throw new NestedApplicationException(exc); 034 } 035 } 036 037 /** 038 * Invokes the method with the specified name on the specified object 039 * and throws a {@link com.mockrunner.base.NestedApplicationException}, 040 * if the invocation fails. The method must be public and must have 041 * exactly one paremeter of the type specified by the given 042 * <code>parameter</code>. 043 * @param object the object the method is invoked from 044 * @param methodName the name of the method 045 * @param parameter the parameter, must not be <code>null</code> 046 * @return the result of the method invocation 047 */ 048 public static Object invoke(Object object, String methodName, Object parameter) 049 { 050 try 051 { 052 Method method = object.getClass().getMethod(methodName, new Class[] {parameter.getClass()}); 053 return method.invoke(object, new Object[] {parameter}); 054 } 055 catch(Exception exc) 056 { 057 throw new NestedApplicationException(exc); 058 } 059 } 060 061 /** 062 * Returns if the two specified methods are equal as 063 * defined by <code>Method.equals()</code> except that 064 * the methods can be defined by different classes. 065 * @param method1 the first method to compare 066 * @param method2 the second method to compare 067 * @return <code>true</code> if the methods are equal, <code>false</code> 068 * otherwise 069 * @throws NullPointerException if one of the methods is <code>null</code> 070 */ 071 public static boolean areMethodsEqual(Method method1, Method method2) 072 { 073 if(method1.equals(method2)) return true; 074 if(!method2.getName().equals(method1.getName())) return false; 075 if(!method1.getReturnType().equals(method2.getReturnType())) return false; 076 return Arrays.equals(method1.getParameterTypes(), method2.getParameterTypes()); 077 } 078 079 /** 080 * Returns if <code>method2</code> overrides <code>method1</code>. 081 * @param method1 method to be overridden 082 * @param method2 overriding method 083 * @return <code>true</code> if <code>method2</code> overrides <code>method1</code>, <code>false</code> 084 * otherwise 085 * @throws NullPointerException if one of the methods is <code>null</code> 086 */ 087 public static boolean overrides(Method method1, Method method2) 088 { 089 if(method1.equals(method2)) return false; 090 if(!method1.getDeclaringClass().isAssignableFrom(method2.getDeclaringClass())) return false; 091 if(!method2.getName().equals(method1.getName())) return false; 092 if(method1.getDeclaringClass().isInterface()) return false; 093 return Arrays.equals(method1.getParameterTypes(), method2.getParameterTypes()); 094 } 095 096 /** 097 * Returns all methods in <code>methods</code> that are overridden in 098 * the specified class hierarchy. The returned <code>Set</code> contains 099 * all overridden methods and all overriding methods. 100 * @param clazz the class hierarchy 101 * @param methods the <code>Set</code> of methods 102 * @return all overridden and overriding methods. 103 */ 104 public static Set getOverriddenMethods(Class clazz, Method[] methods) 105 { 106 Method[][] declaredMethods = MethodUtil.getMethodsSortedByInheritanceHierarchy(clazz); 107 Set overridingMethods = new HashSet(); 108 for(int ii = 0; ii < methods.length; ii++) 109 { 110 Method currentAroundInvokeMethod = methods[ii]; 111 Set currentOverridingMethods = new HashSet(); 112 for(int yy = 0; yy < declaredMethods.length; yy++) 113 { 114 for(int zz = 0; zz < declaredMethods[yy].length; zz++) 115 { 116 if(MethodUtil.overrides(currentAroundInvokeMethod, declaredMethods[yy][zz])) 117 { 118 currentOverridingMethods.add(declaredMethods[yy][zz]); 119 } 120 } 121 } 122 if(!currentOverridingMethods.isEmpty()) 123 { 124 overridingMethods.add(currentAroundInvokeMethod); 125 overridingMethods.addAll(currentOverridingMethods); 126 } 127 } 128 return overridingMethods; 129 } 130 131 /** 132 * Returns the declared methods of the specified class whose names are matching 133 * the specified regular expression. 134 * @param theClass the class whose methods are examined 135 * @param expr the regular expression 136 * @return the matching methods 137 */ 138 public static Method[] getMatchingDeclaredMethods(Class theClass, String expr) 139 { 140 Method[] methods = theClass.getDeclaredMethods(); 141 List resultList = new ArrayList(); 142 for(int ii = 0; ii < methods.length; ii++) 143 { 144 if(StringUtil.matchesPerl5(methods[ii].getName(), expr, true)) 145 { 146 resultList.add(methods[ii]); 147 } 148 } 149 return (Method[])resultList.toArray(new Method[resultList.size()]); 150 } 151 152 /** 153 * Returns all non-static methods declared by the specified class and its 154 * superclasses. The returned array contains the methods of all classes 155 * in the inheritance hierarchy, starting with the methods of the 156 * most general superclass, which is <code>java.lang.Object</code>. 157 * @param theClass the class whose methods are examined 158 * @return the array of method arrays 159 */ 160 public static Method[][] getMethodsSortedByInheritanceHierarchy(Class theClass) 161 { 162 List hierarchyList = new ArrayList(); 163 Class[] hierarchyClasses = ClassUtil.getInheritanceHierarchy(theClass); 164 for(int ii = 0; ii < hierarchyClasses.length; ii++) 165 { 166 addMethodsForClass(hierarchyList, hierarchyClasses[ii]); 167 } 168 return (Method[][])hierarchyList.toArray(new Method[hierarchyList.size()][]); 169 } 170 171 private static void addMethodsForClass(List hierarchyList, Class clazz) 172 { 173 List methodList = new ArrayList(); 174 Method[] methods = clazz.getDeclaredMethods(); 175 for(int ii = 0; ii < methods.length; ii++) 176 { 177 if(!Modifier.isStatic(methods[ii].getModifiers())) 178 { 179 methodList.add(methods[ii]); 180 } 181 } 182 hierarchyList.add(methodList.toArray(new Method[methodList.size()])); 183 } 184 }