package prefuse.data.column;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.logging.Logger;
import prefuse.data.DataReadOnlyException;
import prefuse.data.DataTypeException;
/**
* Column implementation for storing arbitrary Object values.
*
* @author <a href="http://jheer.org">jeffrey heer</a>
*/
public class ObjectColumn extends AbstractColumn {
private Object[] m_values;
private int m_size;
/**
* Create a new empty ObjectColumn. The type is assumed to be Object.
*/
public ObjectColumn() {
this(Object.class);
}
/**
* Create a new ObjectColumn.
* @param type the data type of Objects in this column
*/
public ObjectColumn(Class type) {
this(type, 0, 10, null);
}
/**
* Create a new ObjectColumn. The type is assumed to be Object.
* @param nrows the initial size of the column
*/
public ObjectColumn(int nrows) {
this(Object.class, nrows, nrows, null);
}
/**
* Create a new ObjectColumn.
* @param type the data type of Objects in this column
* @param nrows the initial size of the column
*/
public ObjectColumn(Class type, int nrows) {
this(type, nrows, nrows, null);
}
/**
* Create a new ObjectColumn.
* @param type the data type of Objects in this column
* @param nrows the initial size of the column
* @param capacity the initial capacity of the column
* @param defaultValue the default value for the column. If this value
* is cloneable, it will be cloned when assigned as defaultValue, otherwise
* the input reference will be used for every default value.
*/
public ObjectColumn(Class type, int nrows, int capacity, Object defaultValue) {
super(type, defaultValue);
if ( capacity < nrows ) {
throw new IllegalArgumentException(
"Capacity value can not be less than the row count.");
}
m_values = new Object[capacity];
try {
// since Object's clone method is protected, we default to
// using reflection to create clones.
Cloneable def = (Cloneable)defaultValue;
Method m = def.getClass().getMethod("clone", (Class[])null);
for ( int i=0; i<capacity; ++i ) {
m_values[i] = m.invoke(m_defaultValue, (Object[])null);
}
} catch ( Exception e ) {
if ( defaultValue != null ) {
Logger.getLogger(getClass().getName()).fine(
"Default value of type \"" +
defaultValue.getClass().getName() + "\" is not " +
"cloneable. Using Object reference directly.");
}
Arrays.fill(m_values, defaultValue);
}
m_size = nrows;
}
// ------------------------------------------------------------------------
// Column Metadata
/**
* @see prefuse.data.column.Column#getRowCount()
*/
public int getRowCount() {
return m_size;
}
/**
* @see prefuse.data.column.Column#setMaximumRow(int)
*/
public void setMaximumRow(int nrows) {
if ( nrows > m_values.length ) {
int capacity = Math.max((3*m_values.length)/2 + 1, nrows);
Object[] values = new Object[capacity];
System.arraycopy(m_values, 0, values, 0, m_size);
try {
// since Object's clone method is protected, we default to
// using reflection to create clones.
Cloneable def = (Cloneable)m_defaultValue;
Method m = def.getClass().getMethod("clone", (Class[])null);
for ( int i=m_size; i<capacity; ++i ) {
values[i] = m.invoke(m_defaultValue, (Object[])null);
}
} catch ( Exception e ) {
Arrays.fill(values, m_size, capacity, m_defaultValue);
}
m_values = values;
}
m_size = nrows;
}
// ------------------------------------------------------------------------
// Data Access Methods
public void revertToDefault(int row) {
try {
// since Object's clone method is protected, we default to
// using reflection to create clones.
Cloneable def = (Cloneable)m_defaultValue;
Method m = def.getClass().getMethod("clone", (Class[])null);
set(m.invoke(m_defaultValue, (Object[])null), row);
} catch ( Exception e ) {
set(m_defaultValue, row);
}
}
/**
* Get the data value at the specified row
* @param row the row from which to retrieve the value
* @return the data value
*/
public Object get(int row) {
if ( row < 0 || row > m_size ) {
throw new IllegalArgumentException(
"Row index out of bounds: "+row);
}
return m_values[row];
}
/**
* Set the data value at the specified row
* @param val the value to set
* @param row the row at which to set the value
*/
public void set(Object val, int row) {
if ( m_readOnly ) {
throw new DataReadOnlyException();
} else if ( row < 0 || row > m_size ) {
throw new IllegalArgumentException(
"Row index out of bounds: "+row);
} else if ( val == null || canSet(val.getClass()) ) {
// get the previous value
Object prev = m_values[row];
// exit early if no change
// do we trust .equals() here? for now, no.
if ( prev == val ) return;
// set the new value
m_values[row] = val;
// fire a change event
fireColumnEvent(row, prev);
} else {
throw new DataTypeException(val.getClass());
}
}
} // end of class ObjectColumn