* traversed, row updates may or may not be seen). Multiple cursors may
* traverse the same table simultaneously.
* <p>
+ * 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}.
+ * <p>
* Is not thread-safe.
*
* @author james
/**
* Returns the current state of the cursor which can be restored at a future
* point in time by a call to {@link #restoreSavepoint}.
+ * <p>
+ * 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);
* 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());
}
/**
/**
* 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);
}
/**
/**
* 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;
}
/**
* 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;
}
/**
}
@Override
- protected Cursor reset(boolean moveForward) {
+ protected void reset(boolean moveForward) {
_ownedPagesCursor.reset(moveForward);
- return super.reset(moveForward);
+ super.reset(moveForward);
}
@Override
}
@Override
- protected Cursor reset(boolean moveForward) {
+ protected void reset(boolean moveForward) {
_entryCursor.reset(moveForward);
- return super.reset(moveForward);
+ super.reset(moveForward);
}
@Override
--- /dev/null
+// 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<Column> searchColumns = Arrays.asList(columns);
+ boolean found = false;
+ for(Index index : _table.getIndexes()) {
+
+ Collection<Column> indexColumns = index.getColumns();
+ if(indexColumns.size() != searchColumns.size()) {
+ continue;
+ }
+ Iterator<Column> sIter = searchColumns.iterator();
+ Iterator<Column> 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.
+ * <p>
+ * 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).
+ * <p>
+ * 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.
+ * <p>
+ * 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).
+ * <p>
+ * 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;
+ }
+
+}
* @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(
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()) {
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));
}
/**
* 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()))
} else {
checkForModification();
}
- return this;
}
/**
// 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) {
* 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))
} else {
checkForModification();
}
- return this;
}
/**
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 {