summaryrefslogtreecommitdiffstats
path: root/src/java
diff options
context:
space:
mode:
authorJames Ahlborn <jtahlborn@yahoo.com>2007-11-28 22:07:06 +0000
committerJames Ahlborn <jtahlborn@yahoo.com>2007-11-28 22:07:06 +0000
commitc3ca22f650ee57d207ce387611be86a83560c5fb (patch)
tree5cfc73849be413d6f7ea5f91634effc88b850186 /src/java
parenta926006cdd0ba50273f302bdd4ec69c6028d677c (diff)
downloadjackcess-c3ca22f650ee57d207ce387611be86a83560c5fb.tar.gz
jackcess-c3ca22f650ee57d207ce387611be86a83560c5fb.zip
implement and test index based cursor
git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/trunk@188 f203690c-595d-4dc9-a70b-905162fa7fd2
Diffstat (limited to 'src/java')
-rw-r--r--src/java/com/healthmarketscience/jackcess/Cursor.java134
-rw-r--r--src/java/com/healthmarketscience/jackcess/Index.java188
-rw-r--r--src/java/com/healthmarketscience/jackcess/Table.java6
-rw-r--r--src/java/com/healthmarketscience/jackcess/UsageMap.java65
4 files changed, 289 insertions, 104 deletions
diff --git a/src/java/com/healthmarketscience/jackcess/Cursor.java b/src/java/com/healthmarketscience/jackcess/Cursor.java
index fc0a25c..1838be7 100644
--- a/src/java/com/healthmarketscience/jackcess/Cursor.java
+++ b/src/java/com/healthmarketscience/jackcess/Cursor.java
@@ -5,6 +5,7 @@ package com.healthmarketscience.jackcess;
import java.io.IOException;
import java.util.Collection;
import java.util.Iterator;
+import java.util.LinkedHashMap;
import java.util.Map;
import java.util.NoSuchElementException;
@@ -411,12 +412,22 @@ public abstract class Cursor implements Iterable<Map<String, Object>>
}
/**
- * Restores the current position to the previous position.
+ * Restores a current position for the cursor (current position becomes
+ * previous position).
*/
protected void restorePosition(Position curPos)
throws IOException
{
- if(!curPos.equals(_curPos)) {
+ restorePosition(curPos, _curPos);
+ }
+
+ /**
+ * Restores a current and previous position for the cursor.
+ */
+ protected void restorePosition(Position curPos, Position prevPos)
+ throws IOException
+ {
+ if(!curPos.equals(_curPos) || !prevPos.equals(_prevPos)) {
// make the current position previous, and the new position current
_prevPos = _curPos;
_curPos = curPos;
@@ -474,6 +485,7 @@ public abstract class Cursor implements Iterable<Map<String, Object>>
throws IOException
{
Position curPos = _curPos;
+ Position prevPos = _prevPos;
boolean found = false;
try {
found = findRowImpl(columnPattern, valuePattern);
@@ -481,7 +493,7 @@ public abstract class Cursor implements Iterable<Map<String, Object>>
} finally {
if(!found) {
try {
- restorePosition(curPos);
+ restorePosition(curPos, prevPos);
} catch(IOException e) {
LOG.error("Failed restoring position", e);
}
@@ -504,6 +516,7 @@ public abstract class Cursor implements Iterable<Map<String, Object>>
throws IOException
{
Position curPos = _curPos;
+ Position prevPos = _prevPos;
boolean found = false;
try {
found = findRowImpl(rowPattern);
@@ -511,7 +524,7 @@ public abstract class Cursor implements Iterable<Map<String, Object>>
} finally {
if(!found) {
try {
- restorePosition(curPos);
+ restorePosition(curPos, prevPos);
} catch(IOException e) {
LOG.error("Failed restoring position", e);
}
@@ -627,7 +640,6 @@ public abstract class Cursor implements Iterable<Map<String, Object>>
/**
* Returns the given column from the current row.
*/
- @SuppressWarnings("foo")
public Object getCurrentRowValue(Column column)
throws IOException
{
@@ -754,21 +766,19 @@ public abstract class Cursor implements Iterable<Map<String, Object>>
}
@Override
- protected void restorePosition(Position curPos)
+ protected void restorePosition(Position curPos, Position prevPos)
throws IOException
{
- if(!(curPos instanceof ScanPosition)) {
+ if(!(curPos instanceof ScanPosition) ||
+ !(prevPos instanceof ScanPosition)) {
throw new IllegalArgumentException(
- "New position must be a scan position");
+ "Restored positions must be scan positions");
}
- super.restorePosition(curPos);
- _ownedPagesCursor.setCurrentPage(curPos.getRowId().getPageNumber());
+ super.restorePosition(curPos, prevPos);
+ _ownedPagesCursor.restorePosition(curPos.getRowId().getPageNumber(),
+ prevPos.getRowId().getPageNumber());
}
- /**
- * Position the buffer at the next row in the table
- * @return a ByteBuffer narrowed to the next row, or null if none
- */
@Override
protected Position findAnotherPosition(RowState rowState, Position curPos,
boolean moveForward)
@@ -920,21 +930,97 @@ public abstract class Cursor implements Iterable<Map<String, Object>>
}
@Override
- protected void restorePosition(Position curPos)
+ protected void restorePosition(Position curPos, Position prevPos)
throws IOException
{
- if(!(curPos instanceof IndexPosition)) {
+ if(!(curPos instanceof IndexPosition) ||
+ !(prevPos instanceof IndexPosition)) {
throw new IllegalArgumentException(
- "New position must be an index position");
+ "Restored positions must be index positions");
+ }
+ super.restorePosition(curPos, prevPos);
+ _entryCursor.restorePosition(((IndexPosition)curPos).getEntry(),
+ ((IndexPosition)prevPos).getEntry());
+ }
+
+ @Override
+ protected boolean findRowImpl(Column columnPattern, Object valuePattern)
+ throws IOException
+ {
+ Object[] rowValues = _entryCursor.getIndex().constructIndexRow(
+ columnPattern.getName(), valuePattern);
+
+ if(rowValues == null) {
+ // bummer, use the default table scan
+ return super.findRowImpl(columnPattern, valuePattern);
+ }
+
+ // sweet, we can use our index
+ _entryCursor.beforeEntry(rowValues);
+ Index.Entry startEntry = _entryCursor.getNextEntry();
+ if(!startEntry.getRowId().isValid()) {
+ // at end of index, no potential matches
+ return false;
+ }
+
+ // either we found a row with the given value, or none exist in the
+ // table
+ restorePosition(new IndexPosition(startEntry));
+ return ObjectUtils.equals(getCurrentRowValue(columnPattern),
+ valuePattern);
+ }
+
+ @Override
+ protected boolean findRowImpl(Map<String,Object> rowPattern)
+ throws IOException
+ {
+ Index index = _entryCursor.getIndex();
+ Object[] rowValues = index.constructIndexRow(rowPattern);
+
+ if(rowValues == null) {
+ // bummer, use the default table scan
+ return super.findRowImpl(rowPattern);
+ }
+
+ // sweet, we can use our index
+ _entryCursor.beforeEntry(rowValues);
+ Index.Entry startEntry = _entryCursor.getNextEntry();
+ if(!startEntry.getRowId().isValid()) {
+ // at end of index, no potential matches
+ return false;
+ }
+ restorePosition(new IndexPosition(startEntry));
+
+ Map<String,Object> indexRowPattern =
+ new LinkedHashMap<String,Object>();
+ for(Column idxCol : _entryCursor.getIndex().getColumns()) {
+ indexRowPattern.put(idxCol.getName(),
+ rowValues[idxCol.getColumnNumber()]);
}
- super.restorePosition(curPos);
- _entryCursor.setCurrentEntry(((IndexPosition)curPos).getEntry());
+
+ // there may be multiple columns which fit the pattern subset used by
+ // the index, so we need to keep checking until we no longer our index
+ // values no longer match
+ do {
+
+ if(!ObjectUtils.equals(getCurrentRow(indexRowPattern.keySet()),
+ indexRowPattern)) {
+ // there are no more rows which could possibly match
+ break;
+ }
+
+ if(ObjectUtils.equals(getCurrentRow(rowPattern.keySet()),
+ rowPattern)) {
+ // found it!
+ return true;
+ }
+
+ } while(moveToNextRow());
+
+ // none of the potential rows matched
+ return false;
}
- /**
- * Position the buffer at the next row in the table
- * @return a ByteBuffer narrowed to the next row, or null if none
- */
@Override
protected Position findAnotherPosition(RowState rowState, Position curPos,
boolean moveForward)
@@ -1004,7 +1090,7 @@ public abstract class Cursor implements Iterable<Map<String, Object>>
@Override
public final boolean equals(Object o) {
return((this == o) ||
- ((getClass() == o.getClass()) && equalsImpl(o)));
+ ((o != null) && (getClass() == o.getClass()) && equalsImpl(o)));
}
/**
diff --git a/src/java/com/healthmarketscience/jackcess/Index.java b/src/java/com/healthmarketscience/jackcess/Index.java
index 6793a7b..11d6330 100644
--- a/src/java/com/healthmarketscience/jackcess/Index.java
+++ b/src/java/com/healthmarketscience/jackcess/Index.java
@@ -659,7 +659,60 @@ public class Index implements Comparable<Index> {
return removed;
}
+
+ /**
+ * Constructs an array of values appropriate for this index from the given
+ * column values, expected to match the columns for this index.
+ * @return the appropriate sparse array of data
+ * @throws IllegalArgumentException if the wrong number of values are
+ * provided
+ */
+ public Object[] constructIndexRow(Object... values)
+ {
+ if(values.length != _columns.size()) {
+ throw new IllegalArgumentException(
+ "Wrong number of column values given " + values.length +
+ ", expected " + _columns.size());
+ }
+ int valIdx = 0;
+ Object[] idxRow = new Object[getTable().getMaxColumnCount()];
+ for(Column col : _columns.keySet()) {
+ idxRow[col.getColumnNumber()] = values[valIdx++];
+ }
+ return idxRow;
+ }
+
+ /**
+ * Constructs an array of values appropriate for this index from the given
+ * column value.
+ * @return the appropriate sparse array of data or {@code null} if not all
+ * columns for this index were provided
+ */
+ public Object[] constructIndexRow(String colName, Object value)
+ {
+ return constructIndexRow(Collections.singletonMap(colName, value));
+ }
+ /**
+ * Constructs an array of values appropriate for this index from the given
+ * column values.
+ * @return the appropriate sparse array of data or {@code null} if not all
+ * columns for this index were provided
+ */
+ public Object[] constructIndexRow(Map<String,Object> row)
+ {
+ for(Column col : _columns.keySet()) {
+ if(!row.containsKey(col.getName())) {
+ return null;
+ }
+ }
+
+ Object[] idxRow = new Object[getTable().getMaxColumnCount()];
+ for(Column col : _columns.keySet()) {
+ idxRow[col.getColumnNumber()] = row.get(col.getName());
+ }
+ return idxRow;
+ }
@Override
public String toString() {
@@ -898,7 +951,7 @@ public class Index implements Comparable<Index> {
}
public boolean isValid() {
- return _rowId.isValid();
+ return(_entryColumns != null);
}
/**
@@ -934,7 +987,7 @@ public class Index implements Comparable<Index> {
@Override
public boolean equals(Object o) {
return((this == o) ||
- ((getClass() == o.getClass()) &&
+ ((o != null) && (getClass() == o.getClass()) &&
(compareTo((Entry)o) == 0)));
}
@@ -943,9 +996,11 @@ public class Index implements Comparable<Index> {
return 0;
}
- // note, if the one or both of the entries has no entryColumns, it is a
- // "special" entry which should be compared on the rowId alone
- if((_entryColumns != null) && (other.getEntryColumns() != null)) {
+ // note, if the one or both of the entries are not valid, they are
+ // "special" entries, which are handled below
+ if(isValid() && other.isValid()) {
+
+ // comparing two normal entries
Iterator<EntryColumn> myIter = _entryColumns.iterator();
Iterator<EntryColumn> otherIter = other.getEntryColumns().iterator();
while (myIter.hasNext()) {
@@ -960,8 +1015,34 @@ public class Index implements Comparable<Index> {
return i;
}
}
+
+ // if entry columns are equal, sort by rowIds
+ return _rowId.compareTo(other.getRowId());
+ }
+
+ // this is the odd case where mixed entries are being compared. if both
+ // entries are invalid or the rowIds are not equal, then use the rowId
+ // comparison.
+ int rowCmp = _rowId.compareTo(other.getRowId());
+ if((isValid() == other.isValid()) || (rowCmp != 0)) {
+ return rowCmp;
}
- return _rowId.compareTo(other.getRowId());
+
+ // at this point, the rowId's are equal, but the validity is not. this
+ // will happen when a "special" entry is compared to something created
+ // by EntryCursor.afterEntry or EntryCursor.beforeEntry. in this case,
+ // the FIRST_ENTRY is always least and the LAST_ENTRY is always
+ // greatest.
+ int cmp = 0;
+ Entry invalid = null;
+ if(!isValid()) {
+ cmp = -1;
+ invalid = this;
+ } else {
+ cmp = 1;
+ invalid = other;
+ }
+ return (cmp * (invalid.equals(FIRST_ENTRY.getRowId()) ? 1 : -1));
}
@@ -1016,6 +1097,13 @@ public class Index implements Comparable<Index> {
}
}
+ @Override
+ public boolean equals(Object o) {
+ return((this == o) ||
+ ((o != null) && (o != null) && (getClass() == o.getClass()) &&
+ (compareTo((EntryColumn)o) == 0)));
+ }
+
/**
* Write this non-null entry column to a buffer
*/
@@ -1335,6 +1423,10 @@ public class Index implements Comparable<Index> {
reset();
}
+ public Index getIndex() {
+ return Index.this;
+ }
+
/**
* Returns the DirHandler for the given direction
*/
@@ -1376,7 +1468,6 @@ public class Index implements Comparable<Index> {
public void beforeEntry(Object[] row)
throws IOException
{
- // FIXME, change how row is given?
restorePosition(new Entry(row, RowId.FIRST_ROW_ID, _columns));
}
@@ -1387,23 +1478,8 @@ public class Index implements Comparable<Index> {
public void afterEntry(Object[] row)
throws IOException
{
- // FIXME, change how row is given?
restorePosition(new Entry(row, RowId.LAST_ROW_ID, _columns));
}
-
- /**
- * Returns the current entry.
- */
- public Entry getCurrentEntry() {
- return _curPos.getEntry();
- }
-
- /**
- * Resets the cursor to the given entry.
- */
- public void setCurrentEntry(Entry entry) {
- restorePosition(entry);
- }
/**
* @return valid entry if there was entry, {@link Index#LAST_ENTRY}
@@ -1422,12 +1498,22 @@ public class Index implements Comparable<Index> {
}
/**
- * Restores a previous position for the cursor.
+ * Restores a current position for the cursor (current position becomes
+ * previous position).
*/
- private void restorePosition(Entry curEntry)
+ private void restorePosition(Entry curEntry) {
+ restorePosition(curEntry, _curPos.getEntry());
+ }
+
+ /**
+ * Restores a current and previous position for the cursor.
+ */
+ protected void restorePosition(Entry curEntry, Entry prevEntry)
{
- if(!curEntry.equals(_curPos.getEntry())) {
- _prevPos = updatePosition(_curPos.getEntry());
+ if(!curEntry.equals(_curPos.getEntry()) ||
+ !prevEntry.equals(_prevPos.getEntry()))
+ {
+ _prevPos = updatePosition(prevEntry);
_curPos = updatePosition(curEntry);
_lastModCount = Index.this._modCount;
} else {
@@ -1451,19 +1537,19 @@ public class Index implements Comparable<Index> {
*/
private Position updatePosition(Entry entry) {
int curIdx = FIRST_ENTRY_IDX;
- boolean deleted = false;
+ boolean between = false;
RowId rowId = entry.getRowId();
- if(rowId.isValid()) {
+ if(entry.isValid()) {
// find the new position for this entry
int idx = findEntry(entry);
if(idx >= 0) {
curIdx = idx;
} else {
- // given entry was deleted. our current position is now really
- // between two indexes, but we cannot support that as an integer
- // value so we set a flag instead
+ // given entry was not found exactly. our current position is now
+ // really between two indexes, but we cannot support that as an
+ // integer value so we set a flag instead
curIdx = missingIndexToInsertionPoint(idx);
- deleted = true;
+ between = true;
}
} else if(rowId.equals(RowId.FIRST_ROW_ID)) {
curIdx = FIRST_ENTRY_IDX;
@@ -1472,7 +1558,7 @@ public class Index implements Comparable<Index> {
} else {
throw new IllegalArgumentException("Invalid entry given: " + entry);
}
- return new Position(curIdx, entry, deleted);
+ return new Position(curIdx, entry, between);
}
/**
@@ -1494,7 +1580,7 @@ public class Index implements Comparable<Index> {
_prevPos = _curPos;
_curPos = handler.getAnotherPosition(_curPos.getIndex(),
- _curPos.isDeleted());
+ _curPos.isBetween());
return _curPos.getEntry();
}
@@ -1509,7 +1595,7 @@ public class Index implements Comparable<Index> {
* logic from value storage.
*/
private abstract class DirHandler {
- public abstract Position getAnotherPosition(int curIdx, boolean deleted);
+ public abstract Position getAnotherPosition(int curIdx, boolean between);
public abstract Position getBeginningPosition();
public abstract Position getEndPosition();
protected final Position newPosition(int curIdx) {
@@ -1530,10 +1616,10 @@ public class Index implements Comparable<Index> {
*/
private final class ForwardDirHandler extends DirHandler {
@Override
- public Position getAnotherPosition(int curIdx, boolean deleted) {
+ public Position getAnotherPosition(int curIdx, boolean between) {
// note, curIdx does not need to be advanced if it was pointing at a
- // deleted entry
- if(!deleted) {
+ // between position
+ if(!between) {
curIdx = ((curIdx == getBeginningPosition().getIndex()) ?
0 : (curIdx + 1));
}
@@ -1554,10 +1640,10 @@ public class Index implements Comparable<Index> {
*/
private final class ReverseDirHandler extends DirHandler {
@Override
- public Position getAnotherPosition(int curIdx, boolean deleted) {
- // note, we ignore the deleted flag here because the index will be
- // pointing at the correct next index in either the deleted or
- // non-deleted case
+ public Position getAnotherPosition(int curIdx, boolean between) {
+ // note, we ignore the between flag here because the index will be
+ // pointing at the correct next index in either the between or
+ // non-between case
curIdx = ((curIdx == getBeginningPosition().getIndex()) ?
(_entries.size() - 1) : (curIdx - 1));
return newReversePosition(curIdx);
@@ -1583,16 +1669,16 @@ public class Index implements Comparable<Index> {
private final Entry _entry;
/** {@code true} if this entry does not currently exist in the entry list,
{@code false} otherwise */
- private final boolean _deleted;
+ private final boolean _between;
private Position(int idx, Entry entry) {
this(idx, entry, false);
}
- private Position(int idx, Entry entry, boolean deleted) {
+ private Position(int idx, Entry entry, boolean between) {
_idx = idx;
_entry = entry;
- _deleted = deleted;
+ _between = between;
}
public int getIndex() {
@@ -1603,23 +1689,23 @@ public class Index implements Comparable<Index> {
return _entry;
}
- public boolean isDeleted() {
- return _deleted;
+ public boolean isBetween() {
+ return _between;
}
@Override
public boolean equals(Object o) {
return((this == o) ||
- ((getClass() == o.getClass()) &&
+ ((o != null) && (getClass() == o.getClass()) &&
(_idx == ((Position)o)._idx) &&
_entry.equals(((Position)o)._entry) &&
- (_deleted == ((Position)o)._deleted)));
+ (_between == ((Position)o)._between)));
}
@Override
public String toString() {
- return "Idx = " + _idx + ", Entry = " + _entry + ", Deleted = " +
- _deleted;
+ return "Idx = " + _idx + ", Entry = " + _entry + ", Between = " +
+ _between;
}
}
diff --git a/src/java/com/healthmarketscience/jackcess/Table.java b/src/java/com/healthmarketscience/jackcess/Table.java
index d5eacd2..08c7d4a 100644
--- a/src/java/com/healthmarketscience/jackcess/Table.java
+++ b/src/java/com/healthmarketscience/jackcess/Table.java
@@ -171,6 +171,10 @@ public class Table
return _name;
}
+ public int getMaxColumnCount() {
+ return _maxColumnCount;
+ }
+
public Database getDatabase() {
return _database;
}
@@ -1487,7 +1491,7 @@ public class Table
private RowState(boolean hardRowBuffer) {
_headerRowBufferH = TempPageHolder.newHolder(hardRowBuffer);
- _rowValues = new Object[Table.this._maxColumnCount];
+ _rowValues = new Object[Table.this.getMaxColumnCount()];
_lastModCount = Table.this._modCount;
}
diff --git a/src/java/com/healthmarketscience/jackcess/UsageMap.java b/src/java/com/healthmarketscience/jackcess/UsageMap.java
index 8833edb..4b1c76d 100644
--- a/src/java/com/healthmarketscience/jackcess/UsageMap.java
+++ b/src/java/com/healthmarketscience/jackcess/UsageMap.java
@@ -723,6 +723,10 @@ public class UsageMap
reset();
}
+ public UsageMap getUsageMap() {
+ return UsageMap.this;
+ }
+
/**
* Returns the DirHandler for the given direction
*/
@@ -739,25 +743,6 @@ public class UsageMap
}
/**
- * Returns the current page number.
- */
- public int getCurrentPage() {
- return _curPageNumber;
- }
-
- /**
- * Resets the cursor to the given page number.
- */
- public void setCurrentPage(int curPageNumber) {
- if(curPageNumber < UsageMap.this.getFirstPageNumber()) {
- curPageNumber = RowId.FIRST_PAGE_NUMBER;
- } else if(curPageNumber > UsageMap.this.getLastPageNumber()) {
- curPageNumber = RowId.LAST_PAGE_NUMBER;
- }
- restorePosition(curPageNumber);
- }
-
- /**
* @return valid page number if there was another page to read,
* {@link RowId#LAST_PAGE_NUMBER} otherwise
*/
@@ -829,25 +814,49 @@ public class UsageMap
}
/**
- * Restores a previous position for the cursor.
+ * Restores a current position for the cursor (current position becomes
+ * previous position).
*/
- private void restorePosition(int curPageNumber) {
- if(curPageNumber != _curPageNumber) {
- _prevPageNumber = _curPageNumber;
- _curPageNumber = curPageNumber;
+ private void restorePosition(int curPageNumber)
+ {
+ restorePosition(curPageNumber, _curPageNumber);
+ }
+
+ /**
+ * Restores a current and previous position for the cursor.
+ */
+ protected void restorePosition(int curPageNumber, int prevPageNumber) {
+ if((curPageNumber != _curPageNumber) ||
+ (prevPageNumber != _prevPageNumber))
+ {
+ _prevPageNumber = updatePosition(prevPageNumber);
+ _curPageNumber = updatePosition(curPageNumber);
+ _lastModCount = UsageMap.this._modCount;
+ } else {
+ checkForModification();
}
- _lastModCount = UsageMap.this._modCount;
}
/**
* Checks the usage map for modifications an updates state accordingly.
*/
private void checkForModification() {
- // since page numbers are not affected by modifications, we don't need
- // to adjust anything
- _lastModCount = UsageMap.this._modCount;
+ if(!isUpToDate()) {
+ _prevPageNumber = updatePosition(_prevPageNumber);
+ _curPageNumber = updatePosition(_curPageNumber);
+ _lastModCount = UsageMap.this._modCount;
+ }
}
+ private int updatePosition(int pageNumber) {
+ if(pageNumber < UsageMap.this.getFirstPageNumber()) {
+ pageNumber = RowId.FIRST_PAGE_NUMBER;
+ } else if(pageNumber > UsageMap.this.getLastPageNumber()) {
+ pageNumber = RowId.LAST_PAGE_NUMBER;
+ }
+ return pageNumber;
+ }
+
@Override
public String toString() {
return getClass().getSimpleName() + " CurPosition " + _curPageNumber +