Bladeren bron

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 jaren geleden
bovenliggende
commit
0dc74ed679

+ 1
- 1
src/main/java/com/healthmarketscience/jackcess/impl/BaseEvalContext.java Bestand weergeven

} }
} }


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

+ 16
- 11
src/main/java/com/healthmarketscience/jackcess/impl/expr/Expressionator.java Bestand weergeven

// normal expression handling // normal expression handling
Expr expr = parseExpression(buf, false); 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) { switch(exprType) {
case DEFAULT_VALUE: case DEFAULT_VALUE:
return sb.toString(); return sb.toString();
} }


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


} }


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


@Override @Override
} }


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


super(op, left, right); super(op, left, right);
} }


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

@Override @Override
public Value eval(final EvalContext ctx) { public Value eval(final EvalContext ctx) {


} }


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

+ 68
- 28
src/test/java/com/healthmarketscience/jackcess/impl/expr/ExpressionatorTest.java Bestand weergeven



package com.healthmarketscience.jackcess.impl.expr; package com.healthmarketscience.jackcess.impl.expr;


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


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




validateExpr("' \"A\" '", "<ELiteralValue>{\" \"\"A\"\" \"}", validateExpr("' \"A\" '", "<ELiteralValue>{\" \"\"A\"\" \"}",
"\" \"\"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 private static void doTestSimpleBinOp(String opName, String... ops) throws Exception
assertEquals(-28d, eval("CDbl(9)-37", Value.Type.DOUBLE)); 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) { private static void validateExpr(String exprStr, String debugStr) {
validateExpr(exprStr, debugStr, exprStr); validateExpr(exprStr, debugStr, exprStr);
} }
String cleanStr) { String cleanStr) {
Expression expr = Expressionator.parse( Expression expr = Expressionator.parse(
Expressionator.Type.FIELD_VALIDATOR, exprStr, null, Expressionator.Type.FIELD_VALIDATOR, exprStr, null,
new TestParseContext());
new TestContext());
String foundDebugStr = expr.toDebugString(); String foundDebugStr = expr.toDebugString();
if(foundDebugStr.startsWith("<EImplicitCompOp>")) { if(foundDebugStr.startsWith("<EImplicitCompOp>")) {
assertEquals("<EImplicitCompOp>{<EThisValue>{<THIS_COL>} = " + assertEquals("<EImplicitCompOp>{<EThisValue>{<THIS_COL>} = " +
} }


static Object eval(String exprStr, Value.Type resultType) { static Object eval(String exprStr, Value.Type resultType) {
TestContext tc = new TestContext();
Expression expr = Expressionator.parse( 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( private static void evalFail(
String exprStr, Class<? extends Exception> failure) String exprStr, Class<? extends Exception> failure)
{ {
TestContext tc = new TestContext();
Expression expr = Expressionator.parse( Expression expr = Expressionator.parse(
Expressionator.Type.DEFAULT_VALUE, exprStr, null,
new TestParseContext());
Expressionator.Type.DEFAULT_VALUE, exprStr, null, tc);
try { try {
expr.eval(new TestEvalContext(null));
expr.eval(tc);
fail(failure + " should have been thrown"); fail(failure + " should have been thrown");
} catch(Exception e) { } catch(Exception e) {
assertTrue(failure.isInstance(e)); assertTrue(failure.isInstance(e));
} }


private static Boolean evalCondition(String exprStr, String thisVal) { private static Boolean evalCondition(String exprStr, String thisVal) {
TestContext tc = new TestContext(BuiltinOperators.toValue(thisVal));
Expression expr = Expressionator.parse( 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) { static int roundToLongInt(double d) {
return BuiltinOperators.normalize(bd); 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 Value _thisVal;
private final RandomContext _rndCtx = new RandomContext(); private final RandomContext _rndCtx = new RandomContext();
private final Bindings _bindings = new SimpleBindings(); private final Bindings _bindings = new SimpleBindings();


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

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


return sdf; return sdf;
} }


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

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

+ 91
- 0
src/test/resources/test_exprs.txt Bestand weergeven

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

Laden…
Annuleren
Opslaan