001: package prefuse.data.util;
002:
003: import prefuse.data.Table;
004: import prefuse.util.collections.IntIntSortedMap;
005: import prefuse.util.collections.IntIntTreeMap;
006: import prefuse.util.collections.IntIterator;
007:
008: /**
009: * Manages the set of valid rows for a Table instance, maintains an index of
010: * the available and occupied rows. RowManager instances are used internally
011: * by Table instances.
012: *
013: * @author <a href="http://jheer.org">jeffrey heer</a>
014: */
015: public class RowManager {
016:
017: protected Table m_table;
018: private IntIntSortedMap m_openrows;
019: private int m_firstid = 0;
020: private int m_curid = -1;
021:
022: // ------------------------------------------------------------------------
023: // Constructor
024:
025: /**
026: * Create a new RowManager for the given Table.
027: * @param table the Table to manage
028: */
029: public RowManager(Table table) {
030: m_table = table;
031: }
032:
033: /**
034: * Get the table managed by this RowManager.
035: * @return the managed table
036: */
037: public Table getTable() {
038: return m_table;
039: }
040:
041: // ------------------------------------------------------------------------
042: // Row Information Methods
043:
044: /**
045: * Get the lowest-numbered occupied table row.
046: * @return the minimum row
047: */
048: public int getMinimumRow() {
049: return m_firstid;
050: }
051:
052: /**
053: * Get the highest-numbered occupied table row.
054: * @return the maximum row
055: */
056: public int getMaximumRow() {
057: return m_curid;
058: }
059:
060: /**
061: * Get the total number of occupied rows
062: * @return the number of rows being used by the table
063: */
064: public int getRowCount() {
065: return 1 + m_curid - m_firstid
066: - (m_openrows == null ? 0 : m_openrows.size());
067: }
068:
069: /**
070: * Indicates if a given row value is a valid, occupied row of the table.
071: * @param row the row index to check
072: * @return true if the row is valid and in use by the Table, false if
073: * it is an illegal value or is currently free
074: */
075: public boolean isValidRow(int row) {
076: return (row >= m_firstid && row <= m_curid && (m_openrows == null || !m_openrows
077: .containsKey(row)));
078: }
079:
080: // ------------------------------------------------------------------------
081: // Row Update Methods
082:
083: /**
084: * Clear the row manager status, marking all rows as available.
085: */
086: public void clear() {
087: m_openrows = null;
088: m_firstid = 0;
089: m_curid = -1;
090: }
091:
092: /**
093: * Add a new row to management. The lowest valued available row
094: * will be used.
095: * @return the row index of the newly added row
096: */
097: public int addRow() {
098: int r;
099: if (m_openrows == null || m_openrows.isEmpty()) {
100: r = (m_firstid == 0 ? ++m_curid : --m_firstid);
101: } else {
102: int key = m_openrows.firstKey();
103: r = m_openrows.remove(key);
104: }
105: return r;
106: }
107:
108: /**
109: * Release a row and mark it as free.
110: * @param row the row index of the released row
111: * @return true if the row was successfully released, false if it
112: * was already free or if the input is not a valid row index
113: */
114: public boolean releaseRow(int row) {
115: if (row < 0) {
116: return false;
117: } else if (m_openrows != null && m_openrows.containsKey(row)) {
118: return false;
119: } else if (row == m_curid) {
120: --m_curid;
121: } else if (row == m_firstid) {
122: ++m_firstid;
123: } else {
124: if (m_openrows == null)
125: m_openrows = new IntIntTreeMap(false);
126: m_openrows.put(row, row);
127: }
128: return true;
129: }
130:
131: // ------------------------------------------------------------------------
132: // Column Mapping
133:
134: /**
135: * Given Table row and column indices, return the corresponding row in
136: * the underlying data column. This is of use for CascadedTable instances,
137: * which may reveal only a limited set of a parent table's rows and so
138: * must map between table rows and the actual indices of the inherited
139: * data columns.
140: * @param row the table row
141: * @param col the table column
142: * @return the row value for accessing the correct value of the
143: * referenced data column.
144: */
145: public int getColumnRow(int row, int col) {
146: return this .isValidRow(row) ? row : -1;
147: }
148:
149: /**
150: * Given a column row index and a table column index, return the
151: * table row corresponding to the column value. This is of use for
152: * CascadedTable instances, which may reveal only a limited set of a parent
153: * table's rows and so must map between table rows and the actual indices
154: * of the inherited data columns.
155: * @param columnRow the row of the underlying data column
156: * @param col the table column
157: * @return the row value for the Table that corresponds to the
158: * given column row
159: */
160: public int getTableRow(int columnRow, int col) {
161: return this .isValidRow(columnRow) ? columnRow : -1;
162: }
163:
164: /**
165: * Return an iterator over column row indices.
166: * @param col the table column index
167: * @return an iterator over column row indices corresponding
168: * to valid rows of this RowManager
169: */
170: public IntIterator columnRows(int col) {
171: return new ColumnRowIterator(rows(), col);
172: }
173:
174: /**
175: * Return an iterator over column row indices.
176: * @param col the table column index
177: * @param reverse indicates the direction to iterate over, true
178: * for reverse, false for normal
179: * @return an iterator over column row indices corresponding
180: * to valid rows of this RowManager
181: */
182: public IntIterator columnRows(int col, boolean reverse) {
183: return new ColumnRowIterator(rows(reverse), col);
184: }
185:
186: /**
187: * Return an iterator over column row indices.
188: * @param rows an iterator over table row indices
189: * @param col the table column index
190: * @return an iterator over column row indices corresponding
191: * to valid rows of this RowManager
192: */
193: public IntIterator columnRows(IntIterator rows, int col) {
194: return new ColumnRowIterator(rows, col);
195: }
196:
197: // ------------------------------------------------------------------------
198: // Iterators
199:
200: /**
201: * Get an iterator over the table rows.
202: * @return an iterator over the table rows
203: */
204: public IntIterator rows() {
205: return new RowIterator(false);
206: }
207:
208: /**
209: * Get an iterator over the table rows.
210: * @param reverse indicates the direction to iterate over, true
211: * for reverse, false for normal
212: * @return an iterator over the table rows
213: */
214: public IntIterator rows(boolean reverse) {
215: return new RowIterator(reverse);
216: }
217:
218: /**
219: * Iterator over the occupied rows of this RowManager.
220: */
221: public class RowIterator extends IntIterator {
222: boolean reverse;
223: int last = -1, next;
224:
225: public RowIterator(boolean reverse) {
226: this .reverse = reverse;
227: next = advance(reverse ? m_curid : m_firstid);
228: }
229:
230: public boolean hasNext() {
231: return (reverse ? next >= 0 : next <= m_curid);
232: }
233:
234: public int nextInt() {
235: // advance the iterator
236: last = next;
237: next = advance(reverse ? --next : ++next);
238: return last;
239: }
240:
241: public void remove() {
242: m_table.removeRow(last);
243: }
244:
245: private final int advance(int idx) {
246: if (m_openrows == null)
247: return idx;
248: else if (reverse)
249: for (; idx >= 0 && m_openrows.containsKey(idx); --idx)
250: ;
251: else
252: for (; idx <= m_curid && m_openrows.containsKey(idx); ++idx)
253: ;
254: return idx;
255: }
256: } // end of inner class RowIterator
257:
258: /**
259: * Iterator over the indices into a given data column,
260: * mapped to from the rows of this RowManager.
261: */
262: public class ColumnRowIterator extends IntIterator {
263: private IntIterator rows;
264: private int row;
265: private int col;
266:
267: public ColumnRowIterator(IntIterator rows, int col) {
268: this .rows = rows;
269: this .col = col;
270: }
271:
272: public boolean hasNext() {
273: return rows.hasNext();
274: }
275:
276: public int nextInt() {
277: row = rows.nextInt();
278: return getColumnRow(row, col);
279: }
280:
281: public void remove() {
282: m_table.removeRow(row);
283: }
284: } // end of inner class ColumnRowIterator
285:
286: } // end of class RowManager
|