aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJames Ahlborn <jtahlborn@yahoo.com>2008-11-08 01:47:49 +0000
committerJames Ahlborn <jtahlborn@yahoo.com>2008-11-08 01:47:49 +0000
commit57b74129668f576738cc05b35ea7a10eaedfe06a (patch)
tree74ca0947f42ec80f35a83660913aa4b6d522da7f /src
parent130789d1f0a997288c13a6df763a453641545cc9 (diff)
downloadjackcess-57b74129668f576738cc05b35ea7a10eaedfe06a.tar.gz
jackcess-57b74129668f576738cc05b35ea7a10eaedfe06a.zip
Add ErrorHandler utility for customizing error handling during row
parsing. git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/trunk@381 f203690c-595d-4dc9-a70b-905162fa7fd2
Diffstat (limited to 'src')
-rw-r--r--src/changes/changes.xml4
-rw-r--r--src/java/com/healthmarketscience/jackcess/Column.java4
-rw-r--r--src/java/com/healthmarketscience/jackcess/Cursor.java16
-rw-r--r--src/java/com/healthmarketscience/jackcess/Database.java36
-rw-r--r--src/java/com/healthmarketscience/jackcess/DebugErrorHandler.java80
-rw-r--r--src/java/com/healthmarketscience/jackcess/ErrorHandler.java65
-rw-r--r--src/java/com/healthmarketscience/jackcess/ReplacementErrorHandler.java68
-rw-r--r--src/java/com/healthmarketscience/jackcess/Table.java133
8 files changed, 365 insertions, 41 deletions
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index d04f725..3fc2e4b 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -7,6 +7,10 @@
<body>
<release version="1.1.18" date="TBD">
<action dev="jahlborn" type="add">
+ Add ErrorHandler utility for customizing error handling during row
+ parsing.
+ </action>
+ <action dev="jahlborn" type="add">
Add RowFilter contributed by Patricia Donaldson.
</action>
<action dev="jahlborn" type="update">
diff --git a/src/java/com/healthmarketscience/jackcess/Column.java b/src/java/com/healthmarketscience/jackcess/Column.java
index c27a699..437250e 100644
--- a/src/java/com/healthmarketscience/jackcess/Column.java
+++ b/src/java/com/healthmarketscience/jackcess/Column.java
@@ -144,11 +144,11 @@ public class Column implements Comparable<Column> {
/**
* Only used by unit tests
*/
- Column(boolean testing) {
+ Column(boolean testing, Table table) {
if(!testing) {
throw new IllegalArgumentException();
}
- _table = null;
+ _table = table;
}
/**
diff --git a/src/java/com/healthmarketscience/jackcess/Cursor.java b/src/java/com/healthmarketscience/jackcess/Cursor.java
index 3173308..5804eea 100644
--- a/src/java/com/healthmarketscience/jackcess/Cursor.java
+++ b/src/java/com/healthmarketscience/jackcess/Cursor.java
@@ -299,6 +299,22 @@ public abstract class Cursor implements Iterable<Map<String, Object>>
}
/**
+ * Gets the currently configured ErrorHandler (always non-{@code null}).
+ * This will be used to handle all errors.
+ */
+ public ErrorHandler getErrorHandler() {
+ return _rowState.getErrorHandler();
+ }
+
+ /**
+ * Sets a new ErrorHandler. If {@code null}, resets to using the
+ * ErrorHandler configured at the Table level.
+ */
+ public void setErrorHandler(ErrorHandler newErrorHandler) {
+ _rowState.setErrorHandler(newErrorHandler);
+ }
+
+ /**
* Returns the current state of the cursor which can be restored at a future
* point in time by a call to {@link #restoreSavepoint}.
* <p>
diff --git a/src/java/com/healthmarketscience/jackcess/Database.java b/src/java/com/healthmarketscience/jackcess/Database.java
index df4b0c1..75113e1 100644
--- a/src/java/com/healthmarketscience/jackcess/Database.java
+++ b/src/java/com/healthmarketscience/jackcess/Database.java
@@ -99,6 +99,22 @@ public class Database
default. */
public static final String USE_BIG_INDEX_PROPERTY =
"com.healthmarketscience.jackcess.bigIndex";
+
+ /** default error handler used if none provided (just rethrows exception) */
+ public static final ErrorHandler DEFAULT_ERROR_HANDLER = new ErrorHandler() {
+ public Object handleRowError(Column column,
+ byte[] columnData,
+ Table.RowState rowState,
+ Exception error)
+ throws IOException
+ {
+ // really can only be RuntimeException or IOException
+ if(error instanceof IOException) {
+ throw (IOException)error;
+ }
+ throw (RuntimeException)error;
+ }
+ };
/** Batch commit size for copying other result sets into this database */
private static final int COPY_TABLE_BATCH_SIZE = 200;
@@ -243,6 +259,8 @@ public class Database
private final List<byte[]> _newTableSIDs = new ArrayList<byte[]>();
/** for now, "big index support" is optional */
private boolean _useBigIndex;
+ /** optional error handler to use when row errors are encountered */
+ private ErrorHandler _dbErrorHandler;
/**
* Open an existing Database. If the existing file is not writeable, the
@@ -387,6 +405,24 @@ public class Database
public void setUseBigIndex(boolean useBigIndex) {
_useBigIndex = useBigIndex;
}
+
+ /**
+ * Gets the currently configured ErrorHandler (always non-{@code null}).
+ * This will be used to handle all errors unless overridden at the Table or
+ * Cursor level.
+ */
+ public ErrorHandler getErrorHandler() {
+ return((_dbErrorHandler != null) ? _dbErrorHandler :
+ DEFAULT_ERROR_HANDLER);
+ }
+
+ /**
+ * Sets a new ErrorHandler. If {@code null}, resets to the
+ * {@link #DEFAULT_ERROR_HANDLER}.
+ */
+ public void setErrorHandler(ErrorHandler newErrorHandler) {
+ _dbErrorHandler = newErrorHandler;
+ }
/**
* Read the system catalog
diff --git a/src/java/com/healthmarketscience/jackcess/DebugErrorHandler.java b/src/java/com/healthmarketscience/jackcess/DebugErrorHandler.java
new file mode 100644
index 0000000..2fbd478
--- /dev/null
+++ b/src/java/com/healthmarketscience/jackcess/DebugErrorHandler.java
@@ -0,0 +1,80 @@
+/*
+Copyright (c) 2008 Health Market Science, Inc.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+USA
+
+You can contact Health Market Science at info@healthmarketscience.com
+or at the following address:
+
+Health Market Science
+2700 Horizon Drive
+Suite 200
+King of Prussia, PA 19406
+*/
+
+package com.healthmarketscience.jackcess;
+
+import java.io.IOException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Implementation of ErrorHandler which is useful for generating debug
+ * information about bad row data (great for bug reports!). After logging a
+ * debug entry for the failed column, it will return some sort of replacement
+ * value, see {@link ReplacementErrorHandler}.
+ *
+ * @author James Ahlborn
+ */
+public class DebugErrorHandler extends ReplacementErrorHandler
+{
+ private static final Log LOG = LogFactory.getLog(DebugErrorHandler.class);
+
+ /**
+ * Constructs a DebugErrorHandler which replaces all errored values with
+ * {@code null}.
+ */
+ public DebugErrorHandler() {
+ }
+
+ /**
+ * Constructs a DebugErrorHandler which replaces all errored values with the
+ * given Object.
+ */
+ public DebugErrorHandler(Object replacement) {
+ super(replacement);
+ }
+
+ @Override
+ public Object handleRowError(Column column,
+ byte[] columnData,
+ Table.RowState rowState,
+ Exception error)
+ throws IOException
+ {
+ if(LOG.isDebugEnabled()) {
+ LOG.debug("Failed reading column " + column + ", row " +
+ rowState + ", bytes " +
+ ((columnData != null) ?
+ ByteUtil.toHexString(columnData) : "null"),
+ error);
+ }
+
+ return super.handleRowError(column, columnData, rowState, error);
+ }
+
+}
diff --git a/src/java/com/healthmarketscience/jackcess/ErrorHandler.java b/src/java/com/healthmarketscience/jackcess/ErrorHandler.java
new file mode 100644
index 0000000..d68c856
--- /dev/null
+++ b/src/java/com/healthmarketscience/jackcess/ErrorHandler.java
@@ -0,0 +1,65 @@
+/*
+Copyright (c) 2008 Health Market Science, Inc.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+USA
+
+You can contact Health Market Science at info@healthmarketscience.com
+or at the following address:
+
+Health Market Science
+2700 Horizon Drive
+Suite 200
+King of Prussia, PA 19406
+*/
+
+package com.healthmarketscience.jackcess;
+
+import java.io.IOException;
+
+/**
+ * Handler for errors encountered while reading a column of row data from a
+ * Table. An instance of this class may be configured at the Database, Table,
+ * or Cursor level to customize error handling as desired. The default
+ * instance used is {@link Database#DEFAULT_ERROR_HANDLER}, which just
+ * rethrows any exceptions encountered.
+ *
+ * @author James Ahlborn
+ */
+public interface ErrorHandler
+{
+
+ /**
+ * Handles an error encountered while reading a column of data from a Table
+ * row. Handler may either throw an exception (which will be propagated
+ * back to the caller) or return a replacement for this row's column value
+ * (in which case the row will continue to be read normally).
+ *
+ * @param column the info for the column being read
+ * @param columnData the actual column data for the column being read (which
+ * may be {@code null} dependening on when the exception
+ * was thrown during the reading process)
+ * @param rowState the current row state for the caller
+ * @param error the error that was encountered
+ *
+ * @return replacement for this row's column
+ */
+ public Object handleRowError(Column column,
+ byte[] columnData,
+ Table.RowState rowState,
+ Exception error)
+ throws IOException;
+
+}
diff --git a/src/java/com/healthmarketscience/jackcess/ReplacementErrorHandler.java b/src/java/com/healthmarketscience/jackcess/ReplacementErrorHandler.java
new file mode 100644
index 0000000..bdb003c
--- /dev/null
+++ b/src/java/com/healthmarketscience/jackcess/ReplacementErrorHandler.java
@@ -0,0 +1,68 @@
+/*
+Copyright (c) 2008 Health Market Science, Inc.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+USA
+
+You can contact Health Market Science at info@healthmarketscience.com
+or at the following address:
+
+Health Market Science
+2700 Horizon Drive
+Suite 200
+King of Prussia, PA 19406
+*/
+
+package com.healthmarketscience.jackcess;
+
+import java.io.IOException;
+
+/**
+ * Simple implementation of an ErrorHandler which always returns the
+ * configured object.
+ *
+ * @author James Ahlborn
+ */
+public class ReplacementErrorHandler implements ErrorHandler
+{
+
+ private final Object _replacement;
+
+ /**
+ * Constructs a ReplacementErrorHandler which replaces all errored values
+ * with {@code null}.
+ */
+ public ReplacementErrorHandler() {
+ this(null);
+ }
+
+ /**
+ * Constructs a ReplacementErrorHandler which replaces all errored values
+ * with the given Object.
+ */
+ public ReplacementErrorHandler(Object replacement) {
+ _replacement = replacement;
+ }
+
+ public Object handleRowError(Column column,
+ byte[] columnData,
+ Table.RowState rowState,
+ Exception error)
+ throws IOException
+ {
+ return _replacement;
+ }
+
+}
diff --git a/src/java/com/healthmarketscience/jackcess/Table.java b/src/java/com/healthmarketscience/jackcess/Table.java
index b4424dc..874f2b0 100644
--- a/src/java/com/healthmarketscience/jackcess/Table.java
+++ b/src/java/com/healthmarketscience/jackcess/Table.java
@@ -127,6 +127,8 @@ public class Table
TempPageHolder.newHolder(TempBufferHolder.Type.SOFT);
/** for now, "big index support" is optional */
private final boolean _useBigIndex;
+ /** optional error handler to use when row errors are encountered */
+ private ErrorHandler _tableErrorHandler;
/** common cursor for iterating through the table, kept here for historic
reasons */
@@ -215,6 +217,24 @@ public class Table
return getDatabase().getPageChannel();
}
+ /**
+ * Gets the currently configured ErrorHandler (always non-{@code null}).
+ * This will be used to handle all errors unless overridden at the Cursor
+ * level.
+ */
+ public ErrorHandler getErrorHandler() {
+ return((_tableErrorHandler != null) ? _tableErrorHandler :
+ getDatabase().getErrorHandler());
+ }
+
+ /**
+ * Sets a new ErrorHandler. If {@code null}, resets to using the
+ * ErrorHandler configured at the Database level.
+ */
+ public void setErrorHandler(ErrorHandler newErrorHandler) {
+ _tableErrorHandler = newErrorHandler;
+ }
+
protected int getTableDefPageNumber() {
return _tableDefPageNumber;
}
@@ -383,7 +403,8 @@ public class Table
ByteBuffer rowBuffer = positionAtRowData(rowState, rowId);
requireNonDeletedRow(rowState, rowId);
- Object value = getRowColumn(rowBuffer, getRowNullMask(rowBuffer), column);
+ Object value = getRowColumn(rowBuffer, getRowNullMask(rowBuffer), column,
+ rowState);
// cache the row values in order to be able to update the index on row
// deletion. note, most of the returned values are immutable, except
@@ -430,7 +451,7 @@ public class Table
if((columnNames == null) || (columnNames.contains(column.getName()))) {
// Add the value to the row data
- Object value = getRowColumn(rowBuffer, nullMask, column);
+ Object value = getRowColumn(rowBuffer, nullMask, column, rowState);
rtn.put(column.getName(), value);
// cache the row values in order to be able to update the index on row
@@ -448,51 +469,59 @@ public class Table
*/
private static Object getRowColumn(ByteBuffer rowBuffer,
NullMask nullMask,
- Column column)
+ Column column,
+ RowState rowState)
throws IOException
{
- boolean isNull = nullMask.isNull(column);
- if(column.getType() == DataType.BOOLEAN) {
- return Boolean.valueOf(!isNull); //Boolean values are stored in the null mask
- } else if(isNull) {
- // well, that's easy!
- return null;
- }
+ byte[] columnData = null;
+ try {
+
+ boolean isNull = nullMask.isNull(column);
+ if(column.getType() == DataType.BOOLEAN) {
+ return Boolean.valueOf(!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();
+ // 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();
+ // 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 {
+ } else {
- // read var length value
- int varColumnOffsetPos =
- (rowBuffer.limit() - nullMask.byteSize() - 4) -
- (column.getVarLenTableIndex() * 2);
+ // 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;
- }
+ 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);
+ // grab the column data
+ columnData = new byte[colDataLen];
+ rowBuffer.position(colDataPos);
+ rowBuffer.get(columnData);
- // parse the column data
- return column.read(columnData);
+ // parse the column data
+ return column.read(columnData);
+
+ } catch(Exception e) {
+ return rowState.handleRowError(column, columnData, e);
+ }
}
/**
@@ -1710,7 +1739,9 @@ public class Table
rowState can detect updates to the table and re-read any buffered
data */
private int _lastModCount;
-
+ /** optional error handler to use when row errors are encountered */
+ private ErrorHandler _errorHandler;
+
private RowState(TempBufferHolder.Type headerType) {
_headerRowBufferH = TempPageHolder.newHolder(headerType);
_rowValues = new Object[Table.this.getColumnCount()];
@@ -1720,6 +1751,15 @@ public class Table
public Table getTable() {
return Table.this;
}
+
+ public ErrorHandler getErrorHandler() {
+ return((_errorHandler != null) ? _errorHandler :
+ getTable().getErrorHandler());
+ }
+
+ public void setErrorHandler(ErrorHandler newErrorHandler) {
+ _errorHandler = newErrorHandler;
+ }
public void reset() {
_finalRowId = null;
@@ -1875,6 +1915,21 @@ public class Table
return _finalRowBuffer;
}
+ private Object handleRowError(Column column,
+ byte[] columnData,
+ Exception error)
+ throws IOException
+ {
+ return getErrorHandler().handleRowError(column, columnData,
+ this, error);
+ }
+
+ @Override
+ public String toString()
+ {
+ return "RowState: headerRowId = " + _headerRowId + ", finalRowId = " +
+ _finalRowId;
+ }
}
}