/* 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.cursors; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import xxl.core.functions.Binding; /** * This class is a prototypical implementation for subqueries which may be used * in {@link xxl.core.predicates.ExistPredicate exist}, * {@link xxl.core.predicates.AllPredicate all}, * {@link xxl.core.predicates.AnyPredicate any} predicates, etc. It allows to * treat a part of the parameters of a subquery as free variables and delivers * the result when they are bound. * * <p>The cursor of the subquery has to support the reset-method. After one * reset the result has to be recomputed and must not be buffered.</p> * * @param <E> the type of the elements returned by this iteration. * @see xxl.core.predicates.AllPredicate * @see xxl.core.predicates.AnyPredicate * @see xxl.core.predicates.ExistPredicate */ public class Subquery<E> extends SecureDecoratorCursor<E> implements Binding<E> { /** * The subquery, whose result will be delivered when its free variables are * bound. */ protected Cursor<E> outCursor; /** * This two-dimensional array saves information of the free variables. * Two-dimensional index: (index of the bindings, index of the parameter) */ protected int[][] mConstIndices; /** * The bindings used in this subquery. */ protected List<? extends Binding<E>> bindings; /** * The arguments for prebinding. */ protected List<E> constArguments; /** * The indices for prebinding. */ protected List<Integer> constIndices; /** * Creates a new instance of a subquery. It allow to treat a part of the * parameters of a subquery as free variables and delivers the result when * they are bound. * * @param outCursor the subquery, whose result will be delivered when its * free variables are bound. * @param bindings the bindings used in this subquery. * @param mConstIndices the two-dimensional array saving information of the * free variables.<br /> * <i>Example:</i><br /> * <code>mConstIndices[i][j]=k</code>: the <code>j</code>-th free * variable is the <code>k</code>-th parameter of the * <code>i</code>-th binding.<br /> * <code>k=-1</code>: the free variable is not used in the binding. */ public Subquery(Cursor<E> outCursor, List<? extends Binding<E>> bindings, int[][] mConstIndices) { super(outCursor); this.outCursor = outCursor; this.bindings = bindings; this.mConstIndices = mConstIndices; int i = 0; if (bindings != null) { while ((i < mConstIndices.length) && (i < bindings.size())) { for (int j = 0; j < mConstIndices[i].length; j++) if (mConstIndices[i][j] != -1) bindings.get(i).setBind(mConstIndices[i][j], null); i++; } } constArguments = new ArrayList<E>(); constIndices = new ArrayList<Integer>(); } /** * Set the values of the free variables of the subquery. * * @param arguments the objects to which the free variables of the wrapped * subquery should be bound. */ public void bind(List<? extends E> arguments) { if (arguments == null) arguments = new ArrayList<E>(0); int totalArgumentsLength = constArguments.size() + arguments.size(); List<E> newArguments = new ArrayList<E>(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); if (bindings == null) return; for (int i = 0; i < mConstIndices.length && i < bindings.size(); i++) for (int j = 0; j < mConstIndices[i].length; j++) if (mConstIndices[i][j] != -1 && j < newArguments.size()) bindings.get(i).setBind(mConstIndices[i][j], newArguments.get(j)); } /** * Set the values of the free variables of the subquery. * * @param arguments the objects to which the free variables of the wrapped * subquery should be bound. */ public void setBinds(List<? extends E> arguments) { if (constArguments.size() == arguments.size()) Collections.copy(constArguments, arguments); } /** * Set the values of given parameters of the subquery. * * @param indices the indices of the free variables which should be bound * to the given value. * @param arguments the objects to which the given predicate-parameter of * the wrapped subquery should be bound. */ public void setBinds(List<Integer> indices, List<? extends E> arguments) { if (indices == null) return; int len = indices.size(); for (int i = 0; i < len; i++) { if (indices.get(i) != -1) setBind(indices.get(i), arguments.get(i)); } } /** * Set the value of a given parameter of the subquery. * * @param index the index of the free variable which should be bound to the * given value. * @param argument the object to which the given predicate-parameter of the * wrapped subquery should be bound. */ public void setBind(int index, E argument) { if (index == -1) return; int len; if (constIndices != null) { len = constIndices.size(); for (int i = 0; i < len; i++) if (index == constIndices.get(i)) { constArguments.set(i, argument); return; } List<Integer> tempConstIndices = new ArrayList<Integer>(len+1); List<E> tempConstArguments = new ArrayList<E>(len+1); int pos = 0; while (pos < len && constIndices.get(pos) < index) { tempConstIndices.add(constIndices.get(pos)); tempConstArguments.add(constArguments.get(pos)); pos++; } tempConstIndices.add(index); tempConstArguments.add(argument); 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(index); constArguments = Arrays.asList(argument); } } /** * Remove all bindings from the free variables of the subquery, i.e., * restore the initial state of the subquery without any bindings. */ public void restoreBinds() { constArguments.clear(); constIndices.clear(); } }