From e3099fee5ebba281af00a7d5d24f2cfc65b1a99e Mon Sep 17 00:00:00 2001 From: Gabriele-Cardosi Date: Wed, 16 Oct 2024 14:02:07 +0200 Subject: [PATCH 1/5] [incubator-kie-issues#1543] Add the "id" of executed rules to the AfterEvaluateDecisionTableEvent --- .../AfterEvaluateDecisionTableEvent.java | 4 + .../jsr223/JSR223DTExpressionEvaluator.java | 6 +- .../core/ast/DMNDTExpressionEvaluator.java | 10 +- .../core/compiler/DMNEvaluatorCompiler.java | 2 +- .../DMNAlphaNetworkEvaluatorImpl.java | 5 +- .../core/compiler/alphanetbased/Results.java | 11 +- .../AfterEvaluateDecisionTableEventImpl.java | 17 +- .../impl/DMNRuntimeEventManagerUtils.java | 4 +- .../org/kie/dmn/core/DMNInputRuntimeTest.java | 38 +- .../decisiontables/DTDecisionRule.java | 8 +- .../decisiontables/DecisionTableImpl.java | 14 +- .../runtime/decisiontables/HitPolicy.java | 486 +++++++++--------- .../DecisionTableRulesMatchedEvent.java | 9 +- .../DecisionTableRulesSelectedEvent.java | 9 +- .../functions/DecisionTableFunction.java | 2 +- 15 files changed, 343 insertions(+), 282 deletions(-) diff --git a/kie-dmn/kie-dmn-api/src/main/java/org/kie/dmn/api/core/event/AfterEvaluateDecisionTableEvent.java b/kie-dmn/kie-dmn-api/src/main/java/org/kie/dmn/api/core/event/AfterEvaluateDecisionTableEvent.java index 3fac12be823..6daa2958bd5 100644 --- a/kie-dmn/kie-dmn-api/src/main/java/org/kie/dmn/api/core/event/AfterEvaluateDecisionTableEvent.java +++ b/kie-dmn/kie-dmn-api/src/main/java/org/kie/dmn/api/core/event/AfterEvaluateDecisionTableEvent.java @@ -33,4 +33,8 @@ default String getDecisionTableId() { List getMatches(); List getSelected(); + + List getMatchesIds(); + + List getSelectedIds(); } diff --git a/kie-dmn/kie-dmn-core-jsr223/src/main/java/org/kie/dmn/core/jsr223/JSR223DTExpressionEvaluator.java b/kie-dmn/kie-dmn-core-jsr223/src/main/java/org/kie/dmn/core/jsr223/JSR223DTExpressionEvaluator.java index afa8009e4d7..93fe8a412e9 100644 --- a/kie-dmn/kie-dmn-core-jsr223/src/main/java/org/kie/dmn/core/jsr223/JSR223DTExpressionEvaluator.java +++ b/kie-dmn/kie-dmn-core-jsr223/src/main/java/org/kie/dmn/core/jsr223/JSR223DTExpressionEvaluator.java @@ -106,7 +106,11 @@ public EvaluatorResult evaluate(DMNRuntimeEventManager dmrem, DMNResult dmnr) { LOG.debug("failed evaluate", e); throw new RuntimeException(e); } finally { - DMNRuntimeEventManagerUtils.fireAfterEvaluateDecisionTable( dmrem, node.getName(), node.getName(), dt.getId(), result, (r != null ? r.matchedRules : null), (r != null ? r.fired : null) ); + DMNRuntimeEventManagerUtils.fireAfterEvaluateDecisionTable( dmrem, node.getName(), node.getName(), dt.getId(), result, + (r != null ? r.matchedRules : null), + (r != null ? r.fired : null), + (r != null ? r.matchedIds : null), + (r != null ? r.firedIds : null)); } } diff --git a/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/ast/DMNDTExpressionEvaluator.java b/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/ast/DMNDTExpressionEvaluator.java index 8293f82e672..4b1cc02daf3 100644 --- a/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/ast/DMNDTExpressionEvaluator.java +++ b/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/ast/DMNDTExpressionEvaluator.java @@ -103,7 +103,11 @@ public EvaluatorResult evaluate(DMNRuntimeEventManager dmrem, DMNResult dmnr) { r = processEvents( events, dmrem, result, node ); return new EvaluatorResultImpl( dtr, r.hasErrors ? ResultType.FAILURE : ResultType.SUCCESS ); } finally { - DMNRuntimeEventManagerUtils.fireAfterEvaluateDecisionTable( dmrem, node.getName(), dt.getName(), dtNodeId, result, (r != null ? r.matchedRules : null), (r != null ? r.fired : null) ); + DMNRuntimeEventManagerUtils.fireAfterEvaluateDecisionTable( dmrem, node.getName(), dt.getName(), dtNodeId, result, + (r != null ? r.matchedRules : null), + (r != null ? r.fired : null), + (r != null ? r.matchedIds : null), + (r != null ? r.firedIds : null)); } } @@ -112,8 +116,10 @@ public static EventResults processEvents(List events, DMNRuntimeEvent for ( FEELEvent e : events ) { if ( e instanceof DecisionTableRulesMatchedEvent ) { r.matchedRules = ((DecisionTableRulesMatchedEvent) e).getMatches(); + r.matchedIds = ((DecisionTableRulesMatchedEvent) e).getMatchesIds(); } else if ( e instanceof DecisionTableRulesSelectedEvent ) { r.fired = ((DecisionTableRulesSelectedEvent) e).getFired(); + r.firedIds = ((DecisionTableRulesSelectedEvent) e).getFiredIds(); } else if ( e.getSeverity() == FEELEvent.Severity.ERROR ) { MsgUtil.reportMessage( logger, DMNMessage.Severity.ERROR, @@ -143,6 +149,8 @@ public static class EventResults { public boolean hasErrors = false; public List matchedRules; public List fired; + public List matchedIds; + public List firedIds; } diff --git a/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/compiler/DMNEvaluatorCompiler.java b/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/compiler/DMNEvaluatorCompiler.java index 57b4bc8f12c..a4e384da787 100644 --- a/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/compiler/DMNEvaluatorCompiler.java +++ b/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/compiler/DMNEvaluatorCompiler.java @@ -678,7 +678,7 @@ protected DMNExpressionEvaluator compileDecisionTable(DMNCompilerContext ctx, DM java.util.List rules = new ArrayList<>(); index = 0; for ( DecisionRule dr : dt.getRule() ) { - DTDecisionRule rule = new DTDecisionRule( index ); + DTDecisionRule rule = new DTDecisionRule( index, dr.getId() ); for ( int i = 0; i < dr.getInputEntry().size(); i++ ) { UnaryTests ut = dr.getInputEntry().get(i); final java.util.List tests; diff --git a/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/compiler/alphanetbased/DMNAlphaNetworkEvaluatorImpl.java b/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/compiler/alphanetbased/DMNAlphaNetworkEvaluatorImpl.java index cbffc8b5c8e..33342770490 100644 --- a/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/compiler/alphanetbased/DMNAlphaNetworkEvaluatorImpl.java +++ b/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/compiler/alphanetbased/DMNAlphaNetworkEvaluatorImpl.java @@ -111,7 +111,10 @@ public EvaluatorResult evaluate(DMNRuntimeEventManager eventManager, DMNResult d } finally { evalCtx.exitFrame(); DMNRuntimeEventManagerUtils.fireAfterEvaluateDecisionTable(eventManager, node.getName(), decisionTableName, decisionTableId, dmnResult, - (eventResults != null ? eventResults.matchedRules : null), (eventResults != null ? eventResults.fired : null)); + (eventResults != null ? eventResults.matchedRules : null), + (eventResults != null ? eventResults.fired : null), + (eventResults != null ? eventResults.matchedIds : null), + (eventResults != null ? eventResults.firedIds : null)); } } diff --git a/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/compiler/alphanetbased/Results.java b/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/compiler/alphanetbased/Results.java index ec7de3836cc..ab34d152ac9 100644 --- a/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/compiler/alphanetbased/Results.java +++ b/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/compiler/alphanetbased/Results.java @@ -29,6 +29,7 @@ import org.drools.model.functions.Function1; import org.kie.dmn.api.feel.runtime.events.FEELEvent; import org.kie.dmn.feel.lang.EvaluationContext; +import org.kie.dmn.feel.runtime.decisiontables.DTDecisionRule; import org.kie.dmn.feel.runtime.decisiontables.DecisionTable; import org.kie.dmn.feel.runtime.decisiontables.HitPolicy; import org.kie.dmn.feel.runtime.decisiontables.Indexed; @@ -57,8 +58,8 @@ public void clearResults() { resultGroupedByRow.clear(); } - List matches() { - return indexes().map(i -> (Indexed) () -> i).collect(toList()); + List matches() { + return indexes().map(i -> new DTDecisionRule(i, null)).collect(toList()); } private Stream indexes() { @@ -152,14 +153,16 @@ public Object applyHitPolicy(EvaluationContext evaluationContext, Collections.emptyList())); } - List matchIndexes = items.matches(); + List matchIndexes = items.matches(); evaluationContext.notifyEvt( () -> { List matchedIndexes = matchIndexes.stream().map( dr -> dr.getIndex() + 1 ).collect(Collectors.toList() ); return new DecisionTableRulesMatchedEvent(FEELEvent.Severity.INFO, String.format("Rules matched for decision table '%s': %s", decisionTable.getName(), matchIndexes), decisionTable.getName(), decisionTable.getName(), - matchedIndexes ); + matchedIndexes, + // @TODO gcardosi 1543 + Collections.emptyList()); } ); diff --git a/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/impl/AfterEvaluateDecisionTableEventImpl.java b/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/impl/AfterEvaluateDecisionTableEventImpl.java index edfb392bc06..76d80fccfbe 100644 --- a/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/impl/AfterEvaluateDecisionTableEventImpl.java +++ b/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/impl/AfterEvaluateDecisionTableEventImpl.java @@ -33,14 +33,18 @@ public class AfterEvaluateDecisionTableEventImpl private final DMNResult result; private final List matches; private final List fired; + private final List matchesIds; + private final List firedIds; - public AfterEvaluateDecisionTableEventImpl(String nodeName, String dtName, String dtId, DMNResult result, List matches, List fired) { + public AfterEvaluateDecisionTableEventImpl(String nodeName, String dtName, String dtId, DMNResult result, List matches, List fired, List matchesIds, List firedIds) { this.nodeName = nodeName; this.dtName = dtName; this.dtId = dtId; this.result = result; this.matches = matches; this.fired = fired; + this.matchesIds = matchesIds; + this.firedIds = firedIds; } @Override @@ -73,9 +77,18 @@ public List getSelected() { return fired == null ? Collections.emptyList() : fired; } + @Override + public List getMatchesIds() { + return matchesIds == null ? Collections.emptyList() : matchesIds; + } + + @Override + public List getSelectedIds() {return firedIds == null ? Collections.emptyList() : firedIds; + } + @Override public String toString() { - return "AfterEvaluateDecisionTableEvent{ nodeName='"+nodeName+"' decisionTableName='" + dtName + "' matches=" + getMatches() + " fired=" + getSelected() + " }"; + return "AfterEvaluateDecisionTableEvent{ nodeName='"+nodeName+"' decisionTableName='" + dtName + "' matches=" + getMatches() + " fired=" + getSelected() + "' matchesIds=" + getMatchesIds() + " firedIds=" + getSelectedIds() + " }"; } } diff --git a/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/impl/DMNRuntimeEventManagerUtils.java b/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/impl/DMNRuntimeEventManagerUtils.java index d4a65088c76..6469590ca80 100644 --- a/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/impl/DMNRuntimeEventManagerUtils.java +++ b/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/impl/DMNRuntimeEventManagerUtils.java @@ -103,9 +103,9 @@ public static void fireBeforeEvaluateDecisionTable( DMNRuntimeEventManager event } } - public static void fireAfterEvaluateDecisionTable( DMNRuntimeEventManager eventManager, String nodeName, String dtName, String dtId, DMNResult result, List matches, List fired ) { + public static void fireAfterEvaluateDecisionTable( DMNRuntimeEventManager eventManager, String nodeName, String dtName, String dtId, DMNResult result, List matches, List fired, List matchedIds, List firedIds ) { if( eventManager.hasListeners() ) { - AfterEvaluateDecisionTableEvent event = new AfterEvaluateDecisionTableEventImpl(nodeName, dtName, dtId, result, matches, fired); + AfterEvaluateDecisionTableEvent event = new AfterEvaluateDecisionTableEventImpl(nodeName, dtName, dtId, result, matches, fired, matchedIds, firedIds); notifyListeners(eventManager, l -> l.afterEvaluateDecisionTable(event)); } } diff --git a/kie-dmn/kie-dmn-core/src/test/java/org/kie/dmn/core/DMNInputRuntimeTest.java b/kie-dmn/kie-dmn-core/src/test/java/org/kie/dmn/core/DMNInputRuntimeTest.java index d89ff7ef4ca..5f245869683 100644 --- a/kie-dmn/kie-dmn-core/src/test/java/org/kie/dmn/core/DMNInputRuntimeTest.java +++ b/kie-dmn/kie-dmn-core/src/test/java/org/kie/dmn/core/DMNInputRuntimeTest.java @@ -42,6 +42,7 @@ import org.kie.dmn.api.core.ast.ItemDefNode; import org.kie.dmn.api.core.event.AfterConditionalEvaluationEvent; import org.kie.dmn.api.core.event.AfterEvaluateConditionalEvent; +import org.kie.dmn.api.core.event.AfterEvaluateDecisionTableEvent; import org.kie.dmn.api.core.event.DMNRuntimeEventListener; import org.kie.dmn.core.api.DMNFactory; import org.kie.dmn.core.api.event.DefaultDMNRuntimeEventListener; @@ -384,26 +385,36 @@ void typeConstraintsChecks(boolean useExecModelCompiler) { @ParameterizedTest @MethodSource("params") - void conditionalIfCheck(boolean useExecModelCompiler) { + void evaluationHitIdsCheck(boolean useExecModelCompiler) { init(useExecModelCompiler); final String ifElementId = "_3C702CE4-E5A0-4B6F-905D-C2621FFFA387"; final String thenElementId = "_6481FF12-61B5-451C-B775-4143D9B6CD6B"; final String elseElementId = "_2CD02CB2-6B56-45C4-B461-405E89D45633"; + final String ruleId0 = "_1578BD9E-2BF9-4BFC-8956-1A736959C937"; + final String ruleId1 = "_31CD7AA3-A806-4E7E-B512-821F82043620"; + final String ruleId3 = "_2545E1A8-93D3-4C8A-A0ED-8AD8B10A58F9"; + final String ruleId4 = "_510A50DA-D5A4-4F06-B0BE-7F8F2AA83740"; final DMNRuntime runtime = DMNRuntimeUtil.createRuntime("valid_models/DMNv1_5/RiskScore_Simple.dmn", this.getClass() ); - final List afterEvaluateConditionalEvents = new ArrayList<>(); - final List afterConditionalEvaluationEvents = new ArrayList<>(); + final List evaluateConditionalIds = new ArrayList<>(); + final List conditionalEvaluationIds = new ArrayList<>(); + final List executedRuleIds = new ArrayList<>(); runtime.addListener(new DefaultDMNRuntimeEventListener() { + @Override + public void afterConditionalEvaluation(AfterConditionalEvaluationEvent event) { + conditionalEvaluationIds.add(event.getExecutedId()); + } + @Override public void afterEvaluateConditional(AfterEvaluateConditionalEvent event) { - afterEvaluateConditionalEvents.add(event); + evaluateConditionalIds.add(event.getExecutedId()); } @Override - public void afterConditionalEvaluation(AfterConditionalEvaluationEvent event) { - afterConditionalEvaluationEvents.add(event); + public void afterEvaluateDecisionTable(AfterEvaluateDecisionTableEvent event) { + executedRuleIds.addAll(event.getSelectedIds()); } }); @@ -419,20 +430,23 @@ public void afterConditionalEvaluation(AfterConditionalEvaluationEvent event) { final DMNResult dmnResult1 = runtime.evaluateAll( dmnModel, ctx1 ); assertThat(dmnResult1.hasErrors()).as(DMNRuntimeUtil.formatMessages(dmnResult1.getMessages())).isFalse(); assertThat( dmnResult1.getContext().get( "Risk Score" )).isEqualTo(BigDecimal.valueOf(50)); - assertThat(afterEvaluateConditionalEvents).hasSize(1).allMatch(event -> event.getExecutedId().equals(ifElementId)); - assertThat(afterConditionalEvaluationEvents).hasSize(1).allMatch(event -> event.getExecutedId().equals(elseElementId)); + assertThat(evaluateConditionalIds).hasSize(1).allMatch(id -> id.equals(ifElementId)); + assertThat(conditionalEvaluationIds).hasSize(1).allMatch(id -> id.equals(elseElementId)); + assertThat(executedRuleIds).hasSize(2).contains(ruleId0, ruleId3); // - afterEvaluateConditionalEvents.clear(); - afterConditionalEvaluationEvents.clear(); + evaluateConditionalIds.clear(); + conditionalEvaluationIds.clear(); + executedRuleIds.clear(); final DMNContext ctx2 = runtime.newContext(); ctx2.set("Credit Score", "Excellent"); ctx2.set("DTI", 10); final DMNResult dmnResult2 = runtime.evaluateAll( dmnModel, ctx2 ); assertThat(dmnResult2.hasErrors()).as(DMNRuntimeUtil.formatMessages(dmnResult1.getMessages())).isFalse(); assertThat( dmnResult2.getContext().get( "Risk Score" )).isEqualTo(BigDecimal.valueOf(20)); - assertThat(afterEvaluateConditionalEvents).hasSize(1).allMatch(event -> event.getExecutedId().equals(ifElementId)); - assertThat(afterConditionalEvaluationEvents).hasSize(1).allMatch(event -> event.getExecutedId().equals(thenElementId)); + assertThat(evaluateConditionalIds).hasSize(1).allMatch(id -> id.equals(ifElementId)); + assertThat(conditionalEvaluationIds).hasSize(1).allMatch(id -> id.equals(thenElementId)); + assertThat(executedRuleIds).hasSize(2).contains(ruleId1, ruleId4); } @ParameterizedTest diff --git a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/runtime/decisiontables/DTDecisionRule.java b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/runtime/decisiontables/DTDecisionRule.java index dd08d9f8314..1576c789153 100644 --- a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/runtime/decisiontables/DTDecisionRule.java +++ b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/runtime/decisiontables/DTDecisionRule.java @@ -52,11 +52,13 @@ The class DecisionRule is used to model the rules in a decision table (see 8.2 N */ public class DTDecisionRule implements Indexed { private int index; + private String id; private List inputEntry; private List outputEntry; - public DTDecisionRule(int index) { + public DTDecisionRule(int index, String id) { this.index = index; + this.id = id; } /** @@ -86,4 +88,8 @@ public List getOutputEntry() { public int getIndex() { return index; } + + public String getId() { + return id; + } } diff --git a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/runtime/decisiontables/DecisionTableImpl.java b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/runtime/decisiontables/DecisionTableImpl.java index 362e1100873..27374bcb428 100644 --- a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/runtime/decisiontables/DecisionTableImpl.java +++ b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/runtime/decisiontables/DecisionTableImpl.java @@ -257,12 +257,20 @@ private List findMatches(EvaluationContext ctx, Object[] params) } } ctx.notifyEvt( () -> { - List matches = matchingDecisionRules.stream().map( dr -> dr.getIndex() + 1 ).collect( Collectors.toList() ); + List matches = new ArrayList<>(); + List matchesId = new ArrayList<>(); + matchingDecisionRules.forEach(dr -> { + matches.add( dr.getIndex() + 1 ); + if (dr.getId() != null && !dr.getId().isEmpty()) { + matchesId.add(dr.getId()); + } + }); return new DecisionTableRulesMatchedEvent(FEELEvent.Severity.INFO, - "Rules matched for decision table '" + getName() + "': " + matches.toString(), + "Rules matched for decision table '" + getName() + "': " + matches, getName(), getName(), - matches ); + matches, + matchesId); } ); return matchingDecisionRules; diff --git a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/runtime/decisiontables/HitPolicy.java b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/runtime/decisiontables/HitPolicy.java index 21acca05f1c..828d44d7157 100644 --- a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/runtime/decisiontables/HitPolicy.java +++ b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/runtime/decisiontables/HitPolicy.java @@ -6,9 +6,9 @@ * to you 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 - * + *

+ * 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 @@ -43,28 +43,29 @@ import static java.util.stream.IntStream.range; public enum HitPolicy { - UNIQUE( "U", "UNIQUE", HitPolicy::unique, null ), - FIRST( "F", "FIRST", HitPolicy::first, null ), - PRIORITY( "P", "PRIORITY", HitPolicy::priority, null ), - ANY( "A", "ANY", HitPolicy::any, null ), - COLLECT( "C", "COLLECT", HitPolicy::ruleOrder, Collections.EMPTY_LIST ), // Collect – return a list of the outputs in arbitrary order - COLLECT_SUM( "C+", "COLLECT SUM", HitPolicy::sumCollect, null ), - COLLECT_COUNT( "C#", "COLLECT COUNT", HitPolicy::countCollect, BigDecimal.ZERO ), - COLLECT_MIN( "C<", "COLLECT MIN", HitPolicy::minCollect, null ), - COLLECT_MAX( "C>", "COLLECT MAX", HitPolicy::maxCollect, null ), - RULE_ORDER( "R", "RULE ORDER", HitPolicy::ruleOrder, null ), - OUTPUT_ORDER( "O", "OUTPUT ORDER", HitPolicy::outputOrder, null ); - - private final String shortName; - private final String longName; + UNIQUE("U", "UNIQUE", HitPolicy::unique, null), + FIRST("F", "FIRST", HitPolicy::first, null), + PRIORITY("P", "PRIORITY", HitPolicy::priority, null), + ANY("A", "ANY", HitPolicy::any, null), + COLLECT("C", "COLLECT", HitPolicy::ruleOrder, Collections.EMPTY_LIST), // Collect – return a list of the + // outputs in arbitrary order + COLLECT_SUM("C+", "COLLECT SUM", HitPolicy::sumCollect, null), + COLLECT_COUNT("C#", "COLLECT COUNT", HitPolicy::countCollect, BigDecimal.ZERO), + COLLECT_MIN("C<", "COLLECT MIN", HitPolicy::minCollect, null), + COLLECT_MAX("C>", "COLLECT MAX", HitPolicy::maxCollect, null), + RULE_ORDER("R", "RULE ORDER", HitPolicy::ruleOrder, null), + OUTPUT_ORDER("O", "OUTPUT ORDER", HitPolicy::outputOrder, null); + + private final String shortName; + private final String longName; private final HitPolicyDTI dti; - private final Object defaultValue; + private final Object defaultValue; HitPolicy(final String shortName, final String longName) { - this( shortName, longName, HitPolicy::notImplemented, null ); + this(shortName, longName, HitPolicy::notImplemented, null); } - HitPolicy(final String shortName, final String longName, final HitPolicyDTI dti, Object defaultValue ) { + HitPolicy(final String shortName, final String longName, final HitPolicyDTI dti, Object defaultValue) { this.shortName = shortName; this.longName = longName; this.dti = dti; @@ -83,16 +84,18 @@ public HitPolicyDTI getDti() { return dti; } - public Object getDefaultValue() { return defaultValue; } + public Object getDefaultValue() { + return defaultValue; + } public static HitPolicy fromString(String policy) { policy = policy.toUpperCase(); - for ( HitPolicy c : HitPolicy.values() ) { - if ( c.shortName.equals( policy ) || c.longName.equals( policy ) ) { + for (HitPolicy c : HitPolicy.values()) { + if (c.shortName.equals(policy) || c.longName.equals(policy)) { return c; } } - throw new IllegalArgumentException( "Unknown hit policy: " + policy ); + throw new IllegalArgumentException("Unknown hit policy: " + policy); } /* --------------------------------------- @@ -100,19 +103,20 @@ public static HitPolicy fromString(String policy) { --------------------------------------- */ @FunctionalInterface public interface HitPolicyDTI { + Object dti( EvaluationContext ctx, DecisionTable dt, - List matches, + List matches, List results); } public static Object notImplemented( EvaluationContext ctx, DecisionTable dt, - List matches, + List matches, List results) { - throw new RuntimeException( "Not implemented" ); + throw new RuntimeException("Not implemented"); } /** @@ -121,57 +125,40 @@ public static Object notImplemented( public static Object unique( EvaluationContext ctx, DecisionTable dt, - List matches, + List matches, List results) { - if ( matches.size() > 1 ) { - ctx.notifyEvt( () -> { - List ruleMatches = matches.stream().map( m -> m.getIndex() + 1 ).collect( toList() ); - return new HitPolicyViolationEvent( - FEELEvent.Severity.ERROR, - "UNIQUE hit policy decision tables can only have one matching rule. " + - "Multiple matches found for decision table '" + dt.getName() + "'. Matched rules: " + ruleMatches, - dt.getName(), - ruleMatches ); - } + if (matches.size() > 1) { + ctx.notifyEvt(() -> { + List ruleMatches = matches.stream().map(m -> m.getIndex() + 1).collect(toList()); + return new HitPolicyViolationEvent( + FEELEvent.Severity.ERROR, + "UNIQUE hit policy decision tables can only have one matching rule. " + + "Multiple matches found for decision table '" + dt.getName() + "'. " + + "Matched rules: " + ruleMatches, + dt.getName(), + ruleMatches); + } ); return null; } - if ( matches.size() == 1 ) { - ctx.notifyEvt( () -> { - int index = matches.get( 0 ).getIndex() + 1; - return new DecisionTableRulesSelectedEvent( - FEELEvent.Severity.INFO, - "Rule fired for decision table '" + dt.getName() + "': " + index, - dt.getName(), - dt.getName(), - Collections.singletonList( index ) ); - } - ); - return results.get( 0 ); + if (matches.size() == 1) { + notifyDecisionTableRulesSelectedEvent(ctx, dt, matches); + return results.get(0); } return null; } /** - * First – return the first match in rule order + * First – return the first match in rule order */ public static Object first( EvaluationContext ctx, DecisionTable dt, - List matches, + List matches, List results) { - if ( matches.size() >= 1 ) { - ctx.notifyEvt( () -> { - int index = matches.get( 0 ).getIndex() + 1; - return new DecisionTableRulesSelectedEvent( - FEELEvent.Severity.INFO, - "Rule fired for decision table '" + dt.getName() + "': " + index, - dt.getName(), - dt.getName(), - Collections.singletonList( index ) ); - } - ); - return results.get( 0 ); + if (!matches.isEmpty()) { + notifyDecisionTableRulesSelectedEvent(ctx, dt, matches); + return results.get(0); } return null; } @@ -182,174 +169,87 @@ public static Object first( public static Object any( EvaluationContext ctx, DecisionTable dt, - List matches, + List matches, List results) { - if ( matches.size() >= 1 ) { + if (!matches.isEmpty()) { long distinctOutputEntry = results.stream() .distinct() .count(); - if ( distinctOutputEntry > 1 ) { - ctx.notifyEvt( () -> { - List ruleMatches = matches.stream().map( m -> m.getIndex() + 1 ).collect( toList() ); - return new HitPolicyViolationEvent( - FEELEvent.Severity.ERROR, - "'Multiple rules can match, but they [must] all have the same output '" + dt.getName() + "'. Matched rules: " + ruleMatches, - dt.getName(), - ruleMatches ); - } + if (distinctOutputEntry > 1) { + ctx.notifyEvt(() -> { + List ruleMatches = + matches.stream().map(m -> m.getIndex() + 1).collect(toList()); + return new HitPolicyViolationEvent( + FEELEvent.Severity.ERROR, + "'Multiple rules can match, but they [must] all have the same output '" + dt.getName() + "'. Matched rules: " + ruleMatches, + dt.getName(), + ruleMatches); + } ); return null; } - - ctx.notifyEvt( () -> { - int index = matches.get( 0 ).getIndex() + 1; - return new DecisionTableRulesSelectedEvent( - FEELEvent.Severity.INFO, - "Rule fired for decision table '" + dt.getName() + "': " + index, - dt.getName(), - dt.getName(), - Collections.singletonList( index ) ); - } - ); - return results.get( 0 ); + notifyDecisionTableRulesSelectedEvent(ctx, dt, matches); + return results.get(0); } return null; } /** - * Priority – multiple rules can match, with different outputs. The output that comes first in the supplied output values list is returned + * Priority – multiple rules can match, with different outputs. The output that comes first in the supplied + * output values list is returned */ public static Object priority( EvaluationContext ctx, DecisionTable dt, - List matches, + List matches, List results) { - if ( matches.isEmpty() ) { + if (matches.isEmpty()) { return null; } - List> pairs = sortPairs( ctx, dt, matches, results ); - ctx.notifyEvt( () -> { - List indexes = Collections.singletonList( pairs.get( 0 ).getLeft().getIndex() + 1 ); - return new DecisionTableRulesSelectedEvent( - FEELEvent.Severity.INFO, - "Rules fired for decision table '" + dt.getName() + "': " + indexes, - dt.getName(), - dt.getName(), - indexes ); - } - ); - - return pairs.get( 0 ).getRight(); + List> pairs = sortPairs(ctx, dt, matches, results); + int index = pairs.get(0).getLeft().getIndex() + 1; + String id = pairs.get(0).getLeft().getId(); + notifyDecisionTableRulesSelectedEvent(ctx, dt, index, id); + return pairs.get(0).getRight(); } /** - * Output order – return a list of outputs in the order of the output values list + * Output order – return a list of outputs in the order of the output values list */ public static Object outputOrder( EvaluationContext ctx, DecisionTable dt, - List matches, - List results ) { - if ( matches.isEmpty() ) { + List matches, + List results) { + if (matches.isEmpty()) { return null; } - List> pairs = sortPairs( ctx, dt, matches, results ); - ctx.notifyEvt( () -> { - List indexes = pairs.stream().map( p -> p.getLeft().getIndex() + 1 ).collect( toList() ); - return new DecisionTableRulesSelectedEvent( - FEELEvent.Severity.INFO, - "Rules fired for decision table '" + dt.getName() + "': " + indexes, - dt.getName(), - dt.getName(), - indexes ); - } - ); - - return pairs.stream().map( p -> p.getRight() ).collect( Collectors.toList() ); - } - - private static List> sortPairs( EvaluationContext ctx, DecisionTable dt, List matches, List results) { - List> pairs = new ArrayList<>( ); - for( int i = 0; i < matches.size(); i++ ) { - pairs.add( new Pair<>( matches.get( i ), results.get( i ) ) ); - } - - if ( dt.getOutputs().size() == 1 && !dt.getOutputs().get( 0 ).getOutputValues().isEmpty() ) { - // single output, just sort the results - List outs = dt.getOutputs().get( 0 ).getOutputValues(); - pairs.sort( (r1, r2) -> { - return sortByOutputsOrder( ctx, outs, r1.getRight(), r2.getRight() ); - } ); - } else if ( dt.getOutputs().size() > 1 ) { - // multiple outputs, collect the ones that have values listed - List priorities = dt.getOutputs().stream().filter( o -> !o.getOutputValues().isEmpty() ).collect( toList() ); - pairs.sort( (r1, r2) -> { - Map m1 = (Map) r1.getRight(); - Map m2 = (Map) r2.getRight(); - for ( DecisionTable.OutputClause oc : priorities ) { - int o = sortByOutputsOrder( ctx, oc.getOutputValues(), m1.get( oc.getName() ), m2.get( oc.getName() ) ); - if ( o != 0 ) { - return o; - } - } - // unable to sort, so keep order - return 0; - } ); - } - return pairs; - } - - private static int sortByOutputsOrder(EvaluationContext ctx, List outs, Object r1, Object r2) { - boolean r1found = false; - boolean r2found = false; - for( int index = 0; index < outs.size() && !r1found && !r2found; index++ ) { - UnaryTest ut = outs.get( index ); - if( ut.apply( ctx, r1 ) ) { - r1found = true; - } - if( ut.apply( ctx, r2 ) ) { - r2found = true; - } + List> pairs = sortPairs(ctx, dt, matches, results); + int index = pairs.get(0).getLeft().getIndex() + 1; + String id = pairs.get(0).getLeft().getId(); + notifyDecisionTableRulesSelectedEvent(ctx, dt, index, id); - } - if ( r1found && r2found ) { - return 0; - } else if ( r1found ) { - return -1; - } else if ( r2found ) { - return 1; - } else { - return 0; - } + return pairs.stream().map(Pair::getRight).collect(Collectors.toList()); } /** * Rule order – return a list of outputs in rule order - * Collect – return a list of the outputs in arbitrary order + * Collect – return a list of the outputs in arbitrary order */ public static Object ruleOrder( EvaluationContext ctx, DecisionTable dt, - List matches, + List matches, List results) { - if ( matches.isEmpty() ) { + if (matches.isEmpty()) { return null; } - ctx.notifyEvt( () -> { - List indexes = matches.stream().map( m -> m.getIndex() + 1 ).collect( toList() ); - return new DecisionTableRulesSelectedEvent( - FEELEvent.Severity.INFO, - "Rules fired for decision table '" + dt.getName() + "': " + indexes, - dt.getName(), - dt.getName(), - indexes ); - } - ); + notifyDecisionTableRulesSelectedEventWithList(ctx, dt, matches); return results; } public static Collector singleValueOrContext(List outputs) { - return new SingleValueOrContextCollector<>( outputs.stream().map( DecisionTable.OutputClause::getName ).collect( toList() ) ); + return new SingleValueOrContextCollector<>(outputs.stream().map(DecisionTable.OutputClause::getName).collect(toList())); } public static Object generalizedCollect( @@ -358,16 +258,17 @@ public static Object generalizedCollect( List results, Function, Object> resultCollector) { final List> raw; - final List names = dt.getOutputs().stream().map( o -> o.getName() != null ? o.getName() : dt.getName() ).collect( toList() ); - if ( names.size() > 1 ) { + final List names = + dt.getOutputs().stream().map(o -> o.getName() != null ? o.getName() : dt.getName()).collect(toList()); + if (names.size() > 1) { raw = (List>) results; } else { - raw = results.stream().map( (Object r) -> Collections.singletonMap( names.get( 0 ), r ) ).collect( toList() ); + raw = results.stream().map((Object r) -> Collections.singletonMap(names.get(0), r)).collect(toList()); } - return range( 0, names.size() ) - .mapToObj( index -> names.get( index ) ) - .map( name -> resultCollector.apply( raw.stream().map( r -> r.get( name ) ) ) ) - .collect( singleValueOrContext( dt.getOutputs() ) ); + return range(0, names.size()) + .mapToObj(index -> names.get(index)) + .map(name -> resultCollector.apply(raw.stream().map(r -> r.get(name)))) + .collect(singleValueOrContext(dt.getOutputs())); } /** @@ -376,23 +277,14 @@ public static Object generalizedCollect( public static Object countCollect( EvaluationContext ctx, DecisionTable dt, - List matches, + List matches, List results) { - ctx.notifyEvt( () -> { - List indexes = matches.stream().map( m -> m.getIndex() + 1 ).collect( toList() ); - return new DecisionTableRulesSelectedEvent( - FEELEvent.Severity.INFO, - "Rules fired for decision table '" + dt.getName() + "': " + indexes, - dt.getName(), - dt.getName(), - indexes ); - } - ); + notifyDecisionTableRulesSelectedEventWithList(ctx, dt, matches); return generalizedCollect( ctx, dt, results, - x -> new BigDecimal( x.collect( toSet() ).size() ) ); + x -> new BigDecimal(x.collect(toSet()).size())); } /** @@ -401,24 +293,14 @@ public static Object countCollect( public static Object minCollect( EvaluationContext ctx, DecisionTable dt, - List matches, + List matches, List results) { Object result = generalizedCollect( ctx, dt, results, - x -> x.map( y -> (Comparable) y ).collect( minBy( Comparator.naturalOrder() ) ).orElse( null ) ); - ctx.notifyEvt( () -> { - int resultIdx = results.indexOf( result ); - List indexes = resultIdx >= 0 ? Collections.singletonList( matches.get( resultIdx ).getIndex() + 1 ) : Collections.emptyList(); - return new DecisionTableRulesSelectedEvent( - FEELEvent.Severity.INFO, - "Rules fired for decision table '" + dt.getName() + "': " + indexes, - dt.getName(), - dt.getName(), - indexes ); - } - ); + x -> x.map(y -> (Comparable) y).collect(minBy(Comparator.naturalOrder())).orElse(null)); + notifyDecisionTableRulesSelectedEventByResultIdx(ctx, dt, matches, results, result); return result; } @@ -428,57 +310,159 @@ public static Object minCollect( public static Object maxCollect( EvaluationContext ctx, DecisionTable dt, - List matches, + List matches, List results) { Object result = generalizedCollect( ctx, dt, results, - x -> x.map( y -> (Comparable) y ).collect( maxBy( Comparator.naturalOrder() ) ).orElse( null ) ); - ctx.notifyEvt( () -> { - int resultIdx = results.indexOf( result ); - List indexes = resultIdx >= 0 ? Collections.singletonList( matches.get( resultIdx ).getIndex() + 1 ) : Collections.emptyList(); - return new DecisionTableRulesSelectedEvent( - FEELEvent.Severity.INFO, - "Rules fired for decision table '" + dt.getName() + "': " + indexes, - dt.getName(), - dt.getName(), - indexes ); - } - ); + x -> x.map(y -> (Comparable) y).collect(maxBy(Comparator.naturalOrder())).orElse(null)); + notifyDecisionTableRulesSelectedEventByResultIdx(ctx, dt, matches, results, result); return result; } /** - * C+ – return the sum of the outputs + * C+ – return the sum of the outputs */ public static Object sumCollect( EvaluationContext ctx, DecisionTable dt, - List matches, + List matches, List results) { - ctx.notifyEvt( () -> { - List indexes = matches.stream().map( m -> m.getIndex() + 1 ).collect( toList() ); - return new DecisionTableRulesSelectedEvent( - FEELEvent.Severity.INFO, - "Rules fired for decision table '" + dt.getName() + "': " + indexes, - dt.getName(), - dt.getName(), - indexes ); - } - ); + notifyDecisionTableRulesSelectedEventWithList(ctx, dt, matches); return generalizedCollect( ctx, dt, results, - x -> x.reduce( BigDecimal.ZERO, (a, b) -> { - if ( !(a instanceof Number && b instanceof Number) ) { + x -> x.reduce(BigDecimal.ZERO, (a, b) -> { + if (!(a instanceof Number && b instanceof Number)) { return null; } else { - BigDecimal aB = new BigDecimal( a.toString() ); - BigDecimal bB = new BigDecimal( b.toString() ); - return aB.add( bB ); + BigDecimal aB = new BigDecimal(a.toString()); + BigDecimal bB = new BigDecimal(b.toString()); + return aB.add(bB); } - } ) ); + })); + } + + private static void notifyDecisionTableRulesSelectedEventByResultIdx(EvaluationContext ctx, + DecisionTable dt, + List matches, + List results, + Object result) { + int resultIdx = results.indexOf(result); + List indexes = resultIdx >= 0 ? + Collections.singletonList(matches.get(resultIdx).getIndex() + 1) : + Collections.emptyList(); + List matchesId = resultIdx >= 0 ? + Collections.singletonList(matches.get(resultIdx).getId() + 1) : + Collections.emptyList(); + notifyDecisionTableRulesSelectedEvent(ctx, dt, indexes, matchesId); + } + + private static void notifyDecisionTableRulesSelectedEventWithList(EvaluationContext ctx, + DecisionTable dt, + List matches) { + List indexes = new ArrayList<>(); + List matchesId = new ArrayList<>(); + matches.forEach(dr -> { + indexes.add(dr.getIndex() + 1); + if (dr.getId() != null && !dr.getId().isEmpty()) { + matchesId.add(dr.getId()); + } + }); + notifyDecisionTableRulesSelectedEvent(ctx, dt, indexes, matchesId); + } + + private static void notifyDecisionTableRulesSelectedEvent(EvaluationContext ctx, + DecisionTable dt, + List matches) { + int index = matches.get(0).getIndex() + 1; + String id = matches.get(0).getId(); + notifyDecisionTableRulesSelectedEvent(ctx, dt, index, id); + } + + private static void notifyDecisionTableRulesSelectedEvent(EvaluationContext ctx, + DecisionTable dt, + int index, + String id) { + ctx.notifyEvt(() -> new DecisionTableRulesSelectedEvent( + FEELEvent.Severity.INFO, + "Rule fired for decision table '" + dt.getName() + "': " + index, + dt.getName(), + dt.getName(), + Collections.singletonList(index), + Collections.singletonList(id)) + ); + } + + private static void notifyDecisionTableRulesSelectedEvent(EvaluationContext ctx, + DecisionTable dt, + List indexes, + List matchesId) { + ctx.notifyEvt(() -> new DecisionTableRulesSelectedEvent( + FEELEvent.Severity.INFO, + "Rules fired for decision table '" + dt.getName() + "': " + indexes, + dt.getName(), + dt.getName(), + indexes, + matchesId) + ); + } + + private static List> sortPairs(EvaluationContext ctx, DecisionTable dt, + List matches, List results) { + List> pairs = new ArrayList<>(); + for (int i = 0; i < matches.size(); i++) { + pairs.add(new Pair<>(matches.get(i), results.get(i))); + } + + if (dt.getOutputs().size() == 1 && !dt.getOutputs().get(0).getOutputValues().isEmpty()) { + // single output, just sort the results + List outs = dt.getOutputs().get(0).getOutputValues(); + pairs.sort((r1, r2) -> { + return sortByOutputsOrder(ctx, outs, r1.getRight(), r2.getRight()); + }); + } else if (dt.getOutputs().size() > 1) { + // multiple outputs, collect the ones that have values listed + List priorities = + dt.getOutputs().stream().filter(o -> !o.getOutputValues().isEmpty()).collect(toList()); + pairs.sort((r1, r2) -> { + Map m1 = (Map) r1.getRight(); + Map m2 = (Map) r2.getRight(); + for (DecisionTable.OutputClause oc : priorities) { + int o = sortByOutputsOrder(ctx, oc.getOutputValues(), m1.get(oc.getName()), m2.get(oc.getName())); + if (o != 0) { + return o; + } + } + // unable to sort, so keep order + return 0; + }); + } + return pairs; + } + + private static int sortByOutputsOrder(EvaluationContext ctx, List outs, Object r1, Object r2) { + boolean r1found = false; + boolean r2found = false; + for (int index = 0; index < outs.size() && !r1found && !r2found; index++) { + UnaryTest ut = outs.get(index); + if (ut.apply(ctx, r1)) { + r1found = true; + } + if (ut.apply(ctx, r2)) { + r2found = true; + } + } + if (r1found && r2found) { + return 0; + } else if (r1found) { + return -1; + } else if (r2found) { + return 1; + } else { + return 0; + } } } diff --git a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/runtime/events/DecisionTableRulesMatchedEvent.java b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/runtime/events/DecisionTableRulesMatchedEvent.java index 60d4ee04f02..45d1dad350f 100644 --- a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/runtime/events/DecisionTableRulesMatchedEvent.java +++ b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/runtime/events/DecisionTableRulesMatchedEvent.java @@ -32,12 +32,14 @@ public class DecisionTableRulesMatchedEvent private final String nodeName; private final String dtName; private final List matches; + private final List matchesIds; - public DecisionTableRulesMatchedEvent(Severity severity, String msg, String nodeName, String dtName, List matches) { + public DecisionTableRulesMatchedEvent(Severity severity, String msg, String nodeName, String dtName, List matches, List matchesIds) { super( severity, msg, null ); this.nodeName = nodeName; this.dtName = dtName; this.matches = matches; + this.matchesIds = matchesIds; } public String getNodeName() { @@ -52,6 +54,10 @@ public List getMatches() { return matches; } + public List getMatchesIds() { + return matchesIds; + } + @Override public String toString() { return "DecisionTableRulesMatchedEvent{" + @@ -60,6 +66,7 @@ public String toString() { ", nodeName='" + nodeName + '\'' + ", dtName='" + dtName + '\'' + ", matches='" + matches + '\'' + + ", matchesIds='" + matchesIds + '\'' + '}'; } } diff --git a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/runtime/events/DecisionTableRulesSelectedEvent.java b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/runtime/events/DecisionTableRulesSelectedEvent.java index 4adb80d220a..c1569419833 100644 --- a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/runtime/events/DecisionTableRulesSelectedEvent.java +++ b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/runtime/events/DecisionTableRulesSelectedEvent.java @@ -40,12 +40,14 @@ public class DecisionTableRulesSelectedEvent private final String nodeName; private final String dtName; private final List fired; + private final List firedIds; - public DecisionTableRulesSelectedEvent(Severity severity, String msg, String nodeName, String dtName, List fired) { + public DecisionTableRulesSelectedEvent(Severity severity, String msg, String nodeName, String dtName, List fired, List firedIds) { super( severity, msg, null ); this.nodeName = nodeName; this.dtName = dtName; this.fired = fired; + this.firedIds = firedIds; } public String getNodeName() { @@ -58,6 +60,10 @@ public List getFired() { return fired; } + public List getFiredIds() { + return firedIds; + } + @Override public String toString() { return "DecisionTableRulesMatchedEvent{" + @@ -66,6 +72,7 @@ public String toString() { ", nodeName='" + nodeName + '\'' + ", dtName='" + dtName + '\'' + ", fired='" + fired + '\'' + + ", firedIds='" + firedIds + '\'' + '}'; } } diff --git a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/runtime/functions/DecisionTableFunction.java b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/runtime/functions/DecisionTableFunction.java index 80437477ada..651f334edaa 100644 --- a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/runtime/functions/DecisionTableFunction.java +++ b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/runtime/functions/DecisionTableFunction.java @@ -153,7 +153,7 @@ protected List> objectToUnaryTestList(EvaluationContext ctx, Lis */ private static DTDecisionRule toDecisionRule(EvaluationContext mainCtx, FEEL embeddedFEEL, int index, List rule, int inputSize) { // TODO should be check indeed block of inputSize n inputs, followed by block of outputs. - DTDecisionRule dr = new DTDecisionRule( index ); + DTDecisionRule dr = new DTDecisionRule( index, null ); for ( int i = 0; i < rule.size(); i++ ) { Object o = rule.get( i ); if ( i < inputSize ) { From d45fc7c18f6b413fa4b0b2a16df131cd15ab02e5 Mon Sep 17 00:00:00 2001 From: Gabriele-Cardosi Date: Wed, 16 Oct 2024 14:07:36 +0200 Subject: [PATCH 2/5] [incubator-kie-issues#1543] Fix TODO --- .../core/compiler/alphanetbased/Results.java | 45 ++++++++++++------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/compiler/alphanetbased/Results.java b/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/compiler/alphanetbased/Results.java index ab34d152ac9..45fab5d1ac2 100644 --- a/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/compiler/alphanetbased/Results.java +++ b/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/compiler/alphanetbased/Results.java @@ -6,9 +6,9 @@ * to you 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 - * + *

+ * 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 @@ -50,7 +50,8 @@ static class Items { public void addResult(ResultObject resultObject) { resultGroupedByRow - .computeIfAbsent(resultObject.row, i -> new ArrayList<>(1)) // 10 (the default for Java) columns output are not that usual + .computeIfAbsent(resultObject.row, i -> new ArrayList<>(1)) // 10 (the default for Java) columns + // output are not that usual .add(resultObject); } @@ -148,27 +149,39 @@ public Object applyHitPolicy(EvaluationContext evaluationContext, } events.add(new HitPolicyViolationEvent( FEELEvent.Severity.WARN, - String.format("No rule matched for decision table '%s' and no default values were defined. Setting result to null.", decisionTable.getName()), + String.format("No rule matched for decision table '%s' and no default values were defined. " + + "Setting result to null.", decisionTable.getName()), decisionTable.getName(), Collections.emptyList())); } List matchIndexes = items.matches(); - evaluationContext.notifyEvt( () -> { - List matchedIndexes = matchIndexes.stream().map( dr -> dr.getIndex() + 1 ).collect(Collectors.toList() ); - return new DecisionTableRulesMatchedEvent(FEELEvent.Severity.INFO, - String.format("Rules matched for decision table '%s': %s", decisionTable.getName(), matchIndexes), - decisionTable.getName(), - decisionTable.getName(), - matchedIndexes, - // @TODO gcardosi 1543 - Collections.emptyList()); - } + evaluationContext.notifyEvt(() -> { + List matchedIndexes = new ArrayList<>(); + List matchesId = new ArrayList<>(); + matchIndexes.forEach(dr -> { + matchedIndexes.add(dr.getIndex() + 1); + if (dr.getId() != null && !dr.getId().isEmpty()) { + matchesId.add(dr.getId()); + } + }); + return new DecisionTableRulesMatchedEvent(FEELEvent.Severity.INFO, + String.format("Rules matched for " + + "decision " + + "table '%s': " + + "%s", + decisionTable.getName(), matchIndexes), + decisionTable.getName(), + decisionTable.getName(), + matchedIndexes, + matchesId); + } ); List resultObjects = items.evaluateResults(evaluationContext); - Map errorMessages = checkResults(decisionTable.getOutputs(), evaluationContext, matchIndexes, resultObjects ); + Map errorMessages = checkResults(decisionTable.getOutputs(), evaluationContext, matchIndexes + , resultObjects); if (!errorMessages.isEmpty()) { List offending = new ArrayList<>(errorMessages.keySet()); events.add(new HitPolicyViolationEvent( From f2a2568ae29a2af404292e4f834f56714531bdac Mon Sep 17 00:00:00 2001 From: Gabriele-Cardosi Date: Thu, 17 Oct 2024 10:16:07 +0200 Subject: [PATCH 3/5] [incubator-kie-issues#1543] Fix license header --- .../java/org/kie/dmn/core/compiler/alphanetbased/Results.java | 4 ++-- .../org/kie/dmn/feel/runtime/decisiontables/HitPolicy.java | 4 ++-- .../ast/forexpressioniterators/ForIterationUtilsTest.java | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/compiler/alphanetbased/Results.java b/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/compiler/alphanetbased/Results.java index 45fab5d1ac2..b91dfd4f932 100644 --- a/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/compiler/alphanetbased/Results.java +++ b/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/compiler/alphanetbased/Results.java @@ -6,9 +6,9 @@ * to you 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 diff --git a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/runtime/decisiontables/HitPolicy.java b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/runtime/decisiontables/HitPolicy.java index 828d44d7157..53e5f92c0f3 100644 --- a/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/runtime/decisiontables/HitPolicy.java +++ b/kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/runtime/decisiontables/HitPolicy.java @@ -6,9 +6,9 @@ * to you 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 diff --git a/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/lang/ast/forexpressioniterators/ForIterationUtilsTest.java b/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/lang/ast/forexpressioniterators/ForIterationUtilsTest.java index 43547f15081..852ed9e2057 100644 --- a/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/lang/ast/forexpressioniterators/ForIterationUtilsTest.java +++ b/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/lang/ast/forexpressioniterators/ForIterationUtilsTest.java @@ -6,9 +6,9 @@ * to you 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 From e8d14dcede6633bd8965e430802667e9190d3e3a Mon Sep 17 00:00:00 2001 From: Gabriele-Cardosi Date: Thu, 17 Oct 2024 10:31:04 +0200 Subject: [PATCH 4/5] [incubator-kie-issues#1543] Minor refactoring on unrelated test --- .../org/kie/dmn/feel/runtime/functions/DateFunctionTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/runtime/functions/DateFunctionTest.java b/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/runtime/functions/DateFunctionTest.java index db9ece6621b..f1462b8d79d 100644 --- a/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/runtime/functions/DateFunctionTest.java +++ b/kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/runtime/functions/DateFunctionTest.java @@ -32,7 +32,7 @@ class DateFunctionTest { @BeforeEach void setUp() { - dateFunction = new DateFunction(); + dateFunction = DateFunction.INSTANCE; } @Test From 3e4b7e62045455cce249f624e6c4925a35f2d18a Mon Sep 17 00:00:00 2001 From: Gabriele-Cardosi Date: Thu, 17 Oct 2024 13:07:17 +0200 Subject: [PATCH 5/5] [incubator-kie-issues#1543] Fix as per PR review --- .../core/compiler/alphanetbased/Results.java | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/compiler/alphanetbased/Results.java b/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/compiler/alphanetbased/Results.java index b91dfd4f932..ca42e119aec 100644 --- a/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/compiler/alphanetbased/Results.java +++ b/kie-dmn/kie-dmn-core/src/main/java/org/kie/dmn/core/compiler/alphanetbased/Results.java @@ -32,7 +32,6 @@ import org.kie.dmn.feel.runtime.decisiontables.DTDecisionRule; import org.kie.dmn.feel.runtime.decisiontables.DecisionTable; import org.kie.dmn.feel.runtime.decisiontables.HitPolicy; -import org.kie.dmn.feel.runtime.decisiontables.Indexed; import org.kie.dmn.feel.runtime.events.DecisionTableRulesMatchedEvent; import org.kie.dmn.feel.runtime.events.HitPolicyViolationEvent; @@ -155,12 +154,12 @@ public Object applyHitPolicy(EvaluationContext evaluationContext, Collections.emptyList())); } - List matchIndexes = items.matches(); + List matchingDecisionRules = items.matches(); evaluationContext.notifyEvt(() -> { - List matchedIndexes = new ArrayList<>(); + List matches = new ArrayList<>(); List matchesId = new ArrayList<>(); - matchIndexes.forEach(dr -> { - matchedIndexes.add(dr.getIndex() + 1); + matchingDecisionRules.forEach(dr -> { + matches.add(dr.getIndex() + 1); if (dr.getId() != null && !dr.getId().isEmpty()) { matchesId.add(dr.getId()); } @@ -170,17 +169,17 @@ public Object applyHitPolicy(EvaluationContext evaluationContext, "decision " + "table '%s': " + "%s", - decisionTable.getName(), matchIndexes), + decisionTable.getName(), matchingDecisionRules), decisionTable.getName(), decisionTable.getName(), - matchedIndexes, + matches, matchesId); } ); List resultObjects = items.evaluateResults(evaluationContext); - Map errorMessages = checkResults(decisionTable.getOutputs(), evaluationContext, matchIndexes + Map errorMessages = checkResults(decisionTable.getOutputs(), evaluationContext, matchingDecisionRules , resultObjects); if (!errorMessages.isEmpty()) { List offending = new ArrayList<>(errorMessages.keySet()); @@ -194,6 +193,6 @@ public Object applyHitPolicy(EvaluationContext evaluationContext, return null; } - return hitPolicy.getDti().dti(evaluationContext, decisionTable, matchIndexes, resultObjects); + return hitPolicy.getDti().dti(evaluationContext, decisionTable, matchingDecisionRules, resultObjects); } }