Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
ch.lambdaj.function.closure.AbstractClosure Maven / Gradle / Ivy
// Modified or written by Ex Machina SAGL for inclusion with lambdaj.
// Copyright (c) 2009 Mario Fusco.
// Licensed under the Apache License, Version 2.0 (the "License")
package ch.lambdaj.function.closure;
import static ch.lambdaj.function.closure.ClosuresFactory.*;
import static ch.lambdaj.util.IntrospectionUtil.*;
import java.lang.reflect.*;
import java.util.*;
/**
* The abstract class extended by all the lambdaj closures
* @author Mario Fusco
*/
abstract class AbstractClosure {
public static final String CONSTRUCTOR = "";
private Object closed;
private List invokables = new ArrayList();
private List argsList = new ArrayList();
private Object[] curriedVars;
private boolean[] curriedVarsFlags;
private int freeVarsNumber = 0;
private final List unhandeledInvocations = new ArrayList();
/**
* Binds a free variable of the given class to the this closure.
* @param closedClass The type of the free variable to be bound to this closure
* @return A proxy of the same class of the passed class used to register all the invocation on the closed object
*/
public T of(Class closedClass) {
return of(closedClass, closedClass);
}
/**
* Binds an object to this closure.
* @param closed The object that has to be bound to this closure
* @return A proxy of the same class of the passed object used to register all the invocation on the closed object
*/
public T of(T closed) {
return of(closed, (Class)closed.getClass());
}
/**
* Binds an object to this closure.
* @param closed The object that has to be bound to this closure
* @param closedClass The actual class of the proxied object
* @return An instance of the closedClass that is actually a proxy used to register all the invocation on the closed object
*/
public T of(Object closed, Class closedClass) {
setClosed(closed);
return createProxyClosure(this, closedClass);
}
/**
* Defines the method invoked by this closure.
* @param closedObject The object on which the closure has to be invoked. It can be a fixed object or a Class.
* In this last case, if the method is not static, it is treated as it was an
* unbound argument defined through the {@link ch.lambdaj.Lambda#var(Class)} method
* @param methodName The name of the method invoked by this closure or {@link AbstractClosure#CONSTRUCTOR}
* if you want to call a constructor
* @param args The arguments used to invoke this closure. They can be a mixed of fixed value and
* unbound one defined through the {@link ch.lambdaj.Lambda#var(Class)} method
* @return The closure itself
*/
protected AbstractClosure of(Object closedObject, String methodName, Object ... args) {
Class> closedClass = closedObject instanceof Class> ? (Class>)closedObject : closedObject.getClass();
Invokable invokable = methodName.equals(CONSTRUCTOR) ?
new InvokableConstructor(findConstructor(closedClass, args)) :
new InvokableMethod(findMethod(closedClass, methodName, args));
bindInvocation(invokable, args);
setClosed(invokable.isStatic() ? null : closedObject);
return this;
}
/**
* Returns the number of free variables in this closure
* @return The number of free variables in this closure
*/
public int getFreeVarsNumber() {
return freeVarsNumber;
}
/**
* Dynamically casts this closure to a one-method interface in order to invoke its method in a strongly typed way
* @param asInterface The interface to which this closure should be casted
* @return A proxy that implements the requested interface and wraps this closure
* @throws IllegalArgumentException if the given Class is not actually an interface or if it has more than one method
*/
@SuppressWarnings("unchecked")
public T cast(Class asInterface) throws IllegalArgumentException {
if (!asInterface.isInterface()) throw new IllegalArgumentException("Cannot cast a closure to the concrete class " + asInterface.getName());
Method[] methods = asInterface.getMethods();
if (methods.length != 1) throw new IllegalArgumentException("Cannot cast a closure to an interface with more than one method");
return (T)Proxy.newProxyInstance(asInterface.getClassLoader(), new Class>[] { asInterface },
new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return closeOne(args);
}
}
);
}
void setClosed(Object closed) {
this.closed = closed;
if (isClosedOnFreeVar()) freeVarsNumber++;
}
private boolean isClosedOnFreeVar() {
return closed instanceof Class>;
}
void bindInvocation(Method method, Object[] args) {
if (!method.isAccessible()) method.setAccessible(true);
bindInvocation(new InvokableMethod(method),args);
}
private void bindInvocation(Invokable invokable, Object[] args) {
invokables.add(invokable);
if (args != null) for (Object arg : args) if (isClosureVarPlaceholder(arg)) freeVarsNumber++;
argsList.add(args);
}
void closeUnhandeledInvocations() {
for (Object[] vars : unhandeledInvocations) closeOne(vars);
unhandeledInvocations.clear();
}
/**
* Invokes this closure once by applying the given set of variables to it.
* @param vars The set of variables used to invoke this closure once
* @return The result of the closure invocation
* @throws WrongClosureInvocationException if the number of the passed variables doesn't correspond to one
* with which this closure has been defined
*/
Object closeOne(Object... vars) throws WrongClosureInvocationException {
if (invokables.isEmpty()) {
unhandeledInvocations.add(vars);
return null;
}
List boundParams = bindParams(vars);
Object result = isClosedOnFreeVar() ? vars[0] : closed;
Iterator argsIterator = boundParams != null ? boundParams.iterator() : null;
for (Invokable invokable : invokables)
result = invokable.invoke(result, argsIterator != null ? argsIterator.next() : null);
return result;
}
/**
* Invokes this closure once for each passed variable.
* It is then assumed that this closure has been defined with exactly one free variable
* @param vars The set of variables used to invoke this closure once for each variable
* @return A list of Object containing the results of each closure invocation
* @throws WrongClosureInvocationException if this closure hasn't been defined with exactly one free variable
*/
List> closeAll(Object... vars) throws WrongClosureInvocationException {
List results = new ArrayList();
for (Object var : vars) results.add(closeOne(var));
return results;
}
/**
* Invokes this closure once for each passed set of variables.
* Each iterable is used as a different set of variables with which this closure is invoked
* @param vars The variables used to invoke this closure once for each set of variables
* @return A list of Object containing the results of each closure invocation
* @throws WrongClosureInvocationException if the number of the passed variables doesn't correspond to one
* with which this closure has been defined
*/
List> closeAll(Iterable>... vars) throws WrongClosureInvocationException {
List results = new ArrayList();
int length = vars.length;
Iterator>[] iterators = new Iterator>[length];
for (int i = 0; i < length; i++) iterators[i] = vars[i].iterator();
while (true) {
Object[] varSet = new Object[length];
if (buildParams(length, iterators, varSet)) break;
results.add(closeOne(varSet));
}
return results;
}
private boolean buildParams(int length, Iterator>[] iterators, Object[] varSet) {
for (int i = 0; i < length; i++) {
if (!iterators[i].hasNext()) return true;
varSet[i] = iterators[i].next();
}
return false;
}
private List bindParams(Object... vars) throws WrongClosureInvocationException {
if (checkParams(vars)) return null;
int varCounter = isClosedOnFreeVar() ? 1 : 0;
int curriedParamCounter = 0;
List boundParams = new ArrayList();
for (Object[] args : argsList) {
if (args == null) {
boundParams.add(null);
continue;
}
Object[] objs = new Object[args.length];
for (int i = 0; i < args.length; i++) {
if (isClosureVarPlaceholder(args[i])) {
objs[i] = (curriedVars != null && curriedVarsFlags[curriedParamCounter]) ? curriedVars[curriedParamCounter] : vars[varCounter++];
curriedParamCounter++;
} else objs[i] = args[i];
}
boundParams.add(objs);
}
return boundParams;
}
private boolean checkParams(Object... vars) {
if (vars == null || vars.length == 0) {
if (freeVarsNumber != 0)
throw new WrongClosureInvocationException("Closure invoked without vars instead of the expected " + freeVarsNumber);
if (curriedVars == null && argsList == null) return true;
}
if (freeVarsNumber != vars.length)
throw new WrongClosureInvocationException("Closure invoked with " + vars.length + " vars instead of the expected " + freeVarsNumber);
if (isClosedOnFreeVar()) checkClosedType(vars[0]);
return false;
}
private void checkClosedType(Object toBeClosed) {
if (!((Class>)closed).isInstance(toBeClosed))
throw new WrongClosureInvocationException("The first var must be of class " + closed);
}
/**
* Curry this closure by fixing one of its free variable to a given value.
* @param curriedClosure The closure resulting from this curry operation
* @param curried The value to which the free variable should be curry
* @param position The 1-based position of the variable to which apply the curry operation
* @return A Closure having a free variable less than this one since one of them has been fixed to the given value
* @throws IllegalArgumentException if this closure doesn't have a free variable in the specified position
*/
T curry(T curriedClosure, Object curried, int position) throws IllegalArgumentException {
cloneClosureForCurry(curriedClosure);
curriedClosure.curryParam(curried, position);
return curriedClosure;
}
private void cloneClosureForCurry(T curriedClosure) {
curriedClosure.closed = closed;
curriedClosure.invokables = invokables;
curriedClosure.argsList = argsList;
curriedClosure.freeVarsNumber = freeVarsNumber;
if (curriedVars != null) {
curriedClosure.curriedVars = new Object[curriedVars.length];
System.arraycopy(curriedVars, 0, curriedClosure.curriedVars, 0, curriedVars.length);
}
if (curriedVarsFlags != null) {
curriedClosure.curriedVarsFlags = new boolean[curriedVarsFlags.length];
System.arraycopy(curriedVarsFlags, 0, curriedClosure.curriedVarsFlags, 0, curriedVarsFlags.length);
}
}
private void curryParam(Object curried, int position) throws IllegalArgumentException {
if (checkCurriedOnClosed(curried, position)) return;
if (curriedVars == null) {
curriedVars = new Object[freeVarsNumber];
curriedVarsFlags = new boolean[freeVarsNumber];
}
if (!findVarToBeCurried(curried, position))
throw new IllegalArgumentException("Trying to curry this closure on an already bound or unexisting variable");
}
private boolean checkCurriedOnClosed(Object curried, int position) {
if (position != 1 || !isClosedOnFreeVar()) return false;
checkClosedType(curried);
closed = curried;
freeVarsNumber--;
return true;
}
private boolean findVarToBeCurried(Object curried, int position) {
for (int i = 0; i < curriedVars.length; i++) {
if (curriedVarsFlags[i]) continue;
if (--position == 0) {
curriedVars[i] = curried;
curriedVarsFlags[i] = true;
freeVarsNumber--;
return true;
}
}
return false;
}
}