diff --git a/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/base/utils/boundary/bicreater/BiCreaterStringLowerBounds.java b/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/base/utils/boundary/bicreater/BiCreaterStringLowerBounds.java index 3dd7e3a9923298b5149e47687c6834962f3c3eba..681c4a1b1d6d93643ee5e6186ffef12d0aaa002a 100644 --- a/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/base/utils/boundary/bicreater/BiCreaterStringLowerBounds.java +++ b/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/base/utils/boundary/bicreater/BiCreaterStringLowerBounds.java @@ -1,7 +1,7 @@ package de.unikoblenz.fgbks.base.utils.boundary.bicreater; import static de.unikoblenz.fgbks.base.utils.boundary.checker.BoundaryCheckType.IS_EQUAL; -import static de.unikoblenz.fgbks.base.utils.boundary.checker.BoundaryCheckType.IS_NOT_IN_CONTACT; +import static de.unikoblenz.fgbks.base.utils.boundary.checker.BoundaryCheckType.SUBSUMES; import de.unikoblenz.fgbks.base.utils.boundary.impl.StringBoundary; import java.util.Arrays; @@ -11,8 +11,11 @@ import java.util.Set; /** * The lower bound creator implementation of {@link BoundaryBiCreaterType#LOWER_BOUNDS} for {@link - * StringBoundary}. <br> The result is a new boundary with the lower bounds, or an empty optional, - * if the creation is not possible. <br> In: {@code b1: "a","b"; b2: "a"}<br> Out: {@code "b"} <br> + * StringBoundary}. <br> + * The result is a new boundary with the lower bounds, or an empty optional, if the creation is not + * possible. <br> + * In: {@code b1: "a"; b2: "a", "b"}<br> + * Out: {@code "b"} <br> */ public class BiCreaterStringLowerBounds extends AbstractBoundaryBiCreater<StringBoundary> { @@ -38,17 +41,47 @@ public class BiCreaterStringLowerBounds extends AbstractBoundaryBiCreater<String @Override public Optional<StringBoundary> create(StringBoundary b1, StringBoundary b2) { - if (b1.checkWith(IS_NOT_IN_CONTACT, b2) || b1.checkWith(IS_EQUAL, b2)) { + if (b2.matchesAny()) { return Optional.empty(); } - if (b1.matchesAny() || b1.matchesNoneOfValues()) { + if (b1.matchesAny()) { + return Optional.of( + StringBoundary.getBuilder() + .matchesNoneOfValues(!b2.matchesNoneOfValues()) + .addValues(b2.getValues()) + .build()); + } + + if (b2.checkWith(IS_EQUAL, b1) || b2.checkWith(SUBSUMES, b1)) { + return Optional.empty(); + } + + if (b1.matchesNoneOfValues()) { + if (b2.matchesNoneOfValues()) { + Set<String> values = new HashSet<>(Arrays.asList(b2.getValues())); + values.removeAll(Arrays.asList(b1.getValues())); + if (values.size() == 0) { + return Optional.empty(); + } + return Optional.of(StringBoundary.getBuilder().addValues(values).build()); + } else { + return Optional.of( + StringBoundary.getBuilder() + .matchesNoneOfValues(true) + .addValues(b1.getValues()) + .addValues(b2.getValues()) + .build()); + } + } + + if (b2.matchesNoneOfValues()) { return Optional.of( StringBoundary.getBuilder() .addValues(b1.getValues()) .addValues(b2.getValues()) - .matchesNoneOfValues(!b2.matchesNoneOfValues()) .build()); } + Set<String> values = new HashSet<>(Arrays.asList(b1.getValues())); values.removeAll(Arrays.asList(b2.getValues())); return Optional.of(StringBoundary.getBuilder().addValues(values).build()); diff --git a/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/base/utils/boundary/impl/StringBoundary.java b/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/base/utils/boundary/impl/StringBoundary.java index 973d8d55fa13640d987333d466fcda376c715eb4..7c94b20f817fa9e395d0027e4db06313279db29b 100644 --- a/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/base/utils/boundary/impl/StringBoundary.java +++ b/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/base/utils/boundary/impl/StringBoundary.java @@ -57,8 +57,13 @@ import org.apache.commons.lang3.Validate; * StringBoundary#matchesNoneOfValues} returns true, which are not in the boundary. <br> * * <p>Only value "a": <br> - * {@code "a"} <br> Value "a" or value "b": <br> {@code "a","b"} <br> Not value "a" and not value - * "b": <br> {@code not("a","b")} <br> All values: <br> {@code (empty String)} + * {@code "a"} <br> + * Value "a" or value "b": <br> + * {@code "a","b"} <br> + * Not value "a" and not value "b": <br> + * {@code not("a","b")} <br> + * All values: <br> + * {@code (empty String)} */ public class StringBoundary extends AbstractBoundary<String> { @@ -105,7 +110,21 @@ public class StringBoundary extends AbstractBoundary<String> { @Override public int compareTo(Boundary o) { - return o.getText().compareTo(o.getText()); + if (!(o instanceof StringBoundary)) { + return 0; + } + StringBoundary otherStringB = (StringBoundary) o; + if (matchesAny() || matchesNoneOfValues()) { + if (otherStringB.matchesAny() || otherStringB.matchesNoneOfValues()) { + return 0; + } else { + return -1; + } + } + if (otherStringB.matchesAny() || otherStringB.matchesNoneOfValues()) { + return 1; + } + return this.getValues()[0].compareTo(otherStringB.getValues()[0]); } /** @@ -178,6 +197,7 @@ public class StringBoundary extends AbstractBoundary<String> { } private void calcHashes() { + Arrays.sort(values); valuesHashes = new int[values.length]; for (int i = 0; i < values.length; i++) { values[i] = values[i].replace('"', '\0').trim(); diff --git a/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/impl/IdenticalVerifier.java b/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/impl/IdenticalRuleVerifier.java similarity index 94% rename from dmnverifierapi/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/impl/IdenticalVerifier.java rename to dmnverifierapi/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/impl/IdenticalRuleVerifier.java index 1f4a0a6ec70bb022bf5de50dd26c483ed1fa4e8e..607f8b2f495dbbe113cf3b164d4a83d73df0ad36 100644 --- a/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/impl/IdenticalVerifier.java +++ b/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/impl/IdenticalRuleVerifier.java @@ -16,14 +16,14 @@ import de.unikoblenz.fgbks.core.dmn.verification.result.VerificationResultEntry. import de.unikoblenz.fgbks.core.dmn.verification.result.VerificationResultEntryElement; import de.unikoblenz.fgbks.core.dmn.verification.verifier.AbstractVerifier; import de.unikoblenz.fgbks.core.dmn.verification.verifier.DmnVerifier; -import de.unikoblenz.fgbks.core.dmn.verification.verifier.types.IdenticalBusinessRuleVerification; +import de.unikoblenz.fgbks.core.dmn.verification.verifier.types.IdenticalRuleVerification; import java.util.ArrayList; import java.util.Comparator; import java.util.List; import java.util.stream.Collectors; -@DmnVerifier(verifierType = IdenticalBusinessRuleVerification.class) -public class IdenticalVerifier extends AbstractVerifier { +@DmnVerifier(verifierType = IdenticalRuleVerification.class) +public class IdenticalRuleVerifier extends AbstractVerifier { @Override protected void doVerification() { @@ -51,7 +51,7 @@ public class IdenticalVerifier extends AbstractVerifier { vreFactory.addToEntry( VerificationClassification.WARNING, templateDecision(dmnDecisionTable.getDmnDecision()) - + "Rules %s have identical input.", + + "Rules %s have identical inputs.", getRulesRowsStrings(currentRules)); } else { List<VDmnInputValue> curInVals = new ArrayList<>(); diff --git a/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/impl/MissingRuleVerifier.java b/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/impl/MissingRuleVerifier.java index 0b0d4eaff2dd15a18a87681709e5d21837f8074c..25588d974357e28f59ce98648a7660f7b2f75b17 100644 --- a/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/impl/MissingRuleVerifier.java +++ b/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/impl/MissingRuleVerifier.java @@ -2,6 +2,7 @@ package de.unikoblenz.fgbks.core.dmn.verification.verifier.impl; import static de.unikoblenz.fgbks.core.dmn.domain.vdmn.utils.VDmnFunctions.templateDecision; +import de.unikoblenz.fgbks.base.utils.UniqueIdGenerator; import de.unikoblenz.fgbks.base.utils.boundary.Boundary; import de.unikoblenz.fgbks.base.utils.boundary.bicreater.BoundaryBiCreaterType; import de.unikoblenz.fgbks.base.utils.boundary.checker.BoundaryCheckType; @@ -155,7 +156,8 @@ public class MissingRuleVerifier extends AbstractVerifier { missingRule.setInputValue( i - 1, VDmnInputValueImpl.getBuilder() - .withInputEntryId(new InputEntryId("TODO")) // TODO + .withInputEntryId( + new InputEntryId("Entry_" + Long.toString(UniqueIdGenerator.getNextId(), 16))) .withText("") .withDmnInputColumn((VDmnInputColumn) missingRuleValue.getDmnColumn()) .withDmnRule(missingRule) @@ -201,7 +203,7 @@ public class MissingRuleVerifier extends AbstractVerifier { Builder b = VDmnRuleImpl.getBuilder() - .withRuleId(new RuleId("TODO")) // TODO + .withRuleId(new RuleId("Rule_" + Long.toString(UniqueIdGenerator.getNextId(), 16))) .withDmnDecisionTable(inputs.get(0).getDmnDecisionTable()) .withRowNumber(new RowNumber(INITIAL_ROW_NUMER)); for (VDmnInputValue inputValue : missingRuleValues) { @@ -214,7 +216,8 @@ public class MissingRuleVerifier extends AbstractVerifier { copy.setInputValue( x, VDmnInputValueImpl.getBuilder() - .withInputEntryId(new InputEntryId("TODO")) // TODO + .withInputEntryId( + new InputEntryId("Entry_" + Long.toString(UniqueIdGenerator.getNextId(), 16))) .withText("") .withDmnRule(copy) .withBoundary(x == i ? bi.get() : missingRuleValues.get(x).getBoundary()) diff --git a/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/types/DateVerification.java b/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/types/DateVerification.java index 64ea2417a3c1373bbff9487170f984cdf176a169..e98bda4e11a1e7f288bb6c11689947452ca5c2bb 100644 --- a/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/types/DateVerification.java +++ b/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/types/DateVerification.java @@ -15,7 +15,9 @@ public class DateVerification extends AbstractVerificationType { super( new Name("DateFormatVerification"), new Name("Date Format Check"), - new Description("test")); // TODO + new Description( + "This verification checks the correctness of the date format for all date entries. " + + "Each date should be in the format \"data and time(yyyy-MM-ddThH:mm:ss)\".")); } public static DateVerification getInstance() { diff --git a/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/types/EmptyOutputVerification.java b/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/types/EmptyOutputVerification.java index 842123728b944f2cbaecb3f9984b164b55fcff76..d370feb836a9e72d64f8fc3913afb08367091d2b 100644 --- a/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/types/EmptyOutputVerification.java +++ b/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/types/EmptyOutputVerification.java @@ -15,7 +15,7 @@ public class EmptyOutputVerification extends AbstractVerificationType { super( new Name("EmptyOutputVerification"), new Name("Empty Output"), - new Description("test")); // TODO + new Description("Detection rules with an empty output values.")); } public static EmptyOutputVerification getInstance() { diff --git a/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/types/EquivalentStringVerification.java b/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/types/EquivalentStringVerification.java index 90803b7eb3f5df8f87324ecbcace54910425a0aa..7813aa61a1ac12e2d4b42482dca1dbcf664e2c81 100644 --- a/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/types/EquivalentStringVerification.java +++ b/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/types/EquivalentStringVerification.java @@ -15,7 +15,12 @@ public class EquivalentStringVerification extends AbstractVerificationType { super( new Name("EquivalentStringVerification"), new Name("Equivalent Strings"), - new Description("test")); // TODO + new Description( + "Detecting rules which are not identical, " + + "but still semantically equivalent. " + + "Here, the verifier checks if there exist multiple rules " + + "which use synonyms as inputs and are therefore equivalent, " + + "based on synonym relations via Wordnet.")); } public static EquivalentStringVerification getInstance() { diff --git a/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/types/IdenticalBusinessRuleVerification.java b/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/types/IdenticalRuleVerification.java similarity index 56% rename from dmnverifierapi/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/types/IdenticalBusinessRuleVerification.java rename to dmnverifierapi/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/types/IdenticalRuleVerification.java index 749bd5b0dd06f77a8013876d58634f3b636f1602..81be13f645b5854d98c7e8e5390e681f7f40cb31 100644 --- a/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/types/IdenticalBusinessRuleVerification.java +++ b/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/types/IdenticalRuleVerification.java @@ -7,16 +7,19 @@ import de.unikoblenz.fgbks.core.dmn.verification.verifier.classification.Decisio import javax.validation.constraints.NotNull; @Type -public class IdenticalBusinessRuleVerification extends AbstractVerificationType { +public class IdenticalRuleVerification extends AbstractVerificationType { - private static final IdenticalBusinessRuleVerification instance = - new IdenticalBusinessRuleVerification(); + private static final IdenticalRuleVerification instance = new IdenticalRuleVerification(); - private IdenticalBusinessRuleVerification() { - super(new Name("Identical Rules"), new Description("test")); // TODO + private IdenticalRuleVerification() { + super( + new Name("Identical Rules"), + new Description( + "Detecting rules which have an identical input, " + + "i.e. are redundant.")); } - public static IdenticalBusinessRuleVerification getInstance() { + public static IdenticalRuleVerification getInstance() { return instance; } diff --git a/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/types/InputValueSyntaxVerification.java b/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/types/InputValueSyntaxVerification.java index a4112152d1eccbcd449a5f74c33cb77cfb8a4b9d..d93e886a298783ee3f53f35df477aef2d94b9aaa 100644 --- a/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/types/InputValueSyntaxVerification.java +++ b/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/types/InputValueSyntaxVerification.java @@ -15,7 +15,9 @@ public class InputValueSyntaxVerification extends AbstractVerificationType { super( new Name("InputValueSyntaxVerification"), new Name("Input Value Syntax Check"), - new Description("Check values for syntactical correctness.")); + new Description( + "This verification checks the correctness of the input formats for all input entries. " + + "They should have no syntax errors. ")); } public static InputValueSyntaxVerification getInstance() { diff --git a/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/types/LonelyDataInputVerification.java b/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/types/LonelyDataInputVerification.java index f4a03689c663dc915cbbe823933090b2d83fff76..e78211ceea767b9d634e0a669178bf0d26f77cf1 100644 --- a/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/types/LonelyDataInputVerification.java +++ b/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/types/LonelyDataInputVerification.java @@ -16,7 +16,8 @@ public class LonelyDataInputVerification extends AbstractVerificationType { new Name("LonelyDataInputVerification"), new Name("Lonely Data Input"), new Description( - "Checks for any input data node, if it has no connection to at least one decision table.")); + "Detection input data nodes," + + " which has no connection to at least one decision table.")); } public static LonelyDataInputVerification getInstance() { diff --git a/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/types/MissingInputColumnVerification.java b/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/types/MissingInputColumnVerification.java index 6a5ce32fc2d841bf68fd0c1d283b371edefff137..b7c2275262ac7ab538191190b44c2f19ba408551 100644 --- a/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/types/MissingInputColumnVerification.java +++ b/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/types/MissingInputColumnVerification.java @@ -16,7 +16,10 @@ public class MissingInputColumnVerification extends AbstractVerificationType { super( new Name("MissingInputColumnVerification"), new Name("Missing Input Column"), - new Description("todo")); // TODO + new Description( + "Detecting for all decisions and input data nodes " + + "if their output names are found in the corresponding input columns " + + "of the connected decision.")); } public static MissingInputColumnVerification getInstance() { diff --git a/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/types/MissingInputDataVerification.java b/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/types/MissingInputDataVerification.java index 64cdb7a1a13065a101ded20df616a3cd3652c205..b1879e06c688d8ebca014a7f21799f519c0fe2e7 100644 --- a/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/types/MissingInputDataVerification.java +++ b/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/types/MissingInputDataVerification.java @@ -9,15 +9,15 @@ import javax.validation.constraints.NotNull; @Type public class MissingInputDataVerification extends AbstractVerificationType { - private static final MissingInputDataVerification instance = - new MissingInputDataVerification(); + private static final MissingInputDataVerification instance = new MissingInputDataVerification(); private MissingInputDataVerification() { super( new Name("MissingInputDataVerification"), new Name("Missing Input Data"), new Description( - "todo")); // TODO + "Detecting columns which has no reference to either a input node " + + "or a decision node with a corresponding output column.")); } public static MissingInputDataVerification getInstance() { diff --git a/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/types/MissingRuleVerification.java b/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/types/MissingRuleVerification.java index 7143891d88502d879f063fbcb2e2c6e2e862480f..04e2a134671061ec49aee3b015df33099292c3c3 100644 --- a/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/types/MissingRuleVerification.java +++ b/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/types/MissingRuleVerification.java @@ -15,7 +15,8 @@ public class MissingRuleVerification extends AbstractVerificationType { super( new Name("MissingRuleVerification"), new Name("Missing Rule"), - new Description("test")); // TODO + new Description("Detecting whether there are any missing business rules, " + + "i.e. if there are rules missing for expected inputs.")); } public static MissingRuleVerification getInstance() { diff --git a/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/types/OverlappingVerification.java b/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/types/OverlappingVerification.java index c46f899de87d925b17292dcde594811269f8a2b6..cab8943659105ffc24cdf2c397e5b14ffc4a8331 100644 --- a/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/types/OverlappingVerification.java +++ b/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/types/OverlappingVerification.java @@ -15,7 +15,8 @@ public class OverlappingVerification extends AbstractVerificationType { super( new Name("OverlappingVerification"), new Name("Overlapping Rules"), - new Description("test")); // TODO + new Description("Detecting whether there are any overlaps in rule conditions. " + + "This verifier does not show identical, overlaps or subsumptions.")); } public static OverlappingVerification getInstance() { diff --git a/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/types/PartialReductionVerification.java b/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/types/PartialReductionVerification.java index 16f050f857f3d76a39d269ed56a0f2504c254222..f210e04a976d4bb14be53c2ba05febcffb898679 100644 --- a/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/types/PartialReductionVerification.java +++ b/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/types/PartialReductionVerification.java @@ -15,7 +15,7 @@ public class PartialReductionVerification extends AbstractVerificationType { super( new Name("PartialReductionVerification"), new Name("Partial Reductions"), - new Description("test")); // TODO + new Description("Checking whether ranges can be combined to simplify decision tables.")); } public static PartialReductionVerification getInstance() { diff --git a/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/types/PredefinedExistingValueVerification.java b/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/types/PredefinedExistingValueVerification.java index f8b941b013911ebf6dd8b95ae09160692849e855..c7132b0e67d5ea2d7f5b03debd62a23e5bb563ee 100644 --- a/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/types/PredefinedExistingValueVerification.java +++ b/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/types/PredefinedExistingValueVerification.java @@ -16,8 +16,8 @@ public class PredefinedExistingValueVerification extends AbstractVerificationTyp new Name("PredefinedExistingValueVerification"), new Name("Predefined Existing Values"), new Description( - "Es gibt in einer Tabellen-Zelle einen String Value, der aber nicht in der Liste der " - + "Predefines Values vorgesehen ist.")); // TODO englisch + "Detecting string values, " + + "which are not defined in list of predefined values of the column.")); } public static PredefinedExistingValueVerification getInstance() { diff --git a/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/types/PredefinedMissingValueVerification.java b/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/types/PredefinedMissingValueVerification.java index 803cc04aee026f7324ce36099b448bf608a4c386..622177f39f84fd450063f6b39cf518e12d33f991 100644 --- a/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/types/PredefinedMissingValueVerification.java +++ b/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/types/PredefinedMissingValueVerification.java @@ -17,7 +17,8 @@ public class PredefinedMissingValueVerification extends AbstractVerificationType new Name("PredefinedMissingValueVerification"), new Name("Predefined Missing Values"), new Description( - "Es gibt in einer Tabellen-Zelle predefines values, die jedoch nicht in der spalte vorhanden sind.")); // TODO englisch + "Detecting predefined string values of a column, " + + "which itself are not used in the column.")); } public static PredefinedMissingValueVerification getInstance() { diff --git a/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/types/SubsumptionVerification.java b/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/types/SubsumptionVerification.java index f62160003aa2cb23a8145fa58114a3f20c15416e..d68101d933594556a8e7be53689ae4cd04858d44 100644 --- a/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/types/SubsumptionVerification.java +++ b/dmnverifierapi/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/types/SubsumptionVerification.java @@ -15,7 +15,11 @@ public class SubsumptionVerification extends AbstractVerificationType { super( new Name("SubsumptionVerification"), new Name("Rule Subsumptions"), - new Description("test")); // TODO + new Description( + "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.")); } public static SubsumptionVerification getInstance() { diff --git a/dmnverifierapi/verifier.md b/dmnverifierapi/verifier.md index 662a9615ec81607291b1e5442d8bd2b50d99ed77..995bc297086609dadea1e39f66993213c5f51443 100644 --- a/dmnverifierapi/verifier.md +++ b/dmnverifierapi/verifier.md @@ -13,7 +13,7 @@ - Verifier: [LonelyDataInputVerifier](src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/impl/LonelyDataInputVerifier.java) #### Description -Checks for any input data node, if it has **no** connection to at least one decision table. +Detection input data nodes, which has no connection to at least one decision table. #### Algorithm ``` @@ -33,6 +33,7 @@ doVerification (Definition d) { - Verifier: [MissingInputDataVerifier](src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/impl/MissingInputDataVerifier.java) #### Description +Detecting columns which has no reference to either a input node or a decision node with a corresponding output column. #### Algorithm ``` @@ -44,6 +45,7 @@ TODO - Verifier: [MissingInputColumnVerifier](src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/impl/MissingInputColumnVerifier.java) #### Description +Detecting for all decisions and input data nodes if their output names are found in the corresponding input columns of the connected decision. #### Algorithm ``` @@ -58,7 +60,7 @@ TODO - Verifier: [PredefinedExistingValueVerifier](src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/impl/PredefinedExistingValueVerifier.java) #### Description -TODO +Detecting string values, which are not defined in list of predefined values of the column. #### Algorithm ``` @@ -70,7 +72,7 @@ TODO - Verifier: [PredefinedMissingValueVerifier](src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/impl/PredefinedMissingValueVerifier.java) #### Description -TODO +Detecting predefined string values of a column, which itself are not used in the column. #### Algorithm ``` @@ -82,7 +84,7 @@ TODO - Verifier: [EmptyOutputVerifier](src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/impl/EmptyOutputVerifier.java) #### Description -This verification checks if a output entry is empty. +Detection rules with an empty output values." #### Algorithm ``` @@ -97,7 +99,7 @@ TODO - Verifier: [SubsumptionVerifier](src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/impl/SubsumptionVerifier.java) #### Description -TODO +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. #### Algorithm ``` @@ -109,7 +111,7 @@ TODO - Verifier: [PartialReductionVerifier](src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/impl/PartialReductionVerifier.java) #### Description -TODO +Checking whether ranges can be combined to simplify decision tables. #### Algorithm ``` @@ -121,7 +123,7 @@ TODO - Verifier: [OverlappingVerifier](src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/impl/OverlappingVerifier.java) #### Description -TODO +Detecting whether there are any overlaps in rule conditions. This verifier does not show identical, overlaps or subsumptions. #### Algorithm ``` @@ -133,19 +135,19 @@ TODO - Verifier: [MissingRuleVerifier](src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/impl/MissingRuleVerifier.java) #### Description -TODO +Detecting whether there are any missing business rules, i.e. if there are rules missing for expected inputs. #### Algorithm ``` TODO ``` -### Identical Business Rule Verification -- Type: [IdenticalBusinessRuleVerification](/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/types/IdenticalBusinessRuleVerification.java) -- Verifier: [IdenticalVerifier](src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/impl/IdenticalVerifier.java) +### Identical Rule Verification +- Type: [IdenticalRuleVerification](/src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/types/IdenticalRuleVerification.java) +- Verifier: [IdenticalRuleVerifier](src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/impl/IdenticalRuleVerifier.java) #### Description -TODO +Detecting rules which have an identical input, i.e. are redundant. #### Algorithm ``` @@ -157,7 +159,7 @@ TODO - Verifier: [EquivalentVerifier](src/main/java/de/unikoblenz/fgbks/core/dmn/verification/verifier/impl/EquivalentVerifier.java) #### Description -TODO +Detecting rules which are not identical, but still semantically equivalent. Here, the verifier checks if there exist multiple rules which use synonyms as inputs and are therefore equivalent, based on synonym relations via Wordnet. #### Algorithm ``` diff --git a/dmnverifierfrontend/src/main/resources/META-INF/resources/css/stylesheets.css b/dmnverifierfrontend/src/main/resources/META-INF/resources/css/stylesheets.css index caa50366dbc58bd3d5d02aac49448d3f80636d37..fc98b25c009326c533f127b6e6e7b50a98a84d69 100644 --- a/dmnverifierfrontend/src/main/resources/META-INF/resources/css/stylesheets.css +++ b/dmnverifierfrontend/src/main/resources/META-INF/resources/css/stylesheets.css @@ -18,6 +18,10 @@ h2 { margin: 10px; } +p { + margin: 5px; +} + .status-dot { height: 15px; width: 15px; @@ -52,6 +56,7 @@ select { height: 35px; outline: none; padding-left: 5px; + min-width: 200px; } select option { @@ -70,6 +75,13 @@ select:focus::-ms-value { background-color: transparent; } +span.select2 { + display: table; + table-layout: fixed; + width: 25% !important; + min-width: 200px; +} + .clickable { box-shadow: 2px 2px 3px 2px rgba(0, 0, 0, .3); } @@ -149,12 +161,19 @@ select:focus::-ms-value { display: flex; justify-content: flex-start; align-items: flex-start; + flex-wrap: wrap; } #dmn-button-verify { flex-grow: 1; max-width: 90px; - height: 35px; + background: white; + border-radius: 2px; +} + +#dmn-button-reload-verifier { + flex-grow: 1; + max-width: 120px; background: white; border-radius: 2px; } @@ -162,6 +181,7 @@ select:focus::-ms-value { .dmn-verifier-header-item { margin: 8px 12px; color: #0b3004; + min-height: 35px; cursor: pointer; font-family: 'Open Sans', sans-serif; font-size: 13px; @@ -174,27 +194,79 @@ select:focus::-ms-value { #dmn-verifier-types { flex-grow: 1; - max-width: 200px; + max-width: 250px; +} + +.verifier-type-name { + border-top: #0d82b7 2px; + border-top-style: dashed; +} + +.verifier-type-description { + border-left: #0d82b7 5px; + border-left-style: solid; +} + +.verifier-type-description > p { + max-width: 800px; + text-align: justify; +} + +.verification-container { + display: flex; + flex-direction: row; + justify-content: stretch; + margin: 10px; + border-top: #0d82b7 2px; + border-top-style: ridge; +} + +.dmn-verification-icon { + width: 32px; + height: 30px; + display: inline-block; + background-repeat: no-repeat; + background-size: contain; + background-position: center center; } .dmn-ERROR { - list-style-image: url("img/ERROR.png"); + background-image: url("img/ERROR.png"); } .dmn-WARNING { - list-style-image: url("img/WARNING.png"); + background-image: url("img/WARNING.png"); } .dmn-FATAL_ERROR { - list-style-image: url("img/FATAL_ERROR.png"); + background-image: url("img/FATAL_ERROR.png"); } .dmn-INFO { - list-style-image: url("img/INFO.png"); + background-image: url("img/INFO.png"); } -span.select2 { - display: table; - table-layout: fixed; - width: 25% !important; +.verification-message { + padding-top: 8px; + padding-left: 8px; + width: 100%; + flex-shrink: 1; + flex-basis: 65%; +} + +.verification-container-fix-buttons { + float: right; + padding-top: 8px; + display: flex; + flex-direction: row; + flex-grow: 1; + justify-content: space-between; + flex-wrap: wrap; +} + +.verification-fix-button { + height: 25px; + min-width: 100px; + margin: 2px 2px 2px 10px; + background: white; } diff --git a/dmnverifierfrontend/src/main/resources/META-INF/resources/index.html b/dmnverifierfrontend/src/main/resources/META-INF/resources/index.html index 6b2368b3caa1cac974c14931785515fb19bae729..bf24bcf7181640dcf8cccac708a633d4306be9a6 100644 --- a/dmnverifierfrontend/src/main/resources/META-INF/resources/index.html +++ b/dmnverifierfrontend/src/main/resources/META-INF/resources/index.html @@ -2,6 +2,7 @@ <html lang="en"> <head> <meta charset="UTF-8"> + <meta content="width=device-width, initial-scale=1.0" name="viewport"> <title>DMN App</title> <link href="https://unpkg.com/dmn-js@7.0.1/dist/assets/diagram-js.css" rel="stylesheet"> <link href="https://unpkg.com/dmn-js@7.0.1/dist/assets/dmn-js-shared.css" rel="stylesheet"> diff --git a/dmnverifierfrontend/src/main/resources/META-INF/resources/js/dmnVerifier.js b/dmnverifierfrontend/src/main/resources/META-INF/resources/js/dmnVerifier.js index d97bae525140e295e8005bc93916bfa9afe5f8a9..691014e0f6a8e9c4a9a9f9122749cf278207a607 100644 --- a/dmnverifierfrontend/src/main/resources/META-INF/resources/js/dmnVerifier.js +++ b/dmnverifierfrontend/src/main/resources/META-INF/resources/js/dmnVerifier.js @@ -2,6 +2,10 @@ let rootUrl = 'http://' + window.location.hostname + ':8080/'; let dmnApi = rootUrl + 'api/dmn/'; let inactiveBackend = true; +$(document).ready(function () { + // load types + loadAvailableTypes(); +}); /** * Global definition for verification results * @type {VerifierResultSet} @@ -15,75 +19,97 @@ let verifierResults = {}; let types = []; let $verifierTypes; +let lastTypeSelect = undefined; +let $select = $(` + <select name="verifier" id="verifier" + class="dmn-verifier-header-item dmn-verifier-select clickable" + title="Select calculated verification" + onchange="renderVerifierResult(this)"> + `); function handleStatus() { let $dot = $('#backend-status'); if (inactiveBackend) { + verifierResults = {}; $dot.removeClass('active'); $dot.addClass('inactive'); + alert( + "Error: The backend is currently not available. Please try again later.") } else { $dot.removeClass('inactive'); $dot.addClass('active'); } + cleanDmnVerifierRoot(); } -// load types -$.ajax({ - timeout: 500, - url: dmnApi + 'verification/types', - type: 'GET', - contentType: 'text/xml', - error: function (err) { - verifierResults = {}; - inactiveBackend = true; - handleStatus(); - }, - success: function (data) { - inactiveBackend = false; - handleStatus(); - types = data; - // sort types by classification and name - types.sort(function ( - /** VerificationType */ firstEl, /** VerificationType */ secondEl) { - if (firstEl.classification.name - === secondEl.classification.name) { - if (firstEl.niceName === secondEl.niceName) { - return 0; +function loadAvailableTypes() { + $.ajax({ + timeout: 1000, + url: dmnApi + 'verification/types', + type: 'GET', + contentType: 'text/xml', + error: function (err) { + inactiveBackend = true; + handleStatus(); + }, + success: function (data) { + inactiveBackend = false; + types = data; + // sort types by classification and name + types.sort(function ( + /** VerificationType */ firstEl, /** VerificationType */ secondEl) { + if (firstEl.classification.name + === secondEl.classification.name) { + if (firstEl.niceName === secondEl.niceName) { + return 0; + } + return firstEl.niceName < secondEl.niceName ? -1 : 1; + } else if (firstEl.classification.name + < secondEl.classification.name) { + return -1; + } else { + return 1; } - return firstEl.niceName < secondEl.niceName ? -1 : 1; - } else if (firstEl.classification.name - < secondEl.classification.name) { - return -1; - } else { - return 1; - } - }); - $verifierTypes = renderTypeOptions(); - } -}); + }); + $verifierTypes = renderTypeOptions(); + handleStatus(); + } + }); +} function cleanDmnVerifierRoot() { let $root = $('#root-dmn-verifier').empty(); let $header = $(` <div id="dmn-verifier-header"> `); - // add types - $header.append($verifierTypes); - $verifierTypes.select2({ - containerCssClass: "dmn-verifier-header-item dmn-verifier-select clickable", - placeholder: { - id: -1, - text: "All verifier" - }, - allowClear: true - }); - // Add Button - $header.append($(` - <button class="clickable dmn-verifier-header-item" id="dmn-button-verify" - title="Request verifications" onClick="checkVerifications()"> - Verify</button> - `)); - // add header to root + // check, if backend is available + if ($verifierTypes === undefined) { + $header.append($(` + <button class="clickable dmn-verifier-header-item" id="dmn-button-reload-verifier" + title="Reconnect backend" onClick="loadAvailableTypes()"> + Reconnect verification backend</button> + `)); + } else { + $header.append($verifierTypes); + $verifierTypes.select2({ + containerCssClass: "dmn-verifier-header-item dmn-verifier-select clickable", + placeholder: { + id: -1, + text: "All verifier" + }, + allowClear: true + }); + // Add Button + $header.append($(` + <button class="clickable dmn-verifier-header-item" id="dmn-button-verify" + title="Request verifications" onClick="checkVerifications()"> + Verify</button> + `)); + $select.empty(); + $header.append($select); + } + +// add header to root $root.append($header); $root.append($(`<div id="dmn-verifier-content">`)); return $root; @@ -97,8 +123,7 @@ function checkVerifications() { function getVerifications() { dmnModeler.saveXML({format: true}, function (err, xml) { if (err) { - return console.log( - 'There is an error in requesting the dmn verifications.. ' + err); + return alert(err); } else { console.log('Requesting dmn verifications..'); } @@ -122,6 +147,10 @@ function getVerifications() { contentType: 'text/xml', data: xml, + error: function (err) { + alert( + 'Error: There was a fatal error while parsing the xml of the dmn.'); + }, success: function (data) { console.log('successful dmn verification request:'); console.log(data); @@ -181,16 +210,10 @@ function renderTypeOptions() { } function renderDmnVerifierOptions() { - let $header = $('#dmn-verifier-header'); - let $select = $(` - <select name="verifier" id="verifier" - class="dmn-verifier-header-item dmn-verifier-select clickable" - title="Select calculated verification" - onchange="renderVerifierResult(this)"> - `); $select.append($(`<option>Select a verifier</option>`)); let currentOpt = ''; let $curGroup; + let selOpt = false; for (let i = 0; i < verifierResults.verifier.length; i++) { let verifier = verifierResults.verifier[i]; let bolNewGroup = currentOpt @@ -199,58 +222,79 @@ function renderDmnVerifierOptions() { if (currentOpt === currentOpt) { $select.append($curGroup); } - $curGroup = ($( - `<optgroup label="${verifier.type.classification.niceName}"> + $curGroup = ($(` + <optgroup label="${verifier.type.classification.niceName}"> `)); currentOpt = verifier.type.classification.niceName; } - if (verifierResults.verifier.length === 1) { - $curGroup.append($(` - <option value="${verifier.type.name}" selected>${verifier.type.niceName} (${verifier.size})</option> - `)); + let $option; + if (verifierResults.verifier.length === 1 || + lastTypeSelect !== undefined && lastTypeSelect === verifier.type.name) { + selOpt = true; + $option = $(` + <option value="${verifier.type.name}" selected>${verifier.type.niceName} (${verifier.size})</option> + `); } else { - $curGroup.append($(` - <option value="${verifier.type.name}">${verifier.type.niceName} (${verifier.size})</option> - `)); + $option = $(` + <option value="${verifier.type.name}">${verifier.type.niceName} (${verifier.size})</option> + `); } + if (verifier.size === 0) { + $option.css('color', '#999') + } + $curGroup.append($option); } $select.append($curGroup); - $header.append($select); - if (verifierResults.verifier.length === 1) { + + if (selOpt) { renderVerifierResult($select[0].selectedOptions[0]); } } /** * - * @param verifier - * @type VerifierResult + * @param verifierSelect + * @Type option */ -function renderSelectEntry(verifier) { - return $(` - <option value="${verifier.type.name}">${verifier.type.niceName} (${verifier.size})</option> - `); +function renderVerifierResult(verifierSelect) { + let verifier = findVerifierByName(verifierSelect.value); + if (verifier === undefined) { + lastTypeSelect = undefined; + } else { + lastTypeSelect = verifier.type.name; + renderVerifier(verifier); + } } /** * - * @param verifierSelect - * @Type option + * @param verifier + * @Type VerifierResult */ -function renderVerifierResult(verifierSelect) { - let verifier = findVerifierByName(verifierSelect.value); - let $verifierEntries = $('#dmn-verifier-content'); - $verifierEntries.empty(); - if (verifier !== undefined) { - $verifierEntries.append($(` - <div>${verifier.type.niceName}: ${verifier.type.description}</div> +function renderVerifier(verifier) { + let $verifierContent = $('#dmn-verifier-content'); + $verifierContent.empty(); + $verifierContent.css('display', 'none'); + let $verifierType = $(`<div class="verifier-type"></div>`); + let $verifierEntries = $(`<div class="verifier-entries"></div>`); + + // Add name and description of verifier + $verifierType.append($(` + <div class="verifier-type-name"><h2>${verifier.type.niceName}</h2></div> + `)); + $verifierType.append($(` + <div class="verifier-type-description"><p>${verifier.type.description}</p></div> `)); - let $entryList = $(`<ul>`); - $verifierEntries.append($entryList); + + // Add entries if min 1 result + if (verifier.entries.length > 0) { for (let i = 0; i < verifier.entries.length; i++) { - $entryList.append(renderVerificationEntry(verifier.entries[i])); + $verifierEntries.append(renderVerificationEntry(verifier.entries[i])); } } + $verifierContent.append($verifierType); + $verifierContent.append($verifierEntries); + $verifierContent.fadeIn(250); } /** @@ -259,10 +303,25 @@ function renderVerifierResult(verifierSelect) { * @Type VerificationEntry */ function renderVerificationEntry(verificationEntry) { - console.log(verificationEntry); - return $(` - <li class="dmn-${verificationEntry.verificationClassification}">${verificationEntry.message}</li> - `); + let $entryContainer = $(`<div class="verification-container"></div>`); + $entryContainer.append($(` + <span class="dmn-verification-icon dmn-${verificationEntry.verificationClassification}"/> + `)); + $entryContainer.append($(` + <div class="verification-message">${verificationEntry.message}</div> + `)); + // "dmn-${verificationEntry.verificationClassification}" + let $fixButtons = $(`<div class="verification-container-fix-buttons"></div>`); + + /* + $fixButtons.append( + $(`<button class="verification-fix-button clickable">Show</button>`)); + $fixButtons.append( + $(`<button class="verification-fix-button clickable">Fix</button>`)); + $fixButtons.append( + $(`<button class="verification-fix-button clickable">Fix</button>`)); */ + $entryContainer.append($fixButtons); // TODO: render buttons + return $entryContainer; } /**