package org.springframework.extensions.workflow.support;

import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
import org.springframework.extensions.workflow.annotation.FlowInstanceDescriptorArgument;
import org.springframework.extensions.workflow.annotation.ReturnsState;
import org.springframework.extensions.workflow.context.FlowInstanceDescriptorHolder;
import org.springframework.extensions.workflow.context.TransitionActionContext;

import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @author janm
 */
class TransitionMethodInvoker {
    private Method method;
    private Object target;
    private transient Annotation[][] parametersAnnotations;

    TransitionMethodInvoker(Object target, Method method) {
        Assert.notNull(target, "The 'target' argument must not be null.");
        Assert.notNull(method, "The 'method' argument must not be null.");

        this.target = target;
        this.method = method;
        prepareCall();
    }

    TransitionMethodInvoker(Object target, String methodName) {
        Assert.notNull(target, "The 'target' argument must not be null.");
        Assert.notNull(methodName, "The 'methodName' argument must not be null.");

        this.target = target;
        for (Method method : ReflectionUtils.getAllDeclaredMethods(this.target.getClass())) {
            if (methodName.equals(method.getName())) {
                this.method = method;
                break;
            }
        }
        if (this.method == null) {
            throw new IllegalArgumentException("Object " + target + " does not define [accessible] method " + methodName);
        }
        prepareCall();
    }

    private void prepareCall() {
        this.parametersAnnotations = this.method.getParameterAnnotations();
    }

    Object invoke(TransitionActionContext actionContext) throws IllegalAccessException, InvocationTargetException {
        List<Object> arguments = new ArrayList<Object>();
        int i = 0;
        for (Annotation[] parameterAnnotation : this.parametersAnnotations) {
            boolean fidParameter = false;
            for (Annotation annotation : parameterAnnotation) {
                if (annotation.annotationType().equals(FlowInstanceDescriptorArgument.class)) {
                    fidParameter = true;
                    break;
                }
            }
            if (!fidParameter) {
                arguments.add(actionContext.getArguments()[i]);
                i++;
            } else {
                arguments.add(FlowInstanceDescriptorHolder.getFlowInstanceDescriptor());
            }
        }
        Object ret = this.method.invoke(this.target, arguments.toArray());
        if (this.method.getAnnotation(ReturnsState.class) != null) {
            if (ret != null) {
                actionContext.setTargetStateId(ret.toString());
            }
        }
        
        return ret;
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder();
        sb.append("TransitionMethodInvoker");
        sb.append("{method=").append(method);
        sb.append(", target=").append(target);
        sb.append(", parametersAnnotations=").append(parametersAnnotations == null ? "null" : Arrays.asList(parametersAnnotations).toString());
        sb.append('}');
        return sb.toString();
    }
}

@@ -2768,24 +2768,166 @@
       %7D%0A
+%0A        boolean accessable = this.method.isAccessible();%0A        Object ret;%0A%0A        try %7B%0A            this.method.setAccessible(true);%0A%0A   
         
 Object r
@@ -2914,30 +2914,24 @@
 %0A           
-Object
  ret = this.
@@ -2975,24 +2975,29 @@
 toArray());%0A
+%0A    
         if (
@@ -3061,24 +3061,28 @@
             
+ 
+   
 if (ret != n
@@ -3080,32 +3080,36 @@
 (ret != null) %7B%0A
+    
                 
@@ -3160,33 +3160,122 @@
 ));%0A            
-%7D
+    %7D%0A            %7D%0A        %7D finally %7B%0A            this.method.setAccessible(accessable);
 %0A        %7D%0A     
@@ -3269,24 +3269,16 @@
       %7D%0A
-        
 %0A       

Fix accessable for all invoked methods