]> source.dussan.org Git - jackcess.git/commitdiff
implement save/restore in Cursor; use during find calls
authorJames Ahlborn <jtahlborn@yahoo.com>
Wed, 28 Nov 2007 17:40:59 +0000 (17:40 +0000)
committerJames Ahlborn <jtahlborn@yahoo.com>
Wed, 28 Nov 2007 17:40:59 +0000 (17:40 +0000)
git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/trunk@187 f203690c-595d-4dc9-a70b-905162fa7fd2

src/java/com/healthmarketscience/jackcess/Cursor.java
src/java/com/healthmarketscience/jackcess/Index.java
src/java/com/healthmarketscience/jackcess/UsageMap.java
test/src/java/com/healthmarketscience/jackcess/CursorTest.java

index 310530037c96a50531b8d9a711e3e4b3997150c4..fc0a25cc6621b5741c501a82e48ffa011aad452e 100644 (file)
@@ -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);
 
@@ -67,6 +74,18 @@ public abstract class Cursor implements Iterable<Map<String, Object>>
     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<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
index f703a57dec81a1e87e95e524b0adc4a476d5d5cf..6793a7bde6771ac44ed73ecea69b45f9e0126869 100644 (file)
@@ -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();
+      }
     }
 
     /**
index eee726a749b7f5fc72d3c29a8366d2bb43cc3e10..8833edb9078117b632b1770bc1296a1d81314f21 100644 (file)
@@ -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;
     }
 
     /**
index dc6cc017243158e75bbcc94e4234a1fd9f3e2a1b..a99abef717dc8c88b7ec94074a6c031430415805 100644 (file)
@@ -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();
   }