/* 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.relational;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import xxl.core.functions.AbstractFunction;
import xxl.core.functions.Function;
import xxl.core.predicates.AbstractPredicate;
import xxl.core.predicates.MetaDataPredicate;
import xxl.core.predicates.Predicate;
import xxl.core.predicates.PredicateMetaDataPredicate;
import xxl.core.predicates.Predicates;
import xxl.core.relational.metaData.AppendedResultSetMetaData;
import xxl.core.relational.metaData.MergedResultSetMetaData;
import xxl.core.relational.metaData.ResultSetMetaDatas;
import xxl.core.relational.metaData.UnifiedResultSetMetaData;
import xxl.core.relational.tuples.ArrayTuple;
import xxl.core.relational.tuples.ListTuple;
import xxl.core.relational.tuples.Tuple;
import xxl.core.util.metaData.CompositeMetaData;
import xxl.core.util.metaData.MetaDataException;
/**
* This class contains various <tt>static</tt> methods for managing result set
* metadata, creating result tuples and composing predicates during join
* operations.
*
* <p>Most of these methods are used internally by the join operation of this
* package.</p>
*
* <p>This class cannot become instantiated.</p>
*
* @see xxl.core.relational.cursors.NestedLoopsJoin
*/
public class JoinUtils {
/**
* The default constructor has private access in order to ensure
* non-instantiability.
*/
private JoinUtils() {
// private access in order to ensure non-instantiability
}
/**
* Returns a function that builds up a result tuple after performing a
* join. To compute the resulting relation, this function has to be called
* for every list of tuples that qualify for the result.
*
* @param tupleFactory a function to create a tuple. The FACTORY_METHOD of
* {@link ArrayTuple ArrayTuple} or {@link ListTuple ListTuple} can
* be used.
* @param metadata the metadata information of the resulting relation.
* @return a function that builds up a result tuple after performing a
* join.
*/
public static Function<Tuple, Tuple> genericJoinTupleFactory(final Function<Object, ? extends Tuple> tupleFactory, final MergedResultSetMetaData metadata) {
return new AbstractFunction<Tuple, Tuple>() {
@Override
public Tuple invoke(List<? extends Tuple> tuples) {
try {
int columnCount = metadata.getColumnCount();
List<Object> tuple = new ArrayList<Object>(columnCount);
Iterator<int[]> originalColumnIndices;
int[] originalColumnIndex;
Tuple temp;
column: for (int i = 1; i <= columnCount; i++) {
originalColumnIndices = metadata.originalColumnIndices(i);
while (originalColumnIndices.hasNext())
if ((temp = tuples.get((originalColumnIndex = originalColumnIndices.next())[0])) != null) {
tuple.add(temp.getObject(originalColumnIndex[1]));
continue column;
}
tuple.add(null);
}
return tupleFactory.invoke(tuple);
}
catch (SQLException sqle) {
throw new MetaDataException("meta data cannot be accessed due to the following sql exception: " + sqle.getMessage());
}
}
};
}
/**
* Returns a predicate that compares tuples of the joined relations. This
* predicate can be passed to the join routines of the cursor package. So
* far, this method is used only by methods declared in this class.
*
* @param metadata the metadata information of the resulting relation.
* @return a predicate that compares tuples of the joined relations.
*/
public static Predicate<Tuple> naturalJoinPredicate(final MergedResultSetMetaData metadata) {
return new AbstractPredicate<Tuple>() {
@Override
public boolean invoke(List<? extends Tuple> tuples) {
try {
Predicate<Object> equals = Predicates.newNullSensitiveEqual(true);
int columnCount = metadata.getColumnCount();
Iterator<int[]> originalColumnIndices;
int[] originalColumnIndex;
Object object;
for (int i = 1; i <= columnCount; i++) {
object = tuples.get((originalColumnIndex = (originalColumnIndices = metadata.originalColumnIndices(i)).next())[0]).getObject(originalColumnIndex[1]);
while (originalColumnIndices.hasNext())
if (!equals.invoke(object, tuples.get((originalColumnIndex = originalColumnIndices.next())[0]).getObject(originalColumnIndex[1])))
return false;
}
return true;
}
catch (SQLException sqle) {
throw new MetaDataException("meta data cannot be accessed due to the following sql exception: " + sqle.getMessage());
}
}
};
}
/**
* Returns a metadata predicate for a natural join.
*
* <p>Consequently, a predicate and the metadata is computed.</p>
*
* <p>This factory method is used by the
* {@link xxl.core.relational.cursors.NestedLoopsJoin nested-loops join}.
* There it is used to generate a new metadata predicate that provides the
* metadata for the join and decides whether a list of tuple join or
* not.</p>
*
* @param metadata the metadata information of the resulting relation.
* @return a metadata predicate for a natural join.
*/
public static MetaDataPredicate<Tuple, CompositeMetaData<Object, Object>> naturalJoinMetaDataPredicate(ResultSetMetaData... metadata) {
UnifiedResultSetMetaData localMetaData = new UnifiedResultSetMetaData(metadata);
final CompositeMetaData<Object, Object> globalMetaData = new CompositeMetaData<Object, Object>();
globalMetaData.add(ResultSetMetaDatas.RESULTSET_METADATA_TYPE, localMetaData);
return new PredicateMetaDataPredicate<Tuple, CompositeMetaData<Object, Object>>(naturalJoinPredicate(localMetaData)) {
@Override
public CompositeMetaData<Object, Object> getMetaData() {
return globalMetaData;
}
};
}
/**
* Returns a metadata for a theta join.
*
* <p>Consequently, the metadata for the join (and the given predicate) is
* computed.</p>
*
* <p>This factory method is used by the
* {@link xxl.core.relational.cursors.NestedLoopsJoin nested-loops join}.
* There it is used to generate a new metadata predicate that provides the
* metadata for the join and decides whether a list of tuple join or
* not.</p>
*
* @param theta the predicate that is used to decide whether a list of
* tuples join or not.
* @param metadata the metadata information of the resulting relation.
* @return a metadata predicate for a theta join.
*/
public static MetaDataPredicate<Tuple, CompositeMetaData<Object, Object>> thetaJoinMetaDataPredicate(Predicate<? super Tuple> theta, ResultSetMetaData... metadata) {
AppendedResultSetMetaData localMetaData = new AppendedResultSetMetaData(metadata);
final CompositeMetaData<Object, Object> globalMetaData = new CompositeMetaData<Object, Object>();
globalMetaData.add(ResultSetMetaDatas.RESULTSET_METADATA_TYPE, localMetaData);
return new PredicateMetaDataPredicate<Tuple, CompositeMetaData<Object, Object>>(theta) {
@Override
public CompositeMetaData<Object, Object> getMetaData() {
return globalMetaData;
}
};
}
/**
* Returns a metadata for a semi join.
*
* <p>Consequently, a predicate and the metadata is computed.</p>
*
* <p>This factory method is used by the
* {@link xxl.core.relational.cursors.NestedLoopsJoin nested-loops join}.
* There it is used to generate a new metadata predicate that provides the
* metadata for the join and decides whether a list of tuple join or
* not.</p>
*
* @param joinMetaDataPredicate the metadata predicate that is used to
* decide whether a list of tuples join or not and that provides the
* metadata for the semi join.
* @param metadata the metadata information of the resulting relation.
* @param metadataIndices the indices of the relations that are required
* for building the resulting relation of the semi join.
* @return a metadata predicate for a semi join.
*/
public static MetaDataPredicate<Tuple, CompositeMetaData<Object, Object>> semiJoinMetaDataPredicate(MetaDataPredicate<Tuple, CompositeMetaData<Object, Object>> joinMetaDataPredicate, ResultSetMetaData[] metadata, int... metadataIndices) {
ResultSetMetaData[] joinedMetadata = new ResultSetMetaData[metadataIndices.length];
for (int i = 0; i < metadataIndices.length; i++)
joinedMetadata[i] = metadata[metadataIndices[i]];
UnifiedResultSetMetaData localMetaData = new UnifiedResultSetMetaData(joinedMetadata);
joinMetaDataPredicate.getMetaData().replace(ResultSetMetaDatas.RESULTSET_METADATA_TYPE, localMetaData);
return joinMetaDataPredicate;
}
}