package prefuse.data.tuple;
import java.util.Iterator;
import java.util.logging.Logger;
import prefuse.data.Graph;
import prefuse.data.Table;
import prefuse.data.Tuple;
import prefuse.util.StringLib;
import prefuse.util.collections.IntIterator;
/**
* Manager class for Tuples. There is a unique Tuple for each row of a table.
* All data structures and Tuples are created lazily, on an as-needed basis.
* When a row is deleted from the table, it's corresponding Tuple (if created)
* is invalidated before being removed from this data structure, ensuring that
* any other live references to the Tuple can't be used to corrupt the table.
*
* @author <a href="http://jheer.org">jeffrey heer</a>
*/
public class TupleManager {
protected Graph m_graph;
protected Table m_table;
protected Class m_tupleType;
private TableTuple[] m_tuples;
/**
* Create a new TupleManager for the given Table.
* @param t the data Table to generate Tuples for
*/
public TupleManager(Table t, Graph g, Class tupleType) {
init(t, g, tupleType);
}
/**
* Initialize this TupleManager for use with a given Table.
* @param t the data Table to generate Tuples for
*/
public void init(Table t, Graph g, Class tupleType) {
if ( m_table != null ) {
throw new IllegalStateException(
"This TupleManager has already been initialized");
}
m_table = t;
m_graph = g;
m_tupleType = tupleType;
m_tuples = null;
}
/**
* Get the type of Tuple instances to generate.
* @return the tuple type, as a Class instance
*/
public Class getTupleType() {
return m_tupleType;
}
/**
* Ensure the tuple array exists.
*/
private void ensureTupleArray(int row) {
int nrows = Math.max(m_table.getRowCount(), row+1);
if ( m_tuples == null ) {
m_tuples = new TableTuple[nrows];
} else if ( m_tuples.length < nrows ) {
int capacity = Math.max((3*m_tuples.length)/2 + 1, nrows);
TableTuple[] tuples = new TableTuple[capacity];
System.arraycopy(m_tuples, 0, tuples, 0, m_tuples.length);
m_tuples = tuples;
}
}
/**
* Get a Tuple corresponding to the given row index.
* @param row the row index
* @return the Tuple corresponding to the given row
*/
public Tuple getTuple(int row) {
if ( m_table.isValidRow(row) ) {
ensureTupleArray(row);
if ( m_tuples[row] == null ) {
return (m_tuples[row] = newTuple(row));
} else {
return m_tuples[row];
}
} else {
// TODO: return null instead?
throw new IllegalArgumentException("Invalid row index: "+row);
}
}
/**
* Instantiate a new Tuple instance for the given row index.
* @param row the row index of the tuple
* @return the newly created Tuple
*/
protected TableTuple newTuple(int row) {
try {
TableTuple t = (TableTuple)m_tupleType.newInstance();
t.init(m_table, m_graph, row);
return t;
} catch ( Exception e ) {
Logger.getLogger(getClass().getName()).warning(
e.getMessage()+"\n"+StringLib.getStackTrace(e));
return null;
}
}
/**
* Invalidate the tuple at the given row.
* @param row the row index to invalidate
*/
public void invalidate(int row) {
if ( m_tuples == null || row < 0 || row >= m_tuples.length ) {
return;
} else if ( m_tuples[row] != null ) {
m_tuples[row].invalidate();
m_tuples[row] = null;
}
}
/**
* Invalidate all tuples managed by this TupleManager
*/
public void invalidateAll() {
if ( m_tuples == null ) return;
for ( int i=0; i<m_tuples.length; ++i )
invalidate(i);
}
/**
* Return an iterator over the tuples in this manager.
* @param rows an iterator over table rows
* @return an iterator over the tuples indicated by the input row iterator
*/
public Iterator iterator(IntIterator rows) {
return new TupleManagerIterator(this, rows);
}
// ------------------------------------------------------------------------
// TupleManagerIterator
/**
* Iterator instance for iterating over tuples managed in a TupleManager.
*/
public class TupleManagerIterator implements Iterator {
private TupleManager m_tuples;
private IntIterator m_rows;
/**
* Create a new TupleManagerIterator.
* @param tuples the TupleManager from which to get the tuples
* @param rows the rows to iterate over
*/
public TupleManagerIterator(TupleManager tuples, IntIterator rows) {
m_tuples = tuples;
m_rows = rows;
}
/**
* @see java.util.Iterator#hasNext()
*/
public boolean hasNext() {
return m_rows.hasNext();
}
/**
* @see java.util.Iterator#next()
*/
public Object next() {
return m_tuples.getTuple(m_rows.nextInt());
}
/**
* @see java.util.Iterator#remove()
*/
public void remove() {
// TODO: check to see if this is safe
m_rows.remove();
}
} // end of inner class TupleManagerIterator
} // end of class TupleManager