/* XXL: The eXtensible and fleXible Library for data processing
Copyright (C) 2000-2011 Prof. Dr. Bernhard Seeger
Head of the Database Research Group
Department of Mathematics and Computer Science
University of Marburg
Germany
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; If not, see <http://www.gnu.org/licenses/>.
http://code.google.com/p/xxl/
*/
package xxl.core.predicates;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import xxl.core.collections.Lists;
import xxl.core.functions.Binding;
/**
* This class provides a predicate whose arguments are partially bound to some
* constant objects.
*
* <p>Example:<br />
* Consider the predicate {@link Less} that returns true if the first argument
* is less than the second. By creating a <code>BindingPredicate</code>
* instance
* <code><pre>
* Predicate<Integer> p = new BindingPredicate<Integer>(
* new Less<Integer>(
* new ComparableComparator<Integer>()
* ),
* Arrays.asList(0),
* Arrays.asList(42)
* );
* </pre></code>
* <code>p</code> can be evaluated by calling
* <code><pre>
* p.invoke(new 2); //predicate: 42 < 2
* </pre></code>
* which corresponds to the call
* <code><pre>
* p.invoke(42, 2); //predicate: 42 < 2
* </pre></code>
*
* @param <P> the type of the predicate's parameters.
* @see LeftBind
* @see RightBind
*/
public class BindingPredicate<P> extends AbstractPredicate<P> implements Binding<P> {
/**
* These objects are used as constant objects of this predicate's
* <code>invoke</code> methods. The important is that the attribute
* constIndices should always be sorted(!).
*/
protected Predicate<? super P> predicate;
/**
* Arguments for binding.
*/
protected List<P> constArguments;
/**
* Indices for binding.
*/
protected List<Integer> constIndices;
/**
* Creates a new predicate which binds a part of the arguments of the
* specified predicate to the given constant objects.
*
* @param predicate the predicate whose arguments should be partially
* bound.
* @param constIndices the indices of the arguments which should be bound.
* @param constArguments the constant arguments to be used in the
* predicate.
*/
public BindingPredicate(Predicate<? super P> predicate, List<Integer> constIndices, List<? extends P> constArguments) {
this.predicate = predicate;
this.constIndices = null;
this.constArguments = null;
setBinds(constIndices, constArguments);
}
/**
* Creates a new predicate which binds part of the arguments of the
* specified predicate to <code>null</code>.
*
* @param predicate the predicate whose arguments should be bound to
* <code>null</code>.
*/
public BindingPredicate(Predicate<? super P> predicate) {
this(predicate, null, null);
}
/**
* Creates a new predicate which binds part of the arguments of the
* specified predicate to <code>null</code>.
*
* @param predicate the predicate whose arguments should be partially
* bound to <code>null</code>.
* @param constIndices the indices of the arguments which should be bound
* to <code>null</code>.
*/
public BindingPredicate(Predicate<? super P> predicate, List<Integer> constIndices) {
this.predicate = predicate;
int i = constIndices.size();
setBinds(constIndices, Lists.initializedList((P)null, i));
}
/**
* Set the constant values to which a part of the arguments of the wrapped
* predicate should be bound.
*
* @param constArguments the constant values to which a part of the
* arguments of the wrapped predicate should be bound.
*/
public void setBinds(List<? extends P> constArguments) {
if (this.constArguments.size() == constArguments.size())
Collections.copy(this.constArguments, constArguments);
}
/**
* Set the constant values to which a part of the arguments of the wrapped
* predicate should be bound and returns the changed predicate.
*
* @param constIndices the indices of the arguments which should be bound.
* @param constArguments the constant values to which a part of the
* arguments of the wrapped predicate should be bound.
*/
public void setBinds(List<Integer> constIndices, List<? extends P> constArguments) {
if (constIndices != null) {
int len = constIndices.size();
for (int i = 0; i < len; i++)
if (constIndices.get(i) != -1)
setBind(constIndices.get(i), constArguments.get(i));
}
}
/**
* Set free all bound arguments of the wrapped predicate.
*/
public void restoreBinds() {
this.constIndices = null;
this.constArguments = null;
}
/**
* Set a constant value to which an arguments of the wrapped predicate
* should be bound and returns the changed predicate.
*
* @param constIndex the index of the arguments which should be bound.
* @param constArgument the constant value to which an argument of the
* wrapped predicate should be bound.
*/
public void setBind(int constIndex, P constArgument) {
if (constIndex == -1)
return;
int len;
if (constIndices != null){
len = constIndices.size();
for (int i = 0; i < len; i++){
if (constIndex == constIndices.get(i)) {
constArguments.set(i, constArgument);
return;
}
}
List<Integer> tempConstIndices = new ArrayList<Integer>(len+1);
List<P> tempConstArguments = new ArrayList<P>(len+1);
int pos = 0;
while (pos < len && constIndices.get(pos) < constIndex) {
tempConstIndices.add(constIndices.get(pos));
tempConstArguments.add(constArguments.get(pos));
pos++;
}
tempConstIndices.add(constIndex);
tempConstArguments.add(constArgument);
pos++;
while (pos <= len) {
tempConstIndices.add(constIndices.get(pos-1));
tempConstArguments.add(constArguments.get(pos-1));
pos++;
}
constIndices = tempConstIndices;
constArguments = tempConstArguments;
}
else {
constIndices = Arrays.asList(constIndex);
constArguments = Arrays.asList(constArgument);
}
}
/**
* Returns the result of the underlying predicate's <code>invoke</code>
* method that is called with the partially bound arguments.
*
* @param arguments the arguments to the underlying predicate.
* @return the result of the underlying predicate's <code>invoke</code>
* method that is called with the partially bound arguments.
*/
@Override
public boolean invoke(List<? extends P> arguments) {
if (arguments == null)
arguments = new ArrayList<P>(0);
if (constArguments == null)
constArguments = new ArrayList<P>(0);
int totalArgumentsLength = constArguments.size() + arguments.size();
List<P> newArguments = new ArrayList<P>(totalArgumentsLength);
for (int pos = 0, indConst = 0, ind = 0; pos < totalArgumentsLength; pos++)
if ((indConst < constArguments.size()) && (pos == constIndices.get(indConst))) {
newArguments.add(constArguments.get(indConst));
indConst++;
}
else
if (ind < arguments.size()) {
newArguments.add(arguments.get(ind));
ind++;
}
else
newArguments.add(null);
switch (newArguments.size()) {
case 0:
return predicate.invoke();
case 1:
return predicate.invoke(newArguments.get(0));
case 2:
return predicate.invoke(newArguments.get(0), newArguments.get(1));
default:
return predicate.invoke(newArguments);
}
}
}