package edu.washington.escience.myria.storage;
import java.util.Map;
import java.util.TreeMap;
import java.nio.ByteBuffer;
import org.joda.time.DateTime;
import com.google.common.base.Preconditions;
import com.google.common.collect.Maps;
import edu.washington.escience.myria.Type;
import edu.washington.escience.myria.column.Column;
/**
* A {@link Column} formed by the concatenation of one or more columns.
*
* @param <T> the type of the values in this column.
*/
public class ConcatColumn<T extends Comparable<?>> extends Column<T> {
/** Required for Java serialization. */
private static final long serialVersionUID = 1L;
/** The type of the values in this column. */
private final Type type;
/** The number of values in this column. */
private int numTuples;
/** Used to figure out which of the concatenated columns is the desired one. */
private final TreeMap<Integer, Column<?>> columnIds;
/** Whether this concatenated column is read only and can no longer be concatenated to. */
private boolean readOnly;
/**
* A wrapper for one or more columns of a specified type.
*
* @param type type of the columns that this object exposes.
*/
public ConcatColumn(final Type type) {
this.type = type;
numTuples = 0;
columnIds = Maps.newTreeMap();
readOnly = false;
}
/**
* Add the specified column to this column.
*
* @param column the column to be added.
*/
public void addColumn(final Column<?> column) {
Preconditions.checkState(!readOnly, "Cannot add more data to a read only concat column");
Preconditions.checkArgument(
column.getType() == getType(),
"cannot append a type %s to a column of type %s",
column.getType(),
getType());
if (column instanceof ConcatColumn) {
for (Column<?> c : ((ConcatColumn<?>) column).columnIds.values()) {
addColumn(c);
}
} else {
columnIds.put(numTuples, column);
numTuples += column.size();
}
}
@Override
public Type getType() {
return type;
}
@Override
public int size() {
return numTuples;
}
/**
* Validates the index of the requested row and returns the necessary information to calculate get the correct row out
* of it.
*
* @param row the row of the requested value.
* @return the necessary information to calculate get the correct row and value out of the concatenated columns.
*/
private Map.Entry<Integer, Column<?>> getColumnEntry(final int row) {
Preconditions.checkElementIndex(row, numTuples);
readOnly = true;
return columnIds.floorEntry(row);
}
@Override
public boolean getBoolean(final int row) {
Map.Entry<Integer, Column<?>> entry = getColumnEntry(row);
return entry.getValue().getBoolean(row - entry.getKey());
}
@Override
public DateTime getDateTime(final int row) {
Map.Entry<Integer, Column<?>> entry = getColumnEntry(row);
return entry.getValue().getDateTime(row - entry.getKey());
}
@Override
public double getDouble(final int row) {
Map.Entry<Integer, Column<?>> entry = getColumnEntry(row);
return entry.getValue().getDouble(row - entry.getKey());
}
@Override
public float getFloat(final int row) {
Map.Entry<Integer, Column<?>> entry = getColumnEntry(row);
return entry.getValue().getFloat(row - entry.getKey());
}
@Override
public int getInt(final int row) {
Map.Entry<Integer, Column<?>> entry = getColumnEntry(row);
return entry.getValue().getInt(row - entry.getKey());
}
@Override
public long getLong(final int row) {
Map.Entry<Integer, Column<?>> entry = getColumnEntry(row);
return entry.getValue().getLong(row - entry.getKey());
}
@Override
public ByteBuffer getBlob(final int row) {
Map.Entry<Integer, Column<?>> entry = getColumnEntry(row);
return entry.getValue().getBlob(row - entry.getKey());
}
@SuppressWarnings("unchecked")
@Deprecated
@Override
public T getObject(final int row) {
Map.Entry<Integer, Column<?>> entry = getColumnEntry(row);
return (T) entry.getValue().getObject(row - entry.getKey());
}
@Override
public String getString(final int row) {
Map.Entry<Integer, Column<?>> entry = getColumnEntry(row);
return entry.getValue().getString(row - entry.getKey());
}
}