git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/trunk@1180 f203690c-595d-4dc9-a70b-905162fa7fd2tags/jackcess-2.2.0
@@ -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); | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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(); |
@@ -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 |