Browse Source

fix handling of certain field validator expressions; add some tests for various expressions

git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/trunk@1180 f203690c-595d-4dc9-a70b-905162fa7fd2
tags/jackcess-2.2.0
James Ahlborn 5 years ago
parent
commit
0dc74ed679

+ 1
- 1
src/main/java/com/healthmarketscience/jackcess/impl/BaseEvalContext.java View File

@@ -162,7 +162,7 @@ public abstract class BaseEvalContext implements EvalContext
}
}

protected static Value.Type toValueType(DataType dType) {
public static Value.Type toValueType(DataType dType) {
Value.Type type = TYPE_MAP.get(dType);
return ((type == null) ? Value.Type.STRING : type);
}

+ 16
- 11
src/main/java/com/healthmarketscience/jackcess/impl/expr/Expressionator.java View File

@@ -434,12 +434,12 @@ public class Expressionator
// normal expression handling
Expr expr = parseExpression(buf, false);

if((exprType == Type.FIELD_VALIDATOR) && !expr.isConditionalExpr()) {
// a non-conditional expression for a FIELD_VALIDATOR treats the result
// as an equality comparison with the field in question. so, transform
// the expression accordingly
expr = new EImplicitCompOp(expr);
}
if((exprType == Type.FIELD_VALIDATOR) && !expr.isValidationExpr()) {
// a non-validation expression for a FIELD_VALIDATOR treats the result
// as an equality comparison with the field in question. so, transform
// the expression accordingly
expr = new EImplicitCompOp(expr);
}

switch(exprType) {
case DEFAULT_VALUE:
@@ -1387,7 +1387,7 @@ public class Expressionator
return sb.toString();
}

protected boolean isConditionalExpr() {
protected boolean isValidationExpr() {
return false;
}

@@ -1596,8 +1596,8 @@ public class Expressionator
}

@Override
protected boolean isConditionalExpr() {
return _expr.isConditionalExpr();
protected boolean isValidationExpr() {
return _expr.isValidationExpr();
}

@Override
@@ -1776,7 +1776,7 @@ public class Expressionator
}

@Override
protected boolean isConditionalExpr() {
protected boolean isValidationExpr() {
return true;
}

@@ -1810,6 +1810,11 @@ public class Expressionator
super(op, left, right);
}

@Override
protected boolean isValidationExpr() {
return true;
}

@Override
public Value eval(final EvalContext ctx) {

@@ -1854,7 +1859,7 @@ public class Expressionator
}

@Override
protected boolean isConditionalExpr() {
protected boolean isValidationExpr() {
return true;
}
}

+ 68
- 28
src/test/java/com/healthmarketscience/jackcess/impl/expr/ExpressionatorTest.java View File

@@ -16,21 +16,24 @@ limitations under the License.

package com.healthmarketscience.jackcess.impl.expr;

import java.io.BufferedReader;
import java.io.FileReader;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.script.Bindings;
import javax.script.SimpleBindings;

import com.healthmarketscience.jackcess.DataType;
import com.healthmarketscience.jackcess.DatabaseBuilder;
import com.healthmarketscience.jackcess.TestUtil;
import com.healthmarketscience.jackcess.expr.EvalContext;
import com.healthmarketscience.jackcess.expr.Expression;
import com.healthmarketscience.jackcess.expr.Function;
import com.healthmarketscience.jackcess.expr.FunctionLookup;
import com.healthmarketscience.jackcess.expr.Identifier;
import com.healthmarketscience.jackcess.expr.TemporalConfig;
import com.healthmarketscience.jackcess.expr.Value;
import com.healthmarketscience.jackcess.impl.BaseEvalContext;
import com.healthmarketscience.jackcess.impl.NumberFormatter;
import junit.framework.TestCase;

@@ -93,6 +96,8 @@ public class ExpressionatorTest extends TestCase

validateExpr("' \"A\" '", "<ELiteralValue>{\" \"\"A\"\" \"}",
"\" \"\"A\"\" \"");

validateExpr("<=1 And >=0", "<ELogicalOp>{<ECompOp>{<EThisValue>{<THIS_COL>} <= <ELiteralValue>{1}} And <ECompOp>{<EThisValue>{<THIS_COL>} >= <ELiteralValue>{0}}}", "<THIS_COL> <= 1 And <THIS_COL> >= 0");
}

private static void doTestSimpleBinOp(String opName, String... ops) throws Exception
@@ -416,6 +421,47 @@ public class ExpressionatorTest extends TestCase
assertEquals(-28d, eval("CDbl(9)-37", Value.Type.DOUBLE));
}

public void testParseSomeExprs() throws Exception
{
BufferedReader br = new BufferedReader(new FileReader("src/test/resources/test_exprs.txt"));

TestContext tc = new TestContext() {
@Override
public Value getThisColumnValue() {
return BuiltinOperators.toValue(23.0);
}

@Override
public Value getIdentifierValue(Identifier identifier) {
return BuiltinOperators.toValue(23.0);
}
};

String line = null;
while((line = br.readLine()) != null) {
line = line.trim();
if(line.isEmpty()) {
continue;
}

String[] parts = line.split(";", 3);
Expressionator.Type type = Expressionator.Type.valueOf(parts[0]);
DataType dType =
(("null".equals(parts[1])) ? null : DataType.valueOf(parts[1]));
String exprStr = parts[2];

Value.Type resultType = ((dType != null) ?
BaseEvalContext.toValueType(dType) : null);

Expression expr = Expressionator.parse(
type, exprStr, resultType, tc);

expr.eval(tc);
}

br.close();
}

private static void validateExpr(String exprStr, String debugStr) {
validateExpr(exprStr, debugStr, exprStr);
}
@@ -424,7 +470,7 @@ public class ExpressionatorTest extends TestCase
String cleanStr) {
Expression expr = Expressionator.parse(
Expressionator.Type.FIELD_VALIDATOR, exprStr, null,
new TestParseContext());
new TestContext());
String foundDebugStr = expr.toDebugString();
if(foundDebugStr.startsWith("<EImplicitCompOp>")) {
assertEquals("<EImplicitCompOp>{<EThisValue>{<THIS_COL>} = " +
@@ -440,20 +486,20 @@ public class ExpressionatorTest extends TestCase
}

static Object eval(String exprStr, Value.Type resultType) {
TestContext tc = new TestContext();
Expression expr = Expressionator.parse(
Expressionator.Type.DEFAULT_VALUE, exprStr, resultType,
new TestParseContext());
return expr.eval(new TestEvalContext(null));
Expressionator.Type.DEFAULT_VALUE, exprStr, resultType, tc);
return expr.eval(tc);
}

private static void evalFail(
String exprStr, Class<? extends Exception> failure)
{
TestContext tc = new TestContext();
Expression expr = Expressionator.parse(
Expressionator.Type.DEFAULT_VALUE, exprStr, null,
new TestParseContext());
Expressionator.Type.DEFAULT_VALUE, exprStr, null, tc);
try {
expr.eval(new TestEvalContext(null));
expr.eval(tc);
fail(failure + " should have been thrown");
} catch(Exception e) {
assertTrue(failure.isInstance(e));
@@ -461,10 +507,10 @@ public class ExpressionatorTest extends TestCase
}

private static Boolean evalCondition(String exprStr, String thisVal) {
TestContext tc = new TestContext(BuiltinOperators.toValue(thisVal));
Expression expr = Expressionator.parse(
Expressionator.Type.FIELD_VALIDATOR, exprStr, null,
new TestParseContext());
return (Boolean)expr.eval(new TestEvalContext(BuiltinOperators.toValue(thisVal)));
Expressionator.Type.FIELD_VALIDATOR, exprStr, null, tc);
return (Boolean)expr.eval(tc);
}

static int roundToLongInt(double d) {
@@ -480,28 +526,18 @@ public class ExpressionatorTest extends TestCase
return BuiltinOperators.normalize(bd);
}

private static final class TestParseContext implements Expressionator.ParseContext
{
public TemporalConfig getTemporalConfig() {
return TemporalConfig.US_TEMPORAL_CONFIG;
}
public SimpleDateFormat createDateFormat(String formatStr) {
SimpleDateFormat sdf = DatabaseBuilder.createDateFormat(formatStr);
sdf.setTimeZone(TestUtil.TEST_TZ);
return sdf;
}
public FunctionLookup getFunctionLookup() {
return DefaultFunctions.LOOKUP;
}
}

private static final class TestEvalContext implements EvalContext
private static class TestContext
implements Expressionator.ParseContext, EvalContext
{
private final Value _thisVal;
private final RandomContext _rndCtx = new RandomContext();
private final Bindings _bindings = new SimpleBindings();

private TestEvalContext(Value thisVal) {
private TestContext() {
this(null);
}

private TestContext(Value thisVal) {
_thisVal = thisVal;
}

@@ -519,6 +555,10 @@ public class ExpressionatorTest extends TestCase
return sdf;
}

public FunctionLookup getFunctionLookup() {
return DefaultFunctions.LOOKUP;
}

public Value getThisColumnValue() {
if(_thisVal == null) {
throw new UnsupportedOperationException();

+ 91
- 0
src/test/resources/test_exprs.txt View File

@@ -0,0 +1,91 @@
DEFAULT_VALUE;SHORT_DATE_TIME;Now()
DEFAULT_VALUE;NUMERIC;0
FIELD_VALIDATOR;DOUBLE;<=1 And >=0
FIELD_VALIDATOR;SHORT_DATE_TIME;>=#1/1/1900#
DEFAULT_VALUE;BOOLEAN;No
DEFAULT_VALUE;SHORT_DATE_TIME;Null
DEFAULT_VALUE;TEXT;NEW
DEFAULT_VALUE;LONG;0.0
DEFAULT_VALUE;LONG;1
DEFAULT_VALUE;DOUBLE;1.0
DEFAULT_VALUE;LONG;3
DEFAULT_VALUE;LONG;-1
DEFAULT_VALUE;GUID;GenGUID()
FIELD_VALIDATOR;TEXT;Like "http://*"
DEFAULT_VALUE;BOOLEAN;True
DEFAULT_VALUE;SHORT_DATE_TIME;Date()
DEFAULT_VALUE;TEXT;""
DEFAULT_VALUE;BOOLEAN;False
DEFAULT_VALUE;LONG;524287
DEFAULT_VALUE;LONG;5
DEFAULT_VALUE;LONG;10
DEFAULT_VALUE;TEXT;' '
EXPRESSION;MONEY;[UnitPrice]*[Quantity]
DEFAULT_VALUE;TEXT;"UTC"
EXPRESSION;BIG_INT;[BigNum]+10
EXPRESSION;TEXT;IIf(IsNull([LastName]),IIf(IsNull([FirstName]),[Company],[FirstName]),IIf(IsNull([FirstName]),[LastName],[FirstName] & " " & [LastName]))
EXPRESSION;TEXT;IIf(IsNull([LastName]),IIf(IsNull([FirstName]),[Company],[FirstName]),IIf(IsNull([FirstName]),[LastName],[LastName] & ", " & [FirstName]))
RECORD_VALIDATOR;null;Not IsNull([Email] & [FullName] & [Login])
DEFAULT_VALUE;DOUBLE;=0
EXPRESSION;DOUBLE;[InitialLevel]+[Received]-[Shipped]-[Waste]
EXPRESSION;DOUBLE;[OnHand]-[Allocated]
EXPRESSION;DOUBLE;[Available]+[OnOrder]-[BackOrdered]
EXPRESSION;DOUBLE;IIf([TargetLevel]-[CurrentLevel]>0,[TargetLevel]-[CurrentLevel],0)
EXPRESSION;DOUBLE;IIf([BelowTargetLevel]>0,IIf([BelowTargetLevel]<[MinimumReorderQuantity],[MinimumReorderQuantity],[BelowTargetLevel]),0)
EXPRESSION;MONEY;[SubTotal]+[Tax]+[Shipping]
EXPRESSION;MONEY;[UnitPrice]*[Quantity]*(1-[Discount])
DEFAULT_VALUE;TEXT;="None"
EXPRESSION;BOOLEAN;([StatusID]=10)
EXPRESSION;BOOLEAN;([StatusID]>=30)
EXPRESSION;BOOLEAN;[StatusID]>=40
EXPRESSION;BOOLEAN;[StatusID]>50
EXPRESSION;BOOLEAN;([StatusID]=20)
DEFAULT_VALUE;SHORT_DATE_TIME;=Date()
EXPRESSION;DOUBLE;Month([OrderDate])
EXPRESSION;DOUBLE;Year([OrderDate])
EXPRESSION;MONEY;[OrderSubTotal]+[Tax]+[ShippingFee]
EXPRESSION;DOUBLE;IIf([OrderMonth]<=3,1,IIf([OrderMonth]<=6,2,IIf([OrderMonth]<=9,3,4)))
EXPRESSION;BOOLEAN;([StatusID]=0)
EXPRESSION;BOOLEAN;([StatusID]=30) And (Not IsNull([ClosedDate]))
EXPRESSION;BOOLEAN;([StatusID]>=20) And (Not IsNull([ShippedDate]))
EXPRESSION;BOOLEAN;([StatusID]>=10)
EXPRESSION;BOOLEAN;Not [IsCompleted]
EXPRESSION;DOUBLE;[Quantity]*[UnitCost]
DEFAULT_VALUE;BOOLEAN;=False
EXPRESSION;MONEY;[OrderSubTotal]+[ShippingFee]+[Taxes]
EXPRESSION;BOOLEAN;Not IsNull([ClosedDate])
EXPRESSION;BOOLEAN;Not IsNull([SubmittedDate])
EXPRESSION;BOOLEAN;Not [IsSubmitted]
DEFAULT_VALUE;SHORT_DATE_TIME;=Now()
DEFAULT_VALUE;LONG;=Int(13)
FIELD_VALIDATOR;SHORT_DATE_TIME;<Date()
FIELD_VALIDATOR;MONEY;>=0
FIELD_VALIDATOR;INT;>0
FIELD_VALIDATOR;FLOAT;Between 0 And 1
DEFAULT_VALUE;BOOLEAN;=No
EXPRESSION;TEXT;[LastName] & ", " & [FirstName]
EXPRESSION;LONG;Len([LastFirst])
EXPRESSION;MONEY;[Salary]/12
EXPRESSION;BOOLEAN;[Salary]>100000
EXPRESSION;MEMO;[LastName] & ", " & [FirstName] & "=" & [LastFirst]
EXPRESSION;NUMERIC;[Salary]/52
EXPRESSION;MONEY;[Salary]
EXPRESSION;BOOLEAN;True
EXPRESSION;NUMERIC;[Popularity]
EXPRESSION;FLOAT;[Salary]*0.13/[DecimalTest]
EXPRESSION;NUMERIC;([Salary]*[MonthlySalary]/[DecimalTest])*34.12342134
DEFAULT_VALUE;COMPLEX_TYPE;"data4"
EXPRESSION;DOUBLE;NPer(1,2,3,4,5)
EXPRESSION;DOUBLE;[Time Field]
DEFAULT_VALUE;TEXT;=Rnd(25)
DEFAULT_VALUE;TEXT;=Rnd(13)
DEFAULT_VALUE;TEXT;=Rnd(-1)
DEFAULT_VALUE;TEXT;=Rnd(-2)
DEFAULT_VALUE;TEXT;=Rnd()
RECORD_VALIDATOR;null;[Field4]>10
EXPRESSION;DOUBLE;[Field6]+3
EXPRESSION;DOUBLE;[Table4].[Field1]+4
EXPRESSION;LONG;[Field5]+5
DEFAULT_VALUE;LONG;134217727
DEFAULT_VALUE;TEXT;'+P-E'
FIELD_VALIDATOR;TEXT;Is Not Null

Loading…
Cancel
Save