git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/trunk@864 f203690c-595d-4dc9-a70b-905162fa7fd2tags/jackcess-2.0.5
@@ -4,6 +4,12 @@ | |||
<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 |
@@ -224,6 +224,15 @@ public interface Cursor extends Iterable<Row> | |||
* 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 |
@@ -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 |
@@ -121,6 +121,18 @@ public class IndexCursorImpl extends CursorImpl implements IndexCursor | |||
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; | |||
} | |||
@@ -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<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(); |
@@ -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) |
@@ -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")); | |||
} | |||
} | |||