package ee.telekom.workflow.util; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.apache.commons.lang3.ClassUtils; import ee.telekom.workflow.graph.WorkflowException; public class MethodUtil{ public static Class<?>[] getArgumentClasses( Object[] arguments ){ if( arguments == null || arguments.length == 0 ){ return new Class<?>[0]; } else{ Class<?>[] argumentClasses = new Class[arguments.length]; for( int i = 0; i < arguments.length; i++ ){ argumentClasses[i] = arguments[i] == null ? null : arguments[i].getClass(); } return argumentClasses; } } public static Method findMethod( Class<?> clazz, String methodName, Class<?>[] argumentClasses ) throws SecurityException{ List<Method> candidates = new ArrayList<>(); for( Method method : clazz.getMethods() ){ if( !Modifier.isPublic( method.getModifiers() ) || !method.getName().equals( methodName ) || !hasAssignableArguments( method, argumentClasses, false ) ){ continue; } candidates.add( method ); } if( candidates.size() == 0 ){ throw new WorkflowException( "No suitable method " + methodName + " found on target of class " + clazz.getName() + " with arguments " + Arrays.toString( argumentClasses ) ); } else if( candidates.size() > 1 ){ for( Method method : candidates ){ if( hasAssignableArguments( method, argumentClasses, true ) ){ return method; } } throw new WorkflowException( "Multiple suitable methods " + methodName + " found on target of class " + clazz.getName() + " with arguments " + Arrays.toString( argumentClasses ) ); } else{ return candidates.get( 0 ); } } private static boolean hasAssignableArguments( Method method, Class<?>[] argumentClasses, boolean strict ){ Class<?>[] parameterClasses = method.getParameterTypes(); if( parameterClasses.length == argumentClasses.length ){ return hasAssignableArgumentsWithoutVarArgs( parameterClasses, argumentClasses, strict ); } else if( method.isVarArgs() ){ return hasAssignableArgumentsWithVarArgs( parameterClasses, argumentClasses, strict ); } return false; } private static boolean hasAssignableArgumentsWithoutVarArgs( Class<?>[] parameterClasses, Class<?>[] argumentClasses, boolean strict ){ for( int i = 0; i < argumentClasses.length; i++ ){ if( argumentClasses[i] != null ){ if( !isAssignable( parameterClasses[i], argumentClasses[i], strict ) ){ return false; } } } return true; } private static boolean hasAssignableArgumentsWithVarArgs( Class<?>[] parameterClasses, Class<?>[] argumentClasses, boolean strict ){ for( int i = 0; i < parameterClasses.length - 1; i++ ){ if( argumentClasses[i] != null ){ if( !isAssignable( parameterClasses[i], argumentClasses[i], strict ) ){ return false; } } } Class<?> varArgArrayComponentType = parameterClasses[parameterClasses.length - 1].getComponentType(); for( int i = parameterClasses.length; i < argumentClasses.length; i++ ){ if( !isAssignable( varArgArrayComponentType, argumentClasses[i], strict ) ){ return false; } } return true; } private static boolean isAssignable( Class<?> parameterClass, Class<?> argumentClass, boolean strict ){ return strict ? parameterClass.equals( argumentClass ) : ClassUtils.isAssignable( argumentClass, parameterClass, !strict ); } }