RegularExpressionValueComparer.java
package org.dbunit.assertion.comparer.value;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import org.dbunit.DatabaseUnitException;
import org.dbunit.dataset.ITable;
import org.dbunit.dataset.datatype.DataType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* {@link ValueComparer} implementation that verifies the actual value matches
* the regular expression supplied as the expected value.
*
* <p>
* The expected value, converted to a {@link String}, is the
* {@link java.util.regex.Pattern regular expression}; the actual value, also
* converted to a {@link String}, is the input tested against it. This mirrors
* how {@link IsActualContainingExpectedStringValueComparer} treats the expected
* value as the substring to look for.
*
* <p>
* The comparison succeeds only when the pattern matches the <em>entire</em>
* actual value, using {@link Matcher#matches()}, consistent with
* {@link String#matches(String)}. To match only part of the actual value, make
* the pattern permissive at both ends, for example <code>.*[0-9]{4}.*</code>.
* A <code>.</code> does not match a line terminator unless the
* {@link Pattern#DOTALL DOTALL} flag is set, so prefix such a pattern with
* <code>(?s)</code> when the actual value may contain a newline, for example
* <code>(?s).*[0-9]{4}.*</code>.
*
* <p>
* Useful for columns whose exact content a test does not control but whose
* format it does, such as database-generated identifiers, UUID columns, or
* timestamps rendered into a text column.
*
* <p>
* Special case: if both values are null, they match; if exactly one is null,
* they do not.
*
* <p>
* This comparer adds no dependency beyond {@code java.util.regex}, so it is also
* available as {@link ValueComparers#regularExpressionValueComparer}.
*
* @author Jeff Jensen
* @since 3.6.0
*/
public class RegularExpressionValueComparer extends ValueComparerTemplateBase
{
private final Logger log = LoggerFactory.getLogger(getClass());
@Override
protected boolean isExpected(final ITable expectedTable,
final ITable actualTable, final int rowNum, final String columnName,
final DataType dataType, final Object expectedValue,
final Object actualValue) throws DatabaseUnitException
{
final boolean isExpected;
// handle nulls: prevent NPE and isExpected=true when both null
if (expectedValue == null && actualValue == null)
{
// both are null, so match
isExpected = true;
} else if (expectedValue == null || actualValue == null)
{
// both aren't null, one is null, so no match
isExpected = false;
} else
{
// neither are null, so compare
isExpected = isMatching(rowNum, columnName, expectedValue,
actualValue);
}
return isExpected;
}
/**
* Returns whether the regular expression held in the expected value matches
* the whole actual value, both converted to strings.
*
* @param rowNum
* The current row number comparing, used only to identify an
* invalid pattern.
* @param columnName
* The name of the current column comparing, used only to identify
* an invalid pattern.
* @param expectedValue
* The expected value, holding the regular expression.
* @param actualValue
* The actual value tested against the regular expression.
* @return <code>true</code> if the regular expression matches the entire
* actual value string.
* @throws DatabaseUnitException
* If either value cannot be converted to a string, or the
* expected value is not a valid regular expression.
*/
protected boolean isMatching(final int rowNum, final String columnName,
final Object expectedValue, final Object actualValue)
throws DatabaseUnitException
{
final String regex = DataType.asString(expectedValue);
final String actualValueString = DataType.asString(actualValue);
final Pattern pattern = compilePattern(rowNum, columnName, regex);
final Matcher matcher = pattern.matcher(actualValueString);
final boolean isMatching = matcher.matches();
log.debug("isMatching: regex={}, actualValueString={}, isMatching={}",
regex, actualValueString, isMatching);
return isMatching;
}
/**
* Compiles the expected value into a {@link Pattern}, turning an invalid
* expression into a {@link DatabaseUnitException} identifying the row and
* column, consistent with how
* {@link IsActualEqualToExpectedJsonValueComparer} reports an unparseable
* expected value.
*/
private Pattern compilePattern(final int rowNum, final String columnName,
final String regex) throws DatabaseUnitException
{
try
{
return Pattern.compile(regex);
} catch (final PatternSyntaxException e)
{
final String message = String.format(
"Unable to compile expected value as a regular expression"
+ " for column '%s', row %d: %s",
columnName, rowNum, regex);
throw new DatabaseUnitException(message, e);
}
}
@Override
protected String getFailPhrase()
{
return "not matching the regular expression";
}
}