From c36f8d401ea470811bf0035c2144fba7ef12d8a6 Mon Sep 17 00:00:00 2001 From: James Ahlborn Date: Fri, 29 Mar 2013 12:46:23 +0000 Subject: [PATCH] consolidate custom iteration configuration into iterable builders git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/branches/jackcess-2@700 f203690c-595d-4dc9-a70b-905162fa7fd2 --- TODO.txt | 2 +- .../healthmarketscience/jackcess/Cursor.java | 92 +---- .../jackcess/CursorBuilder.java | 8 +- .../jackcess/IndexCursor.java | 44 +-- .../healthmarketscience/jackcess/Table.java | 5 + .../jackcess/impl/CursorImpl.java | 373 ++++++------------ .../jackcess/impl/DatabaseImpl.java | 17 +- .../jackcess/impl/IndexCursorImpl.java | 93 +++-- .../jackcess/impl/TableImpl.java | 5 + .../impl/complex/ComplexColumnInfoImpl.java | 5 +- .../jackcess/util/Joiner.java | 6 +- .../jackcess/CursorTest.java | 58 ++- .../jackcess/IndexTest.java | 2 +- .../jackcess/impl/CodecHandlerTest.java | 6 +- 14 files changed, 268 insertions(+), 448 deletions(-) diff --git a/TODO.txt b/TODO.txt index 4c86b7a..a8187f3 100644 --- a/TODO.txt +++ b/TODO.txt @@ -31,7 +31,7 @@ Refactor goals: * move database open/create options to DBBuilder * tweak how import filters work to make them more flexible? - tweak lookup apis (specify column vs column name) -- separate classes into more packages (api,builder,util,impl) +* separate classes into more packages (api,builder,util,impl) * remove debug log blocks * add Row interface * change savepoint to use table number instead of name? diff --git a/src/java/com/healthmarketscience/jackcess/Cursor.java b/src/java/com/healthmarketscience/jackcess/Cursor.java index f2a0de2..5c6d12f 100644 --- a/src/java/com/healthmarketscience/jackcess/Cursor.java +++ b/src/java/com/healthmarketscience/jackcess/Cursor.java @@ -24,8 +24,9 @@ import java.util.Collection; import java.util.Iterator; import java.util.Map; -import com.healthmarketscience.jackcess.util.ErrorHandler; import com.healthmarketscience.jackcess.util.ColumnMatcher; +import com.healthmarketscience.jackcess.util.ErrorHandler; +import com.healthmarketscience.jackcess.util.IterableBuilder; /** * Manages iteration for a Table. Different cursors provide different methods @@ -125,101 +126,24 @@ public interface Cursor extends Iterable */ public boolean isCurrentRowDeleted() throws IOException; - /** - * Returns an Iterable whose iterator() method calls {@link #afterLast} on - * this cursor and returns a modifiable Iterator which will iterate through - * all the rows of this table in reverse order. Use of the Iterator follows - * the same restrictions as a call to {@link #getPreviousRow}. - * @throws RuntimeIOException if an IOException is thrown by one of the - * operations, the actual exception will be contained within - */ - public Iterable reverseIterable(); - - /** - * Returns an Iterable whose iterator() method calls {@link #afterLast} on - * this table and returns a modifiable Iterator which will iterate through - * all the rows of this table in reverse order, returning only the given - * columns. Use of the Iterator follows the same restrictions as a call to - * {@link #getPreviousRow}. - * @throws RuntimeIOException if an IOException is thrown by one of the - * operations, the actual exception will be contained within - */ - public Iterable reverseIterable( - Collection columnNames); - /** * Calls {@link #beforeFirst} on this cursor and returns a modifiable * Iterator which will iterate through all the rows of this table. Use of * the Iterator follows the same restrictions as a call to * {@link #getNextRow}. + *

+ * For more flexible iteration see {@link #newIterable}. * @throws RuntimeIOException if an IOException is thrown by one of the * operations, the actual exception will be contained within */ public Iterator iterator(); /** - * Returns an Iterable whose iterator() method calls {@link #beforeFirst} on - * this table and returns a modifiable Iterator which will iterate through - * all the rows of this table, returning only the given columns. Use of the - * Iterator follows the same restrictions as a call to {@link #getNextRow}. - * @throws RuntimeIOException if an IOException is thrown by one of the - * operations, the actual exception will be contained within - */ - public Iterable iterable( - Collection columnNames); - - /** - * Returns an Iterable whose iterator() method calls {@link #beforeFirst} on - * this cursor and returns a modifiable Iterator which will iterate through - * all the rows of this table which match the given column pattern. Use of - * the Iterator follows the same restrictions as a call to {@link - * #getNextRow}. See {@link #findFirstRow(Column,Object)} for details on - * #the columnPattern. - * @throws RuntimeIOException if an IOException is thrown by one of the - * operations, the actual exception will be contained within - */ - public Iterable columnMatchIterable( - Column columnPattern, Object valuePattern); - - /** - * Returns an Iterable whose iterator() method calls {@link #beforeFirst} on - * this table and returns a modifiable Iterator which will iterate through - * all the rows of this table which match the given column pattern, - * returning only the given columns. Use of the Iterator follows the same - * restrictions as a call to {@link #getNextRow}. See {@link - * #findFirstRow(Column,Object)} for details on the columnPattern. - * @throws RuntimeIOException if an IOException is thrown by one of the - * operations, the actual exception will be contained within - */ - public Iterable columnMatchIterable( - Collection columnNames, - Column columnPattern, Object valuePattern); - - /** - * Returns an Iterable whose iterator() method calls {@link #beforeFirst} on - * this cursor and returns a modifiable Iterator which will iterate through - * all the rows of this table which match the given row pattern. Use of the - * Iterator follows the same restrictions as a call to {@link #getNextRow}. - * See {@link #findFirstRow(Map)} for details on the rowPattern. - * @throws RuntimeIOException if an IOException is thrown by one of the - * operations, the actual exception will be contained within - */ - public Iterable rowMatchIterable( - Map rowPattern); - - /** - * Returns an Iterable whose iterator() method calls {@link #beforeFirst} on - * this table and returns a modifiable Iterator which will iterate through - * all the rows of this table which match the given row pattern, returning - * only the given columns. Use of the Iterator follows the same - * restrictions as a call to {@link #getNextRow}. See {@link - * #findFirstRow(Map)} for details on the rowPattern. - * @throws RuntimeIOException if an IOException is thrown by one of the - * operations, the actual exception will be contained within + * Convenience method for constructing a new IterableBuilder for this + * cursor. An IterableBuilder provides a variety of options for more + * flexible iteration. */ - public Iterable rowMatchIterable( - Collection columnNames, - Map rowPattern); + public IterableBuilder newIterable(); /** * Delete the current row. diff --git a/src/java/com/healthmarketscience/jackcess/CursorBuilder.java b/src/java/com/healthmarketscience/jackcess/CursorBuilder.java index 92a9604..9485090 100644 --- a/src/java/com/healthmarketscience/jackcess/CursorBuilder.java +++ b/src/java/com/healthmarketscience/jackcess/CursorBuilder.java @@ -315,7 +315,7 @@ public class CursorBuilder { * @param table the table over which this cursor will traverse */ public static Cursor createCursor(Table table) throws IOException { - return new CursorBuilder(table).toCursor(); + return table.newCursor().toCursor(); } /** @@ -332,7 +332,7 @@ public class CursorBuilder { public static IndexCursor createCursor(Table table, Index index) throws IOException { - return new CursorBuilder(table).setIndex(index).toIndexCursor(); + return table.newCursor().setIndex(index).toIndexCursor(); } /** @@ -355,7 +355,7 @@ public class CursorBuilder { Object[] startRow, Object[] endRow) throws IOException { - return new CursorBuilder(table).setIndex(index) + return table.newCursor().setIndex(index) .setStartRow(startRow) .setEndRow(endRow) .toIndexCursor(); @@ -386,7 +386,7 @@ public class CursorBuilder { boolean endInclusive) throws IOException { - return new CursorBuilder(table).setIndex(index) + return table.newCursor().setIndex(index) .setStartRow(startRow) .setStartRowInclusive(startInclusive) .setEndRow(endRow) diff --git a/src/java/com/healthmarketscience/jackcess/IndexCursor.java b/src/java/com/healthmarketscience/jackcess/IndexCursor.java index 6e1d0c9..7871b65 100644 --- a/src/java/com/healthmarketscience/jackcess/IndexCursor.java +++ b/src/java/com/healthmarketscience/jackcess/IndexCursor.java @@ -20,9 +20,8 @@ USA package com.healthmarketscience.jackcess; import java.io.IOException; -import java.util.Collection; -import java.util.Iterator; -import java.util.Map; + +import com.healthmarketscience.jackcess.util.EntryIterableBuilder; /** * Cursor backed by an index with extended traversal options. @@ -68,38 +67,11 @@ public interface IndexCursor extends Cursor throws IOException; /** - * Returns a modifiable Iterator which will iterate through all the rows of - * this table which match the given index entries. - * @throws RuntimeIOException if an IOException is thrown by one of the - * operations, the actual exception will be contained within - */ - public Iterator entryIterator(Object... entryValues); - - /** - * Returns a modifiable Iterator which will iterate through all the rows of - * this table which match the given index entries, returning only the given - * columns. - * @throws RuntimeIOException if an IOException is thrown by one of the - * operations, the actual exception will be contained within - */ - public Iterator entryIterator( - Collection columnNames, Object... entryValues); - - /** - * Returns an Iterable whose iterator() method returns the result of a call - * to {@link #entryIterator(Object...)} - * @throws RuntimeIOException if an IOException is thrown by one of the - * operations, the actual exception will be contained within - */ - public Iterable entryIterable(Object... entryValues); - - /** - * Returns an Iterable whose iterator() method returns the result of a call - * to {@link #entryIterator(Collection,Object...)} - * @throws RuntimeIOException if an IOException is thrown by one of the - * operations, the actual exception will be contained within + * Convenience method for constructing a new EntryIterableBuilder for this + * cursor. An EntryIterableBuilder provides a variety of options for more + * flexible iteration based on a specific index entry. + * + * @param entryValues the column values for the index's columns. */ - public Iterable entryIterable( - Collection columnNames, Object... entryValues); - + public EntryIterableBuilder newEntryIterable(Object... entryValues); } diff --git a/src/java/com/healthmarketscience/jackcess/Table.java b/src/java/com/healthmarketscience/jackcess/Table.java index 12e9a34..25b91c8 100644 --- a/src/java/com/healthmarketscience/jackcess/Table.java +++ b/src/java/com/healthmarketscience/jackcess/Table.java @@ -274,4 +274,9 @@ public interface Table extends Iterable * use the Cursor directly. */ public Cursor getDefaultCursor(); + + /** + * Convenience method for constructing a new CursorBuilder for this Table. + */ + public CursorBuilder newCursor(); } diff --git a/src/java/com/healthmarketscience/jackcess/impl/CursorImpl.java b/src/java/com/healthmarketscience/jackcess/impl/CursorImpl.java index e0d3938..b0c228a 100644 --- a/src/java/com/healthmarketscience/jackcess/impl/CursorImpl.java +++ b/src/java/com/healthmarketscience/jackcess/impl/CursorImpl.java @@ -34,17 +34,18 @@ import java.util.Iterator; import java.util.Map; import java.util.NoSuchElementException; -import com.healthmarketscience.jackcess.impl.TableImpl.RowState; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import com.healthmarketscience.jackcess.Column; import com.healthmarketscience.jackcess.Cursor; import com.healthmarketscience.jackcess.CursorBuilder; -import com.healthmarketscience.jackcess.util.ErrorHandler; -import com.healthmarketscience.jackcess.util.ColumnMatcher; -import com.healthmarketscience.jackcess.Column; import com.healthmarketscience.jackcess.Row; import com.healthmarketscience.jackcess.RuntimeIOException; +import com.healthmarketscience.jackcess.impl.TableImpl.RowState; +import com.healthmarketscience.jackcess.util.ColumnMatcher; +import com.healthmarketscience.jackcess.util.ErrorHandler; +import com.healthmarketscience.jackcess.util.IterableBuilder; import com.healthmarketscience.jackcess.util.SimpleColumnMatcher; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; /** * Manages iteration for a Table. Different cursors provide different methods @@ -201,22 +202,21 @@ public abstract class CursorImpl implements Cursor reset(MOVE_REVERSE); } - public boolean isBeforeFirst() throws IOException - { - if(getFirstPosition().equals(_curPos)) { - return !recheckPosition(MOVE_REVERSE); - } - return false; + public boolean isBeforeFirst() throws IOException { + return isAtBeginning(MOVE_FORWARD); } - public boolean isAfterLast() throws IOException - { - if(getLastPosition().equals(_curPos)) { - return !recheckPosition(MOVE_FORWARD); + public boolean isAfterLast() throws IOException { + return isAtBeginning(MOVE_REVERSE); + } + + protected boolean isAtBeginning(boolean moveForward) throws IOException { + if(getDirHandler(moveForward).getBeginningPosition().equals(_curPos)) { + return !recheckPosition(!moveForward); } return false; } - + public boolean isCurrentRowDeleted() throws IOException { // we need to ensure that the "deleted" flag has been read for this row @@ -233,176 +233,43 @@ public abstract class CursorImpl implements Cursor _prevPos = _curPos; _rowState.reset(); } - - public Iterable reverseIterable() { - return reverseIterable(null); - } - - public Iterable reverseIterable( - final Collection columnNames) - { - return new Iterable() { - public Iterator iterator() { - return new RowIterator(columnNames, MOVE_REVERSE); - } - }; - } - - public Iterator iterator() - { - return iterator(null); - } - public Iterable iterable( - final Collection columnNames) - { - return new Iterable() { - public Iterator iterator() { - return CursorImpl.this.iterator(columnNames); - } - }; - } - - /** - * Calls beforeFirst on this table and returns a modifiable - * Iterator which will iterate through all the rows of this table, returning - * only the given columns. Use of the Iterator follows the same - * restrictions as a call to getNextRow. - * @throws RuntimeIOException if an IOException is thrown by one of the - * operations, the actual exception will be contained within - */ - public Iterator iterator(Collection columnNames) - { - return new RowIterator(columnNames, MOVE_FORWARD); - } - - public Iterable columnMatchIterable( - Column columnPattern, Object valuePattern) - { - return columnMatchIterable((ColumnImpl)columnPattern, valuePattern); - } - - public Iterable columnMatchIterable( - ColumnImpl columnPattern, Object valuePattern) - { - return columnMatchIterable(null, columnPattern, valuePattern); - } - - /** - * Calls beforeFirst on this cursor and returns a modifiable - * Iterator which will iterate through all the rows of this table which - * match the given column pattern. Use of the Iterator follows the same - * restrictions as a call to getNextRow. See - * {@link #findFirstRow(Column,Object)} for details on the columnPattern. - * @throws RuntimeIOException if an IOException is thrown by one of the - * operations, the actual exception will be contained within - */ - public Iterator columnMatchIterator( - Column columnPattern, Object valuePattern) - { - return columnMatchIterator((ColumnImpl)columnPattern, valuePattern); - } - - public Iterator columnMatchIterator( - ColumnImpl columnPattern, Object valuePattern) - { - return columnMatchIterator(null, columnPattern, valuePattern); + public Iterator iterator() { + return new RowIterator(null, true, MOVE_FORWARD); } - public Iterable columnMatchIterable( - Collection columnNames, - Column columnPattern, Object valuePattern) - { - return columnMatchIterable(columnNames, (ColumnImpl)columnPattern, - valuePattern); - } - - public Iterable columnMatchIterable( - final Collection columnNames, - final ColumnImpl columnPattern, final Object valuePattern) - { - return new Iterable() { - public Iterator iterator() { - return CursorImpl.this.columnMatchIterator( - columnNames, columnPattern, valuePattern); - } - }; + public IterableBuilder newIterable() { + return new IterableBuilder(this); } - /** - * Calls beforeFirst on this table and returns a modifiable - * Iterator which will iterate through all the rows of this table which - * match the given column pattern, returning only the given columns. Use of - * the Iterator follows the same restrictions as a call to - * getNextRow. See {@link #findFirstRow(Column,Object)} for - * details on the columnPattern. - * @throws RuntimeIOException if an IOException is thrown by one of the - * operations, the actual exception will be contained within - */ - public Iterator columnMatchIterator( - Collection columnNames, Column columnPattern, - Object valuePattern) - { - return columnMatchIterator(columnNames, (ColumnImpl)columnPattern, - valuePattern); - } - - public Iterator columnMatchIterator( - Collection columnNames, ColumnImpl columnPattern, - Object valuePattern) - { - return new ColumnMatchIterator(columnNames, columnPattern, valuePattern); - } + public Iterator iterator(IterableBuilder iterBuilder) { - public Iterable rowMatchIterable( - Map rowPattern) - { - return rowMatchIterable(null, rowPattern); - } - - /** - * Calls beforeFirst on this cursor and returns a modifiable - * Iterator which will iterate through all the rows of this table which - * match the given row pattern. Use of the Iterator follows the same - * restrictions as a call to getNextRow. See - * {@link #findFirstRow(Map)} for details on the rowPattern. - * @throws RuntimeIOException if an IOException is thrown by one of the - * operations, the actual exception will be contained within - */ - public Iterator rowMatchIterator( - Map rowPattern) - { - return rowMatchIterator(null, rowPattern); - } - - public Iterable rowMatchIterable( - final Collection columnNames, - final Map rowPattern) - { - return new Iterable() { - public Iterator iterator() { - return CursorImpl.this.rowMatchIterator( - columnNames, rowPattern); - } - }; + switch(iterBuilder.getType()) { + case SIMPLE: + return new RowIterator(iterBuilder.getColumnNames(), + iterBuilder.isReset(), iterBuilder.isForward()); + case COLUMN_MATCH: { + @SuppressWarnings("unchecked") + Map.Entry matchPattern = (Map.Entry) + iterBuilder.getMatchPattern(); + return new ColumnMatchIterator( + iterBuilder.getColumnNames(), (ColumnImpl)matchPattern.getKey(), + matchPattern.getValue(), iterBuilder.isReset(), + iterBuilder.isForward(), iterBuilder.getColumnMatcher()); + } + case ROW_MATCH: { + @SuppressWarnings("unchecked") + Map matchPattern = (Map) + iterBuilder.getMatchPattern(); + return new RowMatchIterator( + iterBuilder.getColumnNames(), matchPattern,iterBuilder.isReset(), + iterBuilder.isForward(), iterBuilder.getColumnMatcher()); + } + default: + throw new RuntimeException("unknown match type " + iterBuilder.getType()); + } } - /** - * Calls beforeFirst on this table and returns a modifiable - * Iterator which will iterate through all the rows of this table which - * match the given row pattern, returning only the given columns. Use of - * the Iterator follows the same restrictions as a call to - * getNextRow. See {@link #findFirstRow(Map)} for details on - * the rowPattern. - * @throws RuntimeIOException if an IOException is thrown by one of the - * operations, the actual exception will be contained within - */ - public Iterator rowMatchIterator( - Collection columnNames, Map rowPattern) - { - return new RowMatchIterator(columnNames, rowPattern); - } - public void deleteCurrentRow() throws IOException { _table.deleteRow(_rowState, _curPos.getRowId()); } @@ -471,7 +338,7 @@ public abstract class CursorImpl implements Cursor * @return {@code true} if another valid row was found in the given * direction, {@code false} otherwise */ - private boolean moveToAnotherRow(boolean moveForward) + protected boolean moveToAnotherRow(boolean moveForward) throws IOException { if(_curPos.equals(getDirHandler(moveForward).getEndPosition())) { @@ -559,22 +426,8 @@ public abstract class CursorImpl implements Cursor public boolean findFirstRow(ColumnImpl columnPattern, Object valuePattern) throws IOException { - PositionImpl curPos = _curPos; - PositionImpl prevPos = _prevPos; - boolean found = false; - try { - beforeFirst(); - found = findNextRowImpl(columnPattern, valuePattern); - return found; - } finally { - if(!found) { - try { - restorePosition(curPos, prevPos); - } catch(IOException e) { - LOG.error("Failed restoring position", e); - } - } - } + return findAnotherRow(columnPattern, valuePattern, true, MOVE_FORWARD, + _columnMatcher); } public boolean findNextRow(Column columnPattern, Object valuePattern) @@ -585,12 +438,25 @@ public abstract class CursorImpl implements Cursor public boolean findNextRow(ColumnImpl columnPattern, Object valuePattern) throws IOException + { + return findAnotherRow(columnPattern, valuePattern, false, MOVE_FORWARD, + _columnMatcher); + } + + protected boolean findAnotherRow(ColumnImpl columnPattern, Object valuePattern, + boolean reset, boolean moveForward, + ColumnMatcher columnMatcher) + throws IOException { PositionImpl curPos = _curPos; PositionImpl prevPos = _prevPos; boolean found = false; try { - found = findNextRowImpl(columnPattern, valuePattern); + if(reset) { + reset(moveForward); + } + found = findAnotherRowImpl(columnPattern, valuePattern, moveForward, + columnMatcher); return found; } finally { if(!found) { @@ -605,32 +471,28 @@ public abstract class CursorImpl implements Cursor public boolean findFirstRow(Map rowPattern) throws IOException { - PositionImpl curPos = _curPos; - PositionImpl prevPos = _prevPos; - boolean found = false; - try { - beforeFirst(); - found = findNextRowImpl(rowPattern); - return found; - } finally { - if(!found) { - try { - restorePosition(curPos, prevPos); - } catch(IOException e) { - LOG.error("Failed restoring position", e); - } - } - } + return findAnotherRow(rowPattern, true, MOVE_FORWARD, _columnMatcher); } public boolean findNextRow(Map rowPattern) throws IOException + { + return findAnotherRow(rowPattern, false, MOVE_FORWARD, _columnMatcher); + } + + protected boolean findAnotherRow(Map rowPattern, boolean reset, + boolean moveForward, + ColumnMatcher columnMatcher) + throws IOException { PositionImpl curPos = _curPos; PositionImpl prevPos = _prevPos; boolean found = false; try { - found = findNextRowImpl(rowPattern); + if(reset) { + reset(moveForward); + } + found = findAnotherRowImpl(rowPattern, moveForward, columnMatcher); return found; } finally { if(!found) { @@ -652,13 +514,28 @@ public abstract class CursorImpl implements Cursor public boolean currentRowMatches(ColumnImpl columnPattern, Object valuePattern) throws IOException { - return _columnMatcher.matches(getTable(), columnPattern.getName(), - valuePattern, - getCurrentRowValue(columnPattern)); + return currentRowMatchesImpl(columnPattern, valuePattern, _columnMatcher); + } + + protected boolean currentRowMatchesImpl(ColumnImpl columnPattern, + Object valuePattern, + ColumnMatcher columnMatcher) + throws IOException + { + return columnMatcher.matches(getTable(), columnPattern.getName(), + valuePattern, + getCurrentRowValue(columnPattern)); } public boolean currentRowMatches(Map rowPattern) throws IOException + { + return currentRowMatchesImpl(rowPattern, _columnMatcher); + } + + protected boolean currentRowMatchesImpl(Map rowPattern, + ColumnMatcher columnMatcher) + throws IOException { Row row = getCurrentRow(rowPattern.keySet()); @@ -668,8 +545,8 @@ public abstract class CursorImpl implements Cursor for(Map.Entry e : row.entrySet()) { String columnName = e.getKey(); - if(!_columnMatcher.matches(getTable(), columnName, - rowPattern.get(columnName), e.getValue())) { + if(!columnMatcher.matches(getTable(), columnName, + rowPattern.get(columnName), e.getValue())) { return false; } } @@ -690,11 +567,13 @@ public abstract class CursorImpl implements Cursor * @return {@code true} if a valid row was found with the given value, * {@code false} if no row was found */ - protected boolean findNextRowImpl(ColumnImpl columnPattern, Object valuePattern) + protected boolean findAnotherRowImpl( + ColumnImpl columnPattern, Object valuePattern, boolean moveForward, + ColumnMatcher columnMatcher) throws IOException { - while(moveToNextRow()) { - if(currentRowMatches(columnPattern, valuePattern)) { + while(moveToAnotherRow(moveForward)) { + if(currentRowMatchesImpl(columnPattern, valuePattern, columnMatcher)) { return true; } } @@ -712,11 +591,13 @@ public abstract class CursorImpl implements Cursor * @return {@code true} if a valid row was found with the given values, * {@code false} if no row was found */ - protected boolean findNextRowImpl(Map rowPattern) + protected boolean findAnotherRowImpl(Map rowPattern, + boolean moveForward, + ColumnMatcher columnMatcher) throws IOException { - while(moveToNextRow()) { - if(currentRowMatches(rowPattern)) { + while(moveToAnotherRow(moveForward)) { + if(currentRowMatchesImpl(rowPattern, columnMatcher)) { return true; } } @@ -821,16 +702,24 @@ public abstract class CursorImpl implements Cursor /** * Base implementation of iterator for this cursor, modifiable. */ - protected abstract class BaseIterator - implements Iterator + protected abstract class BaseIterator implements Iterator { protected final Collection _columnNames; + protected final boolean _moveForward; + protected final ColumnMatcher _colMatcher; protected Boolean _hasNext; protected boolean _validRow; - protected BaseIterator(Collection columnNames) + protected BaseIterator(Collection columnNames, + boolean reset, boolean moveForward, + ColumnMatcher columnMatcher) { _columnNames = columnNames; + _moveForward = moveForward; + _colMatcher = ((columnMatcher != null) ? columnMatcher : _columnMatcher); + if(reset) { + reset(_moveForward); + } } public boolean hasNext() { @@ -880,13 +769,10 @@ public abstract class CursorImpl implements Cursor */ private final class RowIterator extends BaseIterator { - private final boolean _moveForward; - - private RowIterator(Collection columnNames, boolean moveForward) + private RowIterator(Collection columnNames, boolean reset, + boolean moveForward) { - super(columnNames); - _moveForward = moveForward; - reset(_moveForward); + super(columnNames, reset, moveForward, null); } @Override @@ -905,17 +791,19 @@ public abstract class CursorImpl implements Cursor private final Object _valuePattern; private ColumnMatchIterator(Collection columnNames, - ColumnImpl columnPattern, Object valuePattern) + ColumnImpl columnPattern, Object valuePattern, + boolean reset, boolean moveForward, + ColumnMatcher columnMatcher) { - super(columnNames); + super(columnNames, reset, moveForward, columnMatcher); _columnPattern = columnPattern; _valuePattern = valuePattern; - beforeFirst(); } @Override protected boolean findNext() throws IOException { - return findNextRow(_columnPattern, _valuePattern); + return findAnotherRow(_columnPattern, _valuePattern, false, _moveForward, + _colMatcher); } } @@ -928,16 +816,17 @@ public abstract class CursorImpl implements Cursor private final Map _rowPattern; private RowMatchIterator(Collection columnNames, - Map rowPattern) + Map rowPattern, + boolean reset, boolean moveForward, + ColumnMatcher columnMatcher) { - super(columnNames); + super(columnNames, reset, moveForward, columnMatcher); _rowPattern = rowPattern; - beforeFirst(); } @Override protected boolean findNext() throws IOException { - return findNextRow(_rowPattern); + return findAnotherRow(_rowPattern, false, _moveForward, _colMatcher); } } diff --git a/src/java/com/healthmarketscience/jackcess/impl/DatabaseImpl.java b/src/java/com/healthmarketscience/jackcess/impl/DatabaseImpl.java index 5c193b3..9d513af 100644 --- a/src/java/com/healthmarketscience/jackcess/impl/DatabaseImpl.java +++ b/src/java/com/healthmarketscience/jackcess/impl/DatabaseImpl.java @@ -828,7 +828,7 @@ public class DatabaseImpl implements Database try { _tableFinder = new DefaultTableFinder( - new CursorBuilder(_systemCatalog) + _systemCatalog.newCursor() .setIndexByColumnNames(CAT_COL_PARENT_ID, CAT_COL_NAME) .setColumnMatcher(CaseInsensitiveColumnMatcher.INSTANCE) .toIndexCursor()); @@ -837,7 +837,7 @@ public class DatabaseImpl implements Database _systemCatalog.getName()); // use table scan instead _tableFinder = new FallbackTableFinder( - new CursorBuilder(_systemCatalog) + _systemCatalog.newCursor() .setColumnMatcher(CaseInsensitiveColumnMatcher.INSTANCE) .toCursor()); } @@ -1072,7 +1072,8 @@ public class DatabaseImpl implements Database Map> queryRowMap = new HashMap>(); for(Row row : - CursorImpl.createCursor(_systemCatalog).iterable(SYSTEM_CATALOG_COLUMNS)) + CursorImpl.createCursor(_systemCatalog).newIterable().setColumnNames( + SYSTEM_CATALOG_COLUMNS)) { String name = (String) row.get(CAT_COL_NAME); if (name != null && TYPE_QUERY.equals(row.get(CAT_COL_TYPE))) { @@ -1405,7 +1406,7 @@ public class DatabaseImpl implements Database throws IOException { try { - return new CursorBuilder(table) + return table.newCursor() .setIndexByColumns(table.getColumn(colName)) .setSpecificEntry(colValue) .toCursor(); @@ -1814,7 +1815,7 @@ public class DatabaseImpl implements Database boolean systemTables) throws IOException { - for(Row row : getTableNamesCursor().iterable( + for(Row row : getTableNamesCursor().newIterable().setColumnNames( SYSTEM_CATALOG_TABLE_NAME_COLUMNS)) { String tableName = (String)row.get(CAT_COL_NAME); @@ -1867,7 +1868,7 @@ public class DatabaseImpl implements Database private void initIdCursor() throws IOException { if(_systemCatalogIdCursor == null) { - _systemCatalogIdCursor = new CursorBuilder(_systemCatalog) + _systemCatalogIdCursor = _systemCatalog.newCursor() .setIndexByColumnNames(CAT_COL_ID) .toIndexCursor(); } @@ -1916,7 +1917,7 @@ public class DatabaseImpl implements Database @Override protected Cursor getTableNamesCursor() throws IOException { - return new CursorBuilder(_systemCatalog) + return _systemCatalog.newCursor() .setIndex(_systemCatalogCursor.getIndex()) .setStartEntry(_tableParentId, IndexData.MIN_VALUE) .setEndEntry(_tableParentId, IndexData.MAX_VALUE) @@ -1972,7 +1973,7 @@ public class DatabaseImpl implements Database @Override public TableInfo lookupTable(String tableName) throws IOException { - for(Row row : _systemCatalogCursor.iterable( + for(Row row : _systemCatalogCursor.newIterable().setColumnNames( SYSTEM_CATALOG_TABLE_NAME_COLUMNS)) { Short type = (Short)row.get(CAT_COL_TYPE); diff --git a/src/java/com/healthmarketscience/jackcess/impl/IndexCursorImpl.java b/src/java/com/healthmarketscience/jackcess/impl/IndexCursorImpl.java index 3b76fa8..f53b980 100644 --- a/src/java/com/healthmarketscience/jackcess/impl/IndexCursorImpl.java +++ b/src/java/com/healthmarketscience/jackcess/impl/IndexCursorImpl.java @@ -33,6 +33,7 @@ import com.healthmarketscience.jackcess.RuntimeIOException; import com.healthmarketscience.jackcess.impl.TableImpl.RowState; import com.healthmarketscience.jackcess.util.CaseInsensitiveColumnMatcher; import com.healthmarketscience.jackcess.util.ColumnMatcher; +import com.healthmarketscience.jackcess.util.EntryIterableBuilder; import com.healthmarketscience.jackcess.util.SimpleColumnMatcher; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -130,7 +131,8 @@ public class IndexCursorImpl extends CursorImpl implements IndexCursor PositionImpl prevPos = _prevPos; boolean found = false; try { - found = findFirstRowByEntryImpl(toRowValues(entryValues), true); + found = findFirstRowByEntryImpl(toRowValues(entryValues), true, + _columnMatcher); return found; } finally { if(!found) { @@ -150,7 +152,8 @@ public class IndexCursorImpl extends CursorImpl implements IndexCursor PositionImpl prevPos = _prevPos; boolean found = false; try { - findFirstRowByEntryImpl(toRowValues(entryValues), false); + findFirstRowByEntryImpl(toRowValues(entryValues), false, + _columnMatcher); found = true; } finally { if(!found) { @@ -166,33 +169,17 @@ public class IndexCursorImpl extends CursorImpl implements IndexCursor public boolean currentRowMatchesEntry(Object... entryValues) throws IOException { - return currentRowMatchesEntryImpl(toRowValues(entryValues)); + return currentRowMatchesEntryImpl(toRowValues(entryValues), _columnMatcher); } - - public Iterator entryIterator(Object... entryValues) - { - return entryIterator((Collection)null, entryValues); - } - - public Iterator entryIterator( - Collection columnNames, Object... entryValues) - { - return new EntryIterator(columnNames, toRowValues(entryValues)); - } - - public Iterable entryIterable(Object... entryValues) - { - return entryIterable((Collection)null, entryValues); + + public EntryIterableBuilder newEntryIterable(Object... entryValues) { + return new EntryIterableBuilder(this, entryValues); } - - public Iterable entryIterable( - final Collection columnNames, final Object... entryValues) - { - return new Iterable() { - public Iterator iterator() { - return new EntryIterator(columnNames, toRowValues(entryValues)); - } - }; + + public Iterator entryIterator(EntryIterableBuilder iterBuilder) { + return new EntryIterator(iterBuilder.getColumnNames(), + toRowValues(iterBuilder.getEntryValues()), + iterBuilder.getColumnMatcher()); } @Override @@ -226,12 +213,15 @@ public class IndexCursorImpl extends CursorImpl implements IndexCursor } @Override - protected boolean findNextRowImpl(ColumnImpl columnPattern, Object valuePattern) + protected boolean findAnotherRowImpl( + ColumnImpl columnPattern, Object valuePattern, boolean moveForward, + ColumnMatcher columnMatcher) throws IOException { - if(!isBeforeFirst()) { + if(!isAtBeginning(moveForward)) { // use the default table scan for finding rows mid-cursor - return super.findNextRowImpl(columnPattern, valuePattern); + return super.findAnotherRowImpl(columnPattern, valuePattern, moveForward, + columnMatcher); } // searching for the first match @@ -240,7 +230,8 @@ public class IndexCursorImpl extends CursorImpl implements IndexCursor if(rowValues == null) { // bummer, use the default table scan - return super.findNextRowImpl(columnPattern, valuePattern); + return super.findAnotherRowImpl(columnPattern, valuePattern, moveForward, + columnMatcher); } // sweet, we can use our index @@ -250,7 +241,7 @@ public class IndexCursorImpl extends CursorImpl implements IndexCursor // either we found a row with the given value, or none exist in the // table - return currentRowMatches(columnPattern, valuePattern); + return currentRowMatchesImpl(columnPattern, valuePattern, columnMatcher); } /** @@ -263,7 +254,8 @@ public class IndexCursorImpl extends CursorImpl implements IndexCursor * {@code false} if no row was found */ protected boolean findFirstRowByEntryImpl(Object[] rowValues, - boolean requireMatch) + boolean requireMatch, + ColumnMatcher columnMatcher) throws IOException { if(!findPotentialRow(rowValues, requireMatch)) { @@ -273,16 +265,18 @@ public class IndexCursorImpl extends CursorImpl implements IndexCursor return true; } - return currentRowMatchesEntryImpl(rowValues); + return currentRowMatchesEntryImpl(rowValues, columnMatcher); } @Override - protected boolean findNextRowImpl(Map rowPattern) + protected boolean findAnotherRowImpl(Map rowPattern, + boolean moveForward, + ColumnMatcher columnMatcher) throws IOException { - if(!isBeforeFirst()) { + if(!isAtBeginning(moveForward)) { // use the default table scan for finding rows mid-cursor - return super.findNextRowImpl(rowPattern); + return super.findAnotherRowImpl(rowPattern, moveForward, columnMatcher); } // searching for the first match @@ -291,7 +285,7 @@ public class IndexCursorImpl extends CursorImpl implements IndexCursor if(rowValues == null) { // bummer, use the default table scan - return super.findNextRowImpl(rowPattern); + return super.findAnotherRowImpl(rowPattern, moveForward, columnMatcher); } // sweet, we can use our index @@ -321,25 +315,27 @@ public class IndexCursorImpl extends CursorImpl implements IndexCursor // longer match do { - if(!currentRowMatches(indexRowPattern)) { + if(!currentRowMatchesImpl(indexRowPattern, columnMatcher)) { // there are no more rows which could possibly match break; } // note, if rowPattern == indexRowPattern, no need to do an extra // comparison with the current row - if((rowPattern == indexRowPattern) || currentRowMatches(rowPattern)) { + if((rowPattern == indexRowPattern) || + currentRowMatchesImpl(rowPattern, columnMatcher)) { // found it! return true; } - } while(moveToNextRow()); + } while(moveToAnotherRow(moveForward)); // none of the potential rows matched return false; } - private boolean currentRowMatchesEntryImpl(Object[] rowValues) + private boolean currentRowMatchesEntryImpl(Object[] rowValues, + ColumnMatcher columnMatcher) throws IOException { if(_indexEntryPattern == null) { @@ -357,8 +353,7 @@ public class IndexCursorImpl extends CursorImpl implements IndexCursor String columnName = col.getName(); Object patValue = rowValues[col.getColumnIndex()]; Object rowValue = row.get(columnName); - if(!_columnMatcher.matches(getTable(), columnName, - patValue, rowValue)) { + if(!columnMatcher.matches(getTable(), columnName, patValue, rowValue)) { return false; } } @@ -490,12 +485,13 @@ public class IndexCursorImpl extends CursorImpl implements IndexCursor { private final Object[] _rowValues; - private EntryIterator(Collection columnNames, Object[] rowValues) + private EntryIterator(Collection columnNames, Object[] rowValues, + ColumnMatcher columnMatcher) { - super(columnNames); + super(columnNames, false, MOVE_FORWARD, columnMatcher); _rowValues = rowValues; try { - _hasNext = findFirstRowByEntryImpl(rowValues, true); + _hasNext = findFirstRowByEntryImpl(rowValues, true, _columnMatcher); _validRow = _hasNext; } catch(IOException e) { throw new RuntimeIOException(e); @@ -504,7 +500,8 @@ public class IndexCursorImpl extends CursorImpl implements IndexCursor @Override protected boolean findNext() throws IOException { - return (moveToNextRow() && currentRowMatchesEntryImpl(_rowValues)); + return (moveToNextRow() && + currentRowMatchesEntryImpl(_rowValues, _colMatcher)); } } diff --git a/src/java/com/healthmarketscience/jackcess/impl/TableImpl.java b/src/java/com/healthmarketscience/jackcess/impl/TableImpl.java index ae5a33a..6d26014 100644 --- a/src/java/com/healthmarketscience/jackcess/impl/TableImpl.java +++ b/src/java/com/healthmarketscience/jackcess/impl/TableImpl.java @@ -44,6 +44,7 @@ import java.util.Set; import com.healthmarketscience.jackcess.Column; import com.healthmarketscience.jackcess.ColumnBuilder; +import com.healthmarketscience.jackcess.CursorBuilder; import com.healthmarketscience.jackcess.DataType; import com.healthmarketscience.jackcess.Index; import com.healthmarketscience.jackcess.IndexBuilder; @@ -469,6 +470,10 @@ public class TableImpl implements Table } return _defaultCursor; } + + public CursorBuilder newCursor() { + return new CursorBuilder(this); + } public void reset() { getDefaultCursor().reset(); diff --git a/src/java/com/healthmarketscience/jackcess/impl/complex/ComplexColumnInfoImpl.java b/src/java/com/healthmarketscience/jackcess/impl/complex/ComplexColumnInfoImpl.java index 10d4ef5..9773496 100644 --- a/src/java/com/healthmarketscience/jackcess/impl/complex/ComplexColumnInfoImpl.java +++ b/src/java/com/healthmarketscience/jackcess/impl/complex/ComplexColumnInfoImpl.java @@ -142,12 +142,13 @@ public abstract class ComplexColumnInfoImpl throws IOException { if(_complexValIdCursor == null) { - _complexValIdCursor = new CursorBuilder(_flatTable) + _complexValIdCursor = _flatTable.newCursor() .setIndexByColumns(_complexValFkCol) .toIndexCursor(); } - return _complexValIdCursor.entryIterator(columnNames, complexValueFk); + return _complexValIdCursor.newEntryIterable(complexValueFk) + .setColumnNames(columnNames).iterator(); } public List getRawValues(int complexValueFk, diff --git a/src/java/com/healthmarketscience/jackcess/util/Joiner.java b/src/java/com/healthmarketscience/jackcess/util/Joiner.java index 91794db..02aa051 100644 --- a/src/java/com/healthmarketscience/jackcess/util/Joiner.java +++ b/src/java/com/healthmarketscience/jackcess/util/Joiner.java @@ -195,7 +195,8 @@ public class Joiner Collection columnNames) { toEntryValues(fromRow); - return _toCursor.entryIterator(columnNames, _entryValues); + return _toCursor.newEntryIterable(_entryValues) + .setColumnNames(columnNames).iterator(); } /** @@ -211,7 +212,8 @@ public class Joiner Collection columnNames) { toEntryValues(fromRow); - return _toCursor.entryIterator(columnNames, _entryValues); + return _toCursor.newEntryIterable(_entryValues) + .setColumnNames(columnNames).iterator(); } /** diff --git a/test/src/java/com/healthmarketscience/jackcess/CursorTest.java b/test/src/java/com/healthmarketscience/jackcess/CursorTest.java index 49a5fc9..0a5e551 100644 --- a/test/src/java/com/healthmarketscience/jackcess/CursorTest.java +++ b/test/src/java/com/healthmarketscience/jackcess/CursorTest.java @@ -176,7 +176,7 @@ public class CursorTest extends TestCase { int type) throws Exception { - return new CursorBuilder(table) + return table.newCursor() .setIndex(idx) .setStartEntry(3 - type) .setStartRowInclusive(type == 0) @@ -394,7 +394,7 @@ public class CursorTest extends TestCase { List> foundRows = new ArrayList>(); - for(Map row : cursor.reverseIterable()) { + for(Map row : cursor.newIterable().reverse()) { foundRows.add(row); } assertEquals(expectedRows, foundRows); @@ -766,9 +766,8 @@ public class CursorTest extends TestCase { private static void doTestFindAll(Table table, Cursor cursor, Index index) throws Exception { - Column valCol = table.getColumn("value"); List> rows = RowFilterTest.toList( - cursor.columnMatchIterable(valCol, "data2")); + cursor.newIterable().setMatchPattern("value", "data2")); List> expectedRows = null; @@ -801,8 +800,9 @@ public class CursorTest extends TestCase { } assertEquals(expectedRows, rows); + Column valCol = table.getColumn("value"); rows = RowFilterTest.toList( - cursor.columnMatchIterable(valCol, "data4")); + cursor.newIterable().setMatchPattern(valCol, "data4")); if(index == null) { expectedRows = @@ -822,12 +822,13 @@ public class CursorTest extends TestCase { assertEquals(expectedRows, rows); rows = RowFilterTest.toList( - cursor.columnMatchIterable(valCol, "data9")); + cursor.newIterable().setMatchPattern(valCol, "data9")); assertTrue(rows.isEmpty()); rows = RowFilterTest.toList( - cursor.rowMatchIterable(Collections.singletonMap("id", 8))); + cursor.newIterable().setMatchPattern( + Collections.singletonMap("id", 8))); expectedRows = createExpectedTable( @@ -848,14 +849,14 @@ public class CursorTest extends TestCase { expectedRows = tmpRows; assertFalse(expectedRows.isEmpty()); - rows = RowFilterTest.toList(cursor.rowMatchIterable(row)); + rows = RowFilterTest.toList(cursor.newIterable().setMatchPattern(row)); assertEquals(expectedRows, rows); } rows = RowFilterTest.toList( - cursor.rowMatchIterable(createExpectedRow( - "id", 8, "value", "data13"))); + cursor.newIterable().addMatchPattern("id", 8) + .addMatchPattern("value", "data13")); assertTrue(rows.isEmpty()); } @@ -997,6 +998,28 @@ public class CursorTest extends TestCase { "value", "data" + 4), cursor.getCurrentRow()); } + + assertEquals(Arrays.asList(createExpectedRow("id", 4, + "value", "data" + 4)), + RowFilterTest.toList( + cursor.newIterable() + .setMatchPattern("value", "data4") + .setColumnMatcher(SimpleColumnMatcher.INSTANCE))); + + assertEquals(Arrays.asList(createExpectedRow("id", 3, + "value", "data" + 3)), + RowFilterTest.toList( + cursor.newIterable() + .setMatchPattern("value", "DaTa3") + .setColumnMatcher(CaseInsensitiveColumnMatcher.INSTANCE))); + + assertEquals(Arrays.asList(createExpectedRow("id", 2, + "value", "data" + 2)), + RowFilterTest.toList( + cursor.newIterable() + .addMatchPattern("value", "DaTa2") + .addMatchPattern("id", 2) + .setColumnMatcher(CaseInsensitiveColumnMatcher.INSTANCE))); } public void testIndexCursor() throws Exception @@ -1036,15 +1059,16 @@ public class CursorTest extends TestCase { IndexCursor cursor = CursorBuilder.createCursor(t1, idx); List expectedData = new ArrayList(); - for(Map row : cursor.entryIterable( - Arrays.asList("data"), 1)) { + for(Map row : cursor.newEntryIterable(1) + .addColumnNames("data")) { expectedData.add((String)row.get("data")); } assertEquals(Arrays.asList("baz11", "baz11-2"), expectedData); expectedData = new ArrayList(); - for(Iterator> iter = cursor.entryIterator(1); + for(Iterator> iter = + cursor.newEntryIterable(1).iterator(); iter.hasNext(); ) { expectedData.add((String)iter.next().get("data")); iter.remove(); @@ -1068,8 +1092,8 @@ public class CursorTest extends TestCase { assertEquals(Arrays.asList("baz11", "baz11-2"), expectedData); expectedData = new ArrayList(); - for(Map row : cursor.entryIterable( - Arrays.asList("data"), 1)) { + for(Map row : cursor.newEntryIterable(1) + .addColumnNames("data")) { expectedData.add((String)row.get("data")); } @@ -1088,7 +1112,7 @@ public class CursorTest extends TestCase { Cursor cursor = CursorBuilder.createCursor(t1); List expectedData = new ArrayList(); - for(Map row : cursor.iterable( + for(Map row : cursor.newIterable().setColumnNames( Arrays.asList("otherfk1", "data"))) { if(row.get("otherfk1").equals(1)) { expectedData.add((String)row.get("data")); @@ -1125,7 +1149,7 @@ public class CursorTest extends TestCase { assertEquals(Arrays.asList("baz11", "baz11-2"), expectedData); expectedData = new ArrayList(); - for(Map row : cursor.iterable( + for(Map row : cursor.newIterable().setColumnNames( Arrays.asList("otherfk1", "data"))) { if(row.get("otherfk1").equals(1)) { expectedData.add((String)row.get("data")); diff --git a/test/src/java/com/healthmarketscience/jackcess/IndexTest.java b/test/src/java/com/healthmarketscience/jackcess/IndexTest.java index 8eb7cd1..8c6284a 100644 --- a/test/src/java/com/healthmarketscience/jackcess/IndexTest.java +++ b/test/src/java/com/healthmarketscience/jackcess/IndexTest.java @@ -452,7 +452,7 @@ public class IndexTest extends TestCase { t.addRow(1, "row1"); t.addRow(3, "row3"); - Cursor c = new CursorBuilder(t) + Cursor c = t.newCursor() .setIndexByName(IndexBuilder.PRIMARY_KEY_NAME).toCursor(); for(int i = 1; i <= 3; ++i) { diff --git a/test/src/java/com/healthmarketscience/jackcess/impl/CodecHandlerTest.java b/test/src/java/com/healthmarketscience/jackcess/impl/CodecHandlerTest.java index 75d869d..e3cf499 100644 --- a/test/src/java/com/healthmarketscience/jackcess/impl/CodecHandlerTest.java +++ b/test/src/java/com/healthmarketscience/jackcess/impl/CodecHandlerTest.java @@ -129,13 +129,13 @@ public class CodecHandlerTest extends TestCase t2.addRow(null, "rowdata-" + i + DatabaseTest.createString(100)); } - Cursor c1 = new CursorBuilder(t1).setIndex(t1.getPrimaryKeyIndex()) + Cursor c1 = t1.newCursor().setIndex(t1.getPrimaryKeyIndex()) .toCursor(); - Cursor c2 = new CursorBuilder(t2).setIndex(t2.getPrimaryKeyIndex()) + Cursor c2 = t2.newCursor().setIndex(t2.getPrimaryKeyIndex()) .toCursor(); Iterator> i1 = c1.iterator(); - Iterator> i2 = c2.reverseIterable().iterator(); + Iterator> i2 = c2.newIterable().reverse().iterator(); int t1rows = 0; int t2rows = 0; -- 2.39.5