summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/java/com/healthmarketscience/jackcess/Cursor.java121
-rw-r--r--src/java/com/healthmarketscience/jackcess/Index.java10
-rw-r--r--src/java/com/healthmarketscience/jackcess/UsageMap.java9
-rw-r--r--test/src/java/com/healthmarketscience/jackcess/CursorTest.java9
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<Map<String, Object>>
{
+ 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);
@@ -68,6 +75,18 @@ public abstract class Cursor implements Iterable<Map<String, Object>>
}
/**
+ * 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
* rowPattern.
@@ -397,10 +416,12 @@ public abstract class Cursor implements Iterable<Map<String, Object>>
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<Map<String, Object>>
/**
* 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<Map<String, Object>>
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<Map<String, Object>>
public boolean findRow(Map<String,Object> 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.
+ * <p>
+ * 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.
+ * <p>
+ * 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<String,Object> rowPattern)
+ throws IOException
+ {
beforeFirst();
Collection<String> columnNames = rowPattern.keySet();
while(moveToNextRow()) {
@@ -487,8 +567,8 @@ public abstract class Cursor implements Iterable<Map<String, Object>>
}
}
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<Map<String, Object>>
*/
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<Map<String, Object>>
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<Index> {
*/
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();
}