package net.xoetrope.optional.data.collection;
import java.io.IOException;
import java.io.Writer;
import java.util.List;
import net.xoetrope.debug.DumpWriter;
import net.xoetrope.xui.data.table.XRowModel;
import net.xoetrope.xui.data.table.XTableModel;
import net.xoetrope.xui.XProject;
import net.xoetrope.xui.build.BuildProperties;
import net.xoetrope.xui.data.XModel;
import net.xoetrope.xui.data.XRowSelector;
/**
* <p>A wrapper for a database table allowing it to integrate with the XModel</p>
* <p>The table model consists of a set of XRowModel objects and these in
* turn consist of DatabaseFieldModel nodes. These objects all act as wrappers for
* the DatabaseTable object and do not themselves maintain data.</p>
* <p>Copyright: Copyright (c) 2003<br>
* License: see license.txt</p>
* $Revision: 2.12 $
* License: see license.txt
*/
public class XCollectionTableModel extends XTableModel implements DumpWriter, XRowSelector
{
private List sourceData;
private List columnNames;
private boolean dirty;
private boolean readOnly;
private static boolean allowNull = false;
protected String modelId;
protected XProject currentProject;
private int rowIdx, colIdx;
private int numRows, numCols;
/**
* Create a new model node for a database table
* @param project the owner project
* @param name the name of this model node
* @param fieldNames the name of the fields
* @param data the data
*/
public XCollectionTableModel( XProject project, String name, List fieldNames, List data )
{
currentProject = project;
sourceData = data;
modelId = name;
rowIdx = colIdx = -1;
columnNames = fieldNames;
dirty = true;
readOnly = true;
retrieve();
}
/**
* Set a flag to determine how null values are treated.
* @param allow if false then nulls are return as zero or an empty string, if
* true the null is returned for string types and Double.NaN for doubles and
* Integer.MIN_VALUE for int fields and so on
*/
public static void setAllowNull( boolean allow )
{
allowNull = allow;
}
/**
* Ensures that the query is recalled. This is important where PreparedStatements
* are used to retrieve the data. The query itself does not change but the
* parameters do.
* @param isDirty boolean to set the dirty flag
*/
public void setDirty( boolean isDirty )
{
dirty = isDirty;
}
/**
* Sets the name attribute and constructs a new table of that name
* @param newName the new name
*/
public void setName( String newName )
{
modelId = newName;
}
/**
* Get the hashcode for this model node
* @return the hashcode
*/
public int hashCode()
{
return modelId.hashCode();
}
/**
* Sets the read only flag indicating whether or not the underlying table
* is read-only
* @param b true for a read-only table.
*/
public void setReadOnly( boolean b )
{
readOnly = b;
}
/**
* Get the value of the element located at the path in the element parameter
* If the attribName parameter is not null we get the value of the
* attributeValues
* @return The value of the XModel or the attribute
* @param element The path to the XModel we require
*/
public Object get( String element )
{
XRowModel drm = new XRowModel( this, rowIdx );
return drm.get( element );
}
/**
* Set the value of the attribute in the XModel located at the elementName
* @param elementName The path to the XModel in the format 'base/foo
* @param attribName The name of the attribute whose value we require
* @param newObject The new value of the attribute
*/
public void set( String elementName, String attribName, Object newObject )
{
if ( readOnly )
return;
dirty = true;
fireModelUpdated();
}
/**
* Set the value of the XModel located at the elementName
* @param attribName The path to the XModel in the format 'base/foo
* @param newObject The new value of the XModel
*/
public void set( String attribName, Object newObject )
{
if ( readOnly )
return;
dirty = true;
fireModelUpdated();
}
/**
* Get the XModel at element i
* @param i The index of the values array
* @return The XModel at location i
*/
public XModel get( int i )
{
XModel databaseRowModel = new XRowModel( this, i );
databaseRowModel.setParent( this );
return databaseRowModel;
}
/**
* gets the value attribute
* @return the value of the model
*/
public Object get()
{
return this;
}
/**
* This method does not nothing it is provided merely as an implementation of the
* XModel interface. A child or attribute can be logically appended to a table node.
* @param id the node id
* @return null as nothing is appended
*/
public Object append( String id )
{
return null;
}
/**
* Sets the model value
* @param s the new value
*/
public void set( Object s )
{
if ( readOnly )
return;
dirty = true;
fireModelUpdated();
}
/**
* Get the value of an attribute (row)
* @param i The index of the attributeValues array whose value we want
* @return The string value of the attributeValues array at position i
*/
public Object getAttribValue( int i )
{
return sourceData.get( i );
}
/**
* Get an attribute as a String object
* @param i The index of the attributeValues array whose value we want
* @return The string value of the attributeValues array at position i
*/
public String getAttribValueAsString( int i )
{
return ((List)sourceData.get( rowIdx )).get( i ).toString();
}
/**
* Get an attribute as a double value
* @param i The index of the attributeValues array whose value we want
* @return The string value of the attributeValues array at position i
*/
public double getAttribValueAsDouble( int i )
{
try {
Object obj = ((List)sourceData.get( rowIdx )).get( i );
if ( obj == null ) {
if ( allowNull )
return Double.NaN;
return 0.0;
}
if ( obj instanceof Integer )
return ((Integer)obj).intValue();
else if ( obj instanceof Double )
return ((Double)obj).doubleValue();
else if ( obj instanceof Long )
return (double)((Long)obj).longValue();
else if ( obj instanceof Float )
return (double)((Float)obj).floatValue();
return Double.parseDouble( obj.toString());
}
catch ( NullPointerException e ) {
return 0.0;
}
catch ( ArrayIndexOutOfBoundsException e ) {
return 0.0;
}
}
/**
* Get an attribute as an int value
* @param i The index of the attributeValues array whose value we want
* @return The string value of the attributeValues array at position i
*/
public int getAttribValueAsInt( int i )
{
try {
Object obj = ((List)sourceData.get( rowIdx )).get( i );
if ( obj == null ) {
if ( allowNull )
return Integer.MIN_VALUE;
}
if ( obj instanceof Integer )
return ((Integer)obj).intValue();
else if ( obj instanceof String )
return Integer.parseInt( (String)obj );
else if ( obj instanceof Double )
return (int)((Double)obj).doubleValue();
else if ( obj instanceof Long )
return (int)((Long)obj).longValue();
else if ( obj instanceof Float )
return (int)((Float)obj).floatValue();
return Integer.parseInt( obj.toString());
//return new Integer( sourceData.getValue( i ) ).intValue();
}
catch ( NullPointerException e ) {
return 0;
}
}
/**
* returns the index of the attribiteNames array whose value is the same
* as the attribName
* @param attribName The name of the attribute we are trying to locate
* @return The index of the attributeNames array containg the name
*/
public int getAttribute( String attribName )
{
for ( int i = 0; i < numCols; i++ ) {
if ( columnNames.get( i ).toString().equals( attribName ))
return i;
}
return -1;
}
// Table specific access methods----------------------------------------------
public void sync()
{
retrieve();
}
/**
* Get the table data.
*/
public void retrieve()
{
numRows = sourceData.size();
if ( columnNames != null )
numCols = columnNames.size();
else if ( numRows > 0 )
numCols = ((List)sourceData.get( 0 )).size();
else
numCols = 0;
}
/**
* Moves the table's cursor to the first row
*/
public void first()
{
rowIdx = 0;
}
/**
* Moves the table's cursor to the last row
*/
public void last()
{
rowIdx = numRows - 1;
}
/**
* Moves the table's cursor to the next row
* @return true if the operation succeeded
*/
public boolean next()
{
if ( rowIdx < ( numRows -1 )) {
rowIdx++;
return true;
}
return false;
}
/**
* Has the table more records/rows?
* @return true if the current row is not on the last row in the table
*/
public boolean hasMoreRows()
{
return ( rowIdx < ( numRows - 1 ));
}
/**
* Moves the table's cursor to the previous row
* @return true if the operation succeeded
*/
public boolean previous()
{
if ( rowIdx > 0 ) {
rowIdx--;
return true;
}
return false;
}
/**
* Provides iterative access to the table values. The current row in the table
* is used to index the data. This method does not implicitly
* retrieve the data so you must call the retrieve method explicitly.
* @return the data
*/
public String getValue()
{
return getFieldValue( rowIdx, colIdx );
}
/**
* Get a child value/node (a XRowModel) at the specified index
*
*
* @param i The index into the values array
* @return The XModel at position i of the values array.
*/
public XModel getValue( int i )
{
return get( i );
}
/**
* Provides iterative access to the table values. The current row in the table
* is used to index the data. This method does not implicitly
* retrieve the data so you must call the retrieve method explicitly.
* @param colIdx the column of field index
* @return the data
*/
public String getFieldValue( int colIdx )
{
return getFieldValue( rowIdx, colIdx );
}
/**
* Provides random access to the table values. This method does not implicitly
* retrieve the data so you must call the retrieve method explicitly.
* @param rowIdx the row index
* @param colIdx the column of field index
* @return the data
*/
public String getFieldValue( int rowIdx, int colIdx )
{
Object o = ((List)sourceData.get( rowIdx )).get( colIdx );
if ( o != null )
return o.toString();
return null;
}
/**
* Set a field value
* @param colIdx the field index, zero based
* @param newValue the new field value
*/
public void setFieldValue( int colIdx, String newValue )
{
setFieldValue( rowIdx, colIdx, newValue );
fireModelUpdated();
}
/**
* Set a field value
* @param rowIdx the row index, zero based
* @param colIdx the field index, zero based
* @param newValue the new field value
*/
public void setFieldValue( int rowIdx, int colIdx, String newValue )
{
setFieldValue( rowIdx, colIdx, newValue );
fireModelUpdated();
}
// End of table specific access methods---------------------------------------
/**
* Gets the value attribute as a double value from the current record
* @param elementName the field/column name
* @return the field value
*/
public double getValueAsDouble( String elementName )
{
return getAttribValueAsDouble( getAttribute( elementName ) );
}
/**
* Gets the value attribute of the specified node as an int.
* @param elementName the field name
* @return the field value
*/
public int getValueAsInt( String elementName )
{
return getAttribValueAsInt( getAttribute( elementName ) );
}
/**
* Gets the attribute name for field i. For this node the table field name is
* returned.
* @param i The index of the attributeNames array whose value we want
* @return The string value of the attributeNames array at position i
*/
public String getAttribName( int i )
{
return columnNames.get( i ).toString();
}
/**
* Gets the name attribute, by default the table name
* @return the model node's ID
*/
public String getId()
{
return modelId;
}
/**
* Sets the attribute value
* @param i The index of the attributeValues array whose value we want
* @param value the value object
*/
public void setAttribValue( int i, Object value )
{
setFieldValue( i, value.toString() );
}
/**
* Gets the number of rows for this table
* @return the number of rows
*/
public int getNumChildren()
{
return sourceData.size();
}
/**
* Get the number of fields in this TableModel
* @return the number of fields
*/
public int getNumAttributes()
{
return numCols;
}
/**
* Gets the model element tag name, e.g. 'Component' from the XML fragment
* <Component ....
* @return the model element name
*/
public String getTagName()
{
return "Collection";
}
/**
* Look up a table by name in the overall model.
* @param tableName the table name
* @return the table node
*/
public static XCollectionTableModel getTable( XProject currentProject, String tableName )
{
return ( XCollectionTableModel )( (XModel)currentProject.getModel().get( tableName ) );
}
/**
* Write teh contents of this model node to a stream
* @param w the write/stream
*/
public void dump( Writer w )
{
if ( BuildProperties.DEBUG ) {
try {
for ( int i = 0; i < numRows; i++ ) {
for ( int j = 0; j < numCols; j++ ) {
if ( j > 0 )
w.write( ", " );
w.write( getFieldValue( i, j ) );
}
w.write( "\n" );
}
}
catch ( IOException ex ) {
}
}
}
/**
* Set the row selection index
* @param sourceData the new row selection index (zero based)
*/
public void setSelectedRow( int sourceData )
{
rowIdx = sourceData;
}
/**
* Get the row selection index
* @return the current row selection index (zero based)
*/
public int getSelectedRow( )
{
return rowIdx;
}
}