Skip to content

Commit

Permalink
[DROOLS-2766] support for null values in compiled DMN dtables + minor…
Browse files Browse the repository at this point in the history
… refactor (apache#2013)
  • Loading branch information
mariofusco committed Aug 3, 2018
1 parent d1e838e commit 87ac735
Show file tree
Hide file tree
Showing 6 changed files with 85 additions and 26 deletions.
Expand Up @@ -82,7 +82,7 @@ public EvaluatorResult evaluate( DMNRuntimeEventManager eventManager, DMNResult


for (int i = 0; i < decisionTableEvaluator.getInputs().length; i++) { for (int i = 0; i < decisionTableEvaluator.getInputs().length; i++) {
DTableModel.DColumnModel column = dTableModel.getColumns().get(i); DTableModel.DColumnModel column = dTableModel.getColumns().get(i);
FEELEvent error = column.validate( evalCtx, decisionTableEvaluator.getInputs()[i] ); FEELEvent error = column.validate( evalCtx, decisionTableEvaluator.getInputs()[i].getValue() );
if ( error != null ) { if ( error != null ) {
MsgUtil.reportMessage( logger, MsgUtil.reportMessage( logger,
DMNMessage.Severity.ERROR, DMNMessage.Severity.ERROR,
Expand Down
@@ -0,0 +1,62 @@
/*
* Copyright 2005 JBoss Inc
*
* 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.kie.dmn.core.compiler.execmodelbased;

import java.util.List;
import java.util.function.BiPredicate;

import org.kie.dmn.feel.codegen.feel11.CompiledFEELUnaryTests;
import org.kie.dmn.feel.lang.EvaluationContext;
import org.kie.dmn.feel.runtime.UnaryTest;

public class CompiledDTTest implements BiPredicate<EvaluationContext, Object> {

private final BiPredicate<EvaluationContext, Object> predicate;
private final String name;

public CompiledDTTest(CompiledFEELUnaryTests test) {
this.predicate = toPredicate(test);
this.name = test.getClass().getSimpleName();
}

@Override
public boolean test( EvaluationContext evalCtx, Object obj ) {
return predicate.test( evalCtx, obj );
}

public String getName() {
return name;
}

private static BiPredicate<EvaluationContext, Object> toPredicate(CompiledFEELUnaryTests test) {
List<UnaryTest> fs = test.getUnaryTests();
return fs.size() == 1 ? new UnaryPredicate(fs.get(0)) : (a,b) -> fs.stream().map( UnaryPredicate::new ).anyMatch( f -> f.test( a,b ) );
}

public static class UnaryPredicate implements BiPredicate<EvaluationContext, Object> {
private final UnaryTest unaryTest;

public UnaryPredicate( UnaryTest unaryTest ) {
this.unaryTest = unaryTest;
}

@Override
public boolean test( EvaluationContext evalCtx, Object obj ) {
return unaryTest.apply( evalCtx, obj ) == Boolean.TRUE;
}
}
}
Expand Up @@ -45,7 +45,7 @@ DMNDecisionResult execute( String decisionId, RuleUnitExecutor executor) {
return new DMNDecisionResultImpl(decisionId, decisionTable.getName(), DecisionEvaluationStatus.SUCCEEDED, getResult(), Collections.emptyList()); return new DMNDecisionResultImpl(decisionId, decisionTable.getName(), DecisionEvaluationStatus.SUCCEEDED, getResult(), Collections.emptyList());
} }


protected Object getValue( int pos ) { protected FeelValue getValue( int pos ) {
return evaluator.getInputs()[pos]; return evaluator.getInputs()[pos];
} }


Expand Down
Expand Up @@ -24,12 +24,11 @@
import org.kie.dmn.core.compiler.DMNFEELHelper; import org.kie.dmn.core.compiler.DMNFEELHelper;
import org.kie.dmn.feel.lang.EvaluationContext; import org.kie.dmn.feel.lang.EvaluationContext;
import org.kie.dmn.feel.runtime.decisiontables.HitPolicy; import org.kie.dmn.feel.runtime.decisiontables.HitPolicy;
import org.kie.dmn.feel.util.Null;


public class DecisionTableEvaluator { public class DecisionTableEvaluator {
private final DTableModel dTableModel; private final DTableModel dTableModel;
private final EvaluationContext evalCtx; private final EvaluationContext evalCtx;
private final Object[] inputs; private final FeelValue[] inputs;
private final List<FEELEvent> events; private final List<FEELEvent> events;
private final EvaluationContext[] columnEvalCtxs; private final EvaluationContext[] columnEvalCtxs;


Expand All @@ -39,7 +38,7 @@ public DecisionTableEvaluator( DMNFEELHelper feel, DTableModel dTableModel, Eval
this.dTableModel = dTableModel; this.dTableModel = dTableModel;
this.evalCtx = evalCtx; this.evalCtx = evalCtx;
this.events = events; this.events = events;
this.inputs = new Object[dTableModel.getColumns().size()]; this.inputs = new FeelValue[dTableModel.getColumns().size()];
this.columnEvalCtxs = new EvaluationContext[dTableModel.getColumns().size()]; this.columnEvalCtxs = new EvaluationContext[dTableModel.getColumns().size()];
initInputs(feel); initInputs(feel);
} }
Expand All @@ -51,7 +50,7 @@ public Object getOutput(int row, int col) {
private Object[] initInputs(DMNFEELHelper feel) { private Object[] initInputs(DMNFEELHelper feel) {
for (int i = 0; i < inputs.length; i++) { for (int i = 0; i < inputs.length; i++) {
Object result = dTableModel.getColumns().get(i).evaluate( evalCtx ); Object result = dTableModel.getColumns().get(i).evaluate( evalCtx );
inputs[i] = result == null ? Null.INSTANCE : result; inputs[i] = new FeelValue(result);


columnEvalCtxs[i] = feel.newEvaluationContext( Collections.singletonList( events::add ), evalCtx.getAllValues()); columnEvalCtxs[i] = feel.newEvaluationContext( Collections.singletonList( events::add ), evalCtx.getAllValues());
columnEvalCtxs[i].enterFrame(); columnEvalCtxs[i].enterFrame();
Expand All @@ -60,7 +59,7 @@ private Object[] initInputs(DMNFEELHelper feel) {
return inputs; return inputs;
} }


public Object[] getInputs() { public FeelValue[] getInputs() {
return inputs; return inputs;
} }


Expand Down
Expand Up @@ -157,11 +157,12 @@ public String generate( DMNCompilerContext ctx, DMNFEELHelper feel, DTableModel
sb.append( "package " ).append( pkgName ).append( ";\n" ); sb.append( "package " ).append( pkgName ).append( ";\n" );
sb.append( "\n" ); sb.append( "\n" );
sb.append( "import java.util.List;\n" ); sb.append( "import java.util.List;\n" );
sb.append( "import " + FeelValue.class.getCanonicalName() + ";\n" );
sb.append( "import " + DecisionTableEvaluator.class.getCanonicalName() + ";\n" ); sb.append( "import " + DecisionTableEvaluator.class.getCanonicalName() + ";\n" );
sb.append( "import org.kie.api.runtime.rule.DataSource;\n" ); sb.append( "import org.kie.api.runtime.rule.DataSource;\n" );
sb.append( "import org.drools.model.*;\n" ); sb.append( "import org.drools.model.*;\n" );
sb.append( "import org.drools.modelcompiler.dsl.pattern.D;\n" ); sb.append( "import org.drools.modelcompiler.dsl.pattern.D;\n" );
sb.append( "import static " ).append( pkgName ).append( "." ).append( clasName ).append( "UnaryTests.*;\n" ); sb.append( "import static " ).append( pkgName ).append( "." ).append( clasName ).append( "UnaryTests.TEST_ARRAY;\n" );
sb.append( "\n" ); sb.append( "\n" );
sb.append( "public class " ).append( clasName ).append( "ExecModel {\n" ); sb.append( "public class " ).append( clasName ).append( "ExecModel {\n" );
sb.append( "\n" ); sb.append( "\n" );
Expand All @@ -180,7 +181,7 @@ public String generate( DMNCompilerContext ctx, DMNFEELHelper feel, DTableModel
} }
for (int j = 0; j < dTableModel.getInputSize(); j++) { for (int j = 0; j < dTableModel.getInputSize(); j++) {
sb.append( " private static final UnitData<DataSource> var_input" + j + " = D.unitData(DataSource.class, \"input" + j + "\");\n" ); sb.append( " private static final UnitData<DataSource> var_input" + j + " = D.unitData(DataSource.class, \"input" + j + "\");\n" );
sb.append( " private static final Variable<Object> var_$pattern$" + j + "$ = D.declarationOf(Object.class, \"$pattern$" + j + "$\", var_input" + j + ");\n" ); sb.append( " private static final Variable<FeelValue> var_$pattern$" + j + "$ = D.declarationOf(FeelValue.class, \"$pattern$" + j + "$\", var_input" + j + ");\n" );
} }


for (int i = 0; i < dTableModel.getRows().size(); i++) { for (int i = 0; i < dTableModel.getRows().size(); i++) {
Expand All @@ -193,8 +194,8 @@ public String generate( DMNCompilerContext ctx, DMNFEELHelper feel, DTableModel
sb.append( " .build( \n" ); sb.append( " .build( \n" );


for (int j = 0; j < dTableModel.getInputSize(); j++) { for (int j = 0; j < dTableModel.getInputSize(); j++) {
sb.append( " D.pattern(var_$pattern$" + j + "$).expr(GET_TEST_NAME(" + i + ", " + j + "), var_evaluator,\n" ); sb.append( " D.pattern(var_$pattern$" + j + "$).expr(TEST_ARRAY[" + i + "][" + j + "].getName(), var_evaluator,\n" );
sb.append( " (_this, evaluator) -> GET_TEST(" + i + "," + j + ").apply( evaluator.getEvalCtx(" + j + "), _this )),\n" ); sb.append( " (_this, evaluator) -> TEST_ARRAY[" + i + "][" + j + "].test( evaluator.getEvalCtx(" + j + "), _this.getValue() )),\n" );
} }


sb.append( " D.on( var_evaluator, " ); sb.append( " D.on( var_evaluator, " );
Expand Down Expand Up @@ -291,19 +292,11 @@ public String generate( DMNCompilerContext ctx, DMNFEELHelper feel, DTableModel
sb.append( "import org.kie.dmn.feel.codegen.feel11.CompiledCustomFEELFunction;\n" ); sb.append( "import org.kie.dmn.feel.codegen.feel11.CompiledCustomFEELFunction;\n" );
sb.append( "import org.kie.dmn.feel.runtime.UnaryTest;\n" ); sb.append( "import org.kie.dmn.feel.runtime.UnaryTest;\n" );
sb.append( "import org.kie.dmn.feel.lang.EvaluationContext;\n" ); sb.append( "import org.kie.dmn.feel.lang.EvaluationContext;\n" );
sb.append( "import " ).append( CompiledDTTest.class.getCanonicalName() ).append( ";\n" );
sb.append( "import static org.kie.dmn.feel.codegen.feel11.CompiledFEELSemanticMappings.*;\n" ); sb.append( "import static org.kie.dmn.feel.codegen.feel11.CompiledFEELSemanticMappings.*;\n" );
sb.append( "\n" ); sb.append( "\n" );
sb.append( "public class " ).append( clasName ).append( "UnaryTests {\n" ); sb.append( "public class " ).append( clasName ).append( "UnaryTests {\n" );
sb.append( "\n" ); sb.append( "\n" );
sb.append( " public static String GET_TEST_NAME(int i, int j) {\n" +
" return TEST_ARRAY[i][j].getClass().getSimpleName();\n" +
" }\n" +
"\n" +
" public static UnaryTest GET_TEST(int i, int j) {\n" +
" List<UnaryTest> fs = TEST_ARRAY[i][j].getUnaryTests();\n" +
" return fs.size() == 1 ? fs.get(0) : (a,b) -> fs.stream().anyMatch( f -> f.apply( a,b ) );\n" +
" }\n" );
sb.append( "\n" );
sb.append( getUnaryTestsSource(ctx, feel, dTableModel, pkgName, clasName) ); sb.append( getUnaryTestsSource(ctx, feel, dTableModel, pkgName, clasName) );
sb.append( "}\n" ); sb.append( "}\n" );


Expand All @@ -320,7 +313,7 @@ public String getUnaryTestsSource( DMNCompilerContext ctx, DMNFEELHelper feel, D
StringBuilder instancesBuilder = new StringBuilder(); StringBuilder instancesBuilder = new StringBuilder();


Map<String, String> testClassesByInput = new HashMap<>(); Map<String, String> testClassesByInput = new HashMap<>();
testArrayBuilder.append( " private static final CompiledFEELUnaryTests[][] TEST_ARRAY = new CompiledFEELUnaryTests[][] {\n" ); testArrayBuilder.append( " public static final CompiledDTTest[][] TEST_ARRAY = new CompiledDTTest[][] {\n" );


for (int i = 0; i < dTableModel.getRows().size(); i++) { for (int i = 0; i < dTableModel.getRows().size(); i++) {
testArrayBuilder.append( " { " ); testArrayBuilder.append( " { " );
Expand All @@ -332,7 +325,7 @@ public String getUnaryTestsSource( DMNCompilerContext ctx, DMNFEELHelper feel, D
testClass = className + "r" + i + "c" + j; testClass = className + "r" + i + "c" + j;
testClassesByInput.put(input, testClass); testClassesByInput.put(input, testClass);
testsBuilder.append( "\n" ); testsBuilder.append( "\n" );
instancesBuilder.append( " private static final CompiledFEELUnaryTests " + testClass + "_INSTANCE = new " + testClass + "();\n" ); instancesBuilder.append( " private static final CompiledDTTest " + testClass + "_INSTANCE = new CompiledDTTest( new " + testClass + "() );\n" );
testsBuilder.append( feel.getSourceForUnaryTest( pkgName, testClass, input, ctx, dTableModel.getColumns().get(j).getType() ) ); testsBuilder.append( feel.getSourceForUnaryTest( pkgName, testClass, input, ctx, dTableModel.getColumns().get(j).getType() ) );
testsBuilder.append( "\n" ); testsBuilder.append( "\n" );
} }
Expand Down
Expand Up @@ -14,11 +14,16 @@
* limitations under the License. * limitations under the License.
*/ */


package org.kie.dmn.feel.util; package org.kie.dmn.core.compiler.execmodelbased;


public class Null { public class FeelValue {
private final Object value;


public static final Null INSTANCE = new Null(); public FeelValue( Object value ) {
this.value = value;
}


private Null() { } public Object getValue() {
return value;
}
} }

0 comments on commit 87ac735

Please sign in to comment.