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 jeffrey heer */ 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 nrows = m_table.getRowCount(); 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(); 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