diff --git a/dmn-verifier-app/src/main/java/de/unikoblenz/fgbks/dmn/core/dmnhelper/DmnRuleMap.java b/dmn-verifier-app/src/main/java/de/unikoblenz/fgbks/dmn/core/dmnhelper/DmnRuleMap.java index f733227431c63af264a13f5e93594ffe7b7e355e..a1c4fbc85198eb466b5aacb565d3dfb1d8a7bd1c 100644 --- a/dmn-verifier-app/src/main/java/de/unikoblenz/fgbks/dmn/core/dmnhelper/DmnRuleMap.java +++ b/dmn-verifier-app/src/main/java/de/unikoblenz/fgbks/dmn/core/dmnhelper/DmnRuleMap.java @@ -147,8 +147,8 @@ public class DmnRuleMap { for (OutputType ot : outputColumns) { Set<OutputType> equalOuts = new HashSet<>(); for (OutputType otInner : outputColumns) { - if (ot.label != null - && ot.label.equals(otInner.label) + if (ot.expressionName != null + && ot.expressionName.equals(otInner.expressionName) && ot.dataType.equals(otInner.dataType)) { equalOuts.add(otInner); } @@ -181,10 +181,14 @@ public class DmnRuleMap { inputColumns.stream() .filter(i -> i.getDecisionKey().equals(ot.getDecisionKey())) .collect(Collectors.toList())) { - // only, if the label didn't exists in previously created input + // only, if the expressionName didn't exists in previously created input Optional<InputType> existingInputType = newInputTypes.stream() - .filter(i -> i.label.equals(inputType.label) && i.dataType == inputType.dataType) + .filter( + i -> + i.expressionName != null + && i.expressionName.equals(inputType.expressionName) + && i.dataType == inputType.dataType) .findFirst(); if (existingInputType.isPresent()) { inputType @@ -216,7 +220,10 @@ public class DmnRuleMap { // try to find existing col Optional<InputType> orgI = orgInputTypes.stream() - .filter(i -> i.label.equals(inputType.label) && i.dataType == inputType.dataType) + .filter( + i -> + i.expressionName.equals(inputType.expressionName) + && i.dataType == inputType.dataType) .findFirst(); List<Value> vals = inputs.getOrDefault(inputType, new ArrayList<>()); if (orgI.isPresent()) { @@ -234,7 +241,10 @@ public class DmnRuleMap { // try to find existing col Optional<InputType> orgI = orgInputTypes.stream() - .filter(i -> i.label.equals(inputType.label) && i.dataType == inputType.dataType) + .filter( + i -> + i.expressionName.equals(inputType.expressionName) + && i.dataType == inputType.dataType) .findFirst(); List<Value> vals = inputs.getOrDefault(inputType, new ArrayList<>()); if (!orgI.isPresent()) { diff --git a/dmn-verifier-app/src/main/java/de/unikoblenz/fgbks/dmn/core/dmnhelper/InputType.java b/dmn-verifier-app/src/main/java/de/unikoblenz/fgbks/dmn/core/dmnhelper/InputType.java index db069e0855a0069f2823fa06790ff6550f8e7835..6315fee4f5ef391653a7bc0c5c60803aec3f72d5 100644 --- a/dmn-verifier-app/src/main/java/de/unikoblenz/fgbks/dmn/core/dmnhelper/InputType.java +++ b/dmn-verifier-app/src/main/java/de/unikoblenz/fgbks/dmn/core/dmnhelper/InputType.java @@ -1,5 +1,6 @@ package de.unikoblenz.fgbks.dmn.core.dmnhelper; +import java.util.UUID; import org.camunda.bpm.dmn.engine.DmnDecision; import org.camunda.bpm.dmn.engine.impl.DmnDecisionTableInputImpl; @@ -14,7 +15,9 @@ public class InputType extends Type { d, input.getId(), DataType.getTypeFromString(input.getExpression().getTypeDefinition().getTypeName()), - input.getName()); + input.getExpression().getExpression() == null + ? UUID.randomUUID().toString().substring(0, 9) + : input.getExpression().getExpression()); } public InputType(InputType inputType) { diff --git a/dmn-verifier-app/src/main/java/de/unikoblenz/fgbks/dmn/core/dmnhelper/Type.java b/dmn-verifier-app/src/main/java/de/unikoblenz/fgbks/dmn/core/dmnhelper/Type.java index ca97925656002ea00133643981d731fe41055c8e..968211f13c3fd37449156d06d94e7fa23c04dccf 100644 --- a/dmn-verifier-app/src/main/java/de/unikoblenz/fgbks/dmn/core/dmnhelper/Type.java +++ b/dmn-verifier-app/src/main/java/de/unikoblenz/fgbks/dmn/core/dmnhelper/Type.java @@ -12,7 +12,7 @@ public abstract class Type { protected final DmnDecision decision; protected final String id; protected final DataType dataType; - protected final String label; + protected final String expressionName; protected String overwrittenDecisionKey; protected Set<String> predefinedValues; @@ -35,8 +35,8 @@ public abstract class Type { return dataType; } - public String getLabel() { - return label; + public String getExpressionName() { + return expressionName; } public void setOverwrittenDecisionKey(String overwrittenDecisionKey) { @@ -51,11 +51,11 @@ public abstract class Type { predefinedValues.add(predefinedValue); } - protected Type(DmnDecision decision, String id, DataType dataType, String label) { + protected Type(DmnDecision decision, String id, DataType dataType, String expressionName) { this.decision = decision; this.id = id; this.dataType = dataType; - this.label = label; + this.expressionName = expressionName; this.overwrittenDecisionKey = null; this.predefinedValues = new HashSet<>(); } @@ -64,7 +64,7 @@ public abstract class Type { this.decision = type.decision; this.id = type.id; this.dataType = type.dataType; - this.label = type.label; + this.expressionName = type.expressionName; this.overwrittenDecisionKey = type.overwrittenDecisionKey; this.predefinedValues = new HashSet<>(type.predefinedValues); } @@ -106,7 +106,7 @@ public abstract class Type { } public boolean isEqualExpression(Type type) { - return this.label.equals(type.label); + return this.expressionName.equals(type.expressionName); } @Override diff --git a/dmn-verifier-app/src/main/java/de/unikoblenz/fgbks/dmn/core/models/VerifierType.java b/dmn-verifier-app/src/main/java/de/unikoblenz/fgbks/dmn/core/models/VerifierType.java index 7b89e0c42a8f2faf8f18a0c960b2ab6feb9e7caa..d313a4e89430d5dbf1264f56fe315faaf8b9598e 100644 --- a/dmn-verifier-app/src/main/java/de/unikoblenz/fgbks/dmn/core/models/VerifierType.java +++ b/dmn-verifier-app/src/main/java/de/unikoblenz/fgbks/dmn/core/models/VerifierType.java @@ -16,53 +16,80 @@ import org.camunda.bpm.dmn.engine.DmnDecision; import org.slf4j.LoggerFactory; public enum VerifierType { + /** Detecting rules which have an identical input, i.e. are redundant. */ Identical("Checking for identical rules.", IdenticalVerifier.class, true, true, false), + /** + * Detecting individual rules which are subsumed by other rules, i.e. they are not necessary. For + * example, rules containing wildcards often render more specific rules unnecessary due to + * subsumption. + */ Subsumption( "Checking for rules, which subsume other rules.", SubsumptionVerifier.class, true, true, false), + /** + * Detecting rules which are not identical, but still semantically equivalent. Here, our tool can + * verify if there exist multiple rules which use synonyms as inputs and are therefore equivalent, + * based on synonym relations via Wordnet. + */ Equivalent( "Checking for semantically equivalent rules.", EquivalentVerifier.class, true, false, false), + /** Detecting whether there are any overlaps in rule conditions. */ Overlap("Checking for overlapping rules.", OverlappingVerifier.class, true, true, false), + /** + * Detecting whether there are any missing business rules, i.e. if there are rules missing for + * expected inputs. + */ Missing("Checking for missing rules.", MissingVerifier.class, true, false, false), + /** Checking wether ranges can be combined to simplify decision tables. */ PartialReduction( "Checking for partial reduction of rules (combination).", PartialReductionVerifier.class, true, false, false), + /** + * Detecting rules which will always be activated together, but have differing or contradicting + * conclusions. For example, rules which will be activated together must not yield that a customer + * is both credit worthy, and not creditworthy, as this is logically inconsistent. + */ Interdeterminism( "Checking for rules that are activated together but have different conclusions.", InterdeterminismVerifier.class, false, false, false), + /** Same as {@link VerifierType#Identical}, including a multi table view. */ MultiTableIdentical( "Checking for identical rules in multiple tables (identical outcome column).", MultiTableIdenticalVerifier.class, true, true, true), + /** Same as {@link VerifierType#Overlap}, including a multi table view. */ MultiTableOverlapping( "Checking for overlapping rules in multiple tables (identical outcome column).", MultiTableOverlappingVerifier.class, true, true, true), + /** Same as {@link VerifierType#Subsumption}, including a multi table view. */ MultiTableSubsumption( "Checking for subsumptions in multiple tables (identical outcome column).", MultiTableSubsumptionVerifier.class, true, true, true), + /** Same as {@link VerifierType#Equivalent}, including a multi table view. */ MultiTableEquivalent( "Checking for equivalent rules in multiple tables (identical outcome column).", MultiTableEquivalentVerifier.class, true, false, true), + /** Same as {@link VerifierType#PartialReduction}, including a multi table view. */ MultiTablePartialReduction( "Checking for partial reduction of rules in multiple tables (identical outcome column).", MultiTablePartialReductionVerifier.class, @@ -134,7 +161,7 @@ public enum VerifierType { } public static VerifierCollectionResult getAllResults(List<DmnDecision> dmnDecisionList) { - return getAllResults(dmnDecisionList, false); + return getAllResults(dmnDecisionList, true); } public static VerifierCollectionResult getAllResults( diff --git a/dmn-verifier-app/src/main/java/de/unikoblenz/fgbks/dmn/core/verifier/EquivalentVerifier.java b/dmn-verifier-app/src/main/java/de/unikoblenz/fgbks/dmn/core/verifier/EquivalentVerifier.java index ab4d7e65d575622b45de9932f67087c1a86dced2..ffe51624b596659a8d847ec48c662672eea38034 100644 --- a/dmn-verifier-app/src/main/java/de/unikoblenz/fgbks/dmn/core/verifier/EquivalentVerifier.java +++ b/dmn-verifier-app/src/main/java/de/unikoblenz/fgbks/dmn/core/verifier/EquivalentVerifier.java @@ -30,7 +30,7 @@ public class EquivalentVerifier extends AbstractVerifier { for (Type t : allTypesFromDecisionKey) { // only check strings for equvalent rules (input + output) if (t.getDataType() == DataType.STRING) { - // ensure label is not null and not empty + // ensure expression is not null and not empty List<Value> values = dmnRuleMap.getValuesFromType(t).stream() .filter( diff --git a/dmn-verifier-app/src/main/resources/sampleMultiTable.dmn b/dmn-verifier-app/src/main/resources/sampleMultiTable.dmn index 68a9335a1fa1a114dbe94362c8c2c902e529f9f2..e5963b3729d3e62c22a65d1dbeff35c33f7fdfec 100644 --- a/dmn-verifier-app/src/main/resources/sampleMultiTable.dmn +++ b/dmn-verifier-app/src/main/resources/sampleMultiTable.dmn @@ -5,14 +5,14 @@ <biodi:bounds x="311" y="89" width="180" height="80" /> </extensionElements> <decisionTable id="decisionTable_1"> - <input id="input_1" label="a"> + <input id="input_1"> <inputExpression id="inputExpression_1" typeRef="integer"> - <text></text> + <text>a</text> </inputExpression> </input> - <input id="InputClause_0quh9nu" label="b"> + <input id="InputClause_0quh9nu"> <inputExpression id="LiteralExpression_0u964y9" typeRef="integer"> - <text></text> + <text>b</text> </inputExpression> </input> <output id="output_1" name="outX" typeRef="integer" /> @@ -56,8 +56,10 @@ <biodi:bounds x="105" y="88" width="180" height="80" /> </extensionElements> <decisionTable id="DecisionTable_1whu57b"> - <input id="InputClause_00l1ge6" label="a"> - <inputExpression id="LiteralExpression_0ujv368" typeRef="integer" /> + <input id="InputClause_00l1ge6"> + <inputExpression id="LiteralExpression_0ujv368" typeRef="integer"> + <text>a</text> + </inputExpression> </input> <output id="OutputClause_10gzo88" name="outX" typeRef="integer" /> <rule id="DecisionRule_0udq1f7"> diff --git a/dmn-verifier-app/src/main/webapp/index.xhtml b/dmn-verifier-app/src/main/webapp/index.xhtml index 9bbb81461b402a953b6fce4bde05909abc8b400e..767262e0c9c122ce2c39353c59ebeb8e2abc9b4a 100644 --- a/dmn-verifier-app/src/main/webapp/index.xhtml +++ b/dmn-verifier-app/src/main/webapp/index.xhtml @@ -13,7 +13,7 @@ <link rel="stylesheet" href="https://unpkg.com/dmn-js@6.3.1/dist/assets/dmn-js-decision-table-controls.css"/> <link rel="stylesheet" - href="https://unpkg.com/dmn-js@6.3.1/dist/assets/dmn-js-literal-label.css"/> + href="https://unpkg.com/dmn-js@6.3.1/dist/assets/dmn-js-literal-expression.css"/> <link rel="stylesheet" href="https://unpkg.com/dmn-js@6.3.1/dist/assets/dmn-font/css/dmn.css"/> <h:panelGroup id="content" layout="block">