From a926006cdd0ba50273f302bdd4ec69c6028d677c Mon Sep 17 00:00:00 2001 From: James Ahlborn Date: Wed, 28 Nov 2007 17:40:59 +0000 Subject: [PATCH] implement save/restore in Cursor; use during find calls git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/trunk@187 f203690c-595d-4dc9-a70b-905162fa7fd2 --- .../healthmarketscience/jackcess/Cursor.java | 121 +++++++++++++++--- .../healthmarketscience/jackcess/Index.java | 10 +- .../jackcess/UsageMap.java | 9 +- .../jackcess/CursorTest.java | 9 +- 4 files changed, 120 insertions(+), 29 deletions(-) diff --git a/src/java/com/healthmarketscience/jackcess/Cursor.java b/src/java/com/healthmarketscience/jackcess/Cursor.java index 3105300..fc0a25c 100644 --- a/src/java/com/healthmarketscience/jackcess/Cursor.java +++ b/src/java/com/healthmarketscience/jackcess/Cursor.java @@ -3,7 +3,6 @@ package com.healthmarketscience.jackcess; import java.io.IOException; -import java.nio.ByteBuffer; import java.util.Collection; import java.util.Iterator; import java.util.Map; @@ -11,6 +10,8 @@ import java.util.NoSuchElementException; import com.healthmarketscience.jackcess.Table.RowState; import org.apache.commons.lang.ObjectUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; /** @@ -26,13 +27,19 @@ import org.apache.commons.lang.ObjectUtils; */ public abstract class Cursor implements Iterable> { + private static final Log LOG = LogFactory.getLog(Cursor.class); + + /** normal first position for the TableScanCursor */ private static final ScanPosition FIRST_SCAN_POSITION = new ScanPosition(RowId.FIRST_ROW_ID); + /** normal 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); @@ -67,6 +74,18 @@ public abstract class Cursor implements Iterable> return new TableScanCursor(table); } + /** + * Creates an indexed cursor for the given table. + * @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 + */ + public static Cursor createIndexCursor(Table table, Index index) + throws IOException + { + return new IndexCursor(table, index); + } + /** * Convenience method for finding a specific row in a table which matches a * given row "pattern. See {@link #findRow(Map)} for details on the @@ -397,10 +416,12 @@ public abstract class Cursor implements Iterable> protected void restorePosition(Position curPos) throws IOException { - // make the current position previous, and the new position current - _prevPos = _curPos; - _curPos = curPos; - _rowState.reset(); + if(!curPos.equals(_curPos)) { + // make the current position previous, and the new position current + _prevPos = _curPos; + _curPos = curPos; + _rowState.reset(); + } } /** @@ -439,8 +460,8 @@ public abstract class Cursor implements Iterable> /** * Moves to the first row (as defined by the cursor) where the given column * has the given value. This may be more efficient on some cursors than - * others. The location of the cursor when a match is not found is - * undefined. + * others. If a match is not found (or an exception is thrown), the cursor + * is restored to its previous state. * * @param columnPattern column from the table for this cursor which is being * matched by the valuePattern @@ -452,22 +473,27 @@ public abstract class Cursor implements Iterable> public boolean findRow(Column columnPattern, Object valuePattern) throws IOException { - // FIXME, add save restore? - - beforeFirst(); - while(moveToNextRow()) { - if(ObjectUtils.equals(valuePattern, getCurrentRowValue(columnPattern))) { - return true; + Position curPos = _curPos; + boolean found = false; + try { + found = findRowImpl(columnPattern, valuePattern); + return found; + } finally { + if(!found) { + try { + restorePosition(curPos); + } catch(IOException e) { + LOG.error("Failed restoring position", e); + } } } - return false; } /** * Moves to the first row (as defined by the cursor) where the given columns * have the given values. This may be more efficient on some cursors than - * others. The location of the cursor when a match is not found is - * undefined. + * others. If a match is not found (or an exception is thrown), the cursor + * is restored to its previous state. * * @param rowPattern column names and values which must be equal to the * corresponding values in the matched row @@ -477,8 +503,62 @@ public abstract class Cursor implements Iterable> public boolean findRow(Map rowPattern) throws IOException { - // FIXME, add save restore? + Position curPos = _curPos; + boolean found = false; + try { + found = findRowImpl(rowPattern); + return found; + } finally { + if(!found) { + try { + restorePosition(curPos); + } catch(IOException e) { + LOG.error("Failed restoring position", e); + } + } + } + } + + /** + * Moves to the first row (as defined by the cursor) where the given column + * has the given value. Caller manages save/restore on failure. + *

+ * Default implementation scans the table from beginning to end. + * + * @param columnPattern column from the table for this cursor which is being + * matched by the valuePattern + * @param valuePattern value which is equal to the corresponding value in + * the matched row + * @return {@code true} if a valid row was found with the given value, + * {@code false} if no row was found + */ + protected boolean findRowImpl(Column columnPattern, Object valuePattern) + throws IOException + { + beforeFirst(); + while(moveToNextRow()) { + if(ObjectUtils.equals(valuePattern, getCurrentRowValue(columnPattern))) + { + return true; + } + } + return false; + } + /** + * Moves to the first row (as defined by the cursor) where the given columns + * have the given values. Caller manages save/restore on failure. + *

+ * Default implementation scans the table from beginning to end. + * + * @param rowPattern column names and values which must be equal to the + * corresponding values in the matched row + * @return {@code true} if a valid row was found with the given values, + * {@code false} if no row was found + */ + protected boolean findRowImpl(Map rowPattern) + throws IOException + { beforeFirst(); Collection columnNames = rowPattern.keySet(); while(moveToNextRow()) { @@ -487,8 +567,8 @@ public abstract class Cursor implements Iterable> } } return false; - } - + } + /** * Skips as many rows as possible up to the given number of rows. * @return the number of rows skipped. @@ -579,7 +659,6 @@ public abstract class Cursor implements Iterable> */ protected abstract DirHandler getDirHandler(boolean moveForward); - @Override public String toString() { return getClass().getSimpleName() + " CurPosition " + _curPos + @@ -685,7 +764,7 @@ public abstract class Cursor implements Iterable> super.restorePosition(curPos); _ownedPagesCursor.setCurrentPage(curPos.getRowId().getPageNumber()); } - + /** * Position the buffer at the next row in the table * @return a ByteBuffer narrowed to the next row, or null if none diff --git a/src/java/com/healthmarketscience/jackcess/Index.java b/src/java/com/healthmarketscience/jackcess/Index.java index f703a57..6793a7b 100644 --- a/src/java/com/healthmarketscience/jackcess/Index.java +++ b/src/java/com/healthmarketscience/jackcess/Index.java @@ -1426,9 +1426,13 @@ public class Index implements Comparable { */ private void restorePosition(Entry curEntry) { - _prevPos = updatePosition(_curPos.getEntry()); - _curPos = updatePosition(curEntry); - _lastModCount = Index.this._modCount; + if(!curEntry.equals(_curPos.getEntry())) { + _prevPos = updatePosition(_curPos.getEntry()); + _curPos = updatePosition(curEntry); + _lastModCount = Index.this._modCount; + } else { + checkForModification(); + } } /** diff --git a/src/java/com/healthmarketscience/jackcess/UsageMap.java b/src/java/com/healthmarketscience/jackcess/UsageMap.java index eee726a..8833edb 100644 --- a/src/java/com/healthmarketscience/jackcess/UsageMap.java +++ b/src/java/com/healthmarketscience/jackcess/UsageMap.java @@ -30,7 +30,6 @@ package com.healthmarketscience.jackcess; import java.io.IOException; import java.nio.ByteBuffer; import java.util.BitSet; -import java.util.logging.Handler; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -833,9 +832,11 @@ public class UsageMap * Restores a previous position for the cursor. */ private void restorePosition(int curPageNumber) { - _prevPageNumber = _curPageNumber; - _curPageNumber = curPageNumber; - _lastModCount = UsageMap.this._modCount; + if(curPageNumber != _curPageNumber) { + _prevPageNumber = _curPageNumber; + _curPageNumber = curPageNumber; + } + _lastModCount = UsageMap.this._modCount; } /** diff --git a/test/src/java/com/healthmarketscience/jackcess/CursorTest.java b/test/src/java/com/healthmarketscience/jackcess/CursorTest.java index dc6cc01..a99abef 100644 --- a/test/src/java/com/healthmarketscience/jackcess/CursorTest.java +++ b/test/src/java/com/healthmarketscience/jackcess/CursorTest.java @@ -129,11 +129,18 @@ public class CursorTest extends TestCase { "value", "data" + 6), cursor.getCurrentRow()); + assertFalse(cursor.findRow(createExpectedRow( + "id", 13, + "value", "data" + 8))); + assertEquals(createExpectedRow("id", 6, + "value", "data" + 6), + cursor.getCurrentRow()); + assertEquals("data" + 9, Cursor.findValue(table, table.getColumn("value"), table.getColumn("id"), 9)); - + db.close(); } -- 2.39.5