]> source.dussan.org Git - jackcess.git/commitdiff
fix handling of certain field validator expressions; add some tests for various expre...
authorJames Ahlborn <jtahlborn@yahoo.com>
Sat, 14 Jul 2018 03:05:57 +0000 (03:05 +0000)
committerJames Ahlborn <jtahlborn@yahoo.com>
Sat, 14 Jul 2018 03:05:57 +0000 (03:05 +0000)
git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/trunk@1180 f203690c-595d-4dc9-a70b-905162fa7fd2

src/main/java/com/healthmarketscience/jackcess/impl/BaseEvalContext.java
src/main/java/com/healthmarketscience/jackcess/impl/expr/Expressionator.java
src/test/java/com/healthmarketscience/jackcess/impl/expr/ExpressionatorTest.java
src/test/resources/test_exprs.txt [new file with mode: 0644]

index 9d724138c130cbbbd7ff76eec91df00fd32a6f7f..640be964de7184b8bdf2cfb1a011aae3e0124f95 100644 (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);
   }
index 4988f3eba00392f4623738cacb0e8267950ac343..c9af94840fed3e8264fae65e1fc274aa633a8f09 100644 (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;
     }
   }
index ef5cef3411af2d6bd6c33e2efdc3b58a277e646e..f4915264b327352a42a1dfb3e1cf4da73e48d49f 100644 (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();
diff --git a/src/test/resources/test_exprs.txt b/src/test/resources/test_exprs.txt
new file mode 100644 (file)
index 0000000..eb76049
--- /dev/null
@@ -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