aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJames Ahlborn <jtahlborn@yahoo.com>2006-09-21 03:28:41 +0000
committerJames Ahlborn <jtahlborn@yahoo.com>2006-09-21 03:28:41 +0000
commitaa7e5d19856b7fd9cb39197211a78c910bf19b53 (patch)
treec27d4ce949f4d4aaa83fc36c2edbfc5f4aebb875
parent7e61560f9a2c57fed4410e789bf144c54527f228 (diff)
downloadjackcess-aa7e5d19856b7fd9cb39197211a78c910bf19b53.tar.gz
jackcess-aa7e5d19856b7fd9cb39197211a78c910bf19b53.zip
further refactor or row parsing logic
git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/trunk@116 f203690c-595d-4dc9-a70b-905162fa7fd2
-rw-r--r--src/java/com/healthmarketscience/jackcess/PageChannel.java12
-rw-r--r--src/java/com/healthmarketscience/jackcess/Table.java319
2 files changed, 164 insertions, 167 deletions
diff --git a/src/java/com/healthmarketscience/jackcess/PageChannel.java b/src/java/com/healthmarketscience/jackcess/PageChannel.java
index 27f204b..0acaad8 100644
--- a/src/java/com/healthmarketscience/jackcess/PageChannel.java
+++ b/src/java/com/healthmarketscience/jackcess/PageChannel.java
@@ -74,7 +74,12 @@ public class PageChannel implements Channel {
* @return True if the page was successfully read into the buffer, false if
* that page doesn't exist.
*/
- public boolean readPage(ByteBuffer buffer, int pageNumber) throws IOException {
+ public boolean readPage(ByteBuffer buffer, int pageNumber)
+ throws IOException
+ {
+ if(pageNumber == INVALID_PAGE_NUMBER) {
+ throw new IllegalStateException("invalid page number");
+ }
if (LOG.isDebugEnabled()) {
LOG.debug("Reading in page " + Integer.toHexString(pageNumber));
}
@@ -136,7 +141,7 @@ public class PageChannel implements Channel {
/**
* @return a duplicate of the current buffer narrowed to the given position
- * and limit.
+ * and limit. mark will be set at the current position.
*/
public static ByteBuffer narrowBuffer(ByteBuffer buffer, int position,
int limit)
@@ -145,7 +150,8 @@ public class PageChannel implements Channel {
.order(buffer.order())
.clear()
.limit(limit)
- .position(position);
+ .position(position)
+ .mark();
}
}
diff --git a/src/java/com/healthmarketscience/jackcess/Table.java b/src/java/com/healthmarketscience/jackcess/Table.java
index 3b0824b..25008d8 100644
--- a/src/java/com/healthmarketscience/jackcess/Table.java
+++ b/src/java/com/healthmarketscience/jackcess/Table.java
@@ -201,13 +201,6 @@ public class Table
}
/**
- * @return The next row in this table (Column name -> Column value)
- */
- public Map<String, Object> getNextRow() throws IOException {
- return getNextRow(null);
- }
-
- /**
* Delete the current row (retrieved by a call to {@link #getNextRow}).
*/
public void deleteCurrentRow() throws IOException {
@@ -215,6 +208,8 @@ public class Table
throw new IllegalStateException("Must call getNextRow first");
}
+ // FIXME, want to make this static, but writeDataPage is not static, also, this may screw up other rowstates...
+
// delete flag always gets set in the "root" page (even if overflow row)
ByteBuffer rowBuffer = _rowState.getPage(_pageChannel);
int index = getRowStartOffset(_currentRowInPage, _format);
@@ -225,6 +220,13 @@ public class Table
}
/**
+ * @return The next row in this table (Column name -> Column value)
+ */
+ public Map<String, Object> getNextRow() throws IOException {
+ return getNextRow(null);
+ }
+
+ /**
* @param columnNames Only column names in this collection will be returned
* @return The next row in this table (Column name -> Column value)
*/
@@ -236,93 +238,153 @@ public class Table
if (rowBuffer == null) {
return null;
}
-
- // keep track of row bounds
- int rowStart = rowBuffer.position();
- int rowEnd = rowBuffer.limit();
-
- if (LOG.isDebugEnabled()) {
- LOG.debug("Data block at position " +
- Integer.toHexString(rowBuffer.position()) +
- ":\n" + ByteUtil.toHexString(rowBuffer, rowStart,
- (rowEnd - rowStart)));
- }
-
- short columnCount = rowBuffer.getShort(); //Number of columns in this row
- // keep track of where the row data starts
- int dataStart = rowBuffer.position();
+ return getRow(rowBuffer, getRowNullMask(rowBuffer), _columns,
+ columnNames);
+ }
+
+ /**
+ * Reads a single column from the given row.
+ */
+ public static Object getRowSingleColumn(
+ RowState rowState, int pageNumber, int rowNum,
+ Column column, PageChannel pageChannel, JetFormat format)
+ throws IOException
+ {
+ // set row state to correct page
+ rowState.setPage(pageChannel, pageNumber);
+
+ // position at correct row
+ ByteBuffer rowBuffer = positionAtRow(rowState, rowNum, pageChannel,
+ format);
+ if(rowBuffer == null) {
+ // note, row state will indicate that row was deleted
+ return null;
+ }
- // read null mask
- NullMask nullMask = new NullMask(columnCount);
- rowBuffer.position(rowEnd - nullMask.byteSize()); //Null mask at end
- nullMask.read(rowBuffer);
+ return getRowColumn(rowBuffer, getRowNullMask(rowBuffer), column);
+ }
+
+ /**
+ * Reads some columns from the given row.
+ * @param columnNames Only column names in this collection will be returned
+ */
+ public static Map<String, Object> getRow(
+ RowState rowState, int pageNumber, int rowNum,
+ Collection<Column> columns, PageChannel pageChannel, JetFormat format,
+ Collection<String> columnNames)
+ throws IOException
+ {
+ // set row state to correct page
+ rowState.setPage(pageChannel, pageNumber);
- short[] varColumnOffsets = null;
- // if _maxVarColumnCount is 0, then row info does not include varcol info
- if(_maxVarColumnCount > 0) {
- rowBuffer.position(rowEnd - nullMask.byteSize() - 2);
- short rowVarColumnCount = rowBuffer.getShort(); // number of variable length columns in this row
-
- // Read in the offsets of each of the variable length columns (in
- // reverse order)
- varColumnOffsets = new short[rowVarColumnCount + 1];
- int varColumnOffsetPos = rowBuffer.position() - 4;
- for (short i = 0; i < (rowVarColumnCount + 1); i++) {
- varColumnOffsets[i] = rowBuffer.getShort(varColumnOffsetPos);
- varColumnOffsetPos -= 2;
- }
+ // position at correct row
+ ByteBuffer rowBuffer = positionAtRow(rowState, rowNum, pageChannel,
+ format);
+ if(rowBuffer == null) {
+ // note, row state will indicate that row was deleted
+ return null;
}
- //Now read in the fixed length columns and populate the columnData array
- //with the combination of fixed length and variable length data.
- Map<String, Object> rtn = new LinkedHashMap<String, Object>(columnCount);
- for (Iterator iter = _columns.iterator(); iter.hasNext(); ) {
- Column column = (Column) iter.next();
- boolean isNull = nullMask.isNull(column.getColumnNumber());
+ return getRow(rowBuffer, getRowNullMask(rowBuffer), columns,
+ columnNames);
+ }
+
+ /**
+ * Reads the row data from the given row buffer. Leaves limit unchanged.
+ */
+ private static Map<String, Object> getRow(
+ ByteBuffer rowBuffer,
+ NullMask nullMask,
+ Collection<Column> columns,
+ Collection<String> columnNames)
+ throws IOException
+ {
+ Map<String, Object> rtn = new LinkedHashMap<String, Object>(
+ columns.size());
+ for(Column column : columns) {
Object value = null;
if((columnNames == null) || (columnNames.contains(column.getName()))) {
-
- if (column.getType() == DataType.BOOLEAN) {
- value = new Boolean(!isNull); //Boolean values are stored in the null mask
- } else {
- if(!isNull) {
-
- // locate the column data bytes
- int colDataPos = 0;
- int colDataLen = 0;
- if (!column.isVariableLength())
- {
- // find fixed length column data
- colDataPos = dataStart + column.getFixedDataOffset();
- colDataLen = column.getType().getFixedSize();
- }
- else
- {
- // find var length column data
- int varDataIdx = column.getVarLenTableIndex();
- short varDataStart = varColumnOffsets[varDataIdx];
- short varDataEnd = varColumnOffsets[varDataIdx + 1];
- colDataPos = rowStart + varDataStart;
- colDataLen = varDataEnd - varDataStart;
- }
-
- // parse the column data
- byte[] columnData = new byte[colDataLen];
- rowBuffer.position(colDataPos);
- rowBuffer.get(columnData);
- value = column.read(columnData);
- }
- }
-
- //Add the value to the row data
- rtn.put(column.getName(), value);
+ // Add the value to the row data
+ rtn.put(column.getName(), getRowColumn(rowBuffer, nullMask, column));
}
}
return rtn;
+
}
/**
+ * Reads the column data from the given row buffer. Leaves limit unchanged.
+ */
+ private static Object getRowColumn(ByteBuffer rowBuffer,
+ NullMask nullMask,
+ Column column)
+ throws IOException
+ {
+ boolean isNull = nullMask.isNull(column.getColumnNumber());
+ if(column.getType() == DataType.BOOLEAN) {
+ return new Boolean(!isNull); //Boolean values are stored in the null mask
+ } else if(isNull) {
+ // well, that's easy!
+ return null;
+ }
+
+ // reset position to row start
+ rowBuffer.reset();
+
+ // locate the column data bytes
+ int rowStart = rowBuffer.position();
+ int colDataPos = 0;
+ int colDataLen = 0;
+ if(!column.isVariableLength()) {
+
+ // read fixed length value (non-boolean at this point)
+ int dataStart = rowStart + 2;
+ colDataPos = dataStart + column.getFixedDataOffset();
+ colDataLen = column.getType().getFixedSize();
+
+ } else {
+
+ // read var length value
+ int varColumnOffsetPos =
+ (rowBuffer.limit() - nullMask.byteSize() - 4) -
+ (column.getVarLenTableIndex() * 2);
+
+ short varDataStart = rowBuffer.getShort(varColumnOffsetPos);
+ short varDataEnd = rowBuffer.getShort(varColumnOffsetPos - 2);
+ colDataPos = rowStart + varDataStart;
+ colDataLen = varDataEnd - varDataStart;
+ }
+
+ // grab the column data
+ byte[] columnData = new byte[colDataLen];
+ rowBuffer.position(colDataPos);
+ rowBuffer.get(columnData);
+
+ // parse the column data
+ return column.read(columnData);
+ }
+
+ /**
+ * Reads the null mask from the given row buffer. Leaves limit unchanged.
+ */
+ private static NullMask getRowNullMask(ByteBuffer rowBuffer)
+ throws IOException
+ {
+ // reset position to row start
+ rowBuffer.reset();
+
+ short columnCount = rowBuffer.getShort(); // Number of columns in this row
+
+ // read null mask
+ NullMask nullMask = new NullMask(columnCount);
+ rowBuffer.position(rowBuffer.limit() - nullMask.byteSize()); //Null mask at end
+ nullMask.read(rowBuffer);
+
+ return nullMask;
+ }
+
+ /**
* Position the buffer at the next row in the table
* @return a ByteBuffer narrowed to the next row, or null if none
*/
@@ -331,9 +393,6 @@ public class Table
// loop until we find the next valid row or run out of pages
while(true) {
- // prepare to read new row
- _rowState.reset();
-
if (_rowsLeftOnPage == 0) {
// reset row number
@@ -364,12 +423,10 @@ public class Table
_currentRowInPage++;
_rowsLeftOnPage--;
- // reset row state to current "root" page
- _rowState.setPage(_pageChannel);
-
ByteBuffer rowBuffer =
positionAtRow(_rowState, _currentRowInPage, _pageChannel, _format);
if(rowBuffer != null) {
+ // we found a non-deleted row, return it
return rowBuffer;
}
}
@@ -387,9 +444,11 @@ public class Table
JetFormat format)
throws IOException
{
-
+ // reset row state
+ rowState.reset();
+
while(true) {
- ByteBuffer rowBuffer = rowState.getFinalPage();
+ ByteBuffer rowBuffer = rowState.getFinalPage(pageChannel);
// note, we don't use findRowStart here cause we need the unmasked value
short rowStart = rowBuffer.getShort(getRowStartOffset(rowNum, format));
@@ -443,74 +502,7 @@ public class Table
}
}
- /**
- * Reads a single column from the given row in the given buffer.
- */
- public static Object readRowSingleColumn(
- RowState rowState, int pageNumber, int rowNum,
- Column column, PageChannel pageChannel, JetFormat format)
- throws IOException
- {
- // reset state, then set to correct page
- rowState.reset();
- rowState.setPage(pageChannel, pageNumber);
-
- // position at correct row
- ByteBuffer rowBuffer = positionAtRow(rowState, rowNum, pageChannel,
- format);
- if(rowBuffer == null) {
- // note, row state will indicate that row was deleted
- return null;
- }
-
- int rowStart = rowBuffer.position();
- int rowEnd = rowBuffer.limit();
-
- rowBuffer.position(rowStart);
- short columnCount = rowBuffer.getShort(); //Number of columns in this row
-
- NullMask nullMask = new NullMask(columnCount);
- rowBuffer.position(rowEnd - nullMask.byteSize()); //Null mask at end
- nullMask.read(rowBuffer);
-
- boolean isNull = nullMask.isNull(column.getColumnNumber());
- if(column.getType() == DataType.BOOLEAN) {
- return new Boolean(!isNull); //Boolean values are stored in the null mask
- } else if(isNull) {
- // well, that's easy!
- return null;
- }
-
- // locate the column data bytes
- int colDataPos = 0;
- int colDataLen = 0;
- if(!column.isVariableLength()) {
-
- // read fixed length value (non-boolean at this point)
- int dataStart = rowStart + 2;
- colDataPos = dataStart + column.getFixedDataOffset();
- colDataLen = column.getType().getFixedSize();
-
- } else {
-
- // read var length value
- int varColumnOffsetPos =
- (rowEnd - nullMask.byteSize() - 4) -
- (column.getVarLenTableIndex() * 2);
-
- short varDataStart = rowBuffer.getShort(varColumnOffsetPos);
- short varDataEnd = rowBuffer.getShort(varColumnOffsetPos - 2);
- colDataPos = rowStart + varDataStart;
- colDataLen = varDataEnd - varDataStart;
- }
-
- // parse the column data
- byte[] columnData = new byte[colDataLen];
- rowBuffer.position(colDataPos);
- rowBuffer.get(columnData);
- return column.read(columnData);
- }
-
+
/**
* Calls <code>reset</code> on this table and returns a modifiable Iterator
* which will iterate through all the rows of this table. Use of the
@@ -1043,9 +1035,13 @@ public class Table
return _rowBufferH.getPageNumber();
}
- public ByteBuffer getFinalPage()
+ public ByteBuffer getFinalPage(PageChannel pageChannel)
throws IOException
{
+ if(_finalRowBuffer == null) {
+ // (re)load current page
+ _finalRowBuffer = getPage(pageChannel);
+ }
return _finalRowBuffer;
}
@@ -1075,15 +1071,10 @@ public class Table
return _rowBufferH.getPage(pageChannel);
}
- public ByteBuffer setPage(PageChannel pageChannel)
- throws IOException
- {
- return setPage(pageChannel, _rowBufferH.getPageNumber());
- }
-
public ByteBuffer setPage(PageChannel pageChannel, int pageNumber)
throws IOException
{
+ reset();
_finalRowBuffer = _rowBufferH.setPage(pageChannel, pageNumber);
return _finalRowBuffer;
}