package net.sourceforge.javautil.groovy.util;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import net.sourceforge.javautil.common.CollectionUtil;
import net.sourceforge.javautil.common.proxy.AbstractProxy;
import net.sourceforge.javautil.common.proxy.AdaptableProxyHandlerAbstract;
import org.codehaus.groovy.runtime.GroovyCategorySupport;
import org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl;
import groovy.lang.Closure;
import groovy.lang.GroovySystem;
import groovy.lang.MetaClassRegistry;
/**
* This is a group of utility methods for dealing with and calling {@link Closure}'s
*
* @author elponderador
*
*/
public class ClosureUtil {
/**
* @param <T> The type of proxy
* @param iface The class of the proxy type
* @param closure The closure to handle method calls
* @return The new proxy
*/
public static <T> T createProxy (Class<T> iface, Closure closure) {
return (T) ClosureProxy.createProxy(Thread.currentThread().getContextClassLoader(), new Class[] { iface }, closure);
}
/**
* This allows one to call a closure without requiring a certain amount of parameters to be declared.
* If the amount of parameters passed is greater than what it can take (determined
* by calling {@link Closure#getMaximumNumberOfParameters()}) a new {@link Object}[]
* that will have the first parameters up to what the closure can take. If the closure
* cannot take any parameters, it will call {@link Closure#call()} otherwise it will
* call {@link Closure#call(Object[])}.
*
* @param closure The closure to execute
* @param parameters The possible parameters to pass
* @return Whatever the closure returns
*/
public static Object call (Closure closure, Object... parameters) {
Object[] newpars = parameters;
if (newpars.length > closure.getMaximumNumberOfParameters()) {
newpars = new Object[closure.getMaximumNumberOfParameters()];
if (newpars.length > 0) {
System.arraycopy(parameters, 0, newpars, 0, newpars.length);
}
}
return newpars.length == 0 ? closure.call() : closure.call(newpars);
}
/**
* This makes calling closures with dynamic parameters (using {@link ClosureUtil#call(Closure, Object...)}
* along with {@link GroovyCategorySupport} much easier.
*
* @param clazz The category class
* @param closure The closure to be executed
* @param parameters The parameters
* @return The result of the closure execution
*/
public static Object callUsing (Class clazz, final Closure closure, final Object... parameters) {
return GroovyCategorySupport.use(clazz, new Closure (closure) {
public Object call () { return ClosureUtil.call(closure, parameters); }
});
}
/**
* This allows a closure to be used as the target for a proxy.
*
* @author elponderador
* @author $Author: ponderator $
* @version $Id: ClosureUtil.java 1081 2009-09-24 14:12:31Z ponderator $
*/
public static class ClosureProxy extends AbstractProxy {
public static Object createProxy (ClassLoader loader, Class[] ifaces, Closure target) {
return Proxy.newProxyInstance(loader, ifaces, new ClosureProxy(target));
}
protected Closure closure;
private ClosureProxy(Closure closure) { this.closure = closure; }
@Override protected Object call(Object target, Method method, Object... args) throws Throwable {
return closure.call(new Object[] { args, method });
}
@Override protected Object getTarget(Method method, Object[] args) { return closure; }
}
}