package brainslug.flow.builder;
import brainslug.flow.expression.Value;
import brainslug.flow.node.task.CallDefinition;
import brainslug.flow.node.task.InvokeDefinition;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Iterator;
import java.util.ServiceLoader;
import java.util.Stack;
public class ServiceCallInvocationSupport {
Stack<ProxyStackEntry> proxyStack = new Stack<ProxyStackEntry>();
public Value argument(Value value) {
proxyStack.push(new ParameterEntry<Value>(value));
return value;
}
public CallDefinition createCallDefinitionFromCurrentStack() {
if (proxyStack.empty()) {
throw new IllegalStateException("no service invocation given. you must define one first...");
}
ServiceCallInvocationSupport.ProxyStackEntry lastInvocation = proxyStack.pop();
if (lastInvocation instanceof ServiceCallInvocationSupport.ServiceInvocationEntry) {
proxyStack.clear();
return (ServiceCallInvocationSupport.ServiceInvocationEntry) lastInvocation;
} else {
throw new IllegalStateException("you must define a valid service invocation.");
}
}
public interface ProxyStackEntry {
}
public static class ServiceInvocationEntry extends InvokeDefinition implements ProxyStackEntry {
public ServiceInvocationEntry(Class<?> serviceClass) {
super(serviceClass);
}
}
public class ParameterEntry<T> implements ProxyStackEntry {
T value;
ParameterEntry(T value) {
this.value = value;
}
public T getValue() {
return value;
}
}
public boolean empty() {
return proxyStack.empty();
}
public <T> T createServiceProxy(final Class<T> clazz) {
InvocationHandler handler = new InvocationHandler() {
@Override
public Object invoke(Object o, Method method, Object[] arguments) throws Throwable {
if (method.getDeclaringClass().getName().equals(Object.class.getName())) {
// dont create proxyStack entry for toString etc. during debugging
return null;
}
ServiceCallInvocationSupport.ServiceInvocationEntry invocation = new ServiceCallInvocationSupport.ServiceInvocationEntry(clazz);
invocation.method(method);
proxyStack.add(withArgumentsFromStack(invocation));
return null;
}
};
return (T) createProxyInstance(clazz, handler);
}
private ServiceCallInvocationSupport.ServiceInvocationEntry withArgumentsFromStack(ServiceCallInvocationSupport.ServiceInvocationEntry invocation) {
for (int paramIndex = 0; paramIndex < invocation.getMethod().getParameterTypes().length; paramIndex++) {
invocation.arg(proxyStack.get(paramIndex));
}
return invocation;
}
protected <T> Object createProxyInstance(Class<T> clazz, InvocationHandler handler) {
return getServiceProxyFactory().createProxyInstance(clazz, handler);
}
protected ServiceProxyFactory getServiceProxyFactory() {
Iterator<ServiceProxyFactory> proxyFactories = ServiceLoader.load(ServiceProxyFactory.class).iterator();
if (proxyFactories.hasNext()) {
return proxyFactories.next();
} else {
return new JdkServiceProxyFactory();
}
}
}