From 45c48188bc3b3db9d06b59d213b8869f8d7df26a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Wo=C5=BAniak?= Date: Wed, 23 Jan 2019 01:21:52 +0100 Subject: [PATCH] Added support for providing custom functions in Expressions. Resolved #133 --- pom.xml | 13 +-- .../DefaultDecisionEngineConfiguration.java | 32 +++--- .../evaluator/entry/InputEntryEvaluator.java | 6 +- .../evaluator/entry/OutputEntryEvaluator.java | 6 +- ...ltExpressionEvaluationProviderFactory.java | 66 +++++++++++++ .../ExpressionEvaluationConfiguration.java | 41 ++++++++ .../ExpressionEvaluationProviderFactory.java | 29 +----- ...elExpressionEvaluationProviderFactory.java | 35 +++++++ .../GroovyExpressionEvaluationProvider.java | 39 ++++++++ ...vyExpressionEvaluationProviderFactory.java | 35 +++++++ ...avascriptExpressionEvaluationProvider.java | 37 +++++++ ...ptExpressionEvaluationProviderFactory.java | 36 +++++++ ...elExpressionEvaluationProviderFactory.java | 35 +++++++ ...alExpressionEvaluationProviderFactory.java | 35 +++++++ ...ptEngineExpressionEvaluationProvider.java} | 27 +++-- .../binding/AbstractMethodBinding.java | 46 +++++++++ .../binding/BoundMethod.java} | 13 +-- .../ExpressionEvaluationException.java | 22 +++++ .../binding/InstanceMethodBinding.java | 30 ++++++ .../provider/binding/MethodBinding.java | 20 ++++ .../provider/binding/StaticMethodBinding.java | 31 ++++++ .../script/DefaultScriptEngineProvider.java | 42 -------- ...ovider.ExpressionEvaluationProviderFactory | 5 + ...ineConfigurationReferenceSingleSpec.groovy | 13 ++- ...ssionEvaluationProviderFactorySpec.groovy} | 26 ++++- ...ovyExpressionEvaluationProviderSpec.groovy | 89 ++++++++++++++--- ...ptExpressionEvaluationProviderSpec.groovy} | 80 ++++++++++++--- .../provider/binding/MethodBindingSpec.groovy | 99 +++++++++++++++++++ .../DefaultScriptEngineProviderSpec.groovy | 58 ----------- .../ContextVariablesBindingsSpec.groovy | 6 +- .../dmn/engine/configuration/TestMethods.java | 26 +++++ .../provider/sample/MethodSource.java | 37 +++++++ .../engine/configuration/reference-single.yml | 2 +- 33 files changed, 910 insertions(+), 207 deletions(-) create mode 100644 src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/DefaultExpressionEvaluationProviderFactory.java create mode 100644 src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/ExpressionEvaluationConfiguration.java create mode 100644 src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/FeelExpressionEvaluationProviderFactory.java create mode 100644 src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/GroovyExpressionEvaluationProvider.java create mode 100644 src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/GroovyExpressionEvaluationProviderFactory.java create mode 100644 src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/JavascriptExpressionEvaluationProvider.java create mode 100644 src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/JavascriptExpressionEvaluationProviderFactory.java create mode 100644 src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/JuelExpressionEvaluationProviderFactory.java create mode 100644 src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/LiteralExpressionEvaluationProviderFactory.java rename src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/{ScriptExpressionEvaluationProvider.java => ScriptEngineExpressionEvaluationProvider.java} (77%) create mode 100644 src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/binding/AbstractMethodBinding.java rename src/main/java/org/powerflows/dmn/engine/evaluator/expression/{script/ScriptEngineProvider.java => provider/binding/BoundMethod.java} (69%) create mode 100644 src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/binding/ExpressionEvaluationException.java create mode 100644 src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/binding/InstanceMethodBinding.java create mode 100644 src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/binding/MethodBinding.java create mode 100644 src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/binding/StaticMethodBinding.java delete mode 100644 src/main/java/org/powerflows/dmn/engine/evaluator/expression/script/DefaultScriptEngineProvider.java create mode 100644 src/main/resources/META-INF/services/org.powerflows.dmn.engine.evaluator.expression.provider.ExpressionEvaluationProviderFactory rename src/test/groovy/org/powerflows/dmn/engine/evaluator/expression/provider/{ExpressionEvaluationProviderFactorySpec.groovy => DefaultExpressionEvaluationProviderFactorySpec.groovy} (53%) rename src/test/groovy/org/powerflows/dmn/engine/evaluator/expression/provider/{JavaScriptExpressionEvaluationProviderSpec.groovy => JavascriptExpressionEvaluationProviderSpec.groovy} (59%) create mode 100644 src/test/groovy/org/powerflows/dmn/engine/evaluator/expression/provider/binding/MethodBindingSpec.groovy delete mode 100644 src/test/groovy/org/powerflows/dmn/engine/evaluator/expression/script/DefaultScriptEngineProviderSpec.groovy create mode 100644 src/test/java/org/powerflows/dmn/engine/configuration/TestMethods.java create mode 100644 src/test/java/org/powerflows/dmn/engine/evaluator/expression/provider/sample/MethodSource.java diff --git a/pom.xml b/pom.xml index a8063a6..99666e1 100644 --- a/pom.xml +++ b/pom.xml @@ -108,6 +108,13 @@ provided + + + org.codehaus.groovy + groovy-all + ${groovy.version} + + org.spockframework @@ -121,12 +128,6 @@ ${cglib.version} test - - org.codehaus.groovy - groovy-all - ${groovy.version} - test - org.objenesis objenesis diff --git a/src/main/java/org/powerflows/dmn/engine/configuration/DefaultDecisionEngineConfiguration.java b/src/main/java/org/powerflows/dmn/engine/configuration/DefaultDecisionEngineConfiguration.java index 742ef6a..ed58371 100644 --- a/src/main/java/org/powerflows/dmn/engine/configuration/DefaultDecisionEngineConfiguration.java +++ b/src/main/java/org/powerflows/dmn/engine/configuration/DefaultDecisionEngineConfiguration.java @@ -17,34 +17,40 @@ package org.powerflows.dmn.engine.configuration; +import lombok.Setter; +import lombok.experimental.Accessors; import org.powerflows.dmn.engine.DecisionEngine; import org.powerflows.dmn.engine.DefaultDecisionEngine; import org.powerflows.dmn.engine.evaluator.decision.DecisionEvaluator; import org.powerflows.dmn.engine.evaluator.entry.InputEntryEvaluator; import org.powerflows.dmn.engine.evaluator.entry.OutputEntryEvaluator; import org.powerflows.dmn.engine.evaluator.entry.mode.provider.EvaluationModeProviderFactory; -import org.powerflows.dmn.engine.evaluator.expression.provider.ExpressionEvaluationProviderFactory; -import org.powerflows.dmn.engine.evaluator.expression.script.DefaultScriptEngineProvider; -import org.powerflows.dmn.engine.evaluator.expression.script.ScriptEngineProvider; +import org.powerflows.dmn.engine.evaluator.expression.provider.DefaultExpressionEvaluationProviderFactory; +import org.powerflows.dmn.engine.evaluator.expression.provider.ExpressionEvaluationConfiguration; +import org.powerflows.dmn.engine.evaluator.expression.provider.binding.MethodBinding; import org.powerflows.dmn.engine.evaluator.rule.RuleEvaluator; import org.powerflows.dmn.engine.evaluator.type.converter.TypeConverterFactory; -import javax.script.ScriptEngineManager; +import java.util.Collections; +import java.util.List; +@Accessors(chain = true, fluent = true) public class DefaultDecisionEngineConfiguration implements DecisionEngineConfiguration { + @Setter + private List methodBindings = Collections.emptyList(); + private ExpressionEvaluationConfiguration configuration; private DecisionEvaluator decisionEvaluator; private RuleEvaluator ruleEvaluator; private EvaluationModeProviderFactory evaluationModeProviderFactory; private InputEntryEvaluator inputEntryEvaluator; private OutputEntryEvaluator outputEntryEvaluator; - private ScriptEngineProvider scriptEngineProvider; - private ExpressionEvaluationProviderFactory expressionEvaluationProviderFactory; + private DefaultExpressionEvaluationProviderFactory expressionEvaluationProviderFactory; private TypeConverterFactory typeConverterFactory; @Override public DecisionEngine configure() { - initScriptEngineProvider(); + initExpressionEvaluation(); initEvaluationProviderFactory(); initTypeConverterFactory(); initEvaluationModeProviderFactory(); @@ -56,18 +62,20 @@ public DecisionEngine configure() { return new DefaultDecisionEngine(decisionEvaluator); } + private void initExpressionEvaluation() { + configuration = ExpressionEvaluationConfiguration.builder() + .methodBinding(methodBindings) + .build(); + } + private void initEvaluationProviderFactory() { - expressionEvaluationProviderFactory = new ExpressionEvaluationProviderFactory(scriptEngineProvider); + expressionEvaluationProviderFactory = new DefaultExpressionEvaluationProviderFactory(configuration); } private void initTypeConverterFactory() { typeConverterFactory = new TypeConverterFactory(); } - private void initScriptEngineProvider() { - scriptEngineProvider = new DefaultScriptEngineProvider(new ScriptEngineManager()); - } - private void initEvaluationModeProviderFactory() { evaluationModeProviderFactory = new EvaluationModeProviderFactory(); } diff --git a/src/main/java/org/powerflows/dmn/engine/evaluator/entry/InputEntryEvaluator.java b/src/main/java/org/powerflows/dmn/engine/evaluator/entry/InputEntryEvaluator.java index e8edd43..d6e0a0a 100644 --- a/src/main/java/org/powerflows/dmn/engine/evaluator/entry/InputEntryEvaluator.java +++ b/src/main/java/org/powerflows/dmn/engine/evaluator/entry/InputEntryEvaluator.java @@ -22,7 +22,7 @@ import org.powerflows.dmn.engine.evaluator.entry.mode.provider.EvaluationModeProvider; import org.powerflows.dmn.engine.evaluator.entry.mode.provider.EvaluationModeProviderFactory; import org.powerflows.dmn.engine.evaluator.expression.provider.ExpressionEvaluationProvider; -import org.powerflows.dmn.engine.evaluator.expression.provider.ExpressionEvaluationProviderFactory; +import org.powerflows.dmn.engine.evaluator.expression.provider.DefaultExpressionEvaluationProviderFactory; import org.powerflows.dmn.engine.evaluator.type.converter.TypeConverter; import org.powerflows.dmn.engine.evaluator.type.converter.TypeConverterFactory; import org.powerflows.dmn.engine.evaluator.type.value.SpecifiedTypeValue; @@ -36,12 +36,12 @@ @Slf4j public class InputEntryEvaluator { - private final ExpressionEvaluationProviderFactory expressionEvaluationProviderFactory; + private final DefaultExpressionEvaluationProviderFactory expressionEvaluationProviderFactory; private final TypeConverterFactory typeConverterFactory; private final EvaluationModeProviderFactory evaluationModeProviderFactory; - public InputEntryEvaluator(final ExpressionEvaluationProviderFactory expressionEvaluationProviderFactory, + public InputEntryEvaluator(final DefaultExpressionEvaluationProviderFactory expressionEvaluationProviderFactory, final TypeConverterFactory typeConverterFactory, final EvaluationModeProviderFactory evaluationModeProviderFactory) { this.expressionEvaluationProviderFactory = expressionEvaluationProviderFactory; diff --git a/src/main/java/org/powerflows/dmn/engine/evaluator/entry/OutputEntryEvaluator.java b/src/main/java/org/powerflows/dmn/engine/evaluator/entry/OutputEntryEvaluator.java index 9560032..676959e 100644 --- a/src/main/java/org/powerflows/dmn/engine/evaluator/entry/OutputEntryEvaluator.java +++ b/src/main/java/org/powerflows/dmn/engine/evaluator/entry/OutputEntryEvaluator.java @@ -20,7 +20,7 @@ import lombok.extern.slf4j.Slf4j; import org.powerflows.dmn.engine.evaluator.context.EvaluationContext; import org.powerflows.dmn.engine.evaluator.expression.provider.ExpressionEvaluationProvider; -import org.powerflows.dmn.engine.evaluator.expression.provider.ExpressionEvaluationProviderFactory; +import org.powerflows.dmn.engine.evaluator.expression.provider.DefaultExpressionEvaluationProviderFactory; import org.powerflows.dmn.engine.evaluator.type.converter.TypeConverter; import org.powerflows.dmn.engine.evaluator.type.converter.TypeConverterFactory; import org.powerflows.dmn.engine.model.decision.field.Output; @@ -32,10 +32,10 @@ @Slf4j public class OutputEntryEvaluator { - private final ExpressionEvaluationProviderFactory expressionEvaluationProviderFactory; + private final DefaultExpressionEvaluationProviderFactory expressionEvaluationProviderFactory; private final TypeConverterFactory typeConverterFactory; - public OutputEntryEvaluator(ExpressionEvaluationProviderFactory expressionEvaluationProviderFactory, + public OutputEntryEvaluator(DefaultExpressionEvaluationProviderFactory expressionEvaluationProviderFactory, final TypeConverterFactory typeConverterFactory) { this.expressionEvaluationProviderFactory = expressionEvaluationProviderFactory; this.typeConverterFactory = typeConverterFactory; diff --git a/src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/DefaultExpressionEvaluationProviderFactory.java b/src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/DefaultExpressionEvaluationProviderFactory.java new file mode 100644 index 0000000..a7de1bd --- /dev/null +++ b/src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/DefaultExpressionEvaluationProviderFactory.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2018-present PowerFlows.org - all rights reserved. + * + * 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 org.powerflows.dmn.engine.evaluator.expression.provider; + +import lombok.extern.slf4j.Slf4j; +import org.powerflows.dmn.engine.model.decision.expression.ExpressionType; + +import java.util.EnumMap; +import java.util.Optional; +import java.util.ServiceLoader; + +@Slf4j +public class DefaultExpressionEvaluationProviderFactory { + + private static final ServiceLoader serviceLoader = ServiceLoader.load(ExpressionEvaluationProviderFactory.class); + + private final EnumMap factories = new EnumMap<>(ExpressionType.class); + + private final EnumMap providers = new EnumMap<>(ExpressionType.class); + private final ExpressionEvaluationConfiguration configuration; + + + public DefaultExpressionEvaluationProviderFactory() { + this(ExpressionEvaluationConfiguration.simpleConfiguration()); + } + + public DefaultExpressionEvaluationProviderFactory(final ExpressionEvaluationConfiguration configuration) { + this.configuration = configuration; + serviceLoader.forEach(provider -> + provider.supportedExpressionTypes() + .forEach(type -> { + log.debug("Found ExpressionEvaluationProvider for type {} - {}", type, provider); + factories.put(type, provider); + } + ) + ); + } + + public ExpressionEvaluationProvider getInstance(final ExpressionType expressionType) { + final ExpressionEvaluationProvider expressionEvaluationProvider = providers.computeIfAbsent(expressionType, key -> Optional + .ofNullable(factories.get(key)) + .map(factory -> factory.createProvider(configuration)) + .orElse(null) + ); + + if (expressionEvaluationProvider == null) { + throw new IllegalArgumentException("Unknown expression type " + expressionType); + } + + return expressionEvaluationProvider; + } +} diff --git a/src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/ExpressionEvaluationConfiguration.java b/src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/ExpressionEvaluationConfiguration.java new file mode 100644 index 0000000..322b71c --- /dev/null +++ b/src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/ExpressionEvaluationConfiguration.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2018-present PowerFlows.org - all rights reserved. + * + * 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 org.powerflows.dmn.engine.evaluator.expression.provider; + +import lombok.Builder; +import lombok.Getter; +import org.powerflows.dmn.engine.evaluator.expression.provider.binding.MethodBinding; + +import javax.script.ScriptEngineManager; +import java.util.Collections; +import java.util.List; + +@Getter +@Builder +public class ExpressionEvaluationConfiguration { + + private final List methodBinding; + private final ScriptEngineManager scriptEngineManager; + + private ExpressionEvaluationConfiguration(final List methodBinding, final ScriptEngineManager scriptEngineManager) { + this.methodBinding = methodBinding == null ? Collections.emptyList() : methodBinding; + this.scriptEngineManager = scriptEngineManager == null ? new ScriptEngineManager() : scriptEngineManager; + } + + public static ExpressionEvaluationConfiguration simpleConfiguration() { + return ExpressionEvaluationConfiguration.builder().build(); + } +} \ No newline at end of file diff --git a/src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/ExpressionEvaluationProviderFactory.java b/src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/ExpressionEvaluationProviderFactory.java index 273c95f..25ed39d 100644 --- a/src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/ExpressionEvaluationProviderFactory.java +++ b/src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/ExpressionEvaluationProviderFactory.java @@ -13,35 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.powerflows.dmn.engine.evaluator.expression.provider; -import org.powerflows.dmn.engine.evaluator.expression.script.ScriptEngineProvider; import org.powerflows.dmn.engine.model.decision.expression.ExpressionType; -import java.util.EnumMap; - -public class ExpressionEvaluationProviderFactory { - - private final EnumMap factories = new EnumMap<>(ExpressionType.class); - - public ExpressionEvaluationProviderFactory(final ScriptEngineProvider scriptEngineProvider) { - final ExpressionEvaluationProvider scriptExpressionEvaluationProvider = new ScriptExpressionEvaluationProvider(scriptEngineProvider); - - factories.put(ExpressionType.LITERAL, new LiteralExpressionEvaluationProvider()); - factories.put(ExpressionType.FEEL, new FeelExpressionEvaluationProvider()); - factories.put(ExpressionType.JUEL, new JuelExpressionEvaluationProvider()); - factories.put(ExpressionType.GROOVY, scriptExpressionEvaluationProvider); - factories.put(ExpressionType.JAVASCRIPT, scriptExpressionEvaluationProvider); - } - - public ExpressionEvaluationProvider getInstance(final ExpressionType expressionType) { - final ExpressionEvaluationProvider expressionEvaluationProvider = factories.get(expressionType); +import java.util.List; - if (expressionEvaluationProvider == null) { - throw new IllegalArgumentException("Unknown expression type " + expressionType); - } +public interface ExpressionEvaluationProviderFactory { + ExpressionEvaluationProvider createProvider(ExpressionEvaluationConfiguration configuration); - return expressionEvaluationProvider; - } + List supportedExpressionTypes(); } diff --git a/src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/FeelExpressionEvaluationProviderFactory.java b/src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/FeelExpressionEvaluationProviderFactory.java new file mode 100644 index 0000000..4917472 --- /dev/null +++ b/src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/FeelExpressionEvaluationProviderFactory.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2018-present PowerFlows.org - all rights reserved. + * + * 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 org.powerflows.dmn.engine.evaluator.expression.provider; + +import org.powerflows.dmn.engine.model.decision.expression.ExpressionType; + +import java.util.Collections; +import java.util.List; + +public class FeelExpressionEvaluationProviderFactory implements ExpressionEvaluationProviderFactory { + private static final List SUPPORTED = Collections.singletonList(ExpressionType.FEEL); + + @Override + public ExpressionEvaluationProvider createProvider(final ExpressionEvaluationConfiguration configuration) { + return new FeelExpressionEvaluationProvider(); + } + + @Override + public List supportedExpressionTypes() { + return SUPPORTED; + } +} diff --git a/src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/GroovyExpressionEvaluationProvider.java b/src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/GroovyExpressionEvaluationProvider.java new file mode 100644 index 0000000..40b6590 --- /dev/null +++ b/src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/GroovyExpressionEvaluationProvider.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2018-present PowerFlows.org - all rights reserved. + * + * 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 org.powerflows.dmn.engine.evaluator.expression.provider; + +import lombok.extern.slf4j.Slf4j; +import org.codehaus.groovy.runtime.MethodClosure; +import org.powerflows.dmn.engine.evaluator.expression.provider.binding.MethodBinding; + +@Slf4j +class GroovyExpressionEvaluationProvider extends ScriptEngineExpressionEvaluationProvider { + + public GroovyExpressionEvaluationProvider(final ExpressionEvaluationConfiguration configuration) { + super(configuration); + } + + @Override + protected Object createMethodBinding(final MethodBinding methodBinding) { + return new MethodClosure(methodBinding, "execute"); + } + + @Override + protected String getEngineName() { + return "groovy"; + } +} \ No newline at end of file diff --git a/src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/GroovyExpressionEvaluationProviderFactory.java b/src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/GroovyExpressionEvaluationProviderFactory.java new file mode 100644 index 0000000..213d4ae --- /dev/null +++ b/src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/GroovyExpressionEvaluationProviderFactory.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2018-present PowerFlows.org - all rights reserved. + * + * 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 org.powerflows.dmn.engine.evaluator.expression.provider; + +import org.powerflows.dmn.engine.model.decision.expression.ExpressionType; + +import java.util.Collections; +import java.util.List; + +public class GroovyExpressionEvaluationProviderFactory implements ExpressionEvaluationProviderFactory { + private static final List SUPPORTED = Collections.singletonList(ExpressionType.GROOVY); + + @Override + public ExpressionEvaluationProvider createProvider(final ExpressionEvaluationConfiguration configuration) { + return new GroovyExpressionEvaluationProvider(configuration); + } + + @Override + public List supportedExpressionTypes() { + return SUPPORTED; + } +} diff --git a/src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/JavascriptExpressionEvaluationProvider.java b/src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/JavascriptExpressionEvaluationProvider.java new file mode 100644 index 0000000..6effdc8 --- /dev/null +++ b/src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/JavascriptExpressionEvaluationProvider.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2018-present PowerFlows.org - all rights reserved. + * + * 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 org.powerflows.dmn.engine.evaluator.expression.provider; + +import org.powerflows.dmn.engine.evaluator.expression.provider.binding.BoundMethod; +import org.powerflows.dmn.engine.evaluator.expression.provider.binding.MethodBinding; + +class JavascriptExpressionEvaluationProvider extends ScriptEngineExpressionEvaluationProvider { + + public JavascriptExpressionEvaluationProvider(final ExpressionEvaluationConfiguration configuration) { + super(configuration); + } + + @Override + protected Object createMethodBinding(final MethodBinding methodBinding) { + return (BoundMethod) methodBinding::execute; + } + + @Override + protected String getEngineName() { + return "javascript"; + } +} \ No newline at end of file diff --git a/src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/JavascriptExpressionEvaluationProviderFactory.java b/src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/JavascriptExpressionEvaluationProviderFactory.java new file mode 100644 index 0000000..5605505 --- /dev/null +++ b/src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/JavascriptExpressionEvaluationProviderFactory.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2018-present PowerFlows.org - all rights reserved. + * + * 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 org.powerflows.dmn.engine.evaluator.expression.provider; + +import org.powerflows.dmn.engine.model.decision.expression.ExpressionType; + +import java.util.Collections; +import java.util.List; + +public class JavascriptExpressionEvaluationProviderFactory implements ExpressionEvaluationProviderFactory { + + private static final List SUPPORTED = Collections.singletonList(ExpressionType.JAVASCRIPT); + + @Override + public ExpressionEvaluationProvider createProvider(final ExpressionEvaluationConfiguration configuration) { + return new JavascriptExpressionEvaluationProvider(configuration); + } + + @Override + public List supportedExpressionTypes() { + return SUPPORTED; + } +} diff --git a/src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/JuelExpressionEvaluationProviderFactory.java b/src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/JuelExpressionEvaluationProviderFactory.java new file mode 100644 index 0000000..3f037e8 --- /dev/null +++ b/src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/JuelExpressionEvaluationProviderFactory.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2018-present PowerFlows.org - all rights reserved. + * + * 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 org.powerflows.dmn.engine.evaluator.expression.provider; + +import org.powerflows.dmn.engine.model.decision.expression.ExpressionType; + +import java.util.Collections; +import java.util.List; + +public class JuelExpressionEvaluationProviderFactory implements ExpressionEvaluationProviderFactory { + private static final List SUPPORTED = Collections.singletonList(ExpressionType.JUEL); + + @Override + public ExpressionEvaluationProvider createProvider(final ExpressionEvaluationConfiguration configuration) { + return new JuelExpressionEvaluationProvider(); + } + + @Override + public List supportedExpressionTypes() { + return SUPPORTED; + } +} diff --git a/src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/LiteralExpressionEvaluationProviderFactory.java b/src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/LiteralExpressionEvaluationProviderFactory.java new file mode 100644 index 0000000..19a944f --- /dev/null +++ b/src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/LiteralExpressionEvaluationProviderFactory.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2018-present PowerFlows.org - all rights reserved. + * + * 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 org.powerflows.dmn.engine.evaluator.expression.provider; + +import org.powerflows.dmn.engine.model.decision.expression.ExpressionType; + +import java.util.Collections; +import java.util.List; + +public class LiteralExpressionEvaluationProviderFactory implements ExpressionEvaluationProviderFactory { + private static final List SUPPORTED = Collections.singletonList(ExpressionType.LITERAL); + + @Override + public ExpressionEvaluationProvider createProvider(final ExpressionEvaluationConfiguration configuration) { + return new LiteralExpressionEvaluationProvider(); + } + + @Override + public List supportedExpressionTypes() { + return SUPPORTED; + } +} diff --git a/src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/ScriptExpressionEvaluationProvider.java b/src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/ScriptEngineExpressionEvaluationProvider.java similarity index 77% rename from src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/ScriptExpressionEvaluationProvider.java rename to src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/ScriptEngineExpressionEvaluationProvider.java index 7725e23..85f6638 100644 --- a/src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/ScriptExpressionEvaluationProvider.java +++ b/src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/ScriptEngineExpressionEvaluationProvider.java @@ -19,7 +19,7 @@ import lombok.extern.slf4j.Slf4j; import org.powerflows.dmn.engine.evaluator.context.EvaluationContext; import org.powerflows.dmn.engine.evaluator.exception.EvaluationException; -import org.powerflows.dmn.engine.evaluator.expression.script.ScriptEngineProvider; +import org.powerflows.dmn.engine.evaluator.expression.provider.binding.MethodBinding; import org.powerflows.dmn.engine.evaluator.expression.script.bindings.ContextVariablesBindings; import org.powerflows.dmn.engine.model.decision.expression.Expression; import org.powerflows.dmn.engine.model.decision.field.Input; @@ -27,20 +27,33 @@ import org.powerflows.dmn.engine.model.decision.rule.entry.OutputEntry; import javax.script.Bindings; +import javax.script.ScriptContext; import javax.script.ScriptEngine; import javax.script.ScriptException; import java.io.Serializable; - +import java.util.stream.Collectors; @Slf4j -class ScriptExpressionEvaluationProvider implements ExpressionEvaluationProvider { +public abstract class ScriptEngineExpressionEvaluationProvider implements ExpressionEvaluationProvider { + protected final ScriptEngine scriptEngine; - private final ScriptEngineProvider scriptEngineProvider; + public ScriptEngineExpressionEvaluationProvider(final ExpressionEvaluationConfiguration configuration) { + scriptEngine = configuration.getScriptEngineManager().getEngineByName(getEngineName()); + if (scriptEngine == null) { + throw new IllegalStateException("Unsupported script engine: " + getEngineName()); + } - public ScriptExpressionEvaluationProvider(final ScriptEngineProvider scriptEngineProvider) { - this.scriptEngineProvider = scriptEngineProvider; + final Bindings bindings = scriptEngine.createBindings(); + bindings.putAll(configuration.getMethodBinding() + .stream() + .collect(Collectors.toMap(MethodBinding::name, this::createMethodBinding))); + scriptEngine.setBindings(bindings, ScriptContext.GLOBAL_SCOPE); } + protected abstract Object createMethodBinding(MethodBinding methodBinding); + + protected abstract String getEngineName(); + @Override public Serializable evaluateInput(final Input input, final EvaluationContext evaluationContext) { log.debug("Starting evaluation of input: {} with evaluation context: {}", input, evaluationContext); @@ -75,14 +88,12 @@ public Serializable evaluateOutputEntry(final OutputEntry outputEntry, final Eva } private Serializable evaluate(final InputEntry inputEntry, final EvaluationContext evaluationContext) { - final ScriptEngine scriptEngine = scriptEngineProvider.getScriptEngine(inputEntry.getExpression().getType()); final Bindings bindings = ContextVariablesBindings.create(scriptEngine.createBindings(), evaluationContext, inputEntry); return evaluate(inputEntry.getExpression(), scriptEngine, bindings); } private Serializable evaluate(final Expression expression, final EvaluationContext evaluationContext) { - final ScriptEngine scriptEngine = scriptEngineProvider.getScriptEngine(expression.getType()); final Bindings bindings = ContextVariablesBindings.create(scriptEngine.createBindings(), evaluationContext); return evaluate(expression, scriptEngine, bindings); diff --git a/src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/binding/AbstractMethodBinding.java b/src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/binding/AbstractMethodBinding.java new file mode 100644 index 0000000..15369eb --- /dev/null +++ b/src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/binding/AbstractMethodBinding.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2018-present PowerFlows.org - all rights reserved. + * + * 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 org.powerflows.dmn.engine.evaluator.expression.provider.binding; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.function.Supplier; + +public abstract class AbstractMethodBinding implements MethodBinding { + private final String name; + private final Supplier instanceSupplier; + private final Method method; + + public AbstractMethodBinding(final String name, final Method method, final Supplier instanceSupplier) { + this.name = name; + this.instanceSupplier = instanceSupplier; + this.method = method; + } + + @Override + public String name() { + return name; + } + + @Override + public Object execute(final Object... args) { + try { + return method.invoke(instanceSupplier.get(), args); + } catch (IllegalAccessException | InvocationTargetException | IllegalArgumentException | NullPointerException e) { + throw new ExpressionEvaluationException(e); + } + } +} diff --git a/src/main/java/org/powerflows/dmn/engine/evaluator/expression/script/ScriptEngineProvider.java b/src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/binding/BoundMethod.java similarity index 69% rename from src/main/java/org/powerflows/dmn/engine/evaluator/expression/script/ScriptEngineProvider.java rename to src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/binding/BoundMethod.java index 92577fc..3e36de0 100644 --- a/src/main/java/org/powerflows/dmn/engine/evaluator/expression/script/ScriptEngineProvider.java +++ b/src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/binding/BoundMethod.java @@ -13,14 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package org.powerflows.dmn.engine.evaluator.expression.provider.binding; -package org.powerflows.dmn.engine.evaluator.expression.script; - -import org.powerflows.dmn.engine.model.decision.expression.ExpressionType; - -import javax.script.ScriptEngine; - -public interface ScriptEngineProvider { - - ScriptEngine getScriptEngine(ExpressionType expressionType); +@FunctionalInterface +public interface BoundMethod { + Object execute(Object... args); } diff --git a/src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/binding/ExpressionEvaluationException.java b/src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/binding/ExpressionEvaluationException.java new file mode 100644 index 0000000..cc767c7 --- /dev/null +++ b/src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/binding/ExpressionEvaluationException.java @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2018-present PowerFlows.org - all rights reserved. + * + * 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 org.powerflows.dmn.engine.evaluator.expression.provider.binding; + +public class ExpressionEvaluationException extends RuntimeException { + public ExpressionEvaluationException(final Exception e) { + super(e); + } +} \ No newline at end of file diff --git a/src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/binding/InstanceMethodBinding.java b/src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/binding/InstanceMethodBinding.java new file mode 100644 index 0000000..ab27a6a --- /dev/null +++ b/src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/binding/InstanceMethodBinding.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2018-present PowerFlows.org - all rights reserved. + * + * 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 org.powerflows.dmn.engine.evaluator.expression.provider.binding; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.function.Supplier; + +public class InstanceMethodBinding extends AbstractMethodBinding { + + public InstanceMethodBinding(final String name, final Method method, final Supplier instanceSupplier) { + super(name, method, instanceSupplier); + if ((method.getModifiers() & Modifier.STATIC) != 0) { + throw new IllegalArgumentException("Provided method must not be static"); + } + } +} diff --git a/src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/binding/MethodBinding.java b/src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/binding/MethodBinding.java new file mode 100644 index 0000000..6dc43bf --- /dev/null +++ b/src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/binding/MethodBinding.java @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2018-present PowerFlows.org - all rights reserved. + * + * 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 org.powerflows.dmn.engine.evaluator.expression.provider.binding; + +public interface MethodBinding extends BoundMethod { + String name(); +} diff --git a/src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/binding/StaticMethodBinding.java b/src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/binding/StaticMethodBinding.java new file mode 100644 index 0000000..ee50a21 --- /dev/null +++ b/src/main/java/org/powerflows/dmn/engine/evaluator/expression/provider/binding/StaticMethodBinding.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018-present PowerFlows.org - all rights reserved. + * + * 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 org.powerflows.dmn.engine.evaluator.expression.provider.binding; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.function.Supplier; + +public class StaticMethodBinding extends AbstractMethodBinding { + private static final Supplier NULL_SUPPLIER = () -> null; + + public StaticMethodBinding(final String name, final Method method) { + super(name, method, NULL_SUPPLIER); + if ((method.getModifiers() & Modifier.STATIC) == 0) { + throw new IllegalArgumentException("Provided method must be static"); + } + } +} diff --git a/src/main/java/org/powerflows/dmn/engine/evaluator/expression/script/DefaultScriptEngineProvider.java b/src/main/java/org/powerflows/dmn/engine/evaluator/expression/script/DefaultScriptEngineProvider.java deleted file mode 100644 index e1e7376..0000000 --- a/src/main/java/org/powerflows/dmn/engine/evaluator/expression/script/DefaultScriptEngineProvider.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2018-present PowerFlows.org - all rights reserved. - * - * 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 org.powerflows.dmn.engine.evaluator.expression.script; - -import org.powerflows.dmn.engine.model.decision.expression.ExpressionType; - -import javax.script.ScriptEngine; -import javax.script.ScriptEngineManager; - -public class DefaultScriptEngineProvider implements ScriptEngineProvider { - - private final ScriptEngineManager scriptEngineManager; - - public DefaultScriptEngineProvider(final ScriptEngineManager scriptEngineManager) { - this.scriptEngineManager = scriptEngineManager; - } - - @Override - public ScriptEngine getScriptEngine(final ExpressionType expressionType) { - final ScriptEngine scriptEngine = scriptEngineManager.getEngineByName(expressionType.name().toLowerCase()); - - if (scriptEngine == null) { - throw new IllegalArgumentException("Unsupported " + expressionType); - } - - return scriptEngine; - } -} diff --git a/src/main/resources/META-INF/services/org.powerflows.dmn.engine.evaluator.expression.provider.ExpressionEvaluationProviderFactory b/src/main/resources/META-INF/services/org.powerflows.dmn.engine.evaluator.expression.provider.ExpressionEvaluationProviderFactory new file mode 100644 index 0000000..29c9181 --- /dev/null +++ b/src/main/resources/META-INF/services/org.powerflows.dmn.engine.evaluator.expression.provider.ExpressionEvaluationProviderFactory @@ -0,0 +1,5 @@ +org.powerflows.dmn.engine.evaluator.expression.provider.FeelExpressionEvaluationProviderFactory +org.powerflows.dmn.engine.evaluator.expression.provider.GroovyExpressionEvaluationProviderFactory +org.powerflows.dmn.engine.evaluator.expression.provider.JavascriptExpressionEvaluationProviderFactory +org.powerflows.dmn.engine.evaluator.expression.provider.JuelExpressionEvaluationProviderFactory +org.powerflows.dmn.engine.evaluator.expression.provider.LiteralExpressionEvaluationProviderFactory \ No newline at end of file diff --git a/src/test/groovy/org/powerflows/dmn/engine/configuration/DefaultDecisionEngineConfigurationReferenceSingleSpec.groovy b/src/test/groovy/org/powerflows/dmn/engine/configuration/DefaultDecisionEngineConfigurationReferenceSingleSpec.groovy index e4697f4..218e84c 100644 --- a/src/test/groovy/org/powerflows/dmn/engine/configuration/DefaultDecisionEngineConfigurationReferenceSingleSpec.groovy +++ b/src/test/groovy/org/powerflows/dmn/engine/configuration/DefaultDecisionEngineConfigurationReferenceSingleSpec.groovy @@ -17,15 +17,18 @@ package org.powerflows.dmn.engine.configuration import org.powerflows.dmn.engine.DecisionEngine +import org.powerflows.dmn.engine.evaluator.expression.provider.binding.StaticMethodBinding import org.powerflows.dmn.engine.model.decision.Decision -import org.powerflows.dmn.engine.model.evaluation.variable.DecisionVariables import org.powerflows.dmn.engine.model.evaluation.result.DecisionResult import org.powerflows.dmn.engine.model.evaluation.result.EntryResult +import org.powerflows.dmn.engine.model.evaluation.variable.DecisionVariables import org.powerflows.dmn.engine.reader.DecisionReader import org.powerflows.dmn.io.yaml.YamlDecisionReader import spock.lang.Shared import spock.lang.Specification +import java.lang.reflect.Method + class DefaultDecisionEngineConfigurationReferenceSingleSpec extends Specification { @Shared @@ -39,13 +42,13 @@ class DefaultDecisionEngineConfigurationReferenceSingleSpec extends Specificatio void setupSpec() { final DecisionReader decisionReader = new YamlDecisionReader() - decisionEngineConfiguration = new DefaultDecisionEngineConfiguration() - - decisionEngine = decisionEngineConfiguration.configure() - final String decisionFileName = 'reference-single.yml' final InputStream decisionInputStream = this.class.getResourceAsStream(decisionFileName) decision = decisionReader.read(decisionInputStream).get() + + final Method method = TestMethods.class.getMethod('parse', String) + decisionEngineConfiguration = new DefaultDecisionEngineConfiguration().methodBindings([new StaticMethodBinding('parseDate', method)]) + decisionEngine = decisionEngineConfiguration.configure() } void 'should configure default decision engine'() { diff --git a/src/test/groovy/org/powerflows/dmn/engine/evaluator/expression/provider/ExpressionEvaluationProviderFactorySpec.groovy b/src/test/groovy/org/powerflows/dmn/engine/evaluator/expression/provider/DefaultExpressionEvaluationProviderFactorySpec.groovy similarity index 53% rename from src/test/groovy/org/powerflows/dmn/engine/evaluator/expression/provider/ExpressionEvaluationProviderFactorySpec.groovy rename to src/test/groovy/org/powerflows/dmn/engine/evaluator/expression/provider/DefaultExpressionEvaluationProviderFactorySpec.groovy index 9f37573..2d2069f 100644 --- a/src/test/groovy/org/powerflows/dmn/engine/evaluator/expression/provider/ExpressionEvaluationProviderFactorySpec.groovy +++ b/src/test/groovy/org/powerflows/dmn/engine/evaluator/expression/provider/DefaultExpressionEvaluationProviderFactorySpec.groovy @@ -18,12 +18,11 @@ package org.powerflows.dmn.engine.evaluator.expression.provider import org.powerflows.dmn.engine.model.decision.expression.ExpressionType import spock.lang.Specification +import spock.lang.Unroll -class ExpressionEvaluationProviderFactorySpec extends Specification { - - private - final ExpressionEvaluationProviderFactory evaluationProviderFactory = new ExpressionEvaluationProviderFactory() +class DefaultExpressionEvaluationProviderFactorySpec extends Specification { + private final DefaultExpressionEvaluationProviderFactory evaluationProviderFactory = new DefaultExpressionEvaluationProviderFactory() void 'should throw exception when unknown expression type'() { given: @@ -37,4 +36,23 @@ class ExpressionEvaluationProviderFactorySpec extends Specification { exception != null exception.getMessage() == 'Unknown expression type null' } + + @Unroll + void 'should provide expression evaluation provider for #type'(ExpressionType type, Class targetClazz) { + when: + ExpressionEvaluationProvider result = evaluationProviderFactory.getInstance(type) + + then: + noExceptionThrown() + result != null + targetClazz.isAssignableFrom(result.getClass()) + + where: + type | targetClazz + ExpressionType.FEEL | FeelExpressionEvaluationProvider + ExpressionType.GROOVY | GroovyExpressionEvaluationProvider + ExpressionType.JAVASCRIPT | JavascriptExpressionEvaluationProvider + ExpressionType.JUEL | JuelExpressionEvaluationProvider + ExpressionType.LITERAL | LiteralExpressionEvaluationProvider + } } diff --git a/src/test/groovy/org/powerflows/dmn/engine/evaluator/expression/provider/GroovyExpressionEvaluationProviderSpec.groovy b/src/test/groovy/org/powerflows/dmn/engine/evaluator/expression/provider/GroovyExpressionEvaluationProviderSpec.groovy index 5e5ef77..508ae2d 100644 --- a/src/test/groovy/org/powerflows/dmn/engine/evaluator/expression/provider/GroovyExpressionEvaluationProviderSpec.groovy +++ b/src/test/groovy/org/powerflows/dmn/engine/evaluator/expression/provider/GroovyExpressionEvaluationProviderSpec.groovy @@ -18,8 +18,10 @@ package org.powerflows.dmn.engine.evaluator.expression.provider import org.powerflows.dmn.engine.evaluator.context.EvaluationContext import org.powerflows.dmn.engine.evaluator.exception.EvaluationException -import org.powerflows.dmn.engine.evaluator.expression.script.DefaultScriptEngineProvider -import org.powerflows.dmn.engine.evaluator.expression.script.ScriptEngineProvider +import org.powerflows.dmn.engine.evaluator.expression.provider.binding.InstanceMethodBinding +import org.powerflows.dmn.engine.evaluator.expression.provider.binding.MethodBinding +import org.powerflows.dmn.engine.evaluator.expression.provider.binding.StaticMethodBinding +import org.powerflows.dmn.engine.evaluator.expression.provider.sample.MethodSource import org.powerflows.dmn.engine.model.decision.expression.Expression import org.powerflows.dmn.engine.model.decision.expression.ExpressionType import org.powerflows.dmn.engine.model.decision.field.Input @@ -30,16 +32,19 @@ import spock.lang.Specification import spock.lang.Unroll import javax.script.ScriptEngineManager +import java.lang.reflect.Method class GroovyExpressionEvaluationProviderSpec extends Specification { - private final ScriptEngineProvider scriptEngineProvider = new DefaultScriptEngineProvider(new ScriptEngineManager()) - private final ExpressionEvaluationProvider expressionEvaluationProvider = - new ScriptExpressionEvaluationProvider(scriptEngineProvider) + private ExpressionEvaluationProvider expressionEvaluationProvider + + void setup() { + expressionEvaluationProvider = new GroovyExpressionEvaluationProvider(ExpressionEvaluationConfiguration.simpleConfiguration()) + } @Unroll void 'should evaluate input entry groovy expression value #entryExpressionValue and variables #contextVariable with #expectedEntryResult'( - final Object entryExpressionValue, final Object contextVariable, final boolean expectedEntryResult) { + final Object entryExpressionValue, final Object contextVariable, final Serializable expectedEntryResult) { given: final Expression entryExpression = [value: entryExpressionValue, type: ExpressionType.GROOVY] final InputEntry inputEntry = [expression: entryExpression, nameAlias: 'cellInput'] @@ -48,7 +53,7 @@ class GroovyExpressionEvaluationProviderSpec extends Specification { final EvaluationContext evaluationContext = new EvaluationContext(decisionVariables) when: - final boolean inputEntryResult = expressionEvaluationProvider.evaluateInputEntry(inputEntry, evaluationContext) + final Serializable inputEntryResult = expressionEvaluationProvider.evaluateInputEntry(inputEntry, evaluationContext) then: inputEntryResult == expectedEntryResult @@ -71,7 +76,7 @@ class GroovyExpressionEvaluationProviderSpec extends Specification { final EvaluationContext contextVariables = new EvaluationContext(decisionVariables) when: - final boolean inputEntryResult = expressionEvaluationProvider.evaluateInputEntry(inputEntry, contextVariables) + final Serializable inputEntryResult = expressionEvaluationProvider.evaluateInputEntry(inputEntry, contextVariables) then: inputEntryResult @@ -79,7 +84,7 @@ class GroovyExpressionEvaluationProviderSpec extends Specification { @Unroll void 'should evaluate output entry groovy expression value #entryExpressionValue and variables #contextVariable with #expectedEntryResult'( - final Object entryExpressionValue, final Object contextVariable, final boolean expectedEntryResult) { + final Object entryExpressionValue, final Object contextVariable, final Serializable expectedEntryResult) { given: final Expression entryExpression = [value: entryExpressionValue, type: ExpressionType.GROOVY] final OutputEntry outputEntry = [expression: entryExpression] @@ -88,7 +93,7 @@ class GroovyExpressionEvaluationProviderSpec extends Specification { final EvaluationContext evaluationContext = new EvaluationContext(decisionVariables) when: - final boolean outputEntryResult = expressionEvaluationProvider.evaluateOutputEntry(outputEntry, evaluationContext) + final Serializable outputEntryResult = expressionEvaluationProvider.evaluateOutputEntry(outputEntry, evaluationContext) then: outputEntryResult == expectedEntryResult @@ -104,7 +109,7 @@ class GroovyExpressionEvaluationProviderSpec extends Specification { @Unroll void 'should evaluate input groovy expression value #inputExpression and variables #contextVariable with #expectedInputResult'( - final Object inputExpression, final Object contextVariable, final boolean expectedInputResult) { + final Object inputExpression, final Object contextVariable, final Serializable expectedInputResult) { given: final Expression expression = [value: inputExpression, type: ExpressionType.GROOVY] final Input input = [name: 'TestInputName', expression: expression] @@ -113,7 +118,7 @@ class GroovyExpressionEvaluationProviderSpec extends Specification { final EvaluationContext contextVariables = new EvaluationContext(decisionVariables) when: - final boolean inputEntryResult = expressionEvaluationProvider.evaluateInput(input, contextVariables) + final Serializable inputEntryResult = expressionEvaluationProvider.evaluateInput(input, contextVariables) then: inputEntryResult == expectedInputResult @@ -141,4 +146,64 @@ class GroovyExpressionEvaluationProviderSpec extends Specification { exception != null exception.getMessage() == 'Script evaluation exception' } + + void 'should bind static method and make it available in expression'() { + given: + final Method method = MethodSource.class.getMethod('sampleStaticMethod', String, Integer.TYPE) + final List methodBinding = [new StaticMethodBinding('testMethod', method)] + final ExpressionEvaluationConfiguration configuration = ExpressionEvaluationConfiguration + .builder() + .methodBinding(methodBinding) + .build() + final ExpressionEvaluationProvider expressionEvaluationProvider = new GroovyExpressionEvaluationProvider(configuration) + final Expression expression = [value: 'testMethod(x, 1)', type: ExpressionType.GROOVY] + final Input input = [name: 'TestInputName', expression: expression] + + final DecisionVariables decisionVariables = new DecisionVariables([x: 'text']) + final EvaluationContext contextVariables = new EvaluationContext(decisionVariables) + + when: + final Serializable inputEntryResult = expressionEvaluationProvider.evaluateInput(input, contextVariables) + + then: + inputEntryResult == 'static-' + decisionVariables.get('x') + '-1' + } + + void 'should bind instance method and make it available in expression'() { + given: + final MethodSource theInstance = new MethodSource('someValue') + final Method method = MethodSource.class.getMethod('sampleInstanceMethod', Integer.TYPE, String) + final List methodBinding = [new InstanceMethodBinding('testMethod', method, { + theInstance + })] + final ExpressionEvaluationConfiguration configuration = ExpressionEvaluationConfiguration + .builder() + .methodBinding(methodBinding) + .build() + final ExpressionEvaluationProvider expressionEvaluationProvider = new GroovyExpressionEvaluationProvider(configuration) + final Expression expression = [value: 'testMethod(2, y)', type: ExpressionType.GROOVY] + final Input input = [name: 'TestInputName', expression: expression] + + final DecisionVariables decisionVariables = new DecisionVariables([y: 'text']) + final EvaluationContext contextVariables = new EvaluationContext(decisionVariables) + + when: + final Serializable inputEntryResult = expressionEvaluationProvider.evaluateInput(input, contextVariables) + + then: + inputEntryResult == 'static 2 someValue ' + decisionVariables.get('y') + } + + void 'should throw exception when no script engine is provided'() { + given: + final ScriptEngineManager scriptEngineManager = Mock() + final ExpressionEvaluationConfiguration configuration = ExpressionEvaluationConfiguration.builder().scriptEngineManager(scriptEngineManager).build() + + when: + new GroovyExpressionEvaluationProvider(configuration) + + then: + 1 * scriptEngineManager.getEngineByName('groovy') >> null + thrown(IllegalStateException) + } } diff --git a/src/test/groovy/org/powerflows/dmn/engine/evaluator/expression/provider/JavaScriptExpressionEvaluationProviderSpec.groovy b/src/test/groovy/org/powerflows/dmn/engine/evaluator/expression/provider/JavascriptExpressionEvaluationProviderSpec.groovy similarity index 59% rename from src/test/groovy/org/powerflows/dmn/engine/evaluator/expression/provider/JavaScriptExpressionEvaluationProviderSpec.groovy rename to src/test/groovy/org/powerflows/dmn/engine/evaluator/expression/provider/JavascriptExpressionEvaluationProviderSpec.groovy index 4084b1f..c2d5ad4 100644 --- a/src/test/groovy/org/powerflows/dmn/engine/evaluator/expression/provider/JavaScriptExpressionEvaluationProviderSpec.groovy +++ b/src/test/groovy/org/powerflows/dmn/engine/evaluator/expression/provider/JavascriptExpressionEvaluationProviderSpec.groovy @@ -18,8 +18,10 @@ package org.powerflows.dmn.engine.evaluator.expression.provider import org.powerflows.dmn.engine.evaluator.context.EvaluationContext import org.powerflows.dmn.engine.evaluator.exception.EvaluationException -import org.powerflows.dmn.engine.evaluator.expression.script.DefaultScriptEngineProvider -import org.powerflows.dmn.engine.evaluator.expression.script.ScriptEngineProvider +import org.powerflows.dmn.engine.evaluator.expression.provider.binding.InstanceMethodBinding +import org.powerflows.dmn.engine.evaluator.expression.provider.binding.MethodBinding +import org.powerflows.dmn.engine.evaluator.expression.provider.binding.StaticMethodBinding +import org.powerflows.dmn.engine.evaluator.expression.provider.sample.MethodSource import org.powerflows.dmn.engine.model.decision.expression.Expression import org.powerflows.dmn.engine.model.decision.expression.ExpressionType import org.powerflows.dmn.engine.model.decision.field.Input @@ -29,17 +31,20 @@ import org.powerflows.dmn.engine.model.evaluation.variable.DecisionVariables import spock.lang.Specification import spock.lang.Unroll -import javax.script.ScriptEngineManager +import java.lang.reflect.Method -class JavaScriptExpressionEvaluationProviderSpec extends Specification { +class JavascriptExpressionEvaluationProviderSpec extends Specification { - private final ScriptEngineProvider scriptEngineProvider = new DefaultScriptEngineProvider(new ScriptEngineManager()) - private final ExpressionEvaluationProvider expressionEvaluationProvider = - new ScriptExpressionEvaluationProvider(scriptEngineProvider) + private ExpressionEvaluationProvider expressionEvaluationProvider + + void setup() { + def configuration = ExpressionEvaluationConfiguration.simpleConfiguration() + expressionEvaluationProvider = new JavascriptExpressionEvaluationProvider(configuration) + } @Unroll void 'should evaluate input entry javascript expression value #entryExpressionValue and variables #contextVariable with #expectedEntryResult'( - final Object entryExpressionValue, final Object contextVariable, final boolean expectedEntryResult) { + final Object entryExpressionValue, final Object contextVariable, final Serializable expectedEntryResult) { given: final Expression entryExpression = [value: entryExpressionValue, type: ExpressionType.JAVASCRIPT] final InputEntry inputEntry = [expression: entryExpression, nameAlias: 'cellInput'] @@ -48,7 +53,7 @@ class JavaScriptExpressionEvaluationProviderSpec extends Specification { final EvaluationContext evaluationContext = new EvaluationContext(decisionVariables) when: - final boolean inputEntryResult = expressionEvaluationProvider.evaluateInputEntry(inputEntry, evaluationContext) + final Serializable inputEntryResult = expressionEvaluationProvider.evaluateInputEntry(inputEntry, evaluationContext) then: inputEntryResult == expectedEntryResult @@ -71,7 +76,7 @@ class JavaScriptExpressionEvaluationProviderSpec extends Specification { final EvaluationContext contextVariables = new EvaluationContext(decisionVariables) when: - final boolean inputEntryResult = expressionEvaluationProvider.evaluateInputEntry(inputEntry, contextVariables) + final Serializable inputEntryResult = expressionEvaluationProvider.evaluateInputEntry(inputEntry, contextVariables) then: inputEntryResult @@ -79,7 +84,7 @@ class JavaScriptExpressionEvaluationProviderSpec extends Specification { @Unroll void 'should evaluate output entry javascript expression value #entryExpressionValue and variables #contextVariable with #expectedEntryResult'( - final Object entryExpressionValue, final Object contextVariable, final boolean expectedEntryResult) { + final Object entryExpressionValue, final Object contextVariable, final Serializable expectedEntryResult) { given: final Expression entryExpression = [value: entryExpressionValue, type: ExpressionType.JAVASCRIPT] final OutputEntry outputEntry = [expression: entryExpression] @@ -88,7 +93,7 @@ class JavaScriptExpressionEvaluationProviderSpec extends Specification { final EvaluationContext evaluationContext = new EvaluationContext(decisionVariables) when: - final boolean outputEntryResult = expressionEvaluationProvider.evaluateOutputEntry(outputEntry, evaluationContext) + final Serializable outputEntryResult = expressionEvaluationProvider.evaluateOutputEntry(outputEntry, evaluationContext) then: outputEntryResult == expectedEntryResult @@ -104,7 +109,7 @@ class JavaScriptExpressionEvaluationProviderSpec extends Specification { @Unroll void 'should evaluate input javascript expression value #inputExpression and variables #contextVariable with #expectedInputResult'( - final Object inputExpression, final Object contextVariable, final boolean expectedInputResult) { + final Object inputExpression, final Object contextVariable, final Serializable expectedInputResult) { given: final Expression expression = [value: inputExpression, type: ExpressionType.JAVASCRIPT] final Input input = [name: 'TestInputName', expression: expression] @@ -113,7 +118,7 @@ class JavaScriptExpressionEvaluationProviderSpec extends Specification { final EvaluationContext contextVariables = new EvaluationContext(decisionVariables) when: - final boolean inputEntryResult = expressionEvaluationProvider.evaluateInput(input, contextVariables) + final Serializable inputEntryResult = expressionEvaluationProvider.evaluateInput(input, contextVariables) then: inputEntryResult == expectedInputResult @@ -142,4 +147,51 @@ class JavaScriptExpressionEvaluationProviderSpec extends Specification { exception != null exception.getMessage() == 'Script evaluation exception' } + + void 'should bind static method and make it available in expression'() { + given: + final Method method = MethodSource.class.getMethod('sampleStaticMethod', String, Integer.TYPE) + final List methodBinding = [new StaticMethodBinding('testMethod', method)] + final ExpressionEvaluationConfiguration configuration = ExpressionEvaluationConfiguration + .builder() + .methodBinding(methodBinding) + .build() + final ExpressionEvaluationProvider expressionEvaluationProvider = new JavascriptExpressionEvaluationProvider(configuration) + final Expression expression = [value: 'testMethod(x, 1)', type: ExpressionType.JAVASCRIPT] + final Input input = [name: 'TestInputName', expression: expression] + + final DecisionVariables decisionVariables = new DecisionVariables([x: 'text']) + final EvaluationContext contextVariables = new EvaluationContext(decisionVariables) + + when: + final Serializable inputEntryResult = expressionEvaluationProvider.evaluateInput(input, contextVariables) + + then: + inputEntryResult == 'static-' + decisionVariables.get('x') + '-1' + } + + void 'should bind instance method and make it available in expression'() { + given: + final MethodSource theInstance = new MethodSource('someValue') + final Method method = MethodSource.class.getMethod('sampleInstanceMethod', Integer.TYPE, String) + final List methodBinding = [new InstanceMethodBinding('testMethod', method, { + theInstance + })] + final ExpressionEvaluationConfiguration configuration = ExpressionEvaluationConfiguration + .builder() + .methodBinding(methodBinding) + .build() + final ExpressionEvaluationProvider expressionEvaluationProvider = new JavascriptExpressionEvaluationProvider(configuration) + final Expression expression = [value: 'testMethod(2, y)', type: ExpressionType.JAVASCRIPT] + final Input input = [name: 'TestInputName', expression: expression] + + final DecisionVariables decisionVariables = new DecisionVariables([y: 'text']) + final EvaluationContext contextVariables = new EvaluationContext(decisionVariables) + + when: + final Serializable inputEntryResult = expressionEvaluationProvider.evaluateInput(input, contextVariables) + + then: + inputEntryResult == 'static 2 someValue ' + decisionVariables.get('y') + } } diff --git a/src/test/groovy/org/powerflows/dmn/engine/evaluator/expression/provider/binding/MethodBindingSpec.groovy b/src/test/groovy/org/powerflows/dmn/engine/evaluator/expression/provider/binding/MethodBindingSpec.groovy new file mode 100644 index 0000000..40aa673 --- /dev/null +++ b/src/test/groovy/org/powerflows/dmn/engine/evaluator/expression/provider/binding/MethodBindingSpec.groovy @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2018-present PowerFlows.org - all rights reserved. + * + * 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 org.powerflows.dmn.engine.evaluator.expression.provider.binding + +import org.powerflows.dmn.engine.evaluator.expression.provider.sample.MethodSource +import spock.lang.Specification + +import java.lang.reflect.Method +import java.util.function.Supplier + +class MethodBindingSpec extends Specification { + void 'StaticMethodBinding should not allow non static methods for configuration'() { + given: + final Method method = MethodSource.class.getMethod('sampleInstanceMethod', Integer.TYPE, String) + + when: + new StaticMethodBinding('test', method) + + then: + thrown(IllegalArgumentException) + } + + void 'InstanceMethodBinding should not allow static methods for configuration'() { + given: + final Method method = MethodSource.class.getMethod('sampleStaticMethod', String, Integer.TYPE) + final Supplier supplier = { -> null } + + when: + new InstanceMethodBinding('test', method, supplier) + + then: + thrown(IllegalArgumentException) + } + + void 'MethodBinding should throw exception on private methods'() { + given: + final Method method = MethodSource.class.getDeclaredMethod('privateMethod', String) + final Supplier supplier = { -> new MethodSource() } + final MethodBinding methodBinding = new InstanceMethodBinding('test', method, supplier) + + when: + methodBinding.execute('test') + + then: + thrown(ExpressionEvaluationException) + } + + void 'MethodBinding should throw exception on invalid method arguments'() { + given: + final Method method = MethodSource.class.getMethod('sampleInstanceMethod', Integer.TYPE, String) + final Supplier supplier = { -> new MethodSource() } + final MethodBinding methodBinding = new InstanceMethodBinding('test', method, supplier) + + when: + methodBinding.execute('test') + + then: + thrown(ExpressionEvaluationException) + } + + void 'MethodBinding should throw exception on invalid instance source'() { + given: + final Method method = MethodSource.class.getMethod('sampleInstanceMethod', Integer.TYPE, String) + final Supplier supplier = { -> 'this is string!' } + final MethodBinding methodBinding = new InstanceMethodBinding('test', method, supplier) + + when: + methodBinding.execute('test') + + then: + thrown(ExpressionEvaluationException) + } + + void 'MethodBinding should throw exception on null instance'() { + given: + final Method method = MethodSource.class.getMethod('sampleInstanceMethod', Integer.TYPE, String) + final Supplier supplier = { -> null } + final MethodBinding methodBinding = new InstanceMethodBinding('test', method, supplier) + + when: + methodBinding.execute('test') + + then: + thrown(ExpressionEvaluationException) + } +} diff --git a/src/test/groovy/org/powerflows/dmn/engine/evaluator/expression/script/DefaultScriptEngineProviderSpec.groovy b/src/test/groovy/org/powerflows/dmn/engine/evaluator/expression/script/DefaultScriptEngineProviderSpec.groovy deleted file mode 100644 index 12bf3f5..0000000 --- a/src/test/groovy/org/powerflows/dmn/engine/evaluator/expression/script/DefaultScriptEngineProviderSpec.groovy +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2018-present PowerFlows.org - all rights reserved. - * - * 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 org.powerflows.dmn.engine.evaluator.expression.script - -import org.powerflows.dmn.engine.model.decision.expression.ExpressionType -import spock.lang.Shared -import spock.lang.Specification - -import javax.script.ScriptEngine -import javax.script.ScriptEngineManager - -class DefaultScriptEngineProviderSpec extends Specification { - - @Shared - private ScriptEngineProvider scriptEngineProvider - - void setupSpec() { - scriptEngineProvider = new DefaultScriptEngineProvider(new ScriptEngineManager()); - } - - void 'should get script engine instance'() { - given: - final ExpressionType supportedScriptLanguage = ExpressionType.GROOVY - - when: - final ScriptEngine scriptEngine = scriptEngineProvider.getScriptEngine(supportedScriptLanguage) - - then: - scriptEngine != null - } - - void 'should throw exception for non supported script language'() { - given: - final ExpressionType unsupportedScriptLanguage = ExpressionType.LITERAL - - when: - scriptEngineProvider.getScriptEngine(unsupportedScriptLanguage) - - then: - final IllegalArgumentException exception = thrown() - exception != null - exception.getMessage() == 'Unsupported ' + unsupportedScriptLanguage - } -} diff --git a/src/test/groovy/org/powerflows/dmn/engine/evaluator/expression/script/bindings/ContextVariablesBindingsSpec.groovy b/src/test/groovy/org/powerflows/dmn/engine/evaluator/expression/script/bindings/ContextVariablesBindingsSpec.groovy index eddacd8..cb2dc4b 100644 --- a/src/test/groovy/org/powerflows/dmn/engine/evaluator/expression/script/bindings/ContextVariablesBindingsSpec.groovy +++ b/src/test/groovy/org/powerflows/dmn/engine/evaluator/expression/script/bindings/ContextVariablesBindingsSpec.groovy @@ -17,9 +17,6 @@ package org.powerflows.dmn.engine.evaluator.expression.script.bindings import org.powerflows.dmn.engine.evaluator.context.EvaluationContext -import org.powerflows.dmn.engine.evaluator.expression.script.DefaultScriptEngineProvider -import org.powerflows.dmn.engine.evaluator.expression.script.ScriptEngineProvider -import org.powerflows.dmn.engine.model.decision.expression.ExpressionType import org.powerflows.dmn.engine.model.evaluation.variable.DecisionVariables import spock.lang.Shared import spock.lang.Specification @@ -34,8 +31,7 @@ class ContextVariablesBindingsSpec extends Specification { private ScriptEngine scriptEngine void setupSpec() { - final ScriptEngineProvider scriptEngineProvider = new DefaultScriptEngineProvider(new ScriptEngineManager()); - scriptEngine = scriptEngineProvider.getScriptEngine(ExpressionType.GROOVY); + scriptEngine = new ScriptEngineManager().getEngineByName("groovy") } void 'should create ContextVariablesBindings instance'() { diff --git a/src/test/java/org/powerflows/dmn/engine/configuration/TestMethods.java b/src/test/java/org/powerflows/dmn/engine/configuration/TestMethods.java new file mode 100644 index 0000000..b627297 --- /dev/null +++ b/src/test/java/org/powerflows/dmn/engine/configuration/TestMethods.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2018-present PowerFlows.org - all rights reserved. + * + * 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 org.powerflows.dmn.engine.configuration; + +import java.time.LocalDate; +import java.time.ZoneId; +import java.util.Date; + +public class TestMethods { + public static Date parse(final String dateText) { + return Date.from(LocalDate.parse(dateText).atStartOfDay(ZoneId.systemDefault()).toInstant()); + } +} diff --git a/src/test/java/org/powerflows/dmn/engine/evaluator/expression/provider/sample/MethodSource.java b/src/test/java/org/powerflows/dmn/engine/evaluator/expression/provider/sample/MethodSource.java new file mode 100644 index 0000000..c82c37b --- /dev/null +++ b/src/test/java/org/powerflows/dmn/engine/evaluator/expression/provider/sample/MethodSource.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2018-present PowerFlows.org - all rights reserved. + * + * 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 org.powerflows.dmn.engine.evaluator.expression.provider.sample; + +public class MethodSource { + + private final String value; + + public MethodSource(final String value) { + this.value = value; + } + + public String sampleInstanceMethod(final int firstParam, final String secondParam) { + return "static " + firstParam + " " + value + " " + secondParam; + } + + public static String sampleStaticMethod(final String firstParam, final int secondParam) { + return "static-" + firstParam + "-" + secondParam; + } + + private String privateMethod(final String onlyParam) { + return "private-" + onlyParam; + } +} \ No newline at end of file diff --git a/src/test/resources/org/powerflows/dmn/engine/configuration/reference-single.yml b/src/test/resources/org/powerflows/dmn/engine/configuration/reference-single.yml index ff4c43e..6096233 100644 --- a/src/test/resources/org/powerflows/dmn/engine/configuration/reference-single.yml +++ b/src/test/resources/org/powerflows/dmn/engine/configuration/reference-single.yml @@ -95,7 +95,7 @@ rules: in: inputFour: expression-type: GROOVY - expression: "((cellInput instanceof String) ? Date.parse('yyyy-MM-dd', '2018-12-24') : cellInput) < Date.parse('yyyy-MM-dd', '2018-12-14')" + expression: "((cellInput instanceof String) ? parseDate('2018-12-24') : cellInput) < parseDate('2018-12-14')" evaluation-mode: BOOLEAN out: outputOne: true