/* 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; import java.util.ArrayList; import java.util.List; import java.util.Locale; import javax.script.Bindings; import javax.script.SimpleBindings; import com.healthmarketscience.jackcess.expr.EvalConfig; import com.healthmarketscience.jackcess.expr.EvalContext; import com.healthmarketscience.jackcess.expr.Function; import com.healthmarketscience.jackcess.expr.FunctionLookup; import com.healthmarketscience.jackcess.expr.TemporalConfig; import com.healthmarketscience.jackcess.expr.Value; import com.healthmarketscience.jackcess.impl.expr.DefaultFunctions; import com.healthmarketscience.jackcess.impl.expr.FunctionSupport; import com.healthmarketscience.jackcess.impl.expr.ValueSupport; import junit.framework.TestCase; import static com.healthmarketscience.jackcess.Database.*; import static com.healthmarketscience.jackcess.TestUtil.*; import static com.healthmarketscience.jackcess.impl.JetFormatTest.*; import static com.healthmarketscience.jackcess.DatabaseBuilder.*; /** * * @author James Ahlborn */ public class PropertyExpressionTest extends TestCase { public PropertyExpressionTest(String name) { super(name); } public void testDefaultValue() throws Exception { for (final FileFormat fileFormat : SUPPORTED_FILEFORMATS) { Database db = create(fileFormat); db.setEvaluateExpressions(true); Table t = newTable("test") .addColumn(newColumn("id", DataType.LONG).setAutoNumber(true)) .addColumn(newColumn("data1", DataType.TEXT) .putProperty(PropertyMap.DEFAULT_VALUE_PROP, "=\"FOO \" & \"BAR\"")) .addColumn(newColumn("data2", DataType.LONG) .putProperty(PropertyMap.DEFAULT_VALUE_PROP, "37")) .toTable(db); t.addRow(Column.AUTO_NUMBER, null, 13); t.addRow(Column.AUTO_NUMBER, "blah", null); setProp(t, "data1", PropertyMap.DEFAULT_VALUE_PROP, null); setProp(t, "data2", PropertyMap.DEFAULT_VALUE_PROP, "42"); t.addRow(Column.AUTO_NUMBER, null, null); List expectedRows = createExpectedTable( createExpectedRow( "id", 1, "data1", "FOO BAR", "data2", 13), createExpectedRow( "id", 2, "data1", "blah", "data2", 37), createExpectedRow( "id", 3, "data1", null, "data2", 42)); assertTable(expectedRows, t); setProp(t, "data2", PropertyMap.REQUIRED_PROP, true); t.addRow(Column.AUTO_NUMBER, "blah", 13); t.addRow(Column.AUTO_NUMBER, "blah", null); expectedRows = new ArrayList(expectedRows); expectedRows.add( createExpectedRow( "id", 4, "data1", "blah", "data2", 13)); expectedRows.add( createExpectedRow( "id", 5, "data1", "blah", "data2", 42)); assertTable(expectedRows, t); db.close(); } } public void testCalculatedValue() throws Exception { Database db = create(FileFormat.V2016); db.setEvaluateExpressions(true); Table t = newTable("test") .addColumn(newColumn("id", DataType.LONG).setAutoNumber(true)) .addColumn(newColumn("c1", DataType.LONG) .setCalculatedInfo("[c2]+[c3]")) .addColumn(newColumn("c2", DataType.LONG) .setCalculatedInfo("[c3]*5")) .addColumn(newColumn("c3", DataType.LONG) .setCalculatedInfo("[c4]-6")) .addColumn(newColumn("c4", DataType.LONG)) .toTable(db); t.addRow(Column.AUTO_NUMBER, null, null, null, 16); setProp(t, "c1", PropertyMap.EXPRESSION_PROP, "[c4]+2"); setProp(t, "c2", PropertyMap.EXPRESSION_PROP, "[c1]+[c3]"); setProp(t, "c3", PropertyMap.EXPRESSION_PROP, "[c1]*7"); t.addRow(Column.AUTO_NUMBER, null, null, null, 7); List expectedRows = createExpectedTable( createExpectedRow( "id", 1, "c1", 60, "c2", 50, "c3", 10, "c4", 16), createExpectedRow( "id", 2, "c1", 9, "c2", 72, "c3", 63, "c4", 7)); assertTable(expectedRows, t); db.close(); } public void testColumnValidator() throws Exception { for (final FileFormat fileFormat : SUPPORTED_FILEFORMATS) { Database db = create(fileFormat); db.setEvaluateExpressions(true); Table t = newTable("test") .addColumn(newColumn("id", DataType.LONG).setAutoNumber(true)) .addColumn(newColumn("data1", DataType.LONG) .putProperty(PropertyMap.VALIDATION_RULE_PROP, ">37")) .addColumn(newColumn("data2", DataType.LONG) .putProperty(PropertyMap.VALIDATION_RULE_PROP, "between 7 and 10") .putProperty(PropertyMap.VALIDATION_TEXT_PROP, "You failed")) .toTable(db); t.addRow(Column.AUTO_NUMBER, 42, 8); try { t.addRow(Column.AUTO_NUMBER, 42, 20); fail("InvalidValueException should have been thrown"); } catch(InvalidValueException ive) { // success assertTrue(ive.getMessage().contains("You failed")); } try { t.addRow(Column.AUTO_NUMBER, 3, 8); fail("InvalidValueException should have been thrown"); } catch(InvalidValueException ive) { // success assertFalse(ive.getMessage().contains("You failed")); } t.addRow(Column.AUTO_NUMBER, 54, 9); setProp(t, "data1", PropertyMap.VALIDATION_RULE_PROP, null); setProp(t, "data2", PropertyMap.VALIDATION_RULE_PROP, "<100"); setProp(t, "data2", PropertyMap.VALIDATION_TEXT_PROP, "Too big"); try { t.addRow(Column.AUTO_NUMBER, 42, 200); fail("InvalidValueException should have been thrown"); } catch(InvalidValueException ive) { // success assertTrue(ive.getMessage().contains("Too big")); } t.addRow(Column.AUTO_NUMBER, 1, 9); List expectedRows = createExpectedTable( createExpectedRow( "id", 1, "data1", 42, "data2", 8), createExpectedRow( "id", 2, "data1", 54, "data2", 9), createExpectedRow( "id", 3, "data1", 1, "data2", 9)); assertTable(expectedRows, t); db.close(); } } public void testRowValidator() throws Exception { for (final FileFormat fileFormat : SUPPORTED_FILEFORMATS) { Database db = create(fileFormat); db.setEvaluateExpressions(true); Table t = newTable("test") .addColumn(newColumn("id", DataType.LONG).setAutoNumber(true)) .addColumn(newColumn("data1", DataType.LONG)) .addColumn(newColumn("data2", DataType.LONG)) .putProperty(PropertyMap.VALIDATION_RULE_PROP, "([data1] > 10) and ([data2] < 100)") .putProperty(PropertyMap.VALIDATION_TEXT_PROP, "You failed") .toTable(db); t.addRow(Column.AUTO_NUMBER, 42, 8); try { t.addRow(Column.AUTO_NUMBER, 1, 20); fail("InvalidValueException should have been thrown"); } catch(InvalidValueException ive) { // success assertTrue(ive.getMessage().contains("You failed")); } t.addRow(Column.AUTO_NUMBER, 54, 9); setTableProp(t, PropertyMap.VALIDATION_RULE_PROP, "[data2]<100"); setTableProp(t, PropertyMap.VALIDATION_TEXT_PROP, "Too big"); try { t.addRow(Column.AUTO_NUMBER, 42, 200); fail("InvalidValueException should have been thrown"); } catch(InvalidValueException ive) { // success assertTrue(ive.getMessage().contains("Too big")); } t.addRow(Column.AUTO_NUMBER, 1, 9); List expectedRows = createExpectedTable( createExpectedRow( "id", 1, "data1", 42, "data2", 8), createExpectedRow( "id", 2, "data1", 54, "data2", 9), createExpectedRow( "id", 3, "data1", 1, "data2", 9)); assertTable(expectedRows, t); db.close(); } } public static void testCustomEvalConfig() throws Exception { TemporalConfig tempConf = new TemporalConfig("[uuuu/]M/d", "uuuu-MMM-d", "hh.mm.ss a", "HH.mm.ss", '/', '.', Locale.US); FunctionLookup lookup = new FunctionLookup() { public Function getFunction(String name) { if("FooFunc".equalsIgnoreCase(name)) { return FOO; } return DefaultFunctions.LOOKUP.getFunction(name); } }; Bindings bindings = new SimpleBindings(); bindings.put("someKey", "someVal"); for (final FileFormat fileFormat : SUPPORTED_FILEFORMATS) { Database db = create(fileFormat); EvalConfig ec = db.getEvalConfig(); ec.setTemporalConfig(tempConf); ec.setFunctionLookup(lookup); ec.setBindings(bindings); db.setEvaluateExpressions(true); Table t = newTable("test") .addColumn(newColumn("id", DataType.LONG).setAutoNumber(true)) .addColumn(newColumn("data1", DataType.TEXT) .putProperty(PropertyMap.DEFAULT_VALUE_PROP, "=FooFunc()")) .addColumn(newColumn("data2", DataType.TEXT) .putProperty(PropertyMap.DEFAULT_VALUE_PROP, "=Date()")) .addColumn(newColumn("data3", DataType.TEXT) .putProperty(PropertyMap.DEFAULT_VALUE_PROP, "=Time()")) .toTable(db); t.addRow(Column.AUTO_NUMBER, null, null); Row row = t.iterator().next(); assertEquals(1, row.get("id")); assertEquals("FOO_someVal", row.get("data1")); assertTrue(((String)row.get("data2")) .matches("\\d{4}/\\d{1,2}/\\d{1,2}")); assertTrue(((String)row.get("data3")) .matches("\\d{2}.\\d{2}.\\d{2} (AM|PM)")); db.close(); } } private static void setProp(Table t, String colName, String propName, Object propVal) throws Exception { PropertyMap props = t.getColumn(colName).getProperties(); if(propVal != null) { props.put(propName, propVal); } else { props.remove(propName); } props.save(); } private static void setTableProp(Table t, String propName, String propVal) throws Exception { PropertyMap props = t.getProperties(); if(propVal != null) { props.put(propName, propVal); } else { props.remove(propName); } props.save(); } private static final Function FOO = new FunctionSupport.Func0("FooFunc") { @Override public boolean isPure() { return false; } @Override protected Value eval0(EvalContext ctx) { Object val = ctx.get("someKey"); return ValueSupport.toValue("FOO_" + val); } }; }