You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by rd...@apache.org on 2002/11/14 19:51:57 UTC

cvs commit: jakarta-commons/lang/src/java/org/apache/commons/lang/reflect MethodUtils.java

rdonkin     2002/11/14 10:51:57

  Modified:    lang/src/java/org/apache/commons/lang/reflect
                        MethodUtils.java
  Log:
  Updated method utils with latest code from the beanutils version.
  
  Revision  Changes    Path
  1.2       +217 -150  jakarta-commons/lang/src/java/org/apache/commons/lang/reflect/MethodUtils.java
  
  Index: MethodUtils.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/lang/src/java/org/apache/commons/lang/reflect/MethodUtils.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- MethodUtils.java	24 Oct 2002 23:12:54 -0000	1.1
  +++ MethodUtils.java	14 Nov 2002 18:51:57 -0000	1.2
  @@ -74,12 +74,20 @@
    * programmer. This can break an implementation if used incorrectly. This
    * facility should be used with care.
    *
  - * @author Based on code from BeanUtils
    * @author <a href="mailto:scolebourne@apache.org">Stephen Colebourne</a>
  + * @author Based on code from <code>BeanUtils</code> by: Craig R. McClanahan
  + * @author Ralph Schaer
  + * @author Chris Audley
  + * @author Rey Fran�ois
  + * @author Gregor Ra�man
  + * @author Jan Sorensen
  + * @author Robert Burrell Donkin
    * @version $Id$
    */
   public class MethodUtils {
       
  +    public static final boolean debug = true;
  +    
       /** An empty method array */
       public static final Method[] EMPTY_METHOD_ARRAY = new Method[0];
       
  @@ -329,15 +337,15 @@
               args = ArrayUtils.EMPTY_OBJECT_ARRAY;
           }  
   
  -return null;
  -//        Method method = getMatchingAccessibleMethod(
  -//                object.getClass(),
  -//                methodName,
  -//                parameterTypes);
  -//        if (method == null)
  -//            throw new NoSuchMethodException("No such accessible method: " +
  -//                    methodName + "() on object: " + object.getClass().getName());
  -//        return method.invoke(object, args);
  +//return null;
  +        Method method = getMatchingAccessibleMethod(
  +                object.getClass(),
  +                methodName,
  +                parameterTypes);
  +        if (method == null)
  +            throw new NoSuchMethodException("No such accessible method: " +
  +                    methodName + "() on object: " + object.getClass().getName());
  +        return method.invoke(object, args);
       }
   
   
  @@ -608,143 +616,202 @@
   
       }
   
  -//    /**
  -//     * <p>Find an accessible method that matches the given name and has compatible parameters.
  -//     * Compatible parameters mean that every method parameter is assignable from 
  -//     * the given parameters.
  -//     * In other words, it finds a method with the given name 
  -//     * that will take the parameters given.<p>
  -//     *
  -//     * <p>This method is slightly undeterminstic since it loops 
  -//     * through methods names and return the first matching method.</p>
  -//     * 
  -//     * <p>This method is used by 
  -//     * {@link 
  -//     * #invokeMethod(Object object,String methodName,Object [] args,Class[] parameterTypes)}.
  -//     *
  -//     * <p>This method can match primitive parameter by passing in wrapper classes.
  -//     * For example, a <code>Boolean</code> will match a primitive <code>boolean</code>
  -//     * parameter.
  -//     *
  -//     * @param clazz find method in this class
  -//     * @param methodName find method with this name
  -//     * @param parameterTypes find method with compatible parameters 
  -//     */
  -//    private static Method getMatchingAccessibleMethod(
  -//                                                Class clazz,
  -//                                                String methodName,
  -//                                                Class[] parameterTypes) {
  -//        // trace logging
  -//        if (log.isTraceEnabled()) {
  -//            log.trace("Matching name=" + methodName + " on " + clazz);
  -//        }
  -//        
  -//        // see if we can find the method directly
  -//        // most of the time this works and it's much faster
  -//        try {
  -//            Method method = clazz.getMethod(methodName, parameterTypes);
  -//            return method;
  -//            
  -//        } catch (NoSuchMethodException e) { /* SWALLOW */ }
  -//        
  -//        // search through all methods 
  -//        int paramSize = parameterTypes.length;
  -//        Method[] methods = clazz.getMethods();
  -//        for (int i = 0, size = methods.length; i < size ; i++) {
  -//            if (methods[i].getName().equals(methodName)) {	
  -//                // log some trace information
  -//                if (log.isTraceEnabled()) {
  -//                    log.trace("Found matching name:");
  -//                    log.trace(methods[i]);
  -//                }                
  -//                
  -//                // compare parameters
  -//                Class[] methodsParams = methods[i].getParameterTypes();
  -//                int methodParamSize = methodsParams.length;
  -//                if (methodParamSize == paramSize) {          
  -//                    boolean match = true;
  -//                    for (int n = 0 ; n < methodParamSize; n++) {
  -//                        if (log.isTraceEnabled()) {
  -//                            log.trace("Param=" + parameterTypes[n].getName());
  -//                            log.trace("Method=" + methodsParams[n].getName());
  -//                        }
  -//                        if (!isAssignmentCompatible(methodsParams[n], parameterTypes[n])) {
  -//                            if (log.isTraceEnabled()) {
  -//                                log.trace(methodsParams[n] + " is not assignable from " 
  -//                                            + parameterTypes[n]);
  -//                            }    
  -//                            match = false;
  -//                            break;
  -//                        }
  -//                    }
  -//                    
  -//                    if (match) {
  -//                        // get accessible version of method
  -//                        Method method = getAccessibleMethod(methods[i]);
  -//                        if (method != null) {
  -//                            if (log.isTraceEnabled()) {
  -//                                log.trace(method + " accessible version of " 
  -//                                            + methods[i]);
  -//                            }
  -//                            return method;
  -//                        }
  -//                        
  -//                        log.trace("Couldn't find accessible method.");
  -//                    }
  -//                }
  -//            }
  -//        }
  -//        
  -//        // didn't find a match
  -//        log.trace("No match found.");
  -//        return null;                                        
  -//    }
  -//
  -//    /**
  -//     * <p>Determine whether a type can be used as a parameter in a method invocation.
  -//     * This method handles primitive conversions correctly.</p>
  -//     *
  -//     * <p>In order words, it will match a <code>Boolean</code> to a <code>boolean</code>,
  -//     * a <code>Long</code> to a <code>long</code>,
  -//     * a <code>Float</code> to a <code>float</code>,
  -//     * a <code>Integer</code> to a <code>int</code>,
  -//     * and a <code>Double</code> to a <code>double</code>.
  -//     * Now logic widening matches are allowed.
  -//     * For example, a <code>Long</code> will not match a <code>int</code>.
  -//     *
  -//     * @param parameterType the type of parameter accepted by the method
  -//     * @param parameterization the type of parameter being tested 
  -//     *
  -//     * @return true if the assignement is compatible.
  -//     */
  -//    private static final boolean isAssignmentCompatible(Class parameterType, Class parameterization) {
  -//        // try plain assignment
  -//        if (parameterType.isAssignableFrom(parameterization)) {
  -//            return true;
  -//        }
  -//        
  -//        if (parameterType.isPrimitive()) {
  -//            // does anyone know a better strategy than comparing names?
  -//            // also, this method does *not* do widening - you must specify exactly
  -//            // is this the right behaviour?
  -//            if (boolean.class.equals(parameterType)) {
  -//                return Boolean.class.equals(parameterization);
  -//            }         
  -//            if (float.class.equals(parameterType)) {
  -//                return Float.class.equals(parameterization);
  -//            }     
  -//            if (long.class.equals(parameterType)) {
  -//                return Long.class.equals(parameterization);
  -//            }     
  -//            if (int.class.equals(parameterType)) {
  -//                return Integer.class.equals(parameterization);
  -//            }                
  -//            if (double.class.equals(parameterType)) {
  -//                return Double.class.equals(parameterization);
  -//            }               
  -//        }
  -//        
  -//        return false;
  -//    }
  -//    
  +
  +    /**
  +     * <p>Find an accessible method that matches the given name and has compatible parameters.
  +     * Compatible parameters mean that every method parameter is assignable from 
  +     * the given parameters.
  +     * In other words, it finds a method with the given name 
  +     * that will take the parameters given.<p>
  +     *
  +     * <p>This method is slightly undeterminstic since it loops 
  +     * through methods names and return the first matching method.</p>
  +     * 
  +     * <p>This method is used by 
  +     * {@link 
  +     * #invokeMethod(Object object,String methodName,Object [] args,Class[] parameterTypes)}.
  +     *
  +     * <p>This method can match primitive parameter by passing in wrapper classes.
  +     * For example, a <code>Boolean</code> will match a primitive <code>boolean</code>
  +     * parameter.
  +     *
  +     * @param clazz find method in this class
  +     * @param methodName find method with this name
  +     * @param parameterTypes find method with compatible parameters 
  +     */
  +    private static Method getMatchingAccessibleMethod(
  +                                                Class clazz,
  +                                                String methodName,
  +                                                Class[] parameterTypes) {
  +        // trace logging
  +        if (debug) {
  +            log("Matching name=" + methodName + " on " + clazz);
  +        }
  +        
  +        // see if we can find the method directly
  +        // most of the time this works and it's much faster
  +        try {
  +            Method method = clazz.getMethod(methodName, parameterTypes);
  +            if (debug) {
  +                log("Found straight match: " + method);
  +                log("isPublic:" + Modifier.isPublic(method.getModifiers()));
  +            }
  +            
  +            try {
  +                //
  +                // XXX Default access superclass workaround
  +                //
  +                // When a public class has a default access superclass
  +                // with public methods, these methods are accessible.
  +                // Calling them from compiled code works fine.
  +                //
  +                // Unfortunately, using reflection to invoke these methods
  +                // seems to (wrongly) to prevent access even when the method
  +                // modifer is public.
  +                //
  +                // The following workaround solves the problem but will only
  +                // work from sufficiently privilages code. 
  +                //
  +                // Better workarounds would be greatfully accepted.
  +                //
  +                method.setAccessible(true);
  +                
  +            } catch (SecurityException se) {
  +                // log but continue just in case the method.invoke works anyway
  +                log(
  +                "Cannot setAccessible on method. Therefore cannot use jvm access bug workaround.", 
  +                se);
  +            }
  +            return method;
  +            
  +        } catch (NoSuchMethodException e) { /* SWALLOW */ }
  +        
  +        // search through all methods 
  +        int paramSize = parameterTypes.length;
  +        Method[] methods = clazz.getMethods();
  +        for (int i = 0, size = methods.length; i < size ; i++) {
  +            if (methods[i].getName().equals(methodName)) {	
  +                // log some trace information
  +                if (debug) {
  +                    log("Found matching name:");
  +                    log(methods[i]);
  +                }                
  +                
  +                // compare parameters
  +                Class[] methodsParams = methods[i].getParameterTypes();
  +                int methodParamSize = methodsParams.length;
  +                if (methodParamSize == paramSize) {          
  +                    boolean match = true;
  +                    for (int n = 0 ; n < methodParamSize; n++) {
  +                        if (debug) {
  +                            log("Param=" + parameterTypes[n].getName());
  +                            log("Method=" + methodsParams[n].getName());
  +                        }
  +                        if (!isAssignmentCompatible(methodsParams[n], parameterTypes[n])) {
  +                            if (debug) {
  +                                log(methodsParams[n] + " is not assignable from " 
  +                                            + parameterTypes[n]);
  +                            }    
  +                            match = false;
  +                            break;
  +                        }
  +                    }
  +                    
  +                    if (match) {
  +                        // get accessible version of method
  +                        Method method = getAccessibleMethod(methods[i]);
  +                        if (method != null) {
  +                            if (debug) {
  +                                log(method + " accessible version of " 
  +                                            + methods[i]);
  +                            }
  +                            try {
  +                                //
  +                                // XXX Default access superclass workaround
  +                                // (See above for more details.)
  +                                //
  +                                method.setAccessible(true);
  +                                
  +                            } catch (SecurityException se) {
  +                                // log but continue just in case the method.invoke works anyway
  +                                log(
  +                                "Cannot setAccessible on method. Therefore cannot use jvm access bug workaround.", 
  +                                se);
  +                            }
  +                            return method;
  +                        }
  +                        
  +                        log("Couldn't find accessible method.");
  +                    }
  +                }
  +            }
  +        }
  +        
  +        // didn't find a match
  +        log("No match found.");
  +        return null;                                        
  +    }
  +
  +
  +    /**
  +     * <p>Determine whether a type can be used as a parameter in a method invocation.
  +     * This method handles primitive conversions correctly.</p>
  +     *
  +     * <p>In order words, it will match a <code>Boolean</code> to a <code>boolean</code>,
  +     * a <code>Long</code> to a <code>long</code>,
  +     * a <code>Float</code> to a <code>float</code>,
  +     * a <code>Integer</code> to a <code>int</code>,
  +     * and a <code>Double</code> to a <code>double</code>.
  +     * Now logic widening matches are allowed.
  +     * For example, a <code>Long</code> will not match a <code>int</code>.
  +     *
  +     * @param parameterType the type of parameter accepted by the method
  +     * @param parameterization the type of parameter being tested 
  +     *
  +     * @return true if the assignement is compatible.
  +     */
  +    private static final boolean isAssignmentCompatible(Class parameterType, Class parameterization) {
  +        // try plain assignment
  +        if (parameterType.isAssignableFrom(parameterization)) {
  +            return true;
  +        }
  +        
  +        if (parameterType.isPrimitive()) {
  +            // does anyone know a better strategy than comparing names?
  +            // also, this method does *not* do widening - you must specify exactly
  +            // is this the right behaviour?
  +            if (boolean.class.equals(parameterType)) {
  +                return Boolean.class.equals(parameterization);
  +            }         
  +            if (float.class.equals(parameterType)) {
  +                return Float.class.equals(parameterization);
  +            }     
  +            if (long.class.equals(parameterType)) {
  +                return Long.class.equals(parameterization);
  +            }     
  +            if (int.class.equals(parameterType)) {
  +                return Integer.class.equals(parameterization);
  +            }                
  +            if (double.class.equals(parameterType)) {
  +                return Double.class.equals(parameterization);
  +            }               
  +        }
  +        
  +        return false;
  +    }
  +    
  +    
  +    private static void log(Object o) {
  +        if (debug) {
  +            System.err.println(o);
  +        }
  +    }
  +    
  +    private static void log(Object o, Throwable t) {
  +        if (debug) {
  +            System.err.println(o);
  +            System.err.println(t);
  +            
  +        }
  +    }
   }
  
  
  

--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>