diff options
author | James Ahlborn <jtahlborn@yahoo.com> | 2008-11-08 01:47:49 +0000 |
---|---|---|
committer | James Ahlborn <jtahlborn@yahoo.com> | 2008-11-08 01:47:49 +0000 |
commit | 57b74129668f576738cc05b35ea7a10eaedfe06a (patch) | |
tree | 74ca0947f42ec80f35a83660913aa4b6d522da7f /src | |
parent | 130789d1f0a997288c13a6df763a453641545cc9 (diff) | |
download | jackcess-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')
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; + } } } |