summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJames Ahlborn <jtahlborn@yahoo.com>2007-12-02 04:29:59 +0000
committerJames Ahlborn <jtahlborn@yahoo.com>2007-12-02 04:29:59 +0000
commitbca9319ef7c3fba0ee234d2ba8152de6fe0d1329 (patch)
tree2a8b859f6a325b1febeeb8e09367875f08d0d202
parent5cd19292eb5178343233bd6e5ef2ce8367b443ff (diff)
downloadjackcess-bca9319ef7c3fba0ee234d2ba8152de6fe0d1329.tar.gz
jackcess-bca9319ef7c3fba0ee234d2ba8152de6fe0d1329.zip
implement range-based, index cursors
git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/trunk@194 f203690c-595d-4dc9-a70b-905162fa7fd2
-rw-r--r--src/java/com/healthmarketscience/jackcess/Cursor.java149
-rw-r--r--src/java/com/healthmarketscience/jackcess/Index.java191
-rw-r--r--src/java/com/healthmarketscience/jackcess/Table.java7
-rw-r--r--src/java/com/healthmarketscience/jackcess/UsageMap.java29
-rw-r--r--test/src/java/com/healthmarketscience/jackcess/CursorTest.java241
-rw-r--r--test/src/java/com/healthmarketscience/jackcess/DatabaseTest.java10
6 files changed, 466 insertions, 161 deletions
diff --git a/src/java/com/healthmarketscience/jackcess/Cursor.java b/src/java/com/healthmarketscience/jackcess/Cursor.java
index 3bba37e..520481a 100644
--- a/src/java/com/healthmarketscience/jackcess/Cursor.java
+++ b/src/java/com/healthmarketscience/jackcess/Cursor.java
@@ -30,27 +30,20 @@ public abstract class Cursor implements Iterable<Map<String, Object>>
{
private static final Log LOG = LogFactory.getLog(Cursor.class);
- /** normal first position for the TableScanCursor */
+ /** first position for the TableScanCursor */
private static final ScanPosition FIRST_SCAN_POSITION =
new ScanPosition(RowId.FIRST_ROW_ID);
- /** normal last position for the TableScanCursor */
+ /** last position for the TableScanCursor */
private static final ScanPosition LAST_SCAN_POSITION =
new ScanPosition(RowId.LAST_ROW_ID);
- /** normal first position for the IndexCursor */
- private static final IndexPosition FIRST_INDEX_POSITION =
- new IndexPosition(Index.FIRST_ENTRY);
- /** normal last position for the IndexCursor */
- private static final IndexPosition LAST_INDEX_POSITION =
- new IndexPosition(Index.LAST_ENTRY);
-
/** owning table */
private final Table _table;
/** State used for reading the table rows */
private final RowState _rowState;
- /** the first (exclusive) row id for this iterator */
+ /** the first (exclusive) row id for this cursor */
private final Position _firstPos;
- /** the last (exclusive) row id for this iterator */
+ /** the last (exclusive) row id for this cursor */
private final Position _lastPos;
/** the previous row */
private Position _prevPos;
@@ -84,7 +77,54 @@ public abstract class Cursor implements Iterable<Map<String, Object>>
public static Cursor createIndexCursor(Table table, Index index)
throws IOException
{
- return new IndexCursor(table, index);
+ return createIndexCursor(table, index, null, null);
+ }
+
+ /**
+ * Creates an indexed cursor for the given table, narrowed to the given
+ * range.
+ * @param table the table over which this cursor will traverse
+ * @param index index for the table which will define traversal order as
+ * well as enhance certain lookups
+ * @param startRow the first row of data for the cursor (inclusive), or
+ * {@code null} for the first entry
+ * @param endRow the last row of data for the cursor (inclusive), or
+ * {@code null} for the last entry
+ */
+ public static Cursor createIndexCursor(Table table, Index index,
+ Object[] startRow, Object[] endRow)
+ throws IOException
+ {
+ return createIndexCursor(table, index, startRow, true, endRow, true);
+ }
+
+ /**
+ * Creates an indexed cursor for the given table, narrowed to the given
+ * range.
+ * @param table the table over which this cursor will traverse
+ * @param index index for the table which will define traversal order as
+ * well as enhance certain lookups
+ * @param startRow the first row of data for the cursor, or {@code null} for
+ * the first entry
+ * @param startInclusive whether or not startRow is inclusive or exclusive
+ * @param endRow the last row of data for the cursor, or {@code null} for
+ * the last entry
+ * @param endInclusive whether or not endRow is inclusive or exclusive
+ */
+ public static Cursor createIndexCursor(Table table, Index index,
+ Object[] startRow,
+ boolean startInclusive,
+ Object[] endRow,
+ boolean endInclusive)
+ throws IOException
+ {
+ if(table != index.getTable()) {
+ throw new IllegalArgumentException(
+ "Given index is not for given table: " + index + ", " + table);
+ }
+ return new IndexCursor(table, index,
+ index.cursor(startRow, startInclusive,
+ endRow, endInclusive));
}
/**
@@ -209,11 +249,11 @@ public abstract class Cursor implements Iterable<Map<String, Object>>
* Moves the cursor to a savepoint previously returned from
* {@link #getSavepoint}.
*/
- public void restoreSavepoint(Savepoint savepoint)
+ public Cursor restoreSavepoint(Savepoint savepoint)
throws IOException
{
- restorePosition(savepoint.getCurrentPosition(),
- savepoint.getPreviousPosition());
+ return restorePosition(savepoint.getCurrentPosition(),
+ savepoint.getPreviousPosition());
}
/**
@@ -231,26 +271,26 @@ public abstract class Cursor implements Iterable<Map<String, Object>>
}
/**
- * Resets this cursor for forward iteration. Calls {@link #beforeFirst}.
+ * Resets this cursor for forward traversal. Calls {@link #beforeFirst}.
*/
- public void reset() {
- beforeFirst();
+ public Cursor reset() {
+ return beforeFirst();
}
/**
- * Resets this cursor for forward iteration (sets cursor to before the first
+ * Resets this cursor for forward traversal (sets cursor to before the first
* row).
*/
- public void beforeFirst() {
- reset(true);
+ public Cursor beforeFirst() {
+ return reset(true);
}
/**
- * Resets this cursor for reverse iteration (sets cursor to after the last
+ * Resets this cursor for reverse traversal (sets cursor to after the last
* row).
*/
- public void afterLast() {
- reset(false);
+ public Cursor afterLast() {
+ return reset(false);
}
/**
@@ -293,12 +333,13 @@ public abstract class Cursor implements Iterable<Map<String, Object>>
}
/**
- * Resets this cursor for iterating the given direction.
+ * Resets this cursor for traversing the given direction.
*/
- protected void reset(boolean moveForward) {
+ protected Cursor reset(boolean moveForward) {
_curPos = getDirHandler(moveForward).getBeginningPosition();
_prevPos = _curPos;
_rowState.reset();
+ return this;
}
/**
@@ -470,27 +511,38 @@ public abstract class Cursor implements Iterable<Map<String, Object>>
* Restores a current position for the cursor (current position becomes
* previous position).
*/
- protected void restorePosition(Position curPos)
+ protected Cursor restorePosition(Position curPos)
throws IOException
{
- restorePosition(curPos, _curPos);
+ return restorePosition(curPos, _curPos);
}
/**
- * Restores a current and previous position for the cursor.
+ * Restores a current and previous position for the cursor if the given
+ * positions are different from the current positions.
*/
- protected void restorePosition(Position curPos, Position prevPos)
+ protected final Cursor restorePosition(Position curPos, Position prevPos)
throws IOException
{
if(!curPos.equals(_curPos) || !prevPos.equals(_prevPos)) {
- // make the current position previous, and the new position current
- _prevPos = _curPos;
- _curPos = curPos;
- _rowState.reset();
+ restorePositionImpl(curPos, prevPos);
}
+ return this;
}
/**
+ * Restores a current and previous position for the cursor.
+ */
+ protected void restorePositionImpl(Position curPos, Position prevPos)
+ throws IOException
+ {
+ // make the current position previous, and the new position current
+ _prevPos = _curPos;
+ _curPos = curPos;
+ _rowState.reset();
+ }
+
+ /**
* Rechecks the current position if the underlying data structures have been
* modified.
* @return {@code true} if the cursor ended up in a new position,
@@ -815,13 +867,13 @@ public abstract class Cursor implements Iterable<Map<String, Object>>
}
@Override
- protected void reset(boolean moveForward) {
+ protected Cursor reset(boolean moveForward) {
_ownedPagesCursor.reset(moveForward);
- super.reset(moveForward);
+ return super.reset(moveForward);
}
@Override
- protected void restorePosition(Position curPos, Position prevPos)
+ protected void restorePositionImpl(Position curPos, Position prevPos)
throws IOException
{
if(!(curPos instanceof ScanPosition) ||
@@ -829,9 +881,9 @@ public abstract class Cursor implements Iterable<Map<String, Object>>
throw new IllegalArgumentException(
"Restored positions must be scan positions");
}
- super.restorePosition(curPos, prevPos);
_ownedPagesCursor.restorePosition(curPos.getRowId().getPageNumber(),
prevPos.getRowId().getPageNumber());
+ super.restorePositionImpl(curPos, prevPos);
}
@Override
@@ -957,15 +1009,14 @@ public abstract class Cursor implements Iterable<Map<String, Object>>
/** Cursor over the entries of the relvant index */
private final Index.EntryCursor _entryCursor;
- private IndexCursor(Table table, Index index)
+ private IndexCursor(Table table, Index index,
+ Index.EntryCursor entryCursor)
throws IOException
{
- super(table, FIRST_INDEX_POSITION, LAST_INDEX_POSITION);
- if(table != index.getTable()) {
- throw new IllegalArgumentException(
- "Given index is not for given table: " + index + ", " + table);
- }
- _entryCursor = index.cursor();
+ super(table,
+ new IndexPosition(entryCursor.getFirstEntry()),
+ new IndexPosition(entryCursor.getLastEntry()));
+ _entryCursor = entryCursor;
}
@Override
@@ -979,13 +1030,13 @@ public abstract class Cursor implements Iterable<Map<String, Object>>
}
@Override
- protected void reset(boolean moveForward) {
+ protected Cursor reset(boolean moveForward) {
_entryCursor.reset(moveForward);
- super.reset(moveForward);
+ return super.reset(moveForward);
}
@Override
- protected void restorePosition(Position curPos, Position prevPos)
+ protected void restorePositionImpl(Position curPos, Position prevPos)
throws IOException
{
if(!(curPos instanceof IndexPosition) ||
@@ -993,9 +1044,9 @@ public abstract class Cursor implements Iterable<Map<String, Object>>
throw new IllegalArgumentException(
"Restored positions must be index positions");
}
- super.restorePosition(curPos, prevPos);
_entryCursor.restorePosition(((IndexPosition)curPos).getEntry(),
((IndexPosition)prevPos).getEntry());
+ super.restorePositionImpl(curPos, prevPos);
}
@Override
diff --git a/src/java/com/healthmarketscience/jackcess/Index.java b/src/java/com/healthmarketscience/jackcess/Index.java
index 7aed345..fe12832 100644
--- a/src/java/com/healthmarketscience/jackcess/Index.java
+++ b/src/java/com/healthmarketscience/jackcess/Index.java
@@ -597,8 +597,46 @@ public class Index implements Comparable<Index> {
public EntryCursor cursor()
throws IOException
{
+ return cursor(null, true, null, true);
+ }
+
+ /**
+ * Gets a new cursor for this index, narrowed to the range defined by the
+ * given startRow and endRow.
+ * <p>
+ * Forces index initialization.
+ *
+ * @param startRow the first row of data for the cursor, or {@code null} for
+ * the first entry
+ * @param startInclusive whether or not startRow is inclusive or exclusive
+ * @param endRow the last row of data for the cursor, or {@code null} for
+ * the last entry
+ * @param endInclusive whether or not endRow is inclusive or exclusive
+ */
+ public EntryCursor cursor(Object[] startRow,
+ boolean startInclusive,
+ Object[] endRow,
+ boolean endInclusive)
+ throws IOException
+ {
initialize();
- return new EntryCursor();
+ Position startPos = FIRST_POSITION;
+ if(startRow != null) {
+ Entry startEntry = new Entry(startRow,
+ (startInclusive ?
+ RowId.FIRST_ROW_ID : RowId.LAST_ROW_ID),
+ _columns);
+ startPos = new Position(FIRST_ENTRY_IDX, startEntry);
+ }
+ Position endPos = LAST_POSITION;
+ if(endRow != null) {
+ Entry endEntry = new Entry(endRow,
+ (endInclusive ?
+ RowId.LAST_ROW_ID : RowId.FIRST_ROW_ID),
+ _columns);
+ endPos = new Position(LAST_ENTRY_IDX, endEntry);
+ }
+ return new EntryCursor(startPos, endPos);
}
/**
@@ -1413,11 +1451,28 @@ public class Index implements Comparable<Index> {
private final DirHandler _forwardDirHandler = new ForwardDirHandler();
/** handler for moving the page cursor backward */
private final DirHandler _reverseDirHandler = new ReverseDirHandler();
+ /** the first (exclusive) row id for this cursor */
+ private final Position _firstPos;
+ /** the last (exclusive) row id for this cursor */
+ private final Position _lastPos;
+ /** the first valid index for this cursor */
+ private int _minIndex;
+ /** the last valid index for this cursor */
+ private int _maxIndex;
+ /** the current entry */
private Position _curPos;
+ /** the previous entry */
private Position _prevPos;
+ /** the last read modification count on the Index. we track this so that
+ the cursor can detect updates to the index while traversing and act
+ accordingly */
private int _lastModCount;
- private EntryCursor() {
+ private EntryCursor(Position firstPos, Position lastPos) {
+ _firstPos = firstPos;
+ _lastPos = lastPos;
+ // force bounds to be updated
+ _lastModCount = Index.this._modCount - 1;
reset();
}
@@ -1426,6 +1481,20 @@ public class Index implements Comparable<Index> {
}
/**
+ * Returns the first entry (exclusive) as defined by this cursor.
+ */
+ public Entry getFirstEntry() {
+ return _firstPos.getEntry();
+ }
+
+ /**
+ * Returns the last entry (exclusive) as defined by this cursor.
+ */
+ public Entry getLastEntry() {
+ return _lastPos.getEntry();
+ }
+
+ /**
* Returns the DirHandler for the given direction
*/
private DirHandler getDirHandler(boolean moveForward) {
@@ -1440,55 +1509,60 @@ public class Index implements Comparable<Index> {
return(Index.this._modCount == _lastModCount);
}
- public void reset() {
- beforeFirst();
+ public EntryCursor reset() {
+ return beforeFirst();
}
- public void beforeFirst() {
- reset(true);
+ public EntryCursor beforeFirst() {
+ return reset(true);
}
- public void afterLast() {
- reset(false);
+ public EntryCursor afterLast() {
+ return reset(false);
}
- protected void reset(boolean moveForward) {
+ protected EntryCursor reset(boolean moveForward) {
_curPos = getDirHandler(moveForward).getBeginningPosition();
_prevPos = _curPos;
- _lastModCount = Index.this._modCount;
+ if(!isUpToDate()) {
+ // update bounds
+ updateBounds();
+ _lastModCount = Index.this._modCount;
+ }
+ return this;
}
/**
* Repositions the cursor so that the next row will be the first entry
* >= the given row.
*/
- public void beforeEntry(Object[] row)
+ public EntryCursor beforeEntry(Object[] row)
throws IOException
{
- restorePosition(new Entry(row, RowId.FIRST_ROW_ID, _columns));
+ return restorePosition(new Entry(row, RowId.FIRST_ROW_ID, _columns));
}
/**
* Repositions the cursor so that the previous row will be the first
* entry <= the given row.
*/
- public void afterEntry(Object[] row)
+ public EntryCursor afterEntry(Object[] row)
throws IOException
{
- restorePosition(new Entry(row, RowId.LAST_ROW_ID, _columns));
+ return restorePosition(new Entry(row, RowId.LAST_ROW_ID, _columns));
}
/**
- * @return valid entry if there was entry, {@link Index#LAST_ENTRY}
- * otherwise
+ * @return valid entry if there was a next entry,
+ * {@code #getLastEntry} otherwise
*/
public Entry getNextEntry() {
return getAnotherEntry(true);
}
/**
- * @return valid entry if there was entry, {@link Index#FIRST_ENTRY}
- * otherwise
+ * @return valid entry if there was a next entry,
+ * {@code #getFirstEntry} otherwise
*/
public Entry getPreviousEntry() {
return getAnotherEntry(false);
@@ -1498,63 +1572,90 @@ public class Index implements Comparable<Index> {
* Restores a current position for the cursor (current position becomes
* previous position).
*/
- private void restorePosition(Entry curEntry) {
- restorePosition(curEntry, _curPos.getEntry());
+ private EntryCursor restorePosition(Entry curEntry) {
+ return restorePosition(curEntry, _curPos.getEntry());
}
/**
* Restores a current and previous position for the cursor.
*/
- protected void restorePosition(Entry curEntry, Entry prevEntry)
+ protected EntryCursor restorePosition(Entry curEntry, Entry prevEntry)
{
if(!curEntry.equals(_curPos.getEntry()) ||
!prevEntry.equals(_prevPos.getEntry()))
{
_prevPos = updatePosition(prevEntry);
_curPos = updatePosition(curEntry);
- _lastModCount = Index.this._modCount;
+ if(!isUpToDate()) {
+ updateBounds();
+ _lastModCount = Index.this._modCount;
+ }
} else {
checkForModification();
}
+ return this;
}
/**
- * Checks the index for modifications an updates state accordingly.
+ * Checks the index for modifications and updates state accordingly.
*/
private void checkForModification() {
if(!isUpToDate()) {
_prevPos = updatePosition(_prevPos.getEntry());
_curPos = updatePosition(_curPos.getEntry());
+ updateBounds();
_lastModCount = Index.this._modCount;
}
}
+ private void updateBounds() {
+ int idx = findEntry(_firstPos.getEntry());
+ if(idx < 0) {
+ idx = missingIndexToInsertionPoint(idx);
+ }
+ _minIndex = idx;
+
+ idx = findEntry(_lastPos.getEntry());
+ if(idx < 0) {
+ idx = missingIndexToInsertionPoint(idx) - 1;
+ }
+ _maxIndex = idx;
+ }
+
/**
* Gets an up-to-date position for the given entry.
*/
private Position updatePosition(Entry entry) {
- int curIdx = FIRST_ENTRY_IDX;
- boolean between = false;
if(entry.isValid()) {
+
// find the new position for this entry
- int idx = findEntry(entry);
- if(idx >= 0) {
- curIdx = idx;
- } else {
+ int curIdx = findEntry(entry);
+ boolean between = false;
+ if(curIdx < 0) {
// given entry was not found exactly. our current position is now
// really between two indexes, but we cannot support that as an
// integer value so we set a flag instead
- curIdx = missingIndexToInsertionPoint(idx);
+ curIdx = missingIndexToInsertionPoint(curIdx);
between = true;
}
- } else if(entry.equals(FIRST_ENTRY)) {
- curIdx = FIRST_ENTRY_IDX;
- } else if(entry.equals(LAST_ENTRY)) {
- curIdx = LAST_ENTRY_IDX;
+
+ if(curIdx < _minIndex) {
+ curIdx = _minIndex;
+ between = true;
+ } else if(curIdx > _maxIndex) {
+ curIdx = _maxIndex + 1;
+ between = true;
+ }
+
+ return new Position(curIdx, entry, between);
+
+ } else if(entry.equals(_firstPos.getEntry())) {
+ return _firstPos;
+ } else if(entry.equals(_lastPos.getEntry())) {
+ return _lastPos;
} else {
throw new IllegalArgumentException("Invalid entry given: " + entry);
}
- return new Position(curIdx, entry, between);
}
/**
@@ -1598,12 +1699,12 @@ public class Index implements Comparable<Index> {
return new Position(curIdx, _entries.get(curIdx));
}
protected final Position newForwardPosition(int curIdx) {
- return((curIdx < _entries.size()) ?
- newPosition(curIdx) : LAST_POSITION);
+ return((curIdx <= _maxIndex) ?
+ newPosition(curIdx) : _lastPos);
}
protected final Position newReversePosition(int curIdx) {
- return ((curIdx >= 0) ?
- newPosition(curIdx) : FIRST_POSITION);
+ return ((curIdx >= _minIndex) ?
+ newPosition(curIdx) : _firstPos);
}
}
@@ -1617,17 +1718,17 @@ public class Index implements Comparable<Index> {
// between position
if(!between) {
curIdx = ((curIdx == getBeginningPosition().getIndex()) ?
- 0 : (curIdx + 1));
+ _minIndex : (curIdx + 1));
}
return newForwardPosition(curIdx);
}
@Override
public Position getBeginningPosition() {
- return FIRST_POSITION;
+ return _firstPos;
}
@Override
public Position getEndPosition() {
- return LAST_POSITION;
+ return _lastPos;
}
}
@@ -1641,16 +1742,16 @@ public class Index implements Comparable<Index> {
// pointing at the correct next index in either the between or
// non-between case
curIdx = ((curIdx == getBeginningPosition().getIndex()) ?
- (_entries.size() - 1) : (curIdx - 1));
+ _maxIndex : (curIdx - 1));
return newReversePosition(curIdx);
}
@Override
public Position getBeginningPosition() {
- return LAST_POSITION;
+ return _lastPos;
}
@Override
public Position getEndPosition() {
- return FIRST_POSITION;
+ return _firstPos;
}
}
}
diff --git a/src/java/com/healthmarketscience/jackcess/Table.java b/src/java/com/healthmarketscience/jackcess/Table.java
index 08c7d4a..830b17c 100644
--- a/src/java/com/healthmarketscience/jackcess/Table.java
+++ b/src/java/com/healthmarketscience/jackcess/Table.java
@@ -39,7 +39,6 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
-import com.healthmarketscience.jackcess.Table.RowState;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -1048,7 +1047,7 @@ public class Table
// find last data page (Not bothering to check other pages for free
// space.)
- UsageMap.PageCursor revPageCursor = _ownedPages.cursorAtEnd();
+ UsageMap.PageCursor revPageCursor = _ownedPages.cursor().afterLast();
while(true) {
int tmpPageNumber = revPageCursor.getPreviousPage();
if(tmpPageNumber < 0) {
@@ -1486,7 +1485,9 @@ public class Table
private boolean _haveRowValues;
/** values read from the last row */
private final Object[] _rowValues;
- /** last modification count seen on the table */
+ /** last modification count seen on the table we track this so that the
+ rowState can detect updates to the table and re-read any buffered
+ data */
private int _lastModCount;
private RowState(boolean hardRowBuffer) {
diff --git a/src/java/com/healthmarketscience/jackcess/UsageMap.java b/src/java/com/healthmarketscience/jackcess/UsageMap.java
index 231267a..79e772e 100644
--- a/src/java/com/healthmarketscience/jackcess/UsageMap.java
+++ b/src/java/com/healthmarketscience/jackcess/UsageMap.java
@@ -147,12 +147,6 @@ public class UsageMap
return new PageCursor();
}
- public PageCursor cursorAtEnd() {
- PageCursor cursor = new PageCursor();
- cursor.afterLast();
- return cursor;
- }
-
protected short getRowStart() {
return _rowStart;
}
@@ -784,48 +778,50 @@ public class UsageMap
* After calling this method, getNextPage will return the first page in
* the map
*/
- public void reset() {
- beforeFirst();
+ public PageCursor reset() {
+ return beforeFirst();
}
/**
* After calling this method, {@link #getNextPage} will return the first
* page in the map
*/
- public void beforeFirst() {
- reset(true);
+ public PageCursor beforeFirst() {
+ return reset(true);
}
/**
* After calling this method, {@link #getPreviousPage} will return the
* last page in the map
*/
- public void afterLast() {
- reset(false);
+ public PageCursor afterLast() {
+ return reset(false);
}
/**
* Resets this page cursor for traversing the given direction.
*/
- protected void reset(boolean moveForward) {
+ protected PageCursor reset(boolean moveForward) {
_curPageNumber = getDirHandler(moveForward).getBeginningPageNumber();
_prevPageNumber = _curPageNumber;
_lastModCount = UsageMap.this._modCount;
+ return this;
}
/**
* Restores a current position for the cursor (current position becomes
* previous position).
*/
- private void restorePosition(int curPageNumber)
+ private PageCursor restorePosition(int curPageNumber)
{
- restorePosition(curPageNumber, _curPageNumber);
+ return restorePosition(curPageNumber, _curPageNumber);
}
/**
* Restores a current and previous position for the cursor.
*/
- protected void restorePosition(int curPageNumber, int prevPageNumber) {
+ protected PageCursor restorePosition(int curPageNumber, int prevPageNumber)
+ {
if((curPageNumber != _curPageNumber) ||
(prevPageNumber != _prevPageNumber))
{
@@ -835,6 +831,7 @@ public class UsageMap
} else {
checkForModification();
}
+ return this;
}
/**
diff --git a/test/src/java/com/healthmarketscience/jackcess/CursorTest.java b/test/src/java/com/healthmarketscience/jackcess/CursorTest.java
index f6b829d..6b67892 100644
--- a/test/src/java/com/healthmarketscience/jackcess/CursorTest.java
+++ b/test/src/java/com/healthmarketscience/jackcess/CursorTest.java
@@ -33,6 +33,17 @@ public class CursorTest extends TestCase {
}
return expectedRows;
}
+
+ private static List<Map<String,Object>> createTestTableData(
+ int startIdx,
+ int endIdx)
+ throws Exception
+ {
+ List<Map<String,Object>> expectedRows = createTestTableData();
+ expectedRows.subList(endIdx, expectedRows.size()).clear();
+ expectedRows.subList(0, startIdx).clear();
+ return expectedRows;
+ }
private static Database createTestTable() throws Exception {
Database db = create();
@@ -85,6 +96,17 @@ public class CursorTest extends TestCase {
return db;
}
+ private static Cursor createIndexSubRangeCursor(Table table,
+ Index idx,
+ int type)
+ throws Exception
+ {
+ return Cursor.createIndexCursor(
+ table, idx,
+ idx.constructIndexRow(3 - type), (type == 0),
+ idx.constructIndexRow(8 + type), (type == 0));
+ }
+
public void testRowId() throws Exception {
// test special cases
RowId rowId1 = new RowId(1, 2);
@@ -105,12 +127,17 @@ public class CursorTest extends TestCase {
Table table = db.getTable("test");
Cursor cursor = Cursor.createCursor(table);
- doTestSimple(table, cursor);
+ doTestSimple(table, cursor, null);
db.close();
}
- private void doTestSimple(Table table, Cursor cursor) throws Exception {
- List<Map<String,Object>> expectedRows = createTestTableData();
+ private void doTestSimple(Table table, Cursor cursor,
+ List<Map<String,Object>> expectedRows)
+ throws Exception
+ {
+ if(expectedRows == null) {
+ expectedRows = createTestTableData();
+ }
List<Map<String, Object>> foundRows =
new ArrayList<Map<String, Object>>();
@@ -125,13 +152,18 @@ public class CursorTest extends TestCase {
Table table = db.getTable("test");
Cursor cursor = Cursor.createCursor(table);
- doTestMove(table, cursor);
+ doTestMove(table, cursor, null);
db.close();
}
- private void doTestMove(Table table, Cursor cursor) throws Exception {
- List<Map<String,Object>> expectedRows = createTestTableData();
+ private void doTestMove(Table table, Cursor cursor,
+ List<Map<String,Object>> expectedRows)
+ throws Exception
+ {
+ if(expectedRows == null) {
+ expectedRows = createTestTableData();
+ }
expectedRows.subList(1, 4).clear();
List<Map<String, Object>> foundRows =
@@ -177,12 +209,13 @@ public class CursorTest extends TestCase {
Table table = db.getTable("test");
Cursor cursor = Cursor.createCursor(table);
- doTestSearch(table, cursor, null);
+ doTestSearch(table, cursor, null, 42, -13);
db.close();
}
- private void doTestSearch(Table table, Cursor cursor, Index index)
+ private void doTestSearch(Table table, Cursor cursor, Index index,
+ Integer... outOfRangeValues)
throws Exception
{
assertTrue(cursor.findRow(table.getColumn("id"), 3));
@@ -211,28 +244,38 @@ public class CursorTest extends TestCase {
"value", "data" + 7),
cursor.getCurrentRow());
- assertTrue(cursor.findRow(table.getColumn("value"), "data" + 2));
- assertEquals(createExpectedRow("id", 2,
- "value", "data" + 2),
+ assertTrue(cursor.findRow(table.getColumn("value"), "data" + 4));
+ assertEquals(createExpectedRow("id", 4,
+ "value", "data" + 4),
cursor.getCurrentRow());
+
+ for(Integer outOfRangeValue : outOfRangeValues) {
+ assertFalse(cursor.findRow(table.getColumn("id"),
+ outOfRangeValue));
+ assertFalse(cursor.findRow(table.getColumn("value"),
+ "data" + outOfRangeValue));
+ assertFalse(cursor.findRow(createExpectedRow(
+ "id", outOfRangeValue,
+ "value", "data" + outOfRangeValue)));
+ }
- assertEquals("data" + 9,
+ assertEquals("data" + 5,
Cursor.findValue(table,
table.getColumn("value"),
- table.getColumn("id"), 9));
- assertEquals(createExpectedRow("id", 9,
- "value", "data" + 9),
+ table.getColumn("id"), 5));
+ assertEquals(createExpectedRow("id", 5,
+ "value", "data" + 5),
Cursor.findRow(table,
- createExpectedRow("id", 9)));
+ createExpectedRow("id", 5)));
if(index != null) {
- assertEquals("data" + 9,
+ assertEquals("data" + 5,
Cursor.findValue(table, index,
table.getColumn("value"),
- table.getColumn("id"), 9));
- assertEquals(createExpectedRow("id", 9,
- "value", "data" + 9),
+ table.getColumn("id"), 5));
+ assertEquals(createExpectedRow("id", 5,
+ "value", "data" + 5),
Cursor.findRow(table, index,
- createExpectedRow("id", 9)));
+ createExpectedRow("id", 5)));
}
}
@@ -241,13 +284,18 @@ public class CursorTest extends TestCase {
Table table = db.getTable("test");
Cursor cursor = Cursor.createCursor(table);
- doTestReverse(table, cursor);
+ doTestReverse(table, cursor, null);
db.close();
}
- private void doTestReverse(Table table, Cursor cursor) throws Exception {
- List<Map<String,Object>> expectedRows = createTestTableData();
+ private void doTestReverse(Table table, Cursor cursor,
+ List<Map<String,Object>> expectedRows)
+ throws Exception
+ {
+ if(expectedRows == null) {
+ expectedRows = createTestTableData();
+ }
Collections.reverse(expectedRows);
List<Map<String, Object>> foundRows =
@@ -265,14 +313,15 @@ public class CursorTest extends TestCase {
Cursor cursor1 = Cursor.createCursor(table);
Cursor cursor2 = Cursor.createCursor(table);
- doTestLiveAddition(table, cursor1, cursor2);
+ doTestLiveAddition(table, cursor1, cursor2, 11);
db.close();
}
private void doTestLiveAddition(Table table,
Cursor cursor1,
- Cursor cursor2) throws Exception
+ Cursor cursor2,
+ Integer newRowNum) throws Exception
{
cursor1.moveNextRows(11);
cursor2.moveNextRows(11);
@@ -280,7 +329,6 @@ public class CursorTest extends TestCase {
assertTrue(cursor1.isAfterLast());
assertTrue(cursor2.isAfterLast());
- int newRowNum = 11;
table.addRow(newRowNum, "data" + newRowNum);
Map<String,Object> expectedRow =
createExpectedRow("id", newRowNum, "value", "data" + newRowNum);
@@ -306,7 +354,7 @@ public class CursorTest extends TestCase {
Cursor cursor2 = Cursor.createCursor(table);
Cursor cursor3 = Cursor.createCursor(table);
Cursor cursor4 = Cursor.createCursor(table);
- doTestLiveDeletion(table, cursor1, cursor2, cursor3, cursor4);
+ doTestLiveDeletion(table, cursor1, cursor2, cursor3, cursor4, 1);
db.close();
}
@@ -315,20 +363,23 @@ public class CursorTest extends TestCase {
Cursor cursor1,
Cursor cursor2,
Cursor cursor3,
- Cursor cursor4) throws Exception
+ Cursor cursor4,
+ int firstValue) throws Exception
{
- cursor1.moveNextRows(2);
- cursor2.moveNextRows(3);
- cursor3.moveNextRows(3);
- cursor4.moveNextRows(4);
+ assertEquals(2, cursor1.moveNextRows(2));
+ assertEquals(3, cursor2.moveNextRows(3));
+ assertEquals(3, cursor3.moveNextRows(3));
+ assertEquals(4, cursor4.moveNextRows(4));
Map<String,Object> expectedPrevRow =
- createExpectedRow("id", 1, "value", "data" + 1);
+ createExpectedRow("id", firstValue, "value", "data" + firstValue);
+ ++firstValue;
Map<String,Object> expectedDeletedRow =
- createExpectedRow("id", 2, "value", "data" + 2);
+ createExpectedRow("id", firstValue, "value", "data" + firstValue);
+ ++firstValue;
Map<String,Object> expectedNextRow =
- createExpectedRow("id", 3, "value", "data" + 3);
-
+ createExpectedRow("id", firstValue, "value", "data" + firstValue);
+
assertEquals(expectedDeletedRow, cursor2.getCurrentRow());
assertEquals(expectedDeletedRow, cursor3.getCurrentRow());
@@ -356,7 +407,7 @@ public class CursorTest extends TestCase {
assertTable(createUnorderedTestTableData(), table);
Cursor cursor = Cursor.createIndexCursor(table, idx);
- doTestSimple(table, cursor);
+ doTestSimple(table, cursor, null);
db.close();
}
@@ -367,7 +418,7 @@ public class CursorTest extends TestCase {
Table table = db.getTable("test");
Index idx = table.getIndexes().get(0);
Cursor cursor = Cursor.createIndexCursor(table, idx);
- doTestMove(table, cursor);
+ doTestMove(table, cursor, null);
db.close();
}
@@ -378,7 +429,7 @@ public class CursorTest extends TestCase {
Table table = db.getTable("test");
Index idx = table.getIndexes().get(0);
Cursor cursor = Cursor.createIndexCursor(table, idx);
- doTestReverse(table, cursor);
+ doTestReverse(table, cursor, null);
db.close();
}
@@ -389,7 +440,7 @@ public class CursorTest extends TestCase {
Table table = db.getTable("test");
Index idx = table.getIndexes().get(0);
Cursor cursor = Cursor.createIndexCursor(table, idx);
- doTestSearch(table, cursor, idx);
+ doTestSearch(table, cursor, idx, 42, -13);
db.close();
}
@@ -402,7 +453,7 @@ public class CursorTest extends TestCase {
Cursor cursor1 = Cursor.createIndexCursor(table, idx);
Cursor cursor2 = Cursor.createIndexCursor(table, idx);
- doTestLiveAddition(table, cursor1, cursor2);
+ doTestLiveAddition(table, cursor1, cursor2, 11);
db.close();
}
@@ -417,9 +468,113 @@ public class CursorTest extends TestCase {
Cursor cursor2 = Cursor.createIndexCursor(table, idx);
Cursor cursor3 = Cursor.createIndexCursor(table, idx);
Cursor cursor4 = Cursor.createIndexCursor(table, idx);
- doTestLiveDeletion(table, cursor1, cursor2, cursor3, cursor4);
+ doTestLiveDeletion(table, cursor1, cursor2, cursor3, cursor4, 1);
db.close();
}
+
+ public void testSimpleIndexSubRange() throws Exception {
+ for(int i = 0; i < 2; ++i) {
+ Database db = createTestIndexTable();
+
+ Table table = db.getTable("test");
+ Index idx = table.getIndexes().get(0);
+
+ Cursor cursor = createIndexSubRangeCursor(table, idx, i);
+
+ List<Map<String,Object>> expectedRows =
+ createTestTableData(3, 9);
+
+ doTestSimple(table, cursor, expectedRows);
+
+ db.close();
+ }
+ }
+
+ public void testMoveIndexSubRange() throws Exception {
+ for(int i = 0; i < 2; ++i) {
+ Database db = createTestIndexTable();
+
+ Table table = db.getTable("test");
+ Index idx = table.getIndexes().get(0);
+
+ Cursor cursor = createIndexSubRangeCursor(table, idx, i);
+
+ List<Map<String,Object>> expectedRows =
+ createTestTableData(3, 9);
+
+ doTestMove(table, cursor, expectedRows);
+
+ db.close();
+ }
+ }
+
+ public void testSearchIndexSubRange() throws Exception {
+ for(int i = 0; i < 2; ++i) {
+ Database db = createTestIndexTable();
+
+ Table table = db.getTable("test");
+ Index idx = table.getIndexes().get(0);
+
+ Cursor cursor = createIndexSubRangeCursor(table, idx, i);
+
+ doTestSearch(table, cursor, idx, 2, 9);
+
+ db.close();
+ }
+ }
+
+ public void testReverseIndexSubRange() throws Exception {
+ for(int i = 0; i < 2; ++i) {
+ Database db = createTestIndexTable();
+
+ Table table = db.getTable("test");
+ Index idx = table.getIndexes().get(0);
+
+ Cursor cursor = createIndexSubRangeCursor(table, idx, i);
+
+ List<Map<String,Object>> expectedRows =
+ createTestTableData(3, 9);
+
+ doTestReverse(table, cursor, expectedRows);
+
+ db.close();
+ }
+ }
+
+ public void testLiveAdditionIndexSubRange() throws Exception {
+ for(int i = 0; i < 2; ++i) {
+ Database db = createTestIndexTable();
+
+ Table table = db.getTable("test");
+ Index idx = table.getIndexes().get(0);
+
+ Cursor cursor1 = createIndexSubRangeCursor(table, idx, i);
+ Cursor cursor2 = createIndexSubRangeCursor(table, idx, i);
+
+ doTestLiveAddition(table, cursor1, cursor2, 8);
+
+ db.close();
+ }
+ }
+
+ public void testLiveDeletionIndexSubRange() throws Exception {
+ for(int i = 0; i < 2; ++i) {
+ Database db = createTestIndexTable();
+
+ Table table = db.getTable("test");
+ Index idx = table.getIndexes().get(0);
+
+ Cursor cursor1 = createIndexSubRangeCursor(table, idx, i);
+ Cursor cursor2 = createIndexSubRangeCursor(table, idx, i);
+ Cursor cursor3 = createIndexSubRangeCursor(table, idx, i);
+ Cursor cursor4 = createIndexSubRangeCursor(table, idx, i);
+
+ doTestLiveDeletion(table, cursor1, cursor2, cursor3, cursor4, 4);
+
+ db.close();
+ }
+ }
+
}
diff --git a/test/src/java/com/healthmarketscience/jackcess/DatabaseTest.java b/test/src/java/com/healthmarketscience/jackcess/DatabaseTest.java
index a962c46..83930af 100644
--- a/test/src/java/com/healthmarketscience/jackcess/DatabaseTest.java
+++ b/test/src/java/com/healthmarketscience/jackcess/DatabaseTest.java
@@ -18,8 +18,8 @@ import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
-import java.util.HashMap;
import java.util.HashSet;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -368,13 +368,13 @@ public class DatabaseTest extends TestCase {
public void testReadWithDeletedCols() throws Exception {
Table table = Database.open(new File("test/data/delColTest.mdb")).getTable("Table1");
- Map<String, Object> expectedRow0 = new HashMap<String, Object>();
+ Map<String, Object> expectedRow0 = new LinkedHashMap<String, Object>();
expectedRow0.put("id", 0);
expectedRow0.put("id2", 2);
expectedRow0.put("data", "foo");
expectedRow0.put("data2", "foo2");
- Map<String, Object> expectedRow1 = new HashMap<String, Object>();
+ Map<String, Object> expectedRow1 = new LinkedHashMap<String, Object>();
expectedRow1.put("id", 3);
expectedRow1.put("id2", 5);
expectedRow1.put("data", "bar");
@@ -687,7 +687,7 @@ public class DatabaseTest extends TestCase {
Table t = db.getTable("test");
List<String> row = new ArrayList<String>();
- Map<String,Object> expectedRowData = new HashMap<String, Object>();
+ Map<String,Object> expectedRowData = new LinkedHashMap<String, Object>();
for(int i = 0; i < numColumns; ++i) {
String value = "" + i + " some row data";
row.add(value);
@@ -838,7 +838,7 @@ public class DatabaseTest extends TestCase {
}
static Map<String, Object> createExpectedRow(Object... rowElements) {
- Map<String, Object> row = new HashMap<String, Object>();
+ Map<String, Object> row = new LinkedHashMap<String, Object>();
for(int i = 0; i < rowElements.length; i += 2) {
row.put((String)rowElements[i],
rowElements[i + 1]);