*/
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);
/**
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);
}
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();
public Value getThisColumnValue();
public Value getIdentifierValue(Identifier identifier);
+
+ public Bindings getBindings();
+
+ public Object get(String key);
+
+ public void put(String key, Object value);
}
--- /dev/null
+/*
+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);
+}
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;
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);
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;
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;
}
_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) {
}
public Function getExpressionFunction(String name) {
- // FIXME, support custom function context?
- return DefaultFunctions.getFunction(name);
+ return _funcs.getFunction(name);
}
}
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;
*
* @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();
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) {
paramStr.substring(1, paramStr.length() - 1) + ")}";
return new IllegalStateException(msg, t);
}
-
+
@Override
public String toString() {
return getName() + "()";
}
}
- protected abstract Value eval3(EvalContext ctx,
+ protected abstract Value eval3(EvalContext ctx,
Value param1, Value param2, Value param3);
}
}
}
-
+
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);
return params[idx];
}
});
-
+
public static final Function SWITCH = registerFunc(new FuncVar("Switch") {
@Override
protected Value evalVar(EvalContext ctx, Value[] params) {
return BuiltinOperators.NULL_VAL;
}
});
-
+
public static final Function OCT = registerStringFunc(new Func1NullIsNull("Oct") {
@Override
protected Value eval1(EvalContext ctx, Value param1) {
return BuiltinOperators.toValue(Integer.toOctalString(lv));
}
});
-
+
public static final Function CBOOL = registerFunc(new Func1("CBool") {
@Override
protected Value eval1(EvalContext ctx, Value param1) {
case BIG_DEC:
// vbDecimal
vType = 14;
- break;
+ break;
default:
throw new EvalException("Unknown type " + type);
}
break;
case BIG_DEC:
tName = "Decimal";
- break;
+ break;
default:
throw new EvalException("Unknown type " + type);
}
}
});
-
+
// https://www.techonthenet.com/access/functions/
// https://support.office.com/en-us/article/Access-Functions-by-category-b8b136c3-2716-4d39-94a2-658ce330ed83
*
* @author James Ahlborn
*/
-public class RandomContext
+public class RandomContext
{
private Source _defRnd;
private Map<Integer,Source> _rnds;
// returned yet
private float _lastVal = 1.953125E-02f;
- public RandomContext()
+ public RandomContext()
{
}
}
private static Random createRandom(long seed) {
- // FIXME, support SecureRandom?
+ // TODO, support SecureRandom?
return new Random(seed);
}
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;
*
* @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,
public void testParseSimpleExprs() throws Exception
{
validateExpr("\"A\"", "<ELiteralValue>{\"A\"}");
-
+
validateExpr("13", "<ELiteralValue>{13}");
validateExpr("-42", "<EUnaryOp>{- <ELiteralValue>{42}}");
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\"}}");
}
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}}}");
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);
return new BigDecimal(d).setScale(0, BuiltinOperators.ROUND_MODE)
.intValueExact();
}
-
+
private static final class TestParseContext implements Expressionator.ParseContext
{
public TemporalConfig getTemporalConfig() {
{
private final Value _thisVal;
private final RandomContext _rndCtx = new RandomContext();
+ private final Bindings _bindings = new SimpleBindings();
private TestEvalContext(Value thisVal) {
_thisVal = thisVal;
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);
+ }
}
}