/*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
*
* The Original Code is the Kowari Metadata Store.
*
* The Initial Developer of the Original Code is Andrew Newman.
* Copyright (C) 2005. All Rights Reserved.
*
* Contributor(s): N/A.
*
* [NOTE: The text of this Exhibit A may differ slightly from the text
* of the notices in the Source Code files of the Original Code. You
* should use the text of this Exhibit A rather than the text found in the
* Original Code Source Code for Your Modifications.]
*
*/
package org.mulgara.store.tuples;
// Java 2 standard packages
import org.apache.log4j.Logger;
import org.mulgara.query.TuplesException;
import org.mulgara.query.Variable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* Common functionality from UnboundJoin.
*
* @created 2005-03-08
*
* @author Andrew Newman
*
* @version $Revision: 1.2 $
*
* @modified $Date: 2005/03/26 03:44:19 $
*
* @maintenanceAuthor $Author: newmana $
*
* @copyright © 2005 Andrew Newman
*
* @licence <a href="{@docRoot}/../../LICENCE">Mozilla Public License v1.1</a>
*/
public abstract class JoinTuples extends AbstractTuples {
/** Logger. */
protected static final Logger logger = Logger.getLogger(JoinTuples.class.getName());
/**
* Version of {@link #operandBinding}} including only columns to the left of
* the first unbound column.
*/
protected long[][] operandBindingPrefix;
/**
* For each column of the joined result, which operand contains the first
* occurrence of that variable.
*/
protected int[] mapOperand;
/**
* For each column of the joined result, which column of the operand
* determined by {@link #mapOperand} contains the first occurrence of that
* variable.
*/
protected int[] mapColumn;
/**
* Magic value within the {@link #fooOperand} array, indicating that a column
* is bound to one of the columns of the <var>prefix</var> parameter to
* {@link #next}.
*/
protected static final int PREFIX = -1;
/**
* For each column of each operand, which operand contains the first
* occurrence of that variable, or {@link #PREFIX} if the prefix specified
* to {@link #next} contains the occurrence.
*/
protected int[][] fooOperand;
/**
* For each column of each operand, which column of the operand determined by
* {@link #fooOperand} contains the first occurrence of that variable, or if
* the corresponding value of {@link #fooOperand} is {@link #PREFIX}, which
* column of the prefix specified to {@link #next} contains the occurrence.
*/
protected int[][] fooColumn;
/**
* Whether each column of this instance might contain {@link #UNBOUND} rows.
*/
protected boolean[] columnEverUnbound;
/**
* The propositions to conjoin.
*/
protected Tuples[] operands;
/**
* The required values of the columns of each operand. A value of {@link
* Tuples#UNBOUND} indicates that the column is free to vary.
*/
protected long[][] operandBinding;
/**
* For each operand, for each variable, which output column contains the same variable.
*/
protected int[][] operandOutputMap;
/**
* Do any of the operands with variables matching this output variable contain UNBOUND?
*/
protected boolean[] columnOperandEverUnbound;
/**
* Flag indicating that the cursor is before the first row.
*/
protected boolean isBeforeFirst = true;
/**
* Flag indicating that the cursor is after the last row.
*/
protected boolean isAfterLast = false;
/**
* Do any of the operands contain duplicates. Used to shortcircuit hasNoDuplicates.
*/
protected boolean operandsContainDuplicates;
/**
* The prefix of the index.
*/
protected long[] prefix = null;
protected void init(Tuples[] operands) throws TuplesException {
// Validate "operands" parameter
if (operands == null) {
throw new IllegalArgumentException("Null \"operands\" parameter");
}
// Initialize fields
this.operands = clone(operands);
operandBinding = new long[operands.length][];
operandBindingPrefix = new long[operands.length][];
this.operandsContainDuplicates = false;
for (int i = 0; i < operands.length; i++) {
// Debug
if (logger.isDebugEnabled()) {
logger.debug("Operands " + i + " : " + operands[i]);
logger.debug("Operands variables " + i + " : " +
Arrays.asList(operands[i].getVariables()));
logger.debug("Ooperands types " + i + " : " +
operands[i].getClass());
}
operandBinding[i] = new long[operands[i].getVariables().length];
if (!operands[i].hasNoDuplicates()) {
this.operandsContainDuplicates = true;
}
}
fooOperand = new int[operands.length][];
fooColumn = new int[operands.length][];
operandOutputMap = new int[operands.length][];
// Calculate the variables present and their mappings from operand
// columns to result columns
List<Variable> variableList = new ArrayList<Variable>();
List<Integer> mapOperandList = new ArrayList<Integer>();
List<Integer> mapColumnList = new ArrayList<Integer>();
List<Integer> fooOperandList = new ArrayList<Integer>();
List<Integer> fooColumnList = new ArrayList<Integer>();
for (int i = 0; i < operands.length; i++) {
fooOperandList.clear();
fooColumnList.clear();
Variable[] operandVariables = operands[i].getVariables();
operandOutputMap[i] = new int[operandVariables.length];
for (int j = 0; j < operandVariables.length; j++) {
int k = variableList.indexOf(operandVariables[j]);
if (k == -1) {
mapOperandList.add(new Integer(i));
mapColumnList.add(new Integer(j));
fooOperandList.add(new Integer(PREFIX));
fooColumnList.add(new Integer(variableList.size()));
variableList.add(operandVariables[j]);
operandOutputMap[i][j] = j;
} else {
fooOperandList.add(mapOperandList.get(k));
fooColumnList.add(mapColumnList.get(k));
operandOutputMap[i][j] = k;
}
}
// Convert per-operand lists into arrays
assert fooOperandList.size() == fooColumnList.size();
fooOperand[i] = new int[fooOperandList.size()];
fooColumn[i] = new int[fooColumnList.size()];
for (int j = 0; j < fooOperand[i].length; j++) {
fooOperand[i][j] = fooOperandList.get(j).intValue();
fooColumn[i][j] = fooColumnList.get(j).intValue();
}
}
// Convert column mappings from lists to arrays
setVariables(variableList);
mapOperand = new int[mapOperandList.size()];
mapColumn = new int[mapColumnList.size()];
for (int i = 0; i < mapOperand.length; i++) {
mapOperand[i] = ((Integer) mapOperandList.get(i)).intValue();
mapColumn[i] = ((Integer) mapColumnList.get(i)).intValue();
}
// Determine which columns are ever unbound
columnEverUnbound = new boolean[variableList.size()];
columnOperandEverUnbound = new boolean[variableList.size()];
Arrays.fill(columnEverUnbound, true);
Arrays.fill(columnOperandEverUnbound, false);
for (int i = 0; i < operands.length; i++) {
Variable[] variables = operands[i].getVariables();
for (int j = 0; j < variables.length; j++) {
if (!operands[i].isColumnEverUnbound(j)) {
columnEverUnbound[getColumnIndex(variables[j])] = false;
} else {
columnOperandEverUnbound[getColumnIndex(variables[j])] = true;
}
}
}
}
/**
* @return {@inheritDoc} This occurs if and only if every one of the
* {@link #operands} is unconstrained.
* @throws TuplesException {@inheritDoc}
*/
public boolean isUnconstrained() throws TuplesException {
for (int i = 0; i < operands.length; i++) {
if (!operands[i].isUnconstrained()) {
return false;
}
}
return true;
}
public List<Tuples> getOperands() {
return Arrays.asList(operands);
}
public void beforeFirst(long[] prefix, int suffixTruncation) throws TuplesException {
if (prefix == null) {
throw new IllegalArgumentException("Null \"prefix\" parameter");
}
if (suffixTruncation != 0) {
throw new TuplesException("Suffix truncation not implemented");
}
assert operands != null;
assert operandBinding != null;
isBeforeFirst = true;
isAfterLast = false;
this.prefix = prefix;
}
}