diff --git a/dmn-verifier-app/src/main/java/de/unikoblenz/fgbks/dmn/core/verifier/MissingRules.java b/dmn-verifier-app/src/main/java/de/unikoblenz/fgbks/dmn/core/verifier/MissingRules.java index fc09ed1d840ef57d4bbc149bf1dd16d3adcd1c20..ab8380dd306cfa8470adb3217fe6048734cd614c 100644 --- a/dmn-verifier-app/src/main/java/de/unikoblenz/fgbks/dmn/core/verifier/MissingRules.java +++ b/dmn-verifier-app/src/main/java/de/unikoblenz/fgbks/dmn/core/verifier/MissingRules.java @@ -8,9 +8,11 @@ import de.unikoblenz.fgbks.dmn.core.verifier.helper.DataType; import de.unikoblenz.fgbks.dmn.core.verifier.helper.Type; import de.unikoblenz.fgbks.dmn.core.verifier.helper.Value; import java.util.ArrayList; +import java.util.Arrays; import java.util.Comparator; import java.util.List; import java.util.Optional; +import java.util.stream.Collectors; import org.camunda.bpm.dmn.engine.DmnDecision; public class MissingRules extends AbstractVerifier { @@ -20,13 +22,33 @@ public class MissingRules extends AbstractVerifier { } @Override - protected void beforeVerifyDecision() {} + protected void beforeVerifyDecision() { + } @Override protected void verifyDecision(DmnDecision d) { if (d.isDecisionTable()) { List<Type> inputs = new ArrayList<>(ruleMap.getAllInputTypesFromDecisionKey(d.getKey())); - checkForMissingRules(d, inputs, 0, null, new ArrayList<>()); + List<Value[]> missingIntervals = new ArrayList<>(); + checkForMissingRules(d, inputs, 0, new Value[inputs.size()], missingIntervals); + // add errors for missing intervals + // TODO: add duplicate rows + for (Value[] missingInt : missingIntervals) { + StringBuilder sb = new StringBuilder("In table "); + sb.append(d.getName()); // TODO: add real name + sb.append(" following rule is missing: "); + sb.append(Arrays.stream(missingInt) + .map(Value::getOrgExpression) + .collect(Collectors.joining(" / "))); + addVerification( + VerificationResult.getBuilder() + .addRules( + Arrays.stream(missingInt) + .map(Value::getRuleIdentifier) + .collect(Collectors.toList())) + .withMessage(sb.toString()) + .build()); + } } } @@ -34,54 +56,83 @@ public class MissingRules extends AbstractVerifier { DmnDecision d, List<Type> inputs, int i, - List<RuleIdentifier> currentRuleIdentifiers, - List<Boundary> missingIntervals) { - if (inputs.get(i).getDataType() == DataType.NUMBER) { - + Value[] missingValues, + List<Value[]> missingIntervals) { + if (i == inputs.size()) { + missingIntervals.add(missingValues.clone()); + } else { List<Value> sortedBounds = - new ArrayList<>(ruleMap.getValuesFromInputType(inputs.get(i), currentRuleIdentifiers)); + new ArrayList<>(ruleMap.getValuesFromInputType(inputs.get(i))); sortedBounds.sort(Comparator.comparing(Value::getBoundary)); Value lastBound = null; + int z = 0; for (Value currentBound : sortedBounds) { - if (lastBound == null) { - if (currentBound.getBoundary().getLowerBound() != Double.MIN_VALUE) { - missingIntervals.add(currentBound.getBoundary().getNewBoundaryLower().get()); + z++; + Optional<Boundary> newBoundary = Optional.empty(); + String orgExpr = ""; + if (inputs.get(i).getDataType() == DataType.NUMBER) { + if (lastBound == null) { // check current elements + if (currentBound.getBoundary().getLowerBound() != Double.MIN_VALUE) { + newBoundary = currentBound.getBoundary().getNewBoundaryLower(); + } + } else { + newBoundary = + lastBound.getBoundary().getNewBoundaryBetween(currentBound.getBoundary()); + } + if (newBoundary.isPresent()) { + orgExpr = newBoundary.get().toString(); } } else { - Optional<Boundary> newBound = - lastBound.getBoundary().getNewBoundaryBetween(currentBound.getBoundary()); - newBound.ifPresent(missingIntervals::add); + newBoundary = Optional.of(currentBound.getBoundary()); + orgExpr = currentBound.getOrgExpression(); + } + if (newBoundary.isPresent()) { + missingValues[i] = new Value(newBoundary.get(), orgExpr, + RuleIdentifier.getBuilder() + .withRowNumber(999) + .withDecision(d) + .withRuleId("newRule") + .build(), + inputs.get(i)); + checkForMissingRules(d, inputs, i + 1, missingValues, missingIntervals); + missingValues[i] = null; + } + + // TODO: check last element + if (z == sortedBounds.size()) { + if (inputs.get(i).getDataType() == DataType.NUMBER) { + if (currentBound.getBoundary().getUpperBound() != Double.MAX_VALUE) { + newBoundary = currentBound.getBoundary().getNewBoundaryUpper(); + orgExpr = newBoundary.get().toString(); + missingValues[i] = new Value(newBoundary.get(), orgExpr, + RuleIdentifier.getBuilder() + .withRowNumber(999) + .withDecision(d) + .withRuleId("newRule") + .build(), + inputs.get(i)); + checkForMissingRules(d, inputs, i + 1, missingValues, missingIntervals); + missingValues[i] = null; + } + } else { + // not number TODO + } } + // set next last bound if (lastBound == null || !lastBound - .getBoundary() - .isNumberInRange( - currentBound.getBoundary().getUpperBound(), - currentBound.getBoundary().getUpperBoundType())) { + .getBoundary() + .isNumberInRange( + currentBound.getBoundary().getUpperBound(), + currentBound.getBoundary().getUpperBoundType())) { lastBound = currentBound; } } - if (lastBound != null) { - if (lastBound.getBoundary().getUpperBound() != Double.MAX_VALUE) { - missingIntervals.add(lastBound.getBoundary().getNewBoundaryUpper().get()); - } - } - for (Boundary b : missingIntervals) { - addVerification( - VerificationResult.getBuilder() - .addRule( - RuleIdentifier.getBuilder() - .withRowNumber(999) - .withDecision(d) - .withRuleId("a") - .build()) - .withMessage("Missing interval: %s", b.toString()) - .build()); - } } } @Override - protected void afterVerifyDecision() {} + protected void afterVerifyDecision() { + } } diff --git a/dmn-verifier-app/src/main/java/de/unikoblenz/fgbks/dmn/core/verifier/OverlappingRules.java b/dmn-verifier-app/src/main/java/de/unikoblenz/fgbks/dmn/core/verifier/OverlappingRules.java index b0f1338c1dc2ac5f35b9a55caaad9514f73a4dce..ae7765c740aee4ec7120c230aab73460a8c1dcae 100644 --- a/dmn-verifier-app/src/main/java/de/unikoblenz/fgbks/dmn/core/verifier/OverlappingRules.java +++ b/dmn-verifier-app/src/main/java/de/unikoblenz/fgbks/dmn/core/verifier/OverlappingRules.java @@ -50,6 +50,7 @@ public class OverlappingRules extends AbstractVerifier { new ArrayList<>(ruleMap.getValuesFromInputType(inputs.get(i), currentRuleIdentifiers)); sortedBounds.sort(Comparator.comparing(Value::getBoundary)); int z = 0; + boolean foundPOverlap = false; for (Value currentBound : sortedBounds) { z++; boolean foundOverlap = hasOverlap; @@ -75,15 +76,17 @@ public class OverlappingRules extends AbstractVerifier { inputs, i + 1, currentBoundsCpy.stream().map(Value::getRuleIdentifier).collect(Collectors.toList()), - hasOverlap); + hasOverlap || foundPOverlap); + foundPOverlap = false; } - if ((z == sortedBounds.size() || foundOverlap) && currentBounds.size() > 1) { + if (z == sortedBounds.size() && currentBounds.size() > 1) { checkForOverlappingRules( inputs, i + 1, currentBounds.stream().map(Value::getRuleIdentifier).collect(Collectors.toList()), hasOverlap || foundOverlap); } + foundPOverlap |= foundOverlap; } } } diff --git a/dmn-verifier-app/src/main/java/de/unikoblenz/fgbks/dmn/core/verifier/helper/Type.java b/dmn-verifier-app/src/main/java/de/unikoblenz/fgbks/dmn/core/verifier/helper/Type.java index 306dec8cb0db73c77b611bdda03a8147c9989f12..475923744f48295eb5d7dd829df4a15bf9b0156e 100644 --- a/dmn-verifier-app/src/main/java/de/unikoblenz/fgbks/dmn/core/verifier/helper/Type.java +++ b/dmn-verifier-app/src/main/java/de/unikoblenz/fgbks/dmn/core/verifier/helper/Type.java @@ -47,7 +47,7 @@ public abstract class Type { .withRuleId(ruleId) .withConditionId(value.getId()) .withConditionName(value.getName()) - .build()); + .build(), this); } private Boundary getBoundaryValue(DmnExpressionImpl value) { diff --git a/dmn-verifier-app/src/main/java/de/unikoblenz/fgbks/dmn/core/verifier/helper/Value.java b/dmn-verifier-app/src/main/java/de/unikoblenz/fgbks/dmn/core/verifier/helper/Value.java index acbc83488759bebcc070f52d35a3df5272dcc59a..dd4743fdb9d2b15ea1e51afd479677f6c955c11a 100644 --- a/dmn-verifier-app/src/main/java/de/unikoblenz/fgbks/dmn/core/verifier/helper/Value.java +++ b/dmn-verifier-app/src/main/java/de/unikoblenz/fgbks/dmn/core/verifier/helper/Value.java @@ -8,11 +8,13 @@ public class Value { private final Boundary boundary; private final String orgExpression; private final RuleIdentifier ruleIdentifier; + private final Type type; - Value(Boundary boundary, String orgExpression, RuleIdentifier ruleIdentifier) { + public Value(Boundary boundary, String orgExpression, RuleIdentifier ruleIdentifier, Type type) { this.boundary = boundary; this.orgExpression = orgExpression; this.ruleIdentifier = ruleIdentifier; + this.type = type; } public Boundary getBoundary() { @@ -27,6 +29,10 @@ public class Value { return ruleIdentifier; } + public Type getType() { + return type; + } + @Override public boolean equals(Object o) { if (this == o) { @@ -48,4 +54,6 @@ public class Value { public String toString() { return orgExpression; } + + } diff --git a/dmn-verifier-app/src/main/resources/sampleDMN.dmn b/dmn-verifier-app/src/main/resources/sampleDMN.dmn index f5dd00c1c6d0c37b3a675c42fb4020d6d6cd5c08..9cc3fcae8809648e026c69926ee409b0e9f2d23f 100644 --- a/dmn-verifier-app/src/main/resources/sampleDMN.dmn +++ b/dmn-verifier-app/src/main/resources/sampleDMN.dmn @@ -75,7 +75,7 @@ </decision> <decision id="Decision_1vo386g" name="Subsumption 1"> <extensionElements> - <biodi:bounds x="344" y="74" width="180" height="80" /> + <biodi:bounds x="341" y="12" width="180" height="80" /> </extensionElements> <decisionTable id="DecisionTable_0tq1rr0"> <input id="InputClause_0f5fjmn"> @@ -118,7 +118,7 @@ </decision> <decision id="Decision_19xi89b" name="Subsumption 2"> <extensionElements> - <biodi:bounds x="343" y="178" width="180" height="80" /> + <biodi:bounds x="344" y="129" width="180" height="80" /> </extensionElements> <decisionTable id="DecisionTable_0p3gy3p"> <input id="InputClause_095mouf"> @@ -198,6 +198,55 @@ </rule> </decisionTable> </decision> + <decision id="Decision_1883dha" name="Subsumption 3"> + <extensionElements> + <biodi:bounds x="345" y="239" width="180" height="80" /> + </extensionElements> + <decisionTable id="DecisionTable_07e2svi"> + <input id="InputClause_15byi73"> + <inputExpression id="LiteralExpression_1ezqgx5" typeRef="string" /> + </input> + <input id="InputClause_1r9hr53"> + <inputExpression id="LiteralExpression_0hysgxp" typeRef="string"> + <text></text> + </inputExpression> + </input> + <output id="OutputClause_156jkbh" typeRef="string" /> + <rule id="DecisionRule_0hq1x4e"> + <inputEntry id="UnaryTests_1a0h33v"> + <text>"a"</text> + </inputEntry> + <inputEntry id="UnaryTests_048z0r2"> + <text></text> + </inputEntry> + <outputEntry id="LiteralExpression_080g0b0"> + <text></text> + </outputEntry> + </rule> + <rule id="DecisionRule_1vliqpr"> + <inputEntry id="UnaryTests_0xr7jms"> + <text>"a"</text> + </inputEntry> + <inputEntry id="UnaryTests_1d1m69f"> + <text>"b"</text> + </inputEntry> + <outputEntry id="LiteralExpression_00nbyyv"> + <text></text> + </outputEntry> + </rule> + <rule id="DecisionRule_1voq2cq"> + <inputEntry id="UnaryTests_0dnecuo"> + <text></text> + </inputEntry> + <inputEntry id="UnaryTests_10sh6b8"> + <text>"b"</text> + </inputEntry> + <outputEntry id="LiteralExpression_18f95t4"> + <text></text> + </outputEntry> + </rule> + </decisionTable> + </decision> <decision id="Decision_1be8xb9" name="Equivalent"> <extensionElements> <biodi:bounds x="561" y="131" width="180" height="80" /> @@ -265,7 +314,7 @@ </rule> </decisionTable> </decision> - <decision id="Decision_0daxsth" name="Overlapping"> + <decision id="Decision_0daxsth" name="Overlapping 1"> <extensionElements> <biodi:bounds x="768" y="132" width="180" height="80" /> </extensionElements> @@ -314,4 +363,53 @@ </rule> </decisionTable> </decision> + <decision id="Decision_1iijfl3" name="Overlapping 2"> + <extensionElements> + <biodi:bounds x="768" y="227" width="180" height="80" /> + </extensionElements> + <decisionTable id="DecisionTable_0400f42"> + <input id="InputClause_0qut3rd"> + <inputExpression id="LiteralExpression_0xke4bo" typeRef="integer" /> + </input> + <input id="InputClause_1lcfbmy"> + <inputExpression id="LiteralExpression_0l2umls" typeRef="string"> + <text></text> + </inputExpression> + </input> + <output id="OutputClause_1o63cll" typeRef="string" /> + <rule id="DecisionRule_1rnq5nc"> + <inputEntry id="UnaryTests_1mzyv9g"> + <text>[0..10]</text> + </inputEntry> + <inputEntry id="UnaryTests_1qha0w0"> + <text>"a"</text> + </inputEntry> + <outputEntry id="LiteralExpression_1b8izof"> + <text></text> + </outputEntry> + </rule> + <rule id="DecisionRule_0ck2rrm"> + <inputEntry id="UnaryTests_1l0t6nx"> + <text>[5..20]</text> + </inputEntry> + <inputEntry id="UnaryTests_1sb2f9d"> + <text>"a"</text> + </inputEntry> + <outputEntry id="LiteralExpression_11a44rq"> + <text></text> + </outputEntry> + </rule> + <rule id="DecisionRule_0u67njf"> + <inputEntry id="UnaryTests_1pz3afy"> + <text>[15..30]</text> + </inputEntry> + <inputEntry id="UnaryTests_1262959"> + <text></text> + </inputEntry> + <outputEntry id="LiteralExpression_0l5et87"> + <text></text> + </outputEntry> + </rule> + </decisionTable> + </decision> </definitions>