package prefuse.data.util; import prefuse.data.Table; import prefuse.util.collections.IntIntSortedMap; import prefuse.util.collections.IntIntTreeMap; import prefuse.util.collections.IntIterator; /** * Manages the set of valid rows for a Table instance, maintains an index of * the available and occupied rows. RowManager instances are used internally * by Table instances. * * @author <a href="http://jheer.org">jeffrey heer</a> */ public class RowManager { protected Table m_table; private IntIntSortedMap m_openrows; private int m_firstid = 0; private int m_curid = -1; // ------------------------------------------------------------------------ // Constructor /** * Create a new RowManager for the given Table. * @param table the Table to manage */ public RowManager(Table table) { m_table = table; } /** * Get the table managed by this RowManager. * @return the managed table */ public Table getTable() { return m_table; } // ------------------------------------------------------------------------ // Row Information Methods /** * Get the lowest-numbered occupied table row. * @return the minimum row */ public int getMinimumRow() { return m_firstid; } /** * Get the highest-numbered occupied table row. * @return the maximum row */ public int getMaximumRow() { return m_curid; } /** * Get the total number of occupied rows * @return the number of rows being used by the table */ public int getRowCount() { return 1 + m_curid - m_firstid - (m_openrows==null ? 0 : m_openrows.size()); } /** * Indicates if a given row value is a valid, occupied row of the table. * @param row the row index to check * @return true if the row is valid and in use by the Table, false if * it is an illegal value or is currently free */ public boolean isValidRow(int row) { return ( row >= m_firstid && row <=m_curid && (m_openrows == null || !m_openrows.containsKey(row)) ); } // ------------------------------------------------------------------------ // Row Update Methods /** * Clear the row manager status, marking all rows as available. */ public void clear() { m_openrows = null; m_firstid = 0; m_curid = -1; } /** * Add a new row to management. The lowest valued available row * will be used. * @return the row index of the newly added row */ public int addRow() { int r; if ( m_openrows == null || m_openrows.isEmpty() ) { r = ( m_firstid == 0 ? ++m_curid : --m_firstid ); } else { int key = m_openrows.firstKey(); r = m_openrows.remove(key); } return r; } /** * Release a row and mark it as free. * @param row the row index of the released row * @return true if the row was successfully released, false if it * was already free or if the input is not a valid row index */ public boolean releaseRow(int row) { if ( row < 0 ) { return false; } else if ( m_openrows != null && m_openrows.containsKey(row) ) { return false; } else if ( row == m_curid ) { --m_curid; } else if ( row == m_firstid ) { ++m_firstid; } else { if ( m_openrows == null ) m_openrows = new IntIntTreeMap(false); m_openrows.put(row, row); } return true; } // ------------------------------------------------------------------------ // Column Mapping /** * Given Table row and column indices, return the corresponding row in * the underlying data column. This is of use for CascadedTable instances, * which may reveal only a limited set of a parent table's rows and so * must map between table rows and the actual indices of the inherited * data columns. * @param row the table row * @param col the table column * @return the row value for accessing the correct value of the * referenced data column. */ public int getColumnRow(int row, int col) { return this.isValidRow(row) ? row : -1; } /** * Given a column row index and a table column index, return the * table row corresponding to the column value. This is of use for * CascadedTable instances, which may reveal only a limited set of a parent * table's rows and so must map between table rows and the actual indices * of the inherited data columns. * @param columnRow the row of the underlying data column * @param col the table column * @return the row value for the Table that corresponds to the * given column row */ public int getTableRow(int columnRow, int col) { return this.isValidRow(columnRow) ? columnRow : -1; } /** * Return an iterator over column row indices. * @param col the table column index * @return an iterator over column row indices corresponding * to valid rows of this RowManager */ public IntIterator columnRows(int col) { return new ColumnRowIterator(rows(), col); } /** * Return an iterator over column row indices. * @param col the table column index * @param reverse indicates the direction to iterate over, true * for reverse, false for normal * @return an iterator over column row indices corresponding * to valid rows of this RowManager */ public IntIterator columnRows(int col, boolean reverse) { return new ColumnRowIterator(rows(reverse), col); } /** * Return an iterator over column row indices. * @param rows an iterator over table row indices * @param col the table column index * @return an iterator over column row indices corresponding * to valid rows of this RowManager */ public IntIterator columnRows(IntIterator rows, int col) { return new ColumnRowIterator(rows, col); } // ------------------------------------------------------------------------ // Iterators /** * Get an iterator over the table rows. * @return an iterator over the table rows */ public IntIterator rows() { return new RowIterator(false); } /** * Get an iterator over the table rows. * @param reverse indicates the direction to iterate over, true * for reverse, false for normal * @return an iterator over the table rows */ public IntIterator rows(boolean reverse) { return new RowIterator(reverse); } /** * Iterator over the occupied rows of this RowManager. */ public class RowIterator extends IntIterator { boolean reverse; int last = -1, next; public RowIterator(boolean reverse) { this.reverse = reverse; next = advance(reverse ? m_curid : m_firstid); } public boolean hasNext() { return ( reverse ? next >= 0 : next <= m_curid ); } public int nextInt() { // advance the iterator last = next; next = advance(reverse ? --next : ++next); return last; } public void remove() { m_table.removeRow(last); } private final int advance(int idx) { if ( m_openrows == null ) return idx; else if ( reverse ) for (; idx >= 0 && m_openrows.containsKey(idx); --idx); else for (; idx <= m_curid && m_openrows.containsKey(idx); ++idx); return idx; } } // end of inner class RowIterator /** * Iterator over the indices into a given data column, * mapped to from the rows of this RowManager. */ public class ColumnRowIterator extends IntIterator { private IntIterator rows; private int row; private int col; public ColumnRowIterator(IntIterator rows, int col) { this.rows = rows; this.col = col; } public boolean hasNext() { return rows.hasNext(); } public int nextInt() { row = rows.nextInt(); return getColumnRow(row, col); } public void remove() { m_table.removeRow(row); } } // end of inner class ColumnRowIterator } // end of class RowManager