]> source.dussan.org Git - jackcess.git/commitdiff
knock out some fixmes; add support for custom function lookup and custom bindings...
authorJames Ahlborn <jtahlborn@yahoo.com>
Sat, 19 May 2018 20:32:13 +0000 (20:32 +0000)
committerJames Ahlborn <jtahlborn@yahoo.com>
Sat, 19 May 2018 20:32:13 +0000 (20:32 +0000)
git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/branches/exprs@1151 f203690c-595d-4dc9-a70b-905162fa7fd2

src/main/java/com/healthmarketscience/jackcess/Database.java
src/main/java/com/healthmarketscience/jackcess/expr/EvalConfig.java
src/main/java/com/healthmarketscience/jackcess/expr/EvalContext.java
src/main/java/com/healthmarketscience/jackcess/expr/FunctionLookup.java [new file with mode: 0644]
src/main/java/com/healthmarketscience/jackcess/impl/BaseEvalContext.java
src/main/java/com/healthmarketscience/jackcess/impl/DBEvalContext.java
src/main/java/com/healthmarketscience/jackcess/impl/expr/DefaultFunctions.java
src/main/java/com/healthmarketscience/jackcess/impl/expr/RandomContext.java
src/test/java/com/healthmarketscience/jackcess/impl/expr/ExpressionatorTest.java

index a146154a11a186ff5c8f9b266d6745724bd5a45a..d853fe8c6793713ebd699f7a5ece93d2d4159273 100644 (file)
@@ -451,9 +451,18 @@ public interface Database extends Iterable<Table>, Closeable, Flushable
    */
   public void setAllowAutoNumberInsert(Boolean allowAutoNumInsert);
 
-  // FIXME, docme
+  /**
+   * Gets the current expression evaluation policy.  Expression evaluation is
+   * currently an experimental feature, and is therefore disabled by default.
+   */
   public boolean isEvaluateExpressions();
 
+  /**
+   * Sets the current expression evaluation policy.  Expression evaluation is
+   * currently an experimental feature, and is therefore disabled by default.
+   * If {@code null}, resets to the default value.
+   * @usage _intermediate_method_
+   */
   public void setEvaluateExpressions(Boolean evaluateExpressions);
 
   /**
index e83fbbcd87ac7811c8e3be4a205df70f96d2c647..07ac492987cfff142721cd46f7b10c346bab4f1c 100644 (file)
@@ -16,17 +16,23 @@ limitations under the License.
 
 package com.healthmarketscience.jackcess.expr;
 
+import javax.script.Bindings;
+
 /**
  *
  * @author James Ahlborn
  */
-public interface EvalConfig 
+public interface EvalConfig
 {
   public TemporalConfig getTemporalConfig();
 
   public void setTemporalConfig(TemporalConfig temporal);
 
-  public void putCustomExpressionFunction(Function func);
+  public FunctionLookup getFunctionLookup();
+
+  public void setFunctionLookup(FunctionLookup lookup);
+
+  public Bindings getBindings();
 
-  public Function getCustomExpressionFunction(String name);
+  public void setBindings(Bindings bindings);
 }
index c168f1c2b43af87a51924ba6f56b5786a9276d98..f1dbab3d38764371c468f45d2d4e816dbeeb177c 100644 (file)
@@ -17,12 +17,13 @@ limitations under the License.
 package com.healthmarketscience.jackcess.expr;
 
 import java.text.SimpleDateFormat;
+import javax.script.Bindings;
 
 /**
  *
  * @author James Ahlborn
  */
-public interface EvalContext 
+public interface EvalContext
 {
   public TemporalConfig getTemporalConfig();
 
@@ -35,4 +36,10 @@ public interface EvalContext
   public Value getThisColumnValue();
 
   public Value getIdentifierValue(Identifier identifier);
+
+  public Bindings getBindings();
+
+  public Object get(String key);
+
+  public void put(String key, Object value);
 }
diff --git a/src/main/java/com/healthmarketscience/jackcess/expr/FunctionLookup.java b/src/main/java/com/healthmarketscience/jackcess/expr/FunctionLookup.java
new file mode 100644 (file)
index 0000000..8314c41
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+Copyright (c) 2018 James Ahlborn
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package com.healthmarketscience.jackcess.expr;
+
+/**
+ *
+ * @author James Ahlborn
+ */
+public interface FunctionLookup
+{
+  public Function getFunction(String name);
+}
index 30de2a1c93efdf689f52a5db60be222afd81afe6..230afe22ebabfdba3ff01f67cddbc8a24f6ea3cd 100644 (file)
@@ -23,6 +23,7 @@ import java.util.Collection;
 import java.util.Date;
 import java.util.EnumMap;
 import java.util.Map;
+import javax.script.Bindings;
 
 import com.healthmarketscience.jackcess.DataType;
 import com.healthmarketscience.jackcess.JackcessException;
@@ -97,6 +98,18 @@ public abstract class BaseEvalContext implements EvalContext
     throw new UnsupportedOperationException();
   }
 
+  public Bindings getBindings() {
+    return _dbCtx.getBindings();
+  }
+
+  public Object get(String key) {
+    return _dbCtx.getBindings().get(key);
+  }
+
+  public void put(String key, Object value) {
+    _dbCtx.getBindings().put(key, value);
+  }
+
   public Object eval() throws IOException {
     try {
       return _expr.eval(this);
index 2aabd01f61f3f935194fa45d1eb109f5e11fafbc..ab8a2d4c1d7956ba0e8e59f2511907bd8b9c0f7b 100644 (file)
@@ -18,9 +18,12 @@ package com.healthmarketscience.jackcess.impl;
 
 import java.text.SimpleDateFormat;
 import java.util.Map;
+import javax.script.Bindings;
+import javax.script.SimpleBindings;
 
 import com.healthmarketscience.jackcess.expr.EvalConfig;
 import com.healthmarketscience.jackcess.expr.Function;
+import com.healthmarketscience.jackcess.expr.FunctionLookup;
 import com.healthmarketscience.jackcess.expr.TemporalConfig;
 import com.healthmarketscience.jackcess.impl.expr.DefaultFunctions;
 import com.healthmarketscience.jackcess.impl.expr.Expressionator;
@@ -35,11 +38,13 @@ public class DBEvalContext implements Expressionator.ParseContext, EvalConfig
   private static final int MAX_CACHE_SIZE = 10;
 
   private final DatabaseImpl _db;
+  private FunctionLookup _funcs = DefaultFunctions.LOOKUP;
   private Map<String,SimpleDateFormat> _sdfs;
   private TemporalConfig _temporal;
   private final RandomContext _rndCtx = new RandomContext();
+  private Bindings _bindings = new SimpleBindings();
 
-  public DBEvalContext(DatabaseImpl db) 
+  public DBEvalContext(DatabaseImpl db)
   {
     _db = db;
   }
@@ -56,13 +61,20 @@ public class DBEvalContext implements Expressionator.ParseContext, EvalConfig
     _temporal = temporal;
   }
 
-  public void putCustomExpressionFunction(Function func) {
-    // FIXME writeme
+  public FunctionLookup getFunctionLookup() {
+    return _funcs;
   }
 
-  public Function getCustomExpressionFunction(String name) {
-    // FIXME writeme
-    return null;
+  public void setFunctionLookup(FunctionLookup lookup) {
+    _funcs = lookup;
+  }
+
+  public Bindings getBindings() {
+    return _bindings;
+  }
+
+  public void setBindings(Bindings bindings) {
+    _bindings = bindings;
   }
 
   public SimpleDateFormat createDateFormat(String formatStr) {
@@ -82,7 +94,6 @@ public class DBEvalContext implements Expressionator.ParseContext, EvalConfig
   }
 
   public Function getExpressionFunction(String name) {
-    // FIXME, support custom function context?
-    return DefaultFunctions.getFunction(name);
+    return _funcs.getFunction(name);
   }
 }
index 35b8428e67b44b0f6d02b6a90e1ea8c684c90920..a10b5577f36abe886f08f670fb0c1b070d459bc1 100644 (file)
@@ -24,6 +24,7 @@ import java.util.Map;
 import com.healthmarketscience.jackcess.expr.EvalContext;
 import com.healthmarketscience.jackcess.expr.EvalException;
 import com.healthmarketscience.jackcess.expr.Function;
+import com.healthmarketscience.jackcess.expr.FunctionLookup;
 import com.healthmarketscience.jackcess.expr.Value;
 import com.healthmarketscience.jackcess.impl.DatabaseImpl;
 
@@ -31,13 +32,13 @@ import com.healthmarketscience.jackcess.impl.DatabaseImpl;
  *
  * @author James Ahlborn
  */
-public class DefaultFunctions 
+public class DefaultFunctions
 {
-  private static final Map<String,Function> FUNCS = 
+  private static final Map<String,Function> FUNCS =
     new HashMap<String,Function>();
 
   private static final char NON_VAR_SUFFIX = '$';
-  
+
   static {
     // load all default functions
     DefaultTextFunctions.init();
@@ -45,7 +46,13 @@ public class DefaultFunctions
     DefaultDateFunctions.init();
     DefaultFinancialFunctions.init();
   }
-  
+
+  public static final FunctionLookup LOOKUP = new FunctionLookup() {
+    public Function getFunction(String name) {
+      return DefaultFunctions.getFunction(name);
+    }
+  };
+
   private DefaultFunctions() {}
 
   public static Function getFunction(String name) {
@@ -93,7 +100,7 @@ public class DefaultFunctions
         paramStr.substring(1, paramStr.length() - 1) + ")}";
       return new IllegalStateException(msg, t);
     }
-    
+
     @Override
     public String toString() {
       return getName() + "()";
@@ -197,7 +204,7 @@ public class DefaultFunctions
       }
     }
 
-    protected abstract Value eval3(EvalContext ctx, 
+    protected abstract Value eval3(EvalContext ctx,
                                    Value param1, Value param2, Value param3);
   }
 
@@ -257,10 +264,10 @@ public class DefaultFunctions
     }
   }
 
-  
+
   public static final Function IIF = registerFunc(new Func3("IIf") {
     @Override
-    protected Value eval3(EvalContext ctx, 
+    protected Value eval3(EvalContext ctx,
                           Value param1, Value param2, Value param3) {
       // null is false
       return ((!param1.isNull() && param1.getAsBoolean()) ? param2 : param3);
@@ -307,7 +314,7 @@ public class DefaultFunctions
       return params[idx];
     }
   });
-  
+
   public static final Function SWITCH = registerFunc(new FuncVar("Switch") {
     @Override
     protected Value evalVar(EvalContext ctx, Value[] params) {
@@ -322,7 +329,7 @@ public class DefaultFunctions
       return BuiltinOperators.NULL_VAL;
     }
   });
-  
+
   public static final Function OCT = registerStringFunc(new Func1NullIsNull("Oct") {
     @Override
     protected Value eval1(EvalContext ctx, Value param1) {
@@ -334,7 +341,7 @@ public class DefaultFunctions
       return BuiltinOperators.toValue(Integer.toOctalString(lv));
     }
   });
-  
+
   public static final Function CBOOL = registerFunc(new Func1("CBool") {
     @Override
     protected Value eval1(EvalContext ctx, Value param1) {
@@ -480,7 +487,7 @@ public class DefaultFunctions
       case BIG_DEC:
         // vbDecimal
         vType = 14;
-        break;        
+        break;
       default:
         throw new EvalException("Unknown type " + type);
       }
@@ -513,7 +520,7 @@ public class DefaultFunctions
         break;
       case BIG_DEC:
         tName = "Decimal";
-        break;        
+        break;
       default:
         throw new EvalException("Unknown type " + type);
       }
@@ -521,7 +528,7 @@ public class DefaultFunctions
     }
   });
 
-  
+
 
   // https://www.techonthenet.com/access/functions/
   // https://support.office.com/en-us/article/Access-Functions-by-category-b8b136c3-2716-4d39-94a2-658ce330ed83
index b3a21c22a392b811f46a01621336758060ff9efe..71a3f6d1e17c085546b446536644b99fd28a9f93 100644 (file)
@@ -26,7 +26,7 @@ import java.util.Random;
  *
  * @author James Ahlborn
  */
-public class RandomContext 
+public class RandomContext
 {
   private Source _defRnd;
   private Map<Integer,Source> _rnds;
@@ -34,7 +34,7 @@ public class RandomContext
   // returned yet
   private float _lastVal = 1.953125E-02f;
 
-  public RandomContext() 
+  public RandomContext()
   {
   }
 
@@ -85,7 +85,7 @@ public class RandomContext
   }
 
   private static Random createRandom(long seed) {
-    // FIXME, support SecureRandom?
+    // TODO, support SecureRandom?
     return new Random(seed);
   }
 
index d779d5ade17aac01f782de312690a5a947967d1d..f659a861997cfa102019b40ce59c9fcdb712c6b5 100644 (file)
@@ -19,6 +19,8 @@ package com.healthmarketscience.jackcess.impl.expr;
 import java.math.BigDecimal;
 import java.text.SimpleDateFormat;
 import java.util.Date;
+import javax.script.Bindings;
+import javax.script.SimpleBindings;
 
 import com.healthmarketscience.jackcess.DatabaseBuilder;
 import com.healthmarketscience.jackcess.TestUtil;
@@ -34,7 +36,7 @@ import junit.framework.TestCase;
  *
  * @author James Ahlborn
  */
-public class ExpressionatorTest extends TestCase 
+public class ExpressionatorTest extends TestCase
 {
   private static final double[] DBLS = {
     -10.3d,-9.0d,-8.234d,-7.11111d,-6.99999d,-5.5d,-4.0d,-3.4159265d,-2.84d,
@@ -49,7 +51,7 @@ public class ExpressionatorTest extends TestCase
   public void testParseSimpleExprs() throws Exception
   {
     validateExpr("\"A\"", "<ELiteralValue>{\"A\"}");
-    
+
     validateExpr("13", "<ELiteralValue>{13}");
 
     validateExpr("-42", "<EUnaryOp>{- <ELiteralValue>{42}}");
@@ -91,7 +93,7 @@ public class ExpressionatorTest extends TestCase
   private static void doTestSimpleBinOp(String opName, String... ops) throws Exception
   {
     for(String op : ops) {
-      validateExpr("\"A\" " + op + " \"B\"", 
+      validateExpr("\"A\" " + op + " \"B\"",
                    "<" + opName + ">{<ELiteralValue>{\"A\"} " + op +
                    " <ELiteralValue>{\"B\"}}");
     }
@@ -99,37 +101,37 @@ public class ExpressionatorTest extends TestCase
 
   public void testOrderOfOperations() throws Exception
   {
-    validateExpr("\"A\" Eqv \"B\"", 
+    validateExpr("\"A\" Eqv \"B\"",
                  "<ELogicalOp>{<ELiteralValue>{\"A\"} Eqv <ELiteralValue>{\"B\"}}");
 
-    validateExpr("\"A\" Eqv \"B\" Xor \"C\"", 
+    validateExpr("\"A\" Eqv \"B\" Xor \"C\"",
                  "<ELogicalOp>{<ELiteralValue>{\"A\"} Eqv <ELogicalOp>{<ELiteralValue>{\"B\"} Xor <ELiteralValue>{\"C\"}}}");
 
-    validateExpr("\"A\" Eqv \"B\" Xor \"C\" Or \"D\"", 
+    validateExpr("\"A\" Eqv \"B\" Xor \"C\" Or \"D\"",
                  "<ELogicalOp>{<ELiteralValue>{\"A\"} Eqv <ELogicalOp>{<ELiteralValue>{\"B\"} Xor <ELogicalOp>{<ELiteralValue>{\"C\"} Or <ELiteralValue>{\"D\"}}}}");
 
-    validateExpr("\"A\" Eqv \"B\" Xor \"C\" Or \"D\" And \"E\"", 
+    validateExpr("\"A\" Eqv \"B\" Xor \"C\" Or \"D\" And \"E\"",
                  "<ELogicalOp>{<ELiteralValue>{\"A\"} Eqv <ELogicalOp>{<ELiteralValue>{\"B\"} Xor <ELogicalOp>{<ELiteralValue>{\"C\"} Or <ELogicalOp>{<ELiteralValue>{\"D\"} And <ELiteralValue>{\"E\"}}}}}");
 
-    validateExpr("\"A\" Or \"B\" Or \"C\"", 
+    validateExpr("\"A\" Or \"B\" Or \"C\"",
                  "<ELogicalOp>{<ELogicalOp>{<ELiteralValue>{\"A\"} Or <ELiteralValue>{\"B\"}} Or <ELiteralValue>{\"C\"}}");
 
-    validateExpr("\"A\" & \"B\" Is Null", 
+    validateExpr("\"A\" & \"B\" Is Null",
                  "<ENullOp>{<EBinaryOp>{<ELiteralValue>{\"A\"} & <ELiteralValue>{\"B\"}} Is Null}");
 
-    validateExpr("\"A\" Or \"B\" Is Null", 
+    validateExpr("\"A\" Or \"B\" Is Null",
                  "<ELogicalOp>{<ELiteralValue>{\"A\"} Or <ENullOp>{<ELiteralValue>{\"B\"} Is Null}}");
 
-    validateExpr("Not \"A\" & \"B\"", 
+    validateExpr("Not \"A\" & \"B\"",
                  "<EUnaryOp>{Not <EBinaryOp>{<ELiteralValue>{\"A\"} & <ELiteralValue>{\"B\"}}}");
 
-    validateExpr("Not \"A\" Or \"B\"", 
+    validateExpr("Not \"A\" Or \"B\"",
                  "<ELogicalOp>{<EUnaryOp>{Not <ELiteralValue>{\"A\"}} Or <ELiteralValue>{\"B\"}}");
 
-    validateExpr("\"A\" + \"B\" Not Between 37 - 15 And 52 / 4", 
+    validateExpr("\"A\" + \"B\" Not Between 37 - 15 And 52 / 4",
                  "<EBetweenOp>{<EBinaryOp>{<ELiteralValue>{\"A\"} + <ELiteralValue>{\"B\"}} Not Between <EBinaryOp>{<ELiteralValue>{37} - <ELiteralValue>{15}} And <EBinaryOp>{<ELiteralValue>{52} / <ELiteralValue>{4}}}");
 
-    validateExpr("\"A\" + (\"B\" Not Between 37 - 15 And 52) / 4", 
+    validateExpr("\"A\" + (\"B\" Not Between 37 - 15 And 52) / 4",
                  "<EBinaryOp>{<ELiteralValue>{\"A\"} + <EBinaryOp>{<EParen>{(<EBetweenOp>{<ELiteralValue>{\"B\"} Not Between <EBinaryOp>{<ELiteralValue>{37} - <ELiteralValue>{15}} And <ELiteralValue>{52}})} / <ELiteralValue>{4}}}");
 
 
@@ -324,13 +326,13 @@ public class ExpressionatorTest extends TestCase
     validateExpr(exprStr, debugStr, exprStr);
   }
 
-  private static void validateExpr(String exprStr, String debugStr, 
+  private static void validateExpr(String exprStr, String debugStr,
                                    String cleanStr) {
     Expression expr = Expressionator.parse(
         Expressionator.Type.FIELD_VALIDATOR, exprStr, null);
     String foundDebugStr = expr.toDebugString();
     if(foundDebugStr.startsWith("<EImplicitCompOp>")) {
-      assertEquals("<EImplicitCompOp>{<EThisValue>{<THIS_COL>} = " + 
+      assertEquals("<EImplicitCompOp>{<EThisValue>{<THIS_COL>} = " +
                    debugStr + "}", foundDebugStr);
     } else {
       assertEquals(debugStr, foundDebugStr);
@@ -365,7 +367,7 @@ public class ExpressionatorTest extends TestCase
     return new BigDecimal(d).setScale(0, BuiltinOperators.ROUND_MODE)
       .intValueExact();
   }
-  
+
   private static final class TestParseContext implements Expressionator.ParseContext
   {
     public TemporalConfig getTemporalConfig() {
@@ -386,6 +388,7 @@ public class ExpressionatorTest extends TestCase
   {
     private final Value _thisVal;
     private final RandomContext _rndCtx = new RandomContext();
+    private final Bindings _bindings = new SimpleBindings();
 
     private TestEvalContext(Value thisVal) {
       _thisVal = thisVal;
@@ -418,6 +421,18 @@ public class ExpressionatorTest extends TestCase
 
     public float getRandom(Integer seed) {
       return _rndCtx.getRandom(seed);
-    } 
+    }
+
+    public Bindings getBindings() {
+      return _bindings;
+    }
+
+    public Object get(String key) {
+      return _bindings.get(key);
+    }
+
+    public void put(String key, Object value) {
+      _bindings.put(key, value);
+    }
   }
 }