diff --git a/kie-yard/kie-yard-core/pom.xml b/kie-yard/kie-yard-core/pom.xml index 5eeaa6596c7..4d6981620e3 100644 --- a/kie-yard/kie-yard-core/pom.xml +++ b/kie-yard/kie-yard-core/pom.xml @@ -44,6 +44,10 @@ ch.obermuhlner jshell-scriptengine + + org.mvel + mvel2 + com.fasterxml.jackson.dataformat jackson-dataformat-yaml @@ -58,6 +62,11 @@ assertj-core test + + org.mockito + mockito-core + test + ch.qos.logback logback-classic diff --git a/kie-yard/kie-yard-core/src/main/java/org/kie/yard/core/LiteralExpressionInterpreter.java b/kie-yard/kie-yard-core/src/main/java/org/kie/yard/core/JShellLiteralExpressionInterpreter.java similarity index 82% rename from kie-yard/kie-yard-core/src/main/java/org/kie/yard/core/LiteralExpressionInterpreter.java rename to kie-yard/kie-yard-core/src/main/java/org/kie/yard/core/JShellLiteralExpressionInterpreter.java index 31d65ece5b6..eca81a063df 100644 --- a/kie-yard/kie-yard-core/src/main/java/org/kie/yard/core/LiteralExpressionInterpreter.java +++ b/kie-yard/kie-yard-core/src/main/java/org/kie/yard/core/JShellLiteralExpressionInterpreter.java @@ -28,20 +28,21 @@ import javax.script.ScriptEngineManager; import javax.script.ScriptException; -public class LiteralExpressionInterpreter implements Firable { +public class JShellLiteralExpressionInterpreter implements Firable { private final String name; private final QuotedExprParsed quoted; private final ScriptEngine engine; private final CompiledScript compiledScript; - public LiteralExpressionInterpreter(String nameString, QuotedExprParsed quotedExprParsed) { + public JShellLiteralExpressionInterpreter(final String nameString, + final QuotedExprParsed quotedExprParsed) { this.name = nameString; this.quoted = quotedExprParsed; try { - ScriptEngineManager manager = new ScriptEngineManager(); + final ScriptEngineManager manager = new ScriptEngineManager(); engine = manager.getEngineByName("jshell"); - Compilable compiler = (Compilable) engine; + final Compilable compiler = (Compilable) engine; compiledScript = compiler.compile(quoted.getRewrittenExpression()); } catch (Exception e) { throw new IllegalArgumentException("parse error", e); @@ -49,8 +50,9 @@ public LiteralExpressionInterpreter(String nameString, QuotedExprParsed quotedEx } @Override - public int fire(Map context, YaRDDefinitions units) { - Bindings bindings = engine.createBindings(); + public int fire(final Map context, + final YaRDDefinitions units) { + final Bindings bindings = engine.createBindings(); // deliberately escape all symbols; a normal symbol will // never be in the detected-by-unquoting set, so this // set can't be used to selectively put in scope diff --git a/kie-yard/kie-yard-core/src/main/java/org/kie/yard/core/LiteralExpressionBuilder.java b/kie-yard/kie-yard-core/src/main/java/org/kie/yard/core/LiteralExpressionBuilder.java index 6ac46f4e38b..f81eefbe1ff 100644 --- a/kie-yard/kie-yard-core/src/main/java/org/kie/yard/core/LiteralExpressionBuilder.java +++ b/kie-yard/kie-yard-core/src/main/java/org/kie/yard/core/LiteralExpressionBuilder.java @@ -20,21 +20,33 @@ import org.kie.yard.api.model.LiteralExpression; +import java.util.Objects; + public class LiteralExpressionBuilder { + private final String expressionLang; private final YaRDDefinitions definitions; private final String name; private final LiteralExpression decisionLogic; - public LiteralExpressionBuilder(YaRDDefinitions definitions, String name, LiteralExpression decisionLogic) { + public LiteralExpressionBuilder(final String expressionLang, + final YaRDDefinitions definitions, + final String name, + final LiteralExpression decisionLogic) { + this.expressionLang = expressionLang; this.definitions = definitions; this.name = name; this.decisionLogic = decisionLogic; } public Firable build() { - String expr = decisionLogic.getExpression(); + final String expr = decisionLogic.getExpression(); definitions.outputs().put(name, StoreHandle.empty(Object.class)); - return new LiteralExpressionInterpreter(name, QuotedExprParsed.from(expr)); + if(Objects.equals(expressionLang, "jshell")){ + return new JShellLiteralExpressionInterpreter(name, QuotedExprParsed.from(expr)); + } + else { + return new MVELLiteralExpressionInterpreter(name,QuotedExprParsed.from(expr)); + } } } diff --git a/kie-yard/kie-yard-core/src/main/java/org/kie/yard/core/MVELLiteralExpressionInterpreter.java b/kie-yard/kie-yard-core/src/main/java/org/kie/yard/core/MVELLiteralExpressionInterpreter.java new file mode 100644 index 00000000000..70bd6e3a152 --- /dev/null +++ b/kie-yard/kie-yard-core/src/main/java/org/kie/yard/core/MVELLiteralExpressionInterpreter.java @@ -0,0 +1,43 @@ +package org.kie.yard.core; + +import org.drools.base.util.MVELExecutor; +import org.mvel2.MVEL; + +import javax.script.Bindings; +import java.util.HashMap; +import java.util.Map; + +public class MVELLiteralExpressionInterpreter implements Firable { + private final String name; + private final QuotedExprParsed expr; + + public MVELLiteralExpressionInterpreter(final String name, + final QuotedExprParsed expr) { + this.name = name; + this.expr = expr; + } + + @Override + public int fire(final Map context, + final YaRDDefinitions units) { + final Map internalContext = new HashMap<>(); + internalContext.putAll(context); + + for (Map.Entry> outKV : units.outputs().entrySet()) { + if (!outKV.getValue().isValuePresent()) { + continue; + } + internalContext.put(QuotedExprParsed.escapeIdentifier(outKV.getKey()), outKV.getValue().get()); + } + + try { + String rewrittenExpression = expr.getRewrittenExpression(); + final Object result = MVEL.eval(rewrittenExpression, internalContext); + units.outputs().get(name).set(result); + return 1; + } catch (Exception e) { + throw new RuntimeException("interpretation failed at runtime", e); + // TODO why throw and not return 0? + } + } +} diff --git a/kie-yard/kie-yard-core/src/main/java/org/kie/yard/core/YaRDParser.java b/kie-yard/kie-yard-core/src/main/java/org/kie/yard/core/YaRDParser.java index 9ebbc9752dc..6f8076fd43d 100644 --- a/kie-yard/kie-yard-core/src/main/java/org/kie/yard/core/YaRDParser.java +++ b/kie-yard/kie-yard-core/src/main/java/org/kie/yard/core/YaRDParser.java @@ -86,9 +86,6 @@ private YaRD getModel(String yaml) throws IOException { private YaRDDefinitions parse(String yaml) throws IOException { final YaRD sd = new YaRD_YamlMapperImpl().read(yaml); - if (!Objects.equals(sd.getExpressionLang(), "jshell")) { - throw new IllegalArgumentException("Only `jshell` is supported as an expression language"); - } appendInputs(sd.getInputs()); appendUnits(sd.getElements()); return definitions; @@ -107,7 +104,7 @@ private Firable createDecisionLogic(String nameString, DecisionLogic decisionLog if (decisionLogic instanceof org.kie.yard.api.model.DecisionTable decisionTable) { return new SyntheticRuleUnitWrapper(new DTableUnitBuilder(definitions, nameString, decisionTable).build()); } else if (decisionLogic instanceof org.kie.yard.api.model.LiteralExpression literalExpression) { - return new LiteralExpressionBuilder(definitions, nameString, literalExpression).build(); + return new LiteralExpressionBuilder(model.getExpressionLang(), definitions, nameString, literalExpression).build(); } else { throw new UnsupportedOperationException("Not implemented."); } diff --git a/kie-yard/kie-yard-core/src/test/java/org/kie/yard/core/InsuranceBasePriceTest.java b/kie-yard/kie-yard-core/src/test/java/org/kie/yard/core/InsuranceBasePriceTest.java index ed0195e239d..cfed2129397 100644 --- a/kie-yard/kie-yard-core/src/test/java/org/kie/yard/core/InsuranceBasePriceTest.java +++ b/kie-yard/kie-yard-core/src/test/java/org/kie/yard/core/InsuranceBasePriceTest.java @@ -39,7 +39,7 @@ public void testScenario1() throws Exception { """; Map outputJSONasMap = evaluate(CTX, FILE_NAME); assertThat(outputJSONasMap).hasFieldOrPropertyWithValue("Base price", 500); - assertThat(outputJSONasMap).hasFieldOrPropertyWithValue("Downpayment", 50.0); + assertThat(outputJSONasMap).hasFieldOrPropertyWithValue("Downpayment", 50); } @Test @@ -52,6 +52,6 @@ public void testScenario2() throws Exception { """; Map outputJSONasMap = evaluate(CTX, FILE_NAME); assertThat(outputJSONasMap).hasFieldOrPropertyWithValue("Base price", 1000); - assertThat(outputJSONasMap).hasFieldOrPropertyWithValue("Downpayment", 70.0); + assertThat(outputJSONasMap).hasFieldOrPropertyWithValue("Downpayment", 70); } } diff --git a/kie-yard/kie-yard-core/src/test/java/org/kie/yard/core/MVELLiteralExpressionInterpreterTest.java b/kie-yard/kie-yard-core/src/test/java/org/kie/yard/core/MVELLiteralExpressionInterpreterTest.java new file mode 100644 index 00000000000..63227755699 --- /dev/null +++ b/kie-yard/kie-yard-core/src/test/java/org/kie/yard/core/MVELLiteralExpressionInterpreterTest.java @@ -0,0 +1,81 @@ +package org.kie.yard.core; + +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.*; + +public class MVELLiteralExpressionInterpreterTest { + + + private MVELLiteralExpressionInterpreter sum; + private YaRDDefinitions yardDefinitions; + + void setUp(final String expression) { + sum = new MVELLiteralExpressionInterpreter("sum", QuotedExprParsed.from(expression)); + + final Map> outputs = new HashMap<>(); + outputs.put("C", StoreHandle.of(5)); + outputs.put("sum", StoreHandle.empty(Object.class)); + yardDefinitions = new YaRDDefinitions(Collections.emptyMap(), Collections.EMPTY_LIST, outputs); + + } + + @Test + public void testSum() { + + setUp("1+1"); + + final int fire = sum.fire(Collections.emptyMap(), yardDefinitions); + + assertEquals(1, fire); + assertEquals(2, yardDefinitions.outputs().get("sum").get()); + } + + @Test + public void testOutputVar() { + + setUp("Math.max(C, 1)"); + + final int fire = sum.fire(Collections.emptyMap(), yardDefinitions); + + assertEquals(1, fire); + assertEquals(5, yardDefinitions.outputs().get("sum").get()); + } + + @Test + public void testContext() { + + setUp("A+B"); + + final Map context = new HashMap<>(); + context.put("A", 1); + context.put("B", 2); + final int fire = sum.fire(context, yardDefinitions); + + assertEquals(1, fire); + assertEquals(3, yardDefinitions.outputs().get("sum").get()); + } + + @Test + public void testMaps() { + + setUp("person.address.street + ' ' + person.address.number"); + + final Map context = new HashMap<>(); + final Map person = new HashMap<>(); + final Map address = new HashMap<>(); + address.put("street", "My Street"); + address.put("number", 12); + person.put("address", address); + context.put("person", person); + final int fire = sum.fire(context, yardDefinitions); + + assertEquals(1, fire); + assertEquals("My Street 12", yardDefinitions.outputs().get("sum").get()); + } +} \ No newline at end of file diff --git a/kie-yard/kie-yard-core/src/test/resources/domestic-package-prices.yml b/kie-yard/kie-yard-core/src/test/resources/domestic-package-prices.yml index 8b921b676cd..7f5257613b3 100644 --- a/kie-yard/kie-yard-core/src/test/resources/domestic-package-prices.yml +++ b/kie-yard/kie-yard-core/src/test/resources/domestic-package-prices.yml @@ -1,7 +1,6 @@ specVersion: alpha kind: YaRD name: "Traffic Violation" -expressionLang: jshell inputs: - name: "Length" type: integer diff --git a/kie-yard/kie-yard-core/src/test/resources/insurance-base-price.yml b/kie-yard/kie-yard-core/src/test/resources/insurance-base-price.yml index b6efed66d95..263fd92883d 100644 --- a/kie-yard/kie-yard-core/src/test/resources/insurance-base-price.yml +++ b/kie-yard/kie-yard-core/src/test/resources/insurance-base-price.yml @@ -1,7 +1,6 @@ specVersion: alpha kind: YaRD name: 'BasePrice' -expressionLang: jshell inputs: - name: 'Age' type: number diff --git a/kie-yard/kie-yard-core/src/test/resources/logback-test.xml b/kie-yard/kie-yard-core/src/test/resources/logback-test.xml deleted file mode 100644 index 157ea2b895a..00000000000 --- a/kie-yard/kie-yard-core/src/test/resources/logback-test.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - %date{HH:mm:ss.SSS} [%thread] %-5level %class{36}.%method:%line - %msg%n - - - - - - - - - - - - diff --git a/kie-yard/kie-yard-core/src/test/resources/ticket-score-cards.yml b/kie-yard/kie-yard-core/src/test/resources/ticket-score-cards.yml index 65d4fb75597..2dd17e66597 100644 --- a/kie-yard/kie-yard-core/src/test/resources/ticket-score-cards.yml +++ b/kie-yard/kie-yard-core/src/test/resources/ticket-score-cards.yml @@ -1,7 +1,6 @@ specVersion: alpha kind: YaRD name: "Traffic Violation" -expressionLang: jshell inputs: - name: "Tickets" type: List