package hep.aida.ref.tuple;
import hep.aida.IBaseTupleColumn;
import hep.aida.ITuple;
import hep.aida.ref.AidaUtils;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Map;
import java.util.StringTokenizer;
import org.freehep.util.Value;
/**
*
* @author The FreeHEP team @ SLAC.
*
*/
public class Tuple extends AbstractTuple {
protected static final int COLUMN_ROWS = 1000;
private int columnLength;
private int columnMaxLength;
private String tupColumnsString = "";
private int startRow = 0;
private int endRow = -1;
private int filledRows = 0;
private Map optionMap;
private String[] columnDefaultValues;
private ArrayList tupleColumns = new ArrayList();
private ArrayList folderList = new ArrayList();
private Hashtable columnCounters = new Hashtable();
private Value tupleValue = new Value();
public Tuple(String name, String title, String[] columnName, Class[] columnType, String options) {
super(name, title, options);
initTuple(name, title,columnName,columnType,options);
}
public Tuple(String name, String title, String columnsString, String options) {
super(name, title, options);
initTuple(name, title,columnsString,options);
}
private int numberOf(String str, String s) {
int n = 0;
int i = str.indexOf(s);
if ( i <0 )
return n;
else {
n++;
n += numberOf(str.substring(i+1),s);
}
return n;
}
private void initTuple( String name, String title, String columnsString, String options) {
setTitle(title);
this.tupColumnsString = columnsString;
optionMap = AidaUtils.parseOptions(options);
StringTokenizer strT = new StringTokenizer( columnsString,",;");
int bracketCounter = 0;
int nCol = 0;
while( strT.hasMoreTokens() ) {
String token = strT.nextToken();
if ( bracketCounter == 0 ) nCol++;
bracketCounter += numberOf(token,"{");
bracketCounter -= numberOf(token,"}");
}
if ( nCol < 1 ) throw new IllegalArgumentException("Cannot build Tuple with no columns\n");
StringTokenizer st = new StringTokenizer(columnsString,",;");
int columnIndex = 0;
bracketCounter = 0;
String iTupleColString = "";
String type = "";
int colDefCount = 0;
columnDefaultValues = new String[nCol];
while( st.hasMoreTokens() ) {
String token = st.nextToken();
token.trim();
bracketCounter += numberOf(token,"{");
bracketCounter -= numberOf(token,"}");
iTupleColString += token;
if ( st.hasMoreTokens() ) iTupleColString += ";";
if ( bracketCounter == 0 ) {
String colName;
String defaultValue = "";
iTupleColString = iTupleColString.trim();
int brPos = iTupleColString.indexOf("{");
if ( brPos > 0 ) {
iTupleColString.trim();
int pos = iTupleColString.indexOf("(");
if ( pos > 0 && pos < brPos ) {
type = iTupleColString.substring(0,pos).trim();
colName = iTupleColString.substring( iTupleColString.indexOf(")")+1, brPos).trim();
if ( colName.endsWith("=") ) colName = colName.substring(0,colName.length()-1).trim();
defaultValue += iTupleColString.substring( pos+1, iTupleColString.indexOf(")") ).trim();
defaultValue += iTupleColString.substring( brPos );
} else {
int spPos = iTupleColString.indexOf(" ");
int eqPos = iTupleColString.indexOf("=");
type = iTupleColString.substring(0,spPos).trim();
colName = iTupleColString.substring(spPos+1,eqPos).trim();
defaultValue = iTupleColString.substring( brPos ).trim();
}
} else {
StringTokenizer t = new StringTokenizer( token, "=");
int tokens = t.countTokens();
String nameAndType = t.nextToken().trim();
if ( tokens == 2 ) defaultValue = t.nextToken().trim();
else if ( tokens != 1 ) throw new IllegalArgumentException("Wrong format "+token+"\n");
StringTokenizer tt = new StringTokenizer( nameAndType, " " );
if ( tt.countTokens() == 2 ) type = tt.nextToken().trim();
colName = tt.nextToken().trim();
}
Value value = new Value();
Class colType = null;
if ( type.endsWith("ITuple") ) {
String defVal = defaultValue.substring(defaultValue.indexOf("{")+1,defaultValue.lastIndexOf("}"));
Tuple tup = new Tuple( colName, "", defVal, options);
addTuple( tup );
columnDefaultValues[colDefCount++] = defVal;
} else {
if ( type.equals("int") ) colType = Integer.TYPE;
else if ( type.equals("short") ) colType = Short.TYPE;
else if ( type.equals("long") ) colType = Long.TYPE;
else if ( type.equals("float") ) colType = Float.TYPE;
else if ( type.equals("double") ) colType = Double.TYPE;
else if ( type.equals("boolean") ) colType = Boolean.TYPE;
else if ( type.equals("byte") ) colType = Byte.TYPE;
else if ( type.equals("char") ) colType = Character.TYPE;
else if ( type.equals("string") ) colType = String.class;
else {
try {
colType = Class.forName( type );
} catch ( ClassNotFoundException cnfe ) {
throw new IllegalArgumentException("Unsupported type "+type);
}
}
fillDefaultValue(colType, defaultValue,value);
TupleColumn fcolumn = TupleColumnFactory.createTupleColumn(colName, colType, value, options, this);
addColumn( fcolumn );
columnDefaultValues[colDefCount++] = defaultValue;
}
iTupleColString = "";
}
}
start();
}
private void initTuple(String name, String title, String[] columnName, Class[] columnType, String options){
setTitle(title);
optionMap = AidaUtils.parseOptions(options);
int colNames = columnName.length;
int colTypes = columnType.length;
if ( colNames < 1 ) throw new IllegalArgumentException("columnName cannot be empty\n");
if ( colTypes < 1 ) throw new IllegalArgumentException("columnType cannot be empty\n");
if ( colNames != colTypes ) throw new IllegalArgumentException("The number of column names "
+colNames+" is different than the number of column types "+colTypes+"\n");
int colDefCount = 0;
columnDefaultValues = new String[colNames];
for (int i=0; i<colNames; i++) {
Class colType = columnType[i];
String colName ="";
String defaultValue = "";
tupColumnsString += colType + " " + columnName[i];
if ( i < colNames ) tupColumnsString += ";";
Value value = new Value();
if ( colType == ITuple.class ) {
String iTupleString = columnName[i];
int pos = iTupleString.indexOf(")");
if ( pos > 0 ) {
colName = iTupleString.substring(pos+1,iTupleString.indexOf("{")).trim();
if ( colName.endsWith("=") ) colName = colName.substring(0,colName.length()-1).trim();
defaultValue += iTupleString.substring(iTupleString.indexOf("(")+1,pos);
} else {
if (iTupleString.indexOf("=") > 0)
colName = iTupleString.substring(0,iTupleString.indexOf("=")).trim();
else colName = iTupleString.trim();
}
if (iTupleString.indexOf("{") > 0)
defaultValue += iTupleString.substring(iTupleString.indexOf("{"),iTupleString.lastIndexOf("}")+1).trim();
String defVal = defaultValue.substring(defaultValue.indexOf("{")+1,defaultValue.lastIndexOf("}"));
Tuple tup = new Tuple( colName, "", defVal, options);
addTuple( tup );
columnDefaultValues[colDefCount++] = defVal;
} else {
colName = columnName[i];
StringTokenizer st = new StringTokenizer(colName,"=");
colName = st.nextToken().trim();
if ( st.hasMoreTokens() ) defaultValue = st.nextToken().trim();
fillDefaultValue(colType, defaultValue,value);
TupleColumn fcolumn = TupleColumnFactory.createTupleColumn(colName, colType, value, options, this);
addColumn( fcolumn );
columnDefaultValues[colDefCount++] = defaultValue;
}
}
start();
}
// FIXME is this needed?
private String getColString() {
return tupColumnsString;
}
private void fillDefaultValue( Class type, String defaultValue, Value value ) {
if ( type == Integer.TYPE )
value.set( defaultValue.equals("") ? (int)0 : Integer.parseInt(defaultValue) );
else if ( type == Short.TYPE )
value.set( defaultValue.equals("") ? (short)0 : Short.parseShort(defaultValue) );
else if ( type == Long.TYPE )
value.set( defaultValue.equals("") ? (long)0 : Long.parseLong(defaultValue) );
else if ( type == Float.TYPE )
value.set( defaultValue.equals("") ? (float)0 : Float.parseFloat(defaultValue) );
else if ( type == Double.TYPE )
value.set( defaultValue.equals("") ? (double)0 : Double.parseDouble(defaultValue) );
else if ( type == Boolean.TYPE )
value.set( defaultValue.equals("") ? false : Boolean.valueOf(defaultValue).booleanValue() );
else if ( type == Byte.TYPE )
value.set( defaultValue.equals("") ? (byte)0 : Byte.parseByte(defaultValue) );
else if ( type == Character.TYPE )
value.set( defaultValue.equals("") ? (char)0 : defaultValue.charAt(0) );
else value.set( defaultValue );
}
public String columnDefaultString( int column ) {
if ( columnType( column ) != ITuple.class )
return columnDefaultValues[column];
else {
Tuple tup = (Tuple) findTuple(column);
if ( tup != null )
return tup.name()+" = {"+tup.getColString()+"}";
else
return "null";
}
}
public boolean providesColumnDefaultValues() {
return true;
}
/**
* Get Folder at the current cursor position.
* @param index The column's index of the Folder.
* @return The folder.
*
*/
public ITuple findTuple(int index) {
return getFolder( index, filledRows );
}
public void fill(int column, Value value) {
tupleColumn(column).fill( value );
}
public void columnValue(int column, FTupleCursor cursor, Value value) {
if ( columnType( column ) != ITuple.class )
tupleColumn(column).value(cursor, value);
else
value.set( getFolder( column, cursor.row() ) );
}
public void columnValue(int column, Value value) {
if ( columnType( column ) != ITuple.class )
tupleColumn(column).value(internalCursor(), value);
else
value.set( getFolder( column, internalCursor().row() ) );
}
public boolean isInMemory() {
return true;
}
public boolean supportsMultipleCursors() {
return true;
}
public boolean supportsRandomAccess() {
return true;
}
public void addRow() {
for ( int i = 0; i < columns(); i++ )
tupleColumn(i).addRow();
for ( Enumeration keys = columnCounters.keys(); keys.hasMoreElements(); ) {
String folderName = (String)keys.nextElement();
Tuple folder = (Tuple) getFolder( findColumn( folderName ), filledRows );
TupleColumn tc = (TupleColumn)columnCounters.get( folderName );
tc.fill( tupleValue.set( folder.startRow()+folder.rows() ) );
tc.addRow();
}
filledRows++;
newInternalCursor();
}
public void resetRow() {
for ( int i = 0; i<columns(); i++ )
((TupleColumn)tupleColumns.get(i)).resetRow();
for ( Enumeration keys = columnCounters.keys(); keys.hasMoreElements(); ) {
String folderName = (String)keys.nextElement();
Tuple folder = (Tuple) getFolder( findColumn( folderName ), filledRows );
folder.resetRows( folder.rows() );
TupleColumn tc = (TupleColumn)columnCounters.get( folder.name() );
tc.resetRow();
}
}
public void reset() {
for ( int i = 0; i<columns(); i++ )
((TupleColumn)tupleColumns.get(i)).reset();
filledRows = 0;
newInternalCursor();
}
public int rows() {
if ( endRow != -1 ) return endRow-startRow;
return filledRows-startRow;
}
public int findColumn(String name) {
for ( int i = 0; i<columns(); i++ )
if ( columnName(i).equals( name ) )
return i;
throw new IllegalArgumentException("Column "+name+" does not exist\n");
}
public String columnName(int column) {
return column(column).name();
}
public Class columnType(int column) {
return column(column).type();
}
public double columnMin(int column) {
tupleColumn(column).minValue(tupleValue);
return tupleValue.getDouble();
}
public double columnMax(int column) {
tupleColumn(column).maxValue(tupleValue);
return tupleValue.getDouble();
}
public double columnMean(int column) {
tupleColumn(column).meanValue(tupleValue);
return tupleValue.getDouble();
}
public double columnRms(int column) {
tupleColumn(column).rmsValue(tupleValue);
return tupleValue.getDouble();
}
public int columns() {
return tupleColumns.size();
}
int startRow() {
return startRow;
}
void addColumn(TupleColumn column) {
tupleColumns.add( column );
}
void removeColumn(TupleColumn column) {
tupleColumns.remove(column);
}
void addTuple(Tuple tuple) {
columnCounters.put( tuple.name(), TupleColumnFactory.createTupleColumn(tuple.name(), Integer.TYPE, new Value().set((int)0), "length=1000; maxlength=-1", this) );
addColumn( TupleColumnFactory.createFolderColumn( tuple.name(), tuple, "", this ) );
folderList.add(tuple);
}
void removeTuple(Tuple tuple) {
String name = ((Tuple)tuple).name();
columnCounters.remove(name);
TupleColumn col = (TupleColumn) column(name);
removeColumn(col);
folderList.remove(tuple);
}
// PRIVATE METHODS
/**
* Set the start row.
* @param startRow The start row.
*
*/
private void setStartEndRow( int startRow, int endRow ) {
this.startRow = startRow;
this.endRow = endRow;
}
/**
* Clears the values on the stack.
*/
private void resetRows( int numberOfRows ) {
for ( int i = 0; i<columns(); i++ )
((TupleColumn)tupleColumns.get(i)).resetRows( numberOfRows );
int cf = filledRows;
filledRows -= numberOfRows;
for ( Enumeration keys = columnCounters.keys(); keys.hasMoreElements(); ) {
String folderName = (String)keys.nextElement();
while ( numberOfRows-- > 0 ) {
cf--;
Tuple folder = (Tuple) getFolder( findColumn( folderName ), cf );
TupleColumn tc = (TupleColumn)columnCounters.get( folder.name() );
tc.value(numberOfRows, tupleValue);
int end = tupleValue.getInt();
int start = 0;
if ( numberOfRows > 0 ) {
tc.value(numberOfRows-1, tupleValue);
start = tupleValue.getInt();
}
folder.setStartEndRow(start,end);
folder.resetRows( folder.rows() );
tc.resetRows(1);
}
}
}
/**
* Get a Folder in a given configuration.
* @param index The column's index for this Folder.
* @param cursor The cursor position for the Folder.
*
*/
private Tuple getFolder( int index, int cursor ) {
Tuple tup = (Tuple)((TupleColumn)tupleColumns.get( index )).getDefaultValue().getObject();
TupleColumn tc = (TupleColumn)columnCounters.get( tup.name() );
if ( cursor > -1 && cursor < filledRows ) {
tc.value(cursor, tupleValue);
int end = tupleValue.getInt();
int start = 0;
if ( cursor > 0 ) {
tc.value(cursor-1, tupleValue);
start = tupleValue.getInt();
}
tup.setStartEndRow( start, end );
} else if ( cursor == filledRows && cursor > 0 ) {
tc.value(cursor-1, tupleValue);
int start = tupleValue.getInt();
tup.setStartEndRow( start, -1 );
}
tup.newInternalCursor();
return (Tuple) tup;
}
public IBaseTupleColumn column( int column ) {
return (IBaseTupleColumn)tupleColumns.get( column );
}
public IBaseTupleColumn column( String name ) {
return column( findColumn( name ) );
}
private TupleColumn tupleColumn(int column) {
return (TupleColumn) column(column);
}
public abstract static class TupleColumnFactory {
public static TupleColumn createTupleColumn(String name, Class type, Value value, Tuple tuple) {
return createTupleColumn(name, type, value, "", tuple);
}
public static TupleColumn createFolderColumn(String name, Tuple folderTuple, String options, Tuple parentTuple) {
return new TupleColumn.TupleColumnFolder( name, folderTuple, options, parentTuple);
}
public static TupleColumn createTupleColumn(String name, Class type, Value value, String options, Tuple tuple) {
if ( type == ITuple.class )
throw new RuntimeException("Cannot create Folder tuple. Use createFolderColumn method instead.");
if ( type == Integer.TYPE ) return new TupleColumn.TupleColumnInt( name, value, options, tuple);
else if ( type == Short.TYPE ) return new TupleColumn.TupleColumnShort( name, value, options, tuple);
else if ( type == Long.TYPE ) return new TupleColumn.TupleColumnLong( name, value, options, tuple);
else if ( type == Float.TYPE ) return new TupleColumn.TupleColumnFloat( name, value, options, tuple);
else if ( type == Double.TYPE ) return new TupleColumn.TupleColumnDouble( name, value, options, tuple);
else if ( type == Boolean.TYPE ) return new TupleColumn.TupleColumnBoolean( name, value, options, tuple);
else if ( type == Byte.TYPE ) return new TupleColumn.TupleColumnByte( name, value, options, tuple);
else if ( type == Character.TYPE ) return new TupleColumn.TupleColumnChar( name, value, options, tuple);
else if ( type == String.class ) return new TupleColumn.TupleColumnString( name, value, options, tuple);
else return new TupleColumn.TupleColumnObject( name, type, value, options, tuple);
}
}
}