From: James Ahlborn Date: Mon, 3 Dec 2007 04:55:26 +0000 (+0000) Subject: ditch builder-style methods on cursor impls, create CursorBuilder for simpler constru... X-Git-Tag: rel_1_1_10~8 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=8614c39231a6b7ec81ec0c8c6ae53763ff7ad2b0;p=jackcess.git ditch builder-style methods on cursor impls, create CursorBuilder for simpler construction of Cursors git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/trunk@196 f203690c-595d-4dc9-a70b-905162fa7fd2 --- diff --git a/src/java/com/healthmarketscience/jackcess/Cursor.java b/src/java/com/healthmarketscience/jackcess/Cursor.java index 520481a..60fdf14 100644 --- a/src/java/com/healthmarketscience/jackcess/Cursor.java +++ b/src/java/com/healthmarketscience/jackcess/Cursor.java @@ -22,6 +22,11 @@ import org.apache.commons.logging.LogFactory; * traversed, row updates may or may not be seen). Multiple cursors may * traverse the same table simultaneously. *

+ * The Cursor provides a variety of static utility methods to construct + * cursors with given characteristics or easily search for specific values. + * For even friendlier and more flexible construction, see + * {@link CursorBuilder}. + *

* Is not thread-safe. * * @author james @@ -240,6 +245,10 @@ public abstract class Cursor implements Iterable> /** * Returns the current state of the cursor which can be restored at a future * point in time by a call to {@link #restoreSavepoint}. + *

+ * Savepoints may be used across different cursor instances for the same + * table, but they must be of the same type (index based vs. non-index + * based). */ public Savepoint getSavepoint() { return new Savepoint(_curPos, _prevPos); @@ -249,11 +258,11 @@ public abstract class Cursor implements Iterable> * Moves the cursor to a savepoint previously returned from * {@link #getSavepoint}. */ - public Cursor restoreSavepoint(Savepoint savepoint) + public void restoreSavepoint(Savepoint savepoint) throws IOException { - return restorePosition(savepoint.getCurrentPosition(), - savepoint.getPreviousPosition()); + restorePosition(savepoint.getCurrentPosition(), + savepoint.getPreviousPosition()); } /** @@ -273,24 +282,24 @@ public abstract class Cursor implements Iterable> /** * Resets this cursor for forward traversal. Calls {@link #beforeFirst}. */ - public Cursor reset() { - return beforeFirst(); + public void reset() { + beforeFirst(); } /** * Resets this cursor for forward traversal (sets cursor to before the first * row). */ - public Cursor beforeFirst() { - return reset(true); + public void beforeFirst() { + reset(true); } /** * Resets this cursor for reverse traversal (sets cursor to after the last * row). */ - public Cursor afterLast() { - return reset(false); + public void afterLast() { + reset(false); } /** @@ -335,11 +344,10 @@ public abstract class Cursor implements Iterable> /** * Resets this cursor for traversing the given direction. */ - protected Cursor reset(boolean moveForward) { + protected void reset(boolean moveForward) { _curPos = getDirHandler(moveForward).getBeginningPosition(); _prevPos = _curPos; _rowState.reset(); - return this; } /** @@ -511,23 +519,22 @@ public abstract class Cursor implements Iterable> * Restores a current position for the cursor (current position becomes * previous position). */ - protected Cursor restorePosition(Position curPos) + protected void restorePosition(Position curPos) throws IOException { - return restorePosition(curPos, _curPos); + restorePosition(curPos, _curPos); } /** * Restores a current and previous position for the cursor if the given * positions are different from the current positions. */ - protected final Cursor restorePosition(Position curPos, Position prevPos) + protected final void restorePosition(Position curPos, Position prevPos) throws IOException { if(!curPos.equals(_curPos) || !prevPos.equals(_prevPos)) { restorePositionImpl(curPos, prevPos); } - return this; } /** @@ -867,9 +874,9 @@ public abstract class Cursor implements Iterable> } @Override - protected Cursor reset(boolean moveForward) { + protected void reset(boolean moveForward) { _ownedPagesCursor.reset(moveForward); - return super.reset(moveForward); + super.reset(moveForward); } @Override @@ -1030,9 +1037,9 @@ public abstract class Cursor implements Iterable> } @Override - protected Cursor reset(boolean moveForward) { + protected void reset(boolean moveForward) { _entryCursor.reset(moveForward); - return super.reset(moveForward); + super.reset(moveForward); } @Override diff --git a/src/java/com/healthmarketscience/jackcess/CursorBuilder.java b/src/java/com/healthmarketscience/jackcess/CursorBuilder.java new file mode 100644 index 0000000..9852b9e --- /dev/null +++ b/src/java/com/healthmarketscience/jackcess/CursorBuilder.java @@ -0,0 +1,228 @@ +// Copyright (c) 2007 Health Market Science, Inc. + +package com.healthmarketscience.jackcess; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; + +import org.apache.commons.lang.ObjectUtils; + +/** + * Builder style class for constructing a Cursor. By default, a cursor is + * created at the beginning of the table, and any start/end rows are + * inclusive. + * + * @author james + */ +public class CursorBuilder { + /** the table which the cursor will traverse */ + private final Table _table; + /** optional index to use in traversal */ + private Index _index; + /** optional start row for an index cursor */ + private Object[] _startRow; + /** whether or not start row for an index cursor is inclusive */ + private boolean _startRowInclusive = true; + /** optional end row for an index cursor */ + private Object[] _endRow; + /** whether or not end row for an index cursor is inclusive */ + private boolean _endRowInclusive = true; + /** whether to start at beginning or end of cursor */ + private boolean _beforeFirst = true; + /** optional save point to restore to the cursor */ + private Cursor.Savepoint _savepoint; + + public CursorBuilder(Table table) { + _table = table; + } + + /** + * Sets the cursor so that it will start at the beginning (unless a + * savepoint is given). + */ + public CursorBuilder beforeFirst() { + _beforeFirst = true; + return this; + } + + /** + * Sets the cursor so that it will start at the end (unless a savepoint is + * given). + */ + public CursorBuilder afterLast() { + _beforeFirst = false; + return this; + } + + /** + * Sets a savepoint to restore for the initial position of the cursor. + */ + public CursorBuilder restoreSavepoint(Cursor.Savepoint savepoint) { + _savepoint = savepoint; + return this; + } + + /** + * Sets an index to use for the cursor. + */ + public CursorBuilder setIndex(Index index) { + _index = index; + return this; + } + + /** + * Sets an index to use for the cursor by searching the table for an index + * with the given name. + * @throws IllegalArgumentException if no index can be found on the table + * with the given name + */ + public CursorBuilder setIndexByName(String indexName) { + boolean found = false; + for(Index index : _table.getIndexes()) { + if(ObjectUtils.equals(indexName, index.getName())) { + _index = index; + found = true; + break; + } + } + if(!found) { + throw new IllegalArgumentException("Index with name " + indexName + + " does not exist in table " + _table); + } + return this; + } + + /** + * Sets an index to use for the cursor by searching the table for an index + * with exactly the given columns. + * @throws IllegalArgumentException if no index can be found on the table + * with the given name + */ + public CursorBuilder setIndexByColumns(Column... columns) { + List searchColumns = Arrays.asList(columns); + boolean found = false; + for(Index index : _table.getIndexes()) { + + Collection indexColumns = index.getColumns(); + if(indexColumns.size() != searchColumns.size()) { + continue; + } + Iterator sIter = searchColumns.iterator(); + Iterator iIter = indexColumns.iterator(); + boolean matches = true; + while(sIter.hasNext()) { + Column sCol = sIter.next(); + Column iCol = iIter.next(); + if(!ObjectUtils.equals(sCol.getName(), iCol.getName())) { + matches = false; + break; + } + } + + if(matches) { + _index = index; + found = true; + break; + } + } + if(!found) { + throw new IllegalArgumentException("Index with columns " + + searchColumns + + " does not exist in table " + _table); + } + return this; + } + + /** + * Sets the starting row for a range based index cursor. + *

+ * A valid index must be specified before calling this method. + */ + public CursorBuilder setStartRow(Object[] startRow) { + _startRow = startRow; + return this; + } + + /** + * Sets the starting row for a range based index cursor to the given entry + * (where the given values correspond to the indexes columns). + *

+ * A valid index must be specified before calling this method. + */ + public CursorBuilder setStartEntry(Object... startEntry) { + if(startEntry != null) { + setStartRow(_index.constructIndexRowFromEntry(startEntry)); + } + return this; + } + + /** + * Sets whether the starting row for a range based index cursor is inclusive + * or exclusive. + */ + public CursorBuilder setStartRowInclusive(boolean inclusive) { + _startRowInclusive = inclusive; + return this; + } + + /** + * Sets the ending row for a range based index cursor. + *

+ * A valid index must be specified before calling this method. + */ + public CursorBuilder setEndRow(Object[] endRow) { + _endRow = endRow; + return this; + } + + /** + * Sets the ending row for a range based index cursor to the given entry + * (where the given values correspond to the indexes columns). + *

+ * A valid index must be specified before calling this method. + */ + public CursorBuilder setEndEntry(Object... endEntry) { + if(endEntry != null) { + setEndRow(_index.constructIndexRowFromEntry(endEntry)); + } + return this; + } + + /** + * Sets whether the ending row for a range based index cursor is inclusive + * or exclusive. + */ + public CursorBuilder setEndRowInclusive(boolean inclusive) { + _endRowInclusive = inclusive; + return this; + } + + /** + * Returns a new cursor for the table, constructed to the given + * specifications. + */ + public Cursor toCursor() + throws IOException + { + Cursor cursor = null; + if(_index == null) { + cursor = Cursor.createCursor(_table); + } else { + cursor = Cursor.createIndexCursor(_table, _index, + _startRow, _startRowInclusive, + _endRow, _endRowInclusive); + } + if(_savepoint == null) { + if(!_beforeFirst) { + cursor.afterLast(); + } + } else { + cursor.restoreSavepoint(_savepoint); + } + return cursor; + } + +} diff --git a/src/java/com/healthmarketscience/jackcess/Index.java b/src/java/com/healthmarketscience/jackcess/Index.java index fe12832..5c26f64 100644 --- a/src/java/com/healthmarketscience/jackcess/Index.java +++ b/src/java/com/healthmarketscience/jackcess/Index.java @@ -705,7 +705,7 @@ public class Index implements Comparable { * @throws IllegalArgumentException if the wrong number of values are * provided */ - public Object[] constructIndexRow(Object... values) + public Object[] constructIndexRowFromEntry(Object... values) { if(values.length != _columns.size()) { throw new IllegalArgumentException( @@ -1509,19 +1509,19 @@ public class Index implements Comparable { return(Index.this._modCount == _lastModCount); } - public EntryCursor reset() { - return beforeFirst(); + public void reset() { + beforeFirst(); } - public EntryCursor beforeFirst() { - return reset(true); + public void beforeFirst() { + reset(true); } - public EntryCursor afterLast() { - return reset(false); + public void afterLast() { + reset(false); } - protected EntryCursor reset(boolean moveForward) { + protected void reset(boolean moveForward) { _curPos = getDirHandler(moveForward).getBeginningPosition(); _prevPos = _curPos; if(!isUpToDate()) { @@ -1529,27 +1529,26 @@ public class Index implements Comparable { updateBounds(); _lastModCount = Index.this._modCount; } - return this; } /** * Repositions the cursor so that the next row will be the first entry * >= the given row. */ - public EntryCursor beforeEntry(Object[] row) + public void beforeEntry(Object[] row) throws IOException { - return restorePosition(new Entry(row, RowId.FIRST_ROW_ID, _columns)); + 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 EntryCursor afterEntry(Object[] row) + public void afterEntry(Object[] row) throws IOException { - return restorePosition(new Entry(row, RowId.LAST_ROW_ID, _columns)); + restorePosition(new Entry(row, RowId.LAST_ROW_ID, _columns)); } /** @@ -1572,14 +1571,14 @@ public class Index implements Comparable { * Restores a current position for the cursor (current position becomes * previous position). */ - private EntryCursor restorePosition(Entry curEntry) { - return restorePosition(curEntry, _curPos.getEntry()); + private void restorePosition(Entry curEntry) { + restorePosition(curEntry, _curPos.getEntry()); } /** * Restores a current and previous position for the cursor. */ - protected EntryCursor restorePosition(Entry curEntry, Entry prevEntry) + protected void restorePosition(Entry curEntry, Entry prevEntry) { if(!curEntry.equals(_curPos.getEntry()) || !prevEntry.equals(_prevPos.getEntry())) @@ -1593,7 +1592,6 @@ public class Index implements Comparable { } else { checkForModification(); } - return this; } /** diff --git a/src/java/com/healthmarketscience/jackcess/Table.java b/src/java/com/healthmarketscience/jackcess/Table.java index 830b17c..d83c4b8 100644 --- a/src/java/com/healthmarketscience/jackcess/Table.java +++ b/src/java/com/healthmarketscience/jackcess/Table.java @@ -1047,7 +1047,8 @@ public class Table // find last data page (Not bothering to check other pages for free // space.) - UsageMap.PageCursor revPageCursor = _ownedPages.cursor().afterLast(); + UsageMap.PageCursor revPageCursor = _ownedPages.cursor(); + revPageCursor.afterLast(); while(true) { int tmpPageNumber = revPageCursor.getPreviousPage(); if(tmpPageNumber < 0) { diff --git a/src/java/com/healthmarketscience/jackcess/UsageMap.java b/src/java/com/healthmarketscience/jackcess/UsageMap.java index 79e772e..00d87c7 100644 --- a/src/java/com/healthmarketscience/jackcess/UsageMap.java +++ b/src/java/com/healthmarketscience/jackcess/UsageMap.java @@ -778,49 +778,48 @@ public class UsageMap * After calling this method, getNextPage will return the first page in * the map */ - public PageCursor reset() { - return beforeFirst(); + public void reset() { + beforeFirst(); } /** * After calling this method, {@link #getNextPage} will return the first * page in the map */ - public PageCursor beforeFirst() { - return reset(true); + public void beforeFirst() { + reset(true); } /** * After calling this method, {@link #getPreviousPage} will return the * last page in the map */ - public PageCursor afterLast() { - return reset(false); + public void afterLast() { + reset(false); } /** * Resets this page cursor for traversing the given direction. */ - protected PageCursor reset(boolean moveForward) { + protected void 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 PageCursor restorePosition(int curPageNumber) + private void restorePosition(int curPageNumber) { - return restorePosition(curPageNumber, _curPageNumber); + restorePosition(curPageNumber, _curPageNumber); } /** * Restores a current and previous position for the cursor. */ - protected PageCursor restorePosition(int curPageNumber, int prevPageNumber) + protected void restorePosition(int curPageNumber, int prevPageNumber) { if((curPageNumber != _curPageNumber) || (prevPageNumber != _prevPageNumber)) @@ -831,7 +830,6 @@ 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 751c6cc..ad1a214 100644 --- a/test/src/java/com/healthmarketscience/jackcess/CursorTest.java +++ b/test/src/java/com/healthmarketscience/jackcess/CursorTest.java @@ -101,10 +101,13 @@ public class CursorTest extends TestCase { int type) throws Exception { - return Cursor.createIndexCursor( - table, idx, - idx.constructIndexRow(3 - type), (type == 0), - idx.constructIndexRow(8 + type), (type == 0)); + return new CursorBuilder(table) + .setIndex(idx) + .setStartEntry(3 - type) + .setStartRowInclusive(type == 0) + .setEndEntry(8 + type) + .setEndRowInclusive(type == 0) + .toCursor(); } public void testRowId() throws Exception {