From 70497648605fd00bed3a62a41605a9aeb410662d Mon Sep 17 00:00:00 2001 From: James Ahlborn Date: Wed, 11 Jun 2014 22:23:04 +0000 Subject: [PATCH] Add Cursor.findRow(RowId) for moving to a specific Table row using only the RowId git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/trunk@864 f203690c-595d-4dc9-a70b-905162fa7fd2 --- src/changes/changes.xml | 6 ++ .../healthmarketscience/jackcess/Cursor.java | 9 +++ .../jackcess/impl/CursorImpl.java | 36 ++++++++++ .../jackcess/impl/IndexCursorImpl.java | 31 +++++--- .../jackcess/impl/TableScanCursor.java | 6 ++ .../jackcess/CursorTest.java | 70 +++++++++++++++++++ 6 files changed, 149 insertions(+), 9 deletions(-) diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 644f4c3..59af706 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -4,6 +4,12 @@ Tim McCune + + + Add Cursor.findRow(RowId) for moving to a specific Table row using + only the RowId. + + Add ColumnValidator interface which allows column values to be easily diff --git a/src/main/java/com/healthmarketscience/jackcess/Cursor.java b/src/main/java/com/healthmarketscience/jackcess/Cursor.java index b9c47f1..956f545 100644 --- a/src/main/java/com/healthmarketscience/jackcess/Cursor.java +++ b/src/main/java/com/healthmarketscience/jackcess/Cursor.java @@ -224,6 +224,15 @@ public interface Cursor extends Iterable * 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 diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/CursorImpl.java b/src/main/java/com/healthmarketscience/jackcess/impl/CursorImpl.java index 06cda47..1521e3b 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/CursorImpl.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/CursorImpl.java @@ -38,6 +38,7 @@ import com.healthmarketscience.jackcess.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; @@ -417,6 +418,34 @@ public abstract class CursorImpl implements Cursor 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 { @@ -688,6 +717,13 @@ public abstract class CursorImpl implements Cursor 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 diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/IndexCursorImpl.java b/src/main/java/com/healthmarketscience/jackcess/impl/IndexCursorImpl.java index c2f0280..68870d9 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/IndexCursorImpl.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/IndexCursorImpl.java @@ -121,6 +121,18 @@ public class IndexCursorImpl extends CursorImpl implements IndexCursor return cursor; } + private Set getIndexEntryPattern() + { + if(_indexEntryPattern == null) { + // init our set of index column names + _indexEntryPattern = new HashSet(); + for(IndexData.ColumnDescriptor col : getIndex().getColumns()) { + _indexEntryPattern.add(col.getName()); + } + } + return _indexEntryPattern; + } + public IndexImpl getIndex() { return _index; } @@ -222,6 +234,15 @@ public class IndexCursorImpl extends CursorImpl implements IndexCursor 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, @@ -348,16 +369,8 @@ public class IndexCursorImpl extends CursorImpl implements IndexCursor ColumnMatcher columnMatcher) throws IOException { - if(_indexEntryPattern == null) { - // init our set of index column names - _indexEntryPattern = new HashSet(); - 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(); diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/TableScanCursor.java b/src/main/java/com/healthmarketscience/jackcess/impl/TableScanCursor.java index 9fe8dc4..0f5e37f 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/TableScanCursor.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/TableScanCursor.java @@ -84,6 +84,12 @@ public class TableScanCursor extends CursorImpl 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) diff --git a/src/test/java/com/healthmarketscience/jackcess/CursorTest.java b/src/test/java/com/healthmarketscience/jackcess/CursorTest.java index bafee57..cf55e2b 100644 --- a/src/test/java/com/healthmarketscience/jackcess/CursorTest.java +++ b/src/test/java/com/healthmarketscience/jackcess/CursorTest.java @@ -1205,5 +1205,75 @@ public class CursorTest extends TestCase { } } + 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")); + } + } -- 2.39.5