<author email="javajedi@users.sf.net">Tim McCune</author>
</properties>
<body>
+ <release version="2.0.5" date="TBD">
+ <action dev="jahlborn" type="add">
+ Add Cursor.findRow(RowId) for moving to a specific Table row using
+ only the RowId.
+ </action>
+ </release>
<release version="2.0.4" date="2014-04-05">
<action dev="jahlborn" type="add">
Add ColumnValidator interface which allows column values to be easily
* otherwise
*/
public boolean moveToPreviousRow() throws IOException;
+
+ /**
+ * Moves to the row with the given rowId. If the row is not found (or an
+ * exception is thrown), the cursor is restored to its previous state.
+ *
+ * @return {@code true} if a valid row was found with the given id,
+ * {@code false} if no row was found
+ */
+ public boolean findRow(RowId rowId) throws IOException;
/**
* Moves to the first row (as defined by the cursor) where the given column
import com.healthmarketscience.jackcess.Cursor;
import com.healthmarketscience.jackcess.CursorBuilder;
import com.healthmarketscience.jackcess.Row;
+import com.healthmarketscience.jackcess.RowId;
import com.healthmarketscience.jackcess.RuntimeIOException;
import com.healthmarketscience.jackcess.impl.TableImpl.RowState;
import com.healthmarketscience.jackcess.util.ColumnMatcher;
return(!_curPos.equals(getDirHandler(moveForward).getEndPosition()));
}
+ public boolean findRow(RowId rowId) throws IOException
+ {
+ RowIdImpl rowIdImpl = (RowIdImpl)rowId;
+ PositionImpl curPos = _curPos;
+ PositionImpl prevPos = _prevPos;
+ boolean found = false;
+ try {
+ reset(MOVE_FORWARD);
+ if(TableImpl.positionAtRowHeader(_rowState, rowIdImpl) == null) {
+ return false;
+ }
+ restorePosition(getRowPosition(rowIdImpl));
+ if(!isCurrentRowValid()) {
+ return false;
+ }
+ found = true;
+ return true;
+ } finally {
+ if(!found) {
+ try {
+ restorePosition(curPos, prevPos);
+ } catch(IOException e) {
+ LOG.error("Failed restoring position", e);
+ }
+ }
+ }
+ }
+
public boolean findFirstRow(Column columnPattern, Object valuePattern)
throws IOException
{
return getClass().getSimpleName() + " CurPosition " + _curPos +
", PrevPosition " + _prevPos;
}
+
+ /**
+ * Returns the appropriate position information for the given row (which is
+ * the current row and is valid).
+ */
+ protected abstract PositionImpl getRowPosition(RowIdImpl rowId)
+ throws IOException;
/**
* Finds the next non-deleted row after the given row (as defined by this
return cursor;
}
+ private Set<String> getIndexEntryPattern()
+ {
+ if(_indexEntryPattern == null) {
+ // init our set of index column names
+ _indexEntryPattern = new HashSet<String>();
+ for(IndexData.ColumnDescriptor col : getIndex().getColumns()) {
+ _indexEntryPattern.add(col.getName());
+ }
+ }
+ return _indexEntryPattern;
+ }
+
public IndexImpl getIndex() {
return _index;
}
super.restorePositionImpl(curPos, prevPos);
}
+ @Override
+ protected PositionImpl getRowPosition(RowIdImpl rowId) throws IOException
+ {
+ // we need to get the index entry which corresponds with this row
+ Row row = getTable().getRow(getRowState(), rowId, getIndexEntryPattern());
+ _entryCursor.beforeEntry(getTable().asRow(row));
+ return new IndexPosition(_entryCursor.getNextEntry());
+ }
+
@Override
protected boolean findAnotherRowImpl(
ColumnImpl columnPattern, Object valuePattern, boolean moveForward,
ColumnMatcher columnMatcher)
throws IOException
{
- if(_indexEntryPattern == null) {
- // init our set of index column names
- _indexEntryPattern = new HashSet<String>();
- for(IndexData.ColumnDescriptor col : getIndex().getColumns()) {
- _indexEntryPattern.add(col.getName());
- }
- }
-
// check the next row to see if it actually matches
- Row row = getCurrentRow(_indexEntryPattern);
+ Row row = getCurrentRow(getIndexEntryPattern());
for(IndexData.ColumnDescriptor col : getIndex().getColumns()) {
String columnName = col.getName();
super.restorePositionImpl(curPos, prevPos);
}
+ @Override
+ protected PositionImpl getRowPosition(RowIdImpl rowId) throws IOException
+ {
+ return new ScanPosition(rowId);
+ }
+
@Override
protected PositionImpl findAnotherPosition(
RowState rowState, PositionImpl curPos, boolean moveForward)
}
}
+ public void testFindByRowId() throws Exception {
+ for (final FileFormat fileFormat : JetFormatTest.SUPPORTED_FILEFORMATS) {
+ Database db = createTestTable(fileFormat);
+
+ Table table = db.getTable("test");
+ Cursor cursor = CursorBuilder.createCursor(table);
+ doTestFindByRowId(cursor);
+ db.close();
+ }
+ }
+
+ public void testFindByRowIdIndex() throws Exception {
+ for (final TestDB indexCursorDB : INDEX_CURSOR_DBS) {
+ Database db = createTestIndexTable(indexCursorDB);
+
+ Table table = db.getTable("test");
+ Index idx = table.getIndexes().get(0);
+
+ assertTable(createUnorderedTestTableData(), table);
+
+ Cursor cursor = CursorBuilder.createCursor(idx);
+ doTestFindByRowId(cursor);
+
+ db.close();
+ }
+ }
+
+ private static void doTestFindByRowId(Cursor cursor)
+ throws Exception
+ {
+ for(int i = 0; i < 3; ++i) {
+ cursor.moveToNextRow();
+ }
+
+ Row r1 = cursor.getCurrentRow();
+
+ for(int i = 0; i < 3; ++i) {
+ cursor.moveToNextRow();
+ }
+
+ Row r2 = cursor.getCurrentRow();
+
+ doTestFindByRowId(cursor, r1, 2);
+
+ doTestFindByRowId(cursor, r2, 5);
+ }
+
+ private static void doTestFindByRowId(Cursor cursor, Row row, int id)
+ throws Exception
+ {
+ cursor.reset();
+ assertTrue(cursor.findRow(row.getId()));
+ Row rFound = cursor.getCurrentRow();
+ assertEquals(id, rFound.get("id"));
+ assertEquals(row, rFound);
+ Cursor.Savepoint save = cursor.getSavepoint();
+
+ assertTrue(cursor.moveToNextRow());
+ assertEquals(id + 1, cursor.getCurrentRow().get("id"));
+
+ cursor.restoreSavepoint(save);
+
+ assertTrue(cursor.moveToPreviousRow());
+ assertEquals(id - 1, cursor.getCurrentRow().get("id"));
+
+ assertFalse(cursor.findRow(RowIdImpl.FIRST_ROW_ID));
+
+ assertEquals(id - 1, cursor.getCurrentRow().get("id"));
+ }
+
}