In this example we will append to a table a new column in which each cell contains the sum of all the other numeric cells in that row.
First, we define a wrapper table class which contains only a single
column, the one which we want to add.
We subclass AbstractStarTable
,
implementing its abstract methods as well as the getCell
method which may be required if the base table is random-access.
class SumColumnStarTable extends AbstractStarTable { StarTable baseTable_; ColumnInfo colInfo0_ = new ColumnInfo( "Sum", Double.class, "Sum of other columns" ); // Constructs a new summation table from a base table. SumColumnStarTable( StarTable baseTable ) { baseTable_ = baseTable; } // Has a single column. public int getColumnCount() { return 1; } // The single column is the sum of the other columns. public ColumnInfo getColumnInfo( int icol ) { if ( icol != 0 ) throw new IllegalArgumentException(); return colInfo0_; } // Has the same number of rows as the base table. public long getRowCount() { return baseTable_.getRowCount(); } // Provides random access iff the base table does. public boolean isRandom() { return baseTable_.isRandom(); } // Get the row from the base table, and sum elements to produce value. public Object getCell( long irow, int icol ) throws IOException { if ( icol != 0 ) throw new IllegalArgumentException(); return calculateSum( baseTable_.getRow( irow ) ); } // Use a WrapperRowSequence based on the base table's RowSequence. // Wrapping a RowSequence is quite like wrapping the table itself; // we just need to override the methods which require new behaviour. public RowSequence getRowSequence() throws IOException { final RowSequence baseSeq = baseTable_.getRowSequence(); return new WrapperRowSequence( baseSeq ) { public Object getCell( int icol ) throws IOException { if ( icol != 0 ) throw new IllegalArgumentException(); return calculateSum( baseSeq.getRow() ); } public Object[] getRow() throws IOException { return new Object[] { getCell( 0 ) }; } }; } // Do the same for the RowAccess. public RowAccess getRowAccess() throws IOException { final RowAccess baseAcc = baseTable_.getRowAccess(); return new WrapperRowAccess( baseAcc ) { public Object getCell( int icol ) throws IOException { if ( icol != 0 ) throw new IllegalArgumentException(); return calculateSum( baseAcc.getRow() ); } public Object[] getRow() throws IOException { return new Object[] { getCell( 0 ) }; } }; } // getRowSplittable must also be overridden. Here we use the // basic implementation from the utility class Tables, // but if you expect to be doing parallel processing a // more careful implementation based on the base table's // RowSplittable may be required. public RowSplittable getRowSplittable() throws IOException { return Tables.getDefaultRowSplittable( this ); } // This method does the arithmetic work, summing all the numeric // columns in a row (array of cell value objects) and returning // a Double. Double calculateSum( Object[] row ) { double sum = 0.0; for ( int icol = 0; icol < row.length; icol++ ) { Object value = row[ icol ]; if ( value instanceof Number ) { sum += ((Number) value).doubleValue(); } } return new Double( sum ); } }We could use this class on its own if we just wanted a 1-column table containing summed values. The following snippet however combines an instance of this class with the table that it is summing from, resulting in an n+1 column table in which the last column is the sum of the others:
StarTable getCombinedTable( StarTable inTable ) { StarTable[] tableSet = new StarTable[ 2 ]; tableSet[ 0 ] = inTable; tableSet[ 1 ] = new SumColumnStarTable( inTable ); StarTable combinedTable = new JoinStarTable( tableSet ); return combinedTable; }