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(Modifier.isPrivate(method1.getModifiers())) return false;
091            if(Modifier.isPrivate(method2.getModifiers())) return false;
092            if(!method1.getDeclaringClass().isAssignableFrom(method2.getDeclaringClass())) return false;
093            if(!method2.getName().equals(method1.getName())) return false;
094            if(method1.getDeclaringClass().isInterface()) return false;
095            return Arrays.equals(method1.getParameterTypes(), method2.getParameterTypes());
096        }
097        
098        /**
099         * Returns all methods in <code>methods</code> that are overridden in
100         * the specified class hierarchy. The returned <code>Set</code> contains
101         * all overridden methods and all overriding methods.
102         * @param clazz the class hierarchy
103         * @param methods the <code>Set</code> of methods
104         * @return all overridden and overriding methods.
105         */
106        public static Set getOverriddenMethods(Class clazz, Method[] methods)
107        {
108            Method[][] declaredMethods = MethodUtil.getMethodsSortedByInheritanceHierarchy(clazz); 
109            Set overridingMethods = new HashSet();
110            for(int ii = 0; ii < methods.length; ii++)
111            {
112                Method currentAroundInvokeMethod = methods[ii];
113                Set currentOverridingMethods = new HashSet();
114                for(int yy = 0; yy < declaredMethods.length; yy++)
115                {
116                    for(int zz = 0; zz < declaredMethods[yy].length; zz++)
117                    {
118                        if(MethodUtil.overrides(currentAroundInvokeMethod, declaredMethods[yy][zz]))
119                        {
120                            currentOverridingMethods.add(declaredMethods[yy][zz]);
121                        }
122                    }
123                }
124                if(!currentOverridingMethods.isEmpty())
125                {
126                    overridingMethods.add(currentAroundInvokeMethod);
127                    overridingMethods.addAll(currentOverridingMethods);
128                }
129            }
130            return overridingMethods;
131        }
132        
133        /**
134         * Returns the declared methods of the specified class whose names are matching
135         * the specified regular expression.
136         * @param theClass the class whose methods are examined
137         * @param expr the regular expression
138         * @return the matching methods
139         */
140        public static Method[] getMatchingDeclaredMethods(Class theClass, String expr)
141        {
142            Method[] methods = theClass.getDeclaredMethods();
143            List resultList = new ArrayList();
144            for(int ii = 0; ii < methods.length; ii++)
145            {
146                if(StringUtil.matchesPerl5(methods[ii].getName(), expr, true))
147                {
148                    resultList.add(methods[ii]);
149                }
150            }
151            return (Method[])resultList.toArray(new Method[resultList.size()]);
152        }
153        
154        /**
155         * Returns all non-static methods declared by the specified class and its
156         * superclasses. The returned array contains the methods of all classes
157         * in the inheritance hierarchy, starting with the methods of the
158         * most general superclass, which is <code>java.lang.Object</code>.
159         * @param theClass the class whose methods are examined
160         * @return the array of method arrays
161         */
162        public static Method[][] getMethodsSortedByInheritanceHierarchy(Class theClass)
163        {
164            List hierarchyList = new ArrayList();
165            Class[] hierarchyClasses = ClassUtil.getInheritanceHierarchy(theClass);
166            for(int ii = 0; ii < hierarchyClasses.length; ii++)
167            {
168                addMethodsForClass(hierarchyList, hierarchyClasses[ii]);
169            }
170            return (Method[][])hierarchyList.toArray(new Method[hierarchyList.size()][]);
171        }
172        
173        private static void addMethodsForClass(List hierarchyList, Class clazz)
174        {
175            List methodList = new ArrayList();
176            Method[] methods = clazz.getDeclaredMethods();
177            for(int ii = 0; ii < methods.length; ii++)
178            {
179                if(!Modifier.isStatic(methods[ii].getModifiers()))
180                {
181                    methodList.add(methods[ii]);
182                }
183            }
184            hierarchyList.add(methodList.toArray(new Method[methodList.size()]));
185        }
186    }