DefaultFailureHandler.java
/*
*
* The DbUnit Database Testing Framework
* Copyright (C)2002-2008, DbUnit.org
*
* 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
*
*/
package org.dbunit.assertion;
import java.util.Arrays;
import org.dbunit.dataset.Column;
import org.dbunit.dataset.ColumnFilterTable;
import org.dbunit.dataset.Columns;
import org.dbunit.dataset.DataSetException;
import org.dbunit.dataset.ITable;
import org.dbunit.dataset.ITableMetaData;
import org.dbunit.dataset.NoSuchColumnException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Default implementation of the {@link FailureHandler}.
*
* @author gommma (gommma AT users.sourceforge.net)
* @since 2.4.0
*/
public class DefaultFailureHandler implements FailureHandler
{
private static final Logger logger =
LoggerFactory.getLogger(DefaultFailureHandler.class);
private String[] _additionalColumnInfo;
private FailureFactory failureFactory = new DefaultFailureFactory();
/**
* Default constructor which does not provide any additional column
* information.
*/
public DefaultFailureHandler()
{
}
/**
* Create a default failure handler
*
* @param additionalColumnInfo
* the column names of the columns for which additional
* information should be printed when an assertion failed.
*/
public DefaultFailureHandler(final Column[] additionalColumnInfo)
{
// Null-safe access
if (additionalColumnInfo != null)
{
this._additionalColumnInfo =
Columns.getColumnNames(additionalColumnInfo);
}
}
/**
* Create a default failure handler
*
* @param additionalColumnInfo
* the column names of the columns for which additional
* information should be printed when an assertion failed.
*/
public DefaultFailureHandler(final String[] additionalColumnInfo)
{
this._additionalColumnInfo = additionalColumnInfo;
}
/**
* @param failureFactory
* The {@link FailureFactory} to be used for creating assertion
* errors.
*/
public void setFailureFactory(final FailureFactory failureFactory)
{
if (failureFactory == null)
{
throw new NullPointerException(
"The parameter 'failureFactory' must not be null");
}
this.failureFactory = failureFactory;
}
public Error createFailure(final String message, final String expected,
final String actual)
{
return this.failureFactory.createFailure(message, expected, actual);
}
public Error createFailure(final String message)
{
return this.failureFactory.createFailure(message);
}
public String getAdditionalInfo(final ITable expectedTable,
final ITable actualTable, final int row, final String columnName)
{
// add custom column values information for better identification of
// mismatching rows
return buildAdditionalColumnInfo(expectedTable, actualTable, row);
}
private String buildAdditionalColumnInfo(final ITable expectedTable,
final ITable actualTable, final int rowIndex)
{
if (logger.isDebugEnabled())
{
logger.debug(
"buildAdditionalColumnInfo(expectedTable={}, actualTable={}, rowIndex={}, "
+ "additionalColumnInfo={}) - start",
new Object[] {expectedTable, actualTable,
new Integer(rowIndex), _additionalColumnInfo});
}
// No columns specified
if (_additionalColumnInfo == null || _additionalColumnInfo.length <= 0)
{
return null;
}
final StringBuilder sb = new StringBuilder();
sb.append("Additional row info:");
for (int j = 0; j < _additionalColumnInfo.length; j++)
{
final String columnName = _additionalColumnInfo[j];
final Object expectedKeyValue =
getColumnValue(expectedTable, rowIndex, columnName);
final Object actualKeyValue =
getColumnValue(actualTable, rowIndex, columnName);
sb.append(" ('");
sb.append(columnName);
sb.append("': expected=<");
sb.append(expectedKeyValue);
sb.append(">, actual=<");
sb.append(actualKeyValue);
sb.append(">)");
}
return sb.toString();
}
protected Object getColumnValue(final ITable table, final int rowIndex,
final String columnName)
{
Object value = null;
try
{
// Get the ITable object to be used for showing the column values
// (needed in case of Filtered tables)
final ITable tableForCol = getTableForColumn(table, columnName);
value = tableForCol.getValue(rowIndex, columnName);
} catch (final DataSetException e)
{
value = makeAdditionalColumnInfoErrorMessage(columnName, e);
}
return value;
}
protected String makeAdditionalColumnInfoErrorMessage(
final String columnName, final DataSetException e)
{
final StringBuilder sb = new StringBuilder();
sb.append("Exception creating more info for column '");
sb.append(columnName);
sb.append("': ");
sb.append(e.getClass().getName());
sb.append(": ");
sb.append(e.getMessage());
final String msg = sb.toString();
logger.warn(msg, e);
return " (!!!!! " + msg + ")";
}
/**
* @param table
* The table which might be a decorated table
* @param columnName
* The column name for which a table is searched
* @return The table that as a column with the given name
* @throws DataSetException
* If no table could be found having a column with the given
* name
*/
private ITable getTableForColumn(final ITable table,
final String columnName) throws DataSetException
{
final ITableMetaData tableMetaData = table.getTableMetaData();
try
{
tableMetaData.getColumnIndex(columnName);
// if the column index was resolved the table contains the given
// column. So just use this table
return table;
} catch (final NoSuchColumnException e)
{
// If the column was not found check for filtered table
if (table instanceof ColumnFilterTable)
{
final ITableMetaData originalMetaData =
((ColumnFilterTable) table).getOriginalMetaData();
originalMetaData.getColumnIndex(columnName);
// If we get here the column exists - return the table since it
// is not filtered in the CompositeTable.
return table;
} else
{
// Column not available in the table - rethrow the exception
throw e;
}
}
}
public void handle(final Difference diff)
{
final String msg = buildMessage(diff);
final Error err =
this.createFailure(msg, String.valueOf(diff.getExpectedValue()),
String.valueOf(diff.getActualValue()));
// Throw the assertion error
throw err;
}
protected String buildMessage(final Difference diff)
{
final StringBuilder builder = new StringBuilder(200);
final int rowNum = diff.getRowIndex();
final String columnName = diff.getColumnName();
final ITable expectedTable = diff.getExpectedTable();
final ITable actualTable = diff.getActualTable();
addFailMessage(diff, builder);
final String expectedTableName =
expectedTable.getTableMetaData().getTableName();
// example message:
// "value (table=MYTAB, row=232, column=MYCOL, Additional row info:
// (column=MyIdCol, expected=444, actual=555)): expected:<123> but
// was:<1234>"
builder.append("value (table=").append(expectedTableName);
builder.append(", row=").append(rowNum);
builder.append(", col=").append(columnName);
final String additionalInfo = this.getAdditionalInfo(expectedTable,
actualTable, rowNum, columnName);
if (additionalInfo != null && !additionalInfo.trim().equals(""))
{
builder.append(", ").append(additionalInfo);
}
builder.append(")");
return builder.toString();
}
protected void addFailMessage(final Difference diff,
final StringBuilder builder)
{
final String failMessage = diff.getFailMessage();
final boolean isFailMessage = isFailMessage(failMessage);
if (isFailMessage)
{
builder.append(failMessage).append(": ");
}
}
protected boolean isFailMessage(final String failMessage)
{
return failMessage != null && !failMessage.isEmpty();
}
@Override
public String toString()
{
final StringBuilder sb = new StringBuilder();
sb.append(DefaultFailureHandler.class.getName()).append("[");
sb.append("_additionalColumnInfo=").append(_additionalColumnInfo == null
? "null" : Arrays.asList(_additionalColumnInfo).toString());
sb.append("]");
return sb.toString();
}
/**
* Default failure factory which returns DBUnits own assertion error
* instances.
*
* @author gommma (gommma AT users.sourceforge.net)
* @author Last changed by: $Author: gommma $
* @version $Revision: 872 $ $Date: 2008-11-08 09:45:52 -0600 (Sat, 08 Nov
* 2008) $
* @since 2.4.0
*/
public static class DefaultFailureFactory implements FailureFactory
{
public Error createFailure(final String message, final String expected,
final String actual)
{
// Return dbunit's own comparison failure object
return new DbComparisonFailure(message, expected, actual);
}
public Error createFailure(final String message)
{
// Return dbunit's own failure object
return new DbAssertionFailedError(message);
}
}
}