aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJames Ahlborn <jtahlborn@yahoo.com>2018-05-19 20:32:13 +0000
committerJames Ahlborn <jtahlborn@yahoo.com>2018-05-19 20:32:13 +0000
commita0e9ce63ee510723c60d2db8ce6569c2cca356ea (patch)
treef923f353fdee7376fddc402310375e71a93eff2f /src
parent6c0003c01cbe43680f65b4d365c47c3a64573978 (diff)
downloadjackcess-a0e9ce63ee510723c60d2db8ce6569c2cca356ea.tar.gz
jackcess-a0e9ce63ee510723c60d2db8ce6569c2cca356ea.zip
knock out some fixmes; add support for custom function lookup and custom bindings passed into expression evaluation
git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/branches/exprs@1151 f203690c-595d-4dc9-a70b-905162fa7fd2
Diffstat (limited to 'src')
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/Database.java11
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/expr/EvalConfig.java12
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/expr/EvalContext.java9
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/expr/FunctionLookup.java26
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/impl/BaseEvalContext.java13
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/impl/DBEvalContext.java27
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/impl/expr/DefaultFunctions.java35
-rw-r--r--src/main/java/com/healthmarketscience/jackcess/impl/expr/RandomContext.java6
-rw-r--r--src/test/java/com/healthmarketscience/jackcess/impl/expr/ExpressionatorTest.java51
9 files changed, 142 insertions, 48 deletions
diff --git a/src/main/java/com/healthmarketscience/jackcess/Database.java b/src/main/java/com/healthmarketscience/jackcess/Database.java
index a146154..d853fe8 100644
--- a/src/main/java/com/healthmarketscience/jackcess/Database.java
+++ b/src/main/java/com/healthmarketscience/jackcess/Database.java
@@ -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);
/**
diff --git a/src/main/java/com/healthmarketscience/jackcess/expr/EvalConfig.java b/src/main/java/com/healthmarketscience/jackcess/expr/EvalConfig.java
index e83fbbc..07ac492 100644
--- a/src/main/java/com/healthmarketscience/jackcess/expr/EvalConfig.java
+++ b/src/main/java/com/healthmarketscience/jackcess/expr/EvalConfig.java
@@ -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);
}
diff --git a/src/main/java/com/healthmarketscience/jackcess/expr/EvalContext.java b/src/main/java/com/healthmarketscience/jackcess/expr/EvalContext.java
index c168f1c..f1dbab3 100644
--- a/src/main/java/com/healthmarketscience/jackcess/expr/EvalContext.java
+++ b/src/main/java/com/healthmarketscience/jackcess/expr/EvalContext.java
@@ -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
index 0000000..8314c41
--- /dev/null
+++ b/src/main/java/com/healthmarketscience/jackcess/expr/FunctionLookup.java
@@ -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);
+}
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/BaseEvalContext.java b/src/main/java/com/healthmarketscience/jackcess/impl/BaseEvalContext.java
index 30de2a1..230afe2 100644
--- a/src/main/java/com/healthmarketscience/jackcess/impl/BaseEvalContext.java
+++ b/src/main/java/com/healthmarketscience/jackcess/impl/BaseEvalContext.java
@@ -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);
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/DBEvalContext.java b/src/main/java/com/healthmarketscience/jackcess/impl/DBEvalContext.java
index 2aabd01..ab8a2d4 100644
--- a/src/main/java/com/healthmarketscience/jackcess/impl/DBEvalContext.java
+++ b/src/main/java/com/healthmarketscience/jackcess/impl/DBEvalContext.java
@@ -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);
}
}
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/expr/DefaultFunctions.java b/src/main/java/com/healthmarketscience/jackcess/impl/expr/DefaultFunctions.java
index 35b8428..a10b557 100644
--- a/src/main/java/com/healthmarketscience/jackcess/impl/expr/DefaultFunctions.java
+++ b/src/main/java/com/healthmarketscience/jackcess/impl/expr/DefaultFunctions.java
@@ -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
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/expr/RandomContext.java b/src/main/java/com/healthmarketscience/jackcess/impl/expr/RandomContext.java
index b3a21c2..71a3f6d 100644
--- a/src/main/java/com/healthmarketscience/jackcess/impl/expr/RandomContext.java
+++ b/src/main/java/com/healthmarketscience/jackcess/impl/expr/RandomContext.java
@@ -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);
}
diff --git a/src/test/java/com/healthmarketscience/jackcess/impl/expr/ExpressionatorTest.java b/src/test/java/com/healthmarketscience/jackcess/impl/expr/ExpressionatorTest.java
index d779d5a..f659a86 100644
--- a/src/test/java/com/healthmarketscience/jackcess/impl/expr/ExpressionatorTest.java
+++ b/src/test/java/com/healthmarketscience/jackcess/impl/expr/ExpressionatorTest.java
@@ -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);
+ }
}
}