/*
* Tuple.java
*
* Created on May 6, 2002, 2:05 pm
*/
package hep.aida.ref.tuple;
import hep.aida.IManagedObject;
import hep.aida.ITuple;
import org.freehep.util.Value;
/**
*
* @author The AIDA team @ SLAC.
*
*/
public class ChainedTuple extends ReadOnlyAbstractTuple {
private ITuple[] set;
private int currentRow;
private ITuple currentTuple;
private int currentTupleID;
private int currentTupleRow;
private ChainedTuple[] tuples;
/**
* Tuple constructor.
* @param name the Tuple's name
* @param title the Tuple's title
* @param set Array of ITuples to be chained
*/
public ChainedTuple(String name, String title, ITuple[] set) {
super(name, title);
int nCol = 0;;
// Check if ITuples are compatible
if (set.length > 1) {
nCol = set[0].columns();
String[] colNames = new String[nCol];
Class[] colTypes = new Class[nCol];
for (int i=0; i<nCol; i++) {
colNames[i] = set[0].columnName(i);
colTypes[i] = set[0].columnType(i);
}
for (int n=1; n<set.length; n++) {
if (set[n].columns() != nCol)
throw new IllegalArgumentException("ITuples in the set have different number of columns!");
for (int i=0; i<nCol; i++) {
if (!(colNames[i].equals(set[n].columnName(i))))
throw new IllegalArgumentException("ITuples in the set have different column names!");
if (colTypes[i] != set[n].columnType(i))
throw new IllegalArgumentException("ITuples in the set have different column types!");
}
}
} else
throw new IllegalArgumentException("Not enough tuples provided. Two or more tuples must be chained together.");
tuples = new ChainedTuple[nCol];
for ( int i = 0; i < nCol; i++ ) {
if ( set[0].columnType(i) == ITuple.class ) {
ITuple[] folders = new ITuple[set.length];
for ( int j = 0; j < set.length; j++ ) {
set[j].start();
folders[j] = set[j].findTuple(i);
}
tuples[i] = new ChainedTuple(set[0].columnName(i),"",folders);
}
}
setTitle(title);
this.set = set;
currentRow = -1;
currentTuple = set[0];
currentTuple.start();
currentTupleID = 0;
currentTupleRow = -1;
}
public boolean supportsRandomAccess() {
for (int i=0; i<set.length; i++) {
if ( set[i] instanceof FTuple ) {
if ( ! ((FTuple) set[i]).supportsRandomAccess() )
return false;
}
return false;
}
return true;
}
public boolean supportsMultipleCursors() {
for (int i=0; i<set.length; i++) {
if ( set[i] instanceof FTuple ) {
if ( ! ((FTuple) set[i]).supportsMultipleCursors() )
return false;
}
return false;
}
return true;
}
public boolean isInMemory() {
for (int i=0; i<set.length; i++) {
if ( set[i] instanceof FTuple ) {
if ( ! ((FTuple) set[i]).isInMemory() )
return false;
}
return false;
}
return true;
}
public boolean providesColumnDefaultValues() {
for (int i=0; i<set.length; i++) {
if ( set[i] instanceof AbstractTuple ) {
if ( ! ((AbstractTuple) set[i]).providesColumnDefaultValues() )
return false;
}
return false;
}
return true;
}
public void columnValue(int column, Value v) {
Class type = currentTuple.columnType(column);
if ( type == Integer.TYPE )
v.set(currentTuple.getInt(column));
else if ( type == Short.TYPE )
v.set(currentTuple.getShort(column));
else if ( type == Long.TYPE )
v.set(currentTuple.getLong(column));
else if ( type == Float.TYPE )
v.set(currentTuple.getFloat(column));
else if ( type == Double.TYPE )
v.set(currentTuple.getDouble(column));
else if ( type == Boolean.TYPE )
v.set(currentTuple.getBoolean(column));
else if ( type == Byte.TYPE )
v.set(currentTuple.getByte(column));
else if ( type == Character.TYPE )
v.set(currentTuple.getChar(column));
else if ( type == String.class )
v.set(currentTuple.getString(column));
else
v.set(currentTuple.getObject(column));
}
public String columnDefaultString( int column ) {
if (set[0] instanceof Tuple) return ((Tuple) set[0]).columnDefaultString(column);
else if ( columnType( column ) != ITuple.class )
return set[0].columnDefaultValue(column).toString();
else {
ITuple tup = findTuple(column);
if ( tup != null ) {
String tupName = "";
if (tup instanceof IManagedObject) tupName = ((IManagedObject) tup).name();
else tupName = tup.title();
String tmpColumnsString = "";
int nCol = tup.columns();
for (int i=0; i<nCol; i++) {
Class colType = tup.columnType(i);
String colName =tup.columnName(i);
tmpColumnsString += colType + " " + colName;
if ( i < nCol ) tmpColumnsString += ";";
}
return tupName+" = {"+tmpColumnsString+"}";
} else
return "null";
}
}
/**
* Get the number of columns in the Tuple.
* @return the number of columns in the Tuple
*/
public int columns() {
return set[0].columns();
}
/**
* Get the name of a column from its index
* @param column the column's index
* @return the column's name
*/
public String columnName(int column) {
return set[0].columnName(column);
}
/**
* Get the type of the column from its index
* @param column the column's index
* @return the column's type
*/
public Class columnType(int column) {
return set[0].columnType(column);
}
/**
* Get the minimum value of a column.
* @param column The column's index.
* @return The minimum value of the column. If the minimum
* cannot be calculated Double.NaN is returned.
*
*/
public double columnMin(int column) {
double min = Double.NaN;
for (int i=0; i<set.length; i++) {
double tmp = set[i].columnMin(column);
if (Double.isNaN(min) || tmp<min) min = tmp;
}
return min;
}
/**
* Get the maximum value of a column.
* @param column The column's index.
* @return The maximum value of the column. If the maximum
* cannot be calculated Double.NaN is returned.
*
*/
public double columnMax(int column) {
double max = Double.NaN;
for (int i=0; i<set.length; i++) {
double tmp = set[i].columnMax(column);
if (Double.isNaN(max) || tmp>max) max = tmp;
}
return max;
}
/**
* Get the mean value of a column.
* @param column The column's index.
* @return The mean value of the column. If the mean
* cannot be calculated Double.NaN is returned.
*
*/
public double columnMean(int column) {
double mean = 0.;
int rows = 0;
for (int i=0; i<set.length; i++) {
double m = set[i].columnMean(column);
int r = set[i].rows();
if (r<=0) continue;
if (!Double.isNaN(m)) {
mean += m*r;
rows += r;
} else return Double.NaN;
}
return mean/rows;
}
/**
* Get the rms of a column.
* @param column The column's index.
* @return The rms of the column. If the rms
* cannot be calculated Double.NaN is returned.
*
*/
public double columnRms(int column) {
double rms = 0;
int rows = 0;
for (int i=0; i<set.length; i++) {
double m = set[i].columnMean(column);
double rm = set[i].columnRms(column);
int r = set[i].rows();
if (r<=0) continue;
if (!Double.isNaN(m) && !Double.isNaN(rm)) {
rms += (rm*rm + m*m)*r;
rows += r;
} else return Double.NaN;
}
return Math.sqrt(rms/rows-Math.pow(columnMean(column),2));
}
/**
* The number of rows currently in the ntuple.
* @return -1 if cannot be determined.
*/
public int rows() {
int rows = 0;
for (int i=0; i<set.length; i++) {
int r = set[i].rows();
if (r>0) rows += r;
}
return rows;
}
/**
* Get the current row.
* @return The current row;
*
*/
public int getRow() {
return currentRow;
}
public hep.aida.ITuple findTuple(int column) {
return tuples[column];
}
/**
* Set the current row.
* @param row The current row;
* @return True if the opeartion was succesfull.
*
*/
public void setRow( int row ) {
if (row > rows())
throw new IllegalArgumentException("Row "+row+" is bigger than the length of this ChainedTuple ("+rows()+")");
int rows = 0;
int i = 0;
int r = 0;
ITuple tup = null;
for (i=0; i<set.length; i++) {
r = set[i].rows();
if (rows<row && (rows+r)>row) break;
if (r>0) rows += r;
}
currentRow = row;
currentTupleID = i;
currentTuple = set[currentTupleID];
currentTupleRow = row-rows;
currentTuple.setRow(currentTupleRow);
}
/**
* Positions the read cursor immediately before the first row.
*/
public void start() {
currentRow = -1;
currentTupleID = 0;
currentTuple = set[currentTupleID];
currentTuple.start();
currentTupleRow = -1;
}
/**
* Skips rows.
* @param rows number of rows to skip, greater than 0.
* @return false if it cannot skip rows.
*/
public void skip(int rows) {
setRow(currentRow+rows);
}
/**
* Positions the cursor at the next row.
* @return false if there is no next row.
*/
public boolean next() {
if (currentTuple.next()) {
currentRow++;
currentTupleRow++;
return true;
} else if (currentTupleID < set.length-1) {
currentTupleID++;
currentTuple = set[currentTupleID];
currentTuple.start();
currentTupleRow = 0;
if ( !currentTuple.next()) return next();
currentRow++;
return true;
}
return false;
}
/**
* Convert a name to a column index.
* Note: C++ version may return -1 if column not found
* @return the column number for a given name
*/
public int findColumn(String name) throws IllegalArgumentException {
return set[0].findColumn(name);
}
public Object columnDefaultValue(int column) {
return set[0].columnDefaultValue(column);
}
}