diff --git a/dmn-generator/pom.xml b/dmn-generator/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..ac43643f6e095dca61cf3534f04ae390ea50f44d --- /dev/null +++ b/dmn-generator/pom.xml @@ -0,0 +1,116 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <groupId>de.unikoblenz.fgbks</groupId> + <artifactId>dmn-generator</artifactId> + <version>1.0-SNAPSHOT</version> + <name>DMN generator</name> + <packaging>jar</packaging> + + <properties> + <!--Define versions for dependencies--> + <org.w3c.dom.version>2.3.0-jaxb-1.0.6</org.w3c.dom.version> + </properties> + + <dependencies> + <!--DOM XML parser--> + <dependency> + <groupId>org.w3c</groupId> + <artifactId>dom</artifactId> + <version>${org.w3c.dom.version}</version> + </dependency> + + <!--Backend application--> + <dependency> + <groupId>de.unikoblenz.fgbks</groupId> + <artifactId>dmn-verifier-app</artifactId> + <version>1.0-SNAPSHOT</version> + </dependency> + + <dependency> + <groupId>org.camunda.bpm.dmn</groupId> + <artifactId>camunda-engine-dmn</artifactId> + <version>7.10.0</version> + </dependency> + <dependency> + <groupId>com.google.guava</groupId> + <artifactId>guava</artifactId> + <version>25.1-jre</version> + </dependency> + <dependency> + <groupId>javax.xml.bind</groupId> + <artifactId>jaxb-api</artifactId> + <version>2.3.0-b170201.1204</version> + </dependency> + + <!-- https://mvnrepository.com/artifact/javax.activation/activation --> + <dependency> + <groupId>javax.activation</groupId> + <artifactId>activation</artifactId> + <version>1.1</version> + </dependency> + + <!-- https://mvnrepository.com/artifact/org.glassfish.jaxb/jaxb-runtime --> + <dependency> + <groupId>org.glassfish.jaxb</groupId> + <artifactId>jaxb-runtime</artifactId> + <version>2.3.0-b170127.1453</version> + </dependency> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-api</artifactId> + <version>5.3.2</version> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <version>3.7.0</version> + <configuration> + <source>8</source> + <target>8</target> + </configuration> + </plugin> + <plugin> + <groupId>com.coveo</groupId> + <artifactId>fmt-maven-plugin</artifactId> + <version>2.6.0</version> + <executions> + <execution> + <goals> + <goal>format</goal> + </goals> + </execution> + </executions> + </plugin> + <plugin> + <artifactId>maven-assembly-plugin</artifactId> + <configuration> + <archive> + <manifest> + <mainClass>de.uniko.bks.statistic.PerformanceTester</mainClass> + </manifest> + </archive> + <descriptorRefs> + <descriptorRef>jar-with-dependencies</descriptorRef> + </descriptorRefs> + </configuration> + <executions> + <execution> + <id>make-assembly</id> + <phase>package</phase> + <goals> + <goal>single</goal> + </goals> + </execution> + </executions> + </plugin> + </plugins> + </build> +</project> diff --git a/dmn-generator/src/main/java/de/uniko/bks/cli/CliArgument.java b/dmn-generator/src/main/java/de/uniko/bks/cli/CliArgument.java new file mode 100644 index 0000000000000000000000000000000000000000..b8c231650d1bf5e3908fc368a1a309b5d233a462 --- /dev/null +++ b/dmn-generator/src/main/java/de/uniko/bks/cli/CliArgument.java @@ -0,0 +1,137 @@ +/* + * Developed by Hans-Henning Ramberger as part of a master thesis. + * Copyright (c) 2018. + */ + +package de.uniko.bks.cli; + +import de.uniko.bks.generator.entry.OutputEntryList; +import de.uniko.bks.statistic.PerformanceTestConfiguration; +import java.util.Arrays; + +/** Wrapper for all available cli arguments of the DMN gemerator */ +public class CliArgument { + private static final String FLAG_EXCEPTION_MESSAGE = + "Ignoring invalid or incomplete parameter and using the default value."; + /** Sign that all flags have */ + private static String FLAG_SIGN = "-"; + /** Flag which identifies the row number steps */ + private static final String ROW_NUMBER_STEPS_FLAG = FLAG_SIGN + "r"; + + private static final String NUMBER_OF_TEST_RUNS_FLAG = FLAG_SIGN + "t"; + private static final String NUMBER_OF_DATA_POINTS_FLAG = FLAG_SIGN + "d"; + private static final String INITIAL_COLUMN_COUNT_FLAG = FLAG_SIGN + "icc"; + private static final String INITIAL_ROW_COUNT_FLAG = FLAG_SIGN + "irc"; + private static final String OUTPUT_ENTRY_LIST_FLAG = FLAG_SIGN + "o"; + /** Difference in rows in each test dmn file */ + private int rowSteps; + /** Number of runs for each file to calculate the average run-time */ + private int numberOfTestRuns; + /** Amount (square root) of different data points (files) created */ + private int numberOfDataPoints; + /** Number of columns the generator starts with */ + private int initialColumnCount; + /** Number of rows the generator starts with */ + private int initialRowCount; + /** List of output entries used in the dmn file (note the negations are created automatically) */ + private OutputEntryList outputEntryList = new OutputEntryList(); + + public CliArgument(String[] args) { + this.rowSteps = + this.getFlagValue( + ROW_NUMBER_STEPS_FLAG, args, PerformanceTestConfiguration.getRowNumberSteps()); + this.numberOfTestRuns = + this.getFlagValue( + NUMBER_OF_TEST_RUNS_FLAG, args, PerformanceTestConfiguration.getNumberOfTests()); + this.numberOfDataPoints = + this.getFlagValue( + NUMBER_OF_DATA_POINTS_FLAG, args, PerformanceTestConfiguration.getNumberOfDataPoints()); + this.initialColumnCount = + this.getFlagValue( + INITIAL_COLUMN_COUNT_FLAG, + args, + PerformanceTestConfiguration.getInitialColumnCount()) + - 1; + this.initialRowCount = + this.getFlagValue( + INITIAL_ROW_COUNT_FLAG, args, PerformanceTestConfiguration.getInitialRowCount()) + - 1; + this.outputEntryList.setValues( + this.getFlagValue(OUTPUT_ENTRY_LIST_FLAG, args, new OutputEntryList().getValues())); + } + + /** + * Function which gets the value of a cli flag + * + * @param flag Flag of which the values is extracted + * @param args All cli arguments given + * @param defaultValue Default value if the argument is not set correctly + * @return The correct value of the cli argument + */ + private int getFlagValue(String flag, String[] args, int defaultValue) { + try { + int flagIndex = Arrays.asList(args).indexOf(flag); + // Check if flag is set and the next item is not a flag + if (flagIndex >= 0 && !args[flagIndex + 1].contains(FLAG_SIGN)) { + return Integer.parseInt(args[flagIndex + 1]); + } else { + // Set the default value for the row steps + return defaultValue; + } + } catch (ArrayIndexOutOfBoundsException | NumberFormatException e) { + System.err.println(FLAG_EXCEPTION_MESSAGE); + } + // Set the default value for the parameter + return defaultValue; + } + + /** + * Function which gets the value of a cli flag + * + * @param flag Flag of which the values is extracted + * @param args All cli arguments given + * @param defaultValue Default value if the argument is not set correctly + * @return The correct value of the cli argument + */ + private String[] getFlagValue(String flag, String[] args, String[] defaultValue) { + try { + int flagIndex = Arrays.asList(args).indexOf(flag); + // Check if flag is set and the next item is not a flag + if (flagIndex >= 0 && !args[flagIndex + 1].contains(FLAG_SIGN)) { + // Split the comma separated items of the flag + return args[flagIndex + 1].split(","); + } else { + // Set the default value for the row steps + return defaultValue; + } + } catch (ArrayIndexOutOfBoundsException e) { + System.err.println(FLAG_EXCEPTION_MESSAGE); + } + // Set the default value for the parameter + return defaultValue; + } + + public int getRowSteps() { + return rowSteps; + } + + public int getNumberOfTestRuns() { + return numberOfTestRuns; + } + + public int getNumberOfDataPoints() { + return numberOfDataPoints; + } + + public int getInitialColumnCount() { + return initialColumnCount; + } + + public int getInitialRowCount() { + return initialRowCount; + } + + public OutputEntryList getOutputEntryList() { + return outputEntryList; + } +} diff --git a/dmn-generator/src/main/java/de/uniko/bks/dom/dfl/Date.java b/dmn-generator/src/main/java/de/uniko/bks/dom/dfl/Date.java new file mode 100644 index 0000000000000000000000000000000000000000..7d4d6b20c7068ae2a01503dd65156cf19474143b --- /dev/null +++ b/dmn-generator/src/main/java/de/uniko/bks/dom/dfl/Date.java @@ -0,0 +1,111 @@ +/* + * Developed by Hans-Henning Ramberger as part of a master thesis. + * Copyright (c) 2018. + */ + +package de.uniko.bks.dom.dfl; + +import de.uniko.bks.dom.dfl.rule.Comparator; +import de.uniko.bks.dom.dfl.rule.RuleVariable; + +/** Class specifying how a date reasonable in SPINdle looks like */ +public class Date { + /** Year of a date (four digits: YYYY) */ + private String year; + /** Month of a date (two digits: MM) */ + private String month; + /** Day of a date (two digits: DD) */ + private String day; + /** Hour of a date (two digits: hh) */ + private String hour; + /** Minute of a date (two digits: ii) */ + private String minute; + /** Second of the date (two digits: ss) */ + private String second; + /** Comparator used for comparisons of dates */ + private String comparator; + + /** Constructor for an empty date */ + public Date() { + this.year = ""; + this.month = ""; + this.day = ""; + this.hour = ""; + this.minute = ""; + this.second = ""; + this.comparator = Comparator.EQUALS.getValue(); + } + + /** + * Constructor for a date without a time + * + * @param year Year of a date (four digits: YYYY) + * @param month Month of a date (two digits: MM) + * @param day Day of a date (two digits: DD) + */ + public Date(String year, String month, String day) { + this.year = year; + this.month = month; + this.day = day; + this.hour = "00"; + this.minute = "00"; + this.second = "00"; + this.comparator = Comparator.EQUALS.getValue(); + } + + /** + * Constructor for a date with a time + * + * @param year Year of a date (four digits: YYYY) + * @param month Month of a date (two digits: MM) + * @param day Day of a date (two digits: DD) + * @param hour Hour of a date (two digits: hh) + * @param minute Minute of a date (two digits: ii) + * @param second Second of the date (two digits: ss) + */ + public Date(String year, String month, String day, String hour, String minute, String second) { + this.year = year; + this.month = month; + this.day = day; + this.hour = hour; + this.minute = minute; + this.second = second; + this.comparator = Comparator.EQUALS.getValue(); + } + + /** + * Getter for the date comparator + * + * @return The comparator used for date comparisons + */ + public String getComparator() { + return comparator; + } + + /** + * Setter for the date comparator + * + * @param comparator The comparator used for date comparisons + */ + public void setComparator(String comparator) { + this.comparator = comparator; + } + + @Override + public String toString() { + return RuleVariable.VAR_SIGN.getValue() + + "date(" + + year + + ',' + + month + + ',' + + day + + ',' + + hour + + ',' + + minute + + ',' + + second + + ')'; + } +} diff --git a/dmn-generator/src/main/java/de/uniko/bks/dom/dfl/Fact.java b/dmn-generator/src/main/java/de/uniko/bks/dom/dfl/Fact.java new file mode 100644 index 0000000000000000000000000000000000000000..b8d8b3404d5981e3b7af27cfbe747b3aaf487411 --- /dev/null +++ b/dmn-generator/src/main/java/de/uniko/bks/dom/dfl/Fact.java @@ -0,0 +1,90 @@ +/* + * Developed by Hans-Henning Ramberger as part of a master thesis. + * Copyright (c) 2018. + */ + +package de.uniko.bks.dom.dfl; + +/** + * Class representing a fact of any type + * + * @param <T> Java type of the fact + */ +public class Fact<T> { + /** Name of the fact */ + private String name; + /** Value of the fact */ + private T value; + /** Java class the value of the fact belongs to */ + private Class<T> type; + + /** Constructor for an empty fact */ + public Fact() {} + + /** + * Constructor for a completely set fact + * + * @param name Name of the fact + * @param value Value of the fact + * @param type Java class the value of the fact belongs to + */ + public Fact(String name, T value, Class<T> type) { + this.name = name; + this.value = value; + this.type = type; + } + + /** + * Getter for the fact name + * + * @return The name of the fact + */ + public String getName() { + return name; + } + + /** + * Setter for the fact name + * + * @param name The name of the fact + */ + public void setName(String name) { + this.name = name; + } + + /** + * Getter for the value of the fact + * + * @return The value of the fact + */ + public T getValue() { + return value; + } + + /** + * Getter for the value of the fact + * + * @param value The value of the fact + */ + public void setValue(T value) { + this.value = value; + } + + /** + * Getter for the type of the fact + * + * @return The class name of the type of the fact + */ + public Class<T> getType() { + return type; + } + + /** + * Setter for the type of the fact + * + * @param type The class name of the type of the fact + */ + public void setType(Class<T> type) { + this.type = type; + } +} diff --git a/dmn-generator/src/main/java/de/uniko/bks/dom/dfl/LiteralRange.java b/dmn-generator/src/main/java/de/uniko/bks/dom/dfl/LiteralRange.java new file mode 100644 index 0000000000000000000000000000000000000000..fed5ce4a1bcc96d1e6d2efe3800b31bac77d92e8 --- /dev/null +++ b/dmn-generator/src/main/java/de/uniko/bks/dom/dfl/LiteralRange.java @@ -0,0 +1,114 @@ +/* + * Developed by Hans-Henning Ramberger as part of a master thesis. + * Copyright (c) 2018. + */ + +package de.uniko.bks.dom.dfl; + +/** + * Class which represents a wrapper for literals containing a range + * + * @param <T> Type of the limits of the range + */ +public class LiteralRange<T> { + /** Whether or not the literal contains a range */ + private boolean range; + /** Comparator for the lower limit of the range */ + private String lowerLimitComparator; + /** Comparator for the upper limit of the range */ + private String upperLimitComparator; + /** Lower limit of the range */ + private T lowerLimit; + /** Upper limit of the range */ + private T upperLimit; + + /** + * Construtor for an empty range + * + * @param range Whether or not the literal contains a range + */ + public LiteralRange(boolean range) { + this.range = range; + } + + /** + * Getter for the range + * + * @return Whether or not the literal contains a range + */ + public boolean isRange() { + return range; + } + + /** + * Getter for the lower limit comparator + * + * @return The lower limit comparator + */ + public String getLowerLimitComparator() { + return lowerLimitComparator; + } + + /** + * Setter for the lower limit comparator + * + * @param lowerLimitComparator The lower limit comparator + */ + public void setLowerLimitComparator(String lowerLimitComparator) { + this.lowerLimitComparator = lowerLimitComparator; + } + + /** + * Getter for the upper limit comparator + * + * @return The upper limit comparator + */ + public String getUpperLimitComparator() { + return upperLimitComparator; + } + + /** + * Setter for the upper limit comparator + * + * @param upperLimitComparator The upper limit comparator + */ + public void setUpperLimitComparator(String upperLimitComparator) { + this.upperLimitComparator = upperLimitComparator; + } + + /** + * Getter for the lower limit + * + * @return The lower limit + */ + public T getLowerLimit() { + return lowerLimit; + } + + /** + * Setter for the lower limit + * + * @param lowerLimit The lower limit + */ + public void setLowerLimit(T lowerLimit) { + this.lowerLimit = lowerLimit; + } + + /** + * Getter for the upper limit + * + * @return The upper limit + */ + public T getUpperLimit() { + return upperLimit; + } + + /** + * Setter for the upper limit + * + * @param upperLimit The upper limit + */ + public void setUpperLimit(T upperLimit) { + this.upperLimit = upperLimit; + } +} diff --git a/dmn-generator/src/main/java/de/uniko/bks/dom/dfl/rule/Comparator.java b/dmn-generator/src/main/java/de/uniko/bks/dom/dfl/rule/Comparator.java new file mode 100644 index 0000000000000000000000000000000000000000..f3a782a1d7c3fd4f9509a0e1bdf734e346a3d4d1 --- /dev/null +++ b/dmn-generator/src/main/java/de/uniko/bks/dom/dfl/rule/Comparator.java @@ -0,0 +1,63 @@ +/* + * Developed by Hans-Henning Ramberger as part of a master thesis. + * Copyright (c) 2018. + */ + +package de.uniko.bks.dom.dfl.rule; + +/** Enum containing all mathematical comparators for comparison expressions */ +public enum Comparator { + /** Comparator for smaller comparisons */ + SMALLER("<"), + /** Comparator for smaller or equals comparisons */ + SMALLER_OR_EQUALS("<="), + /** Comparator for equals comparisons */ + EQUALS("=="), + /** Comparator for greater or equals comparisons */ + GREATER_OR_EQUALS(">="), + /** Comparator for greater comparisons */ + GREATER(">"); + /** Actual value of the enum */ + private String value; + + /** + * Constructor to set the value of each enum entry + * + * @param value String value of the enum + */ + Comparator(String value) { + this.value = value; + } + + /** + * Getter for the (string) value of one enum entry + * + * @return The value of the enum entry + */ + public String getValue() { + return value; + } + + /** + * Function which gets the opposite of a given comparator + * + * @param comparator Comparator or string containing the comparator + * @return The comparator which describes the opposite set + */ + public static String getOppositeValue(String comparator) { + if (comparator.contains(SMALLER_OR_EQUALS.value)) { + return GREATER.value; + } + if (comparator.contains(GREATER_OR_EQUALS.value)) { + return SMALLER.value; + } + if (comparator.contains(SMALLER.value)) { + return GREATER_OR_EQUALS.value; + } + if (comparator.contains(GREATER.value)) { + return SMALLER_OR_EQUALS.value; + } + + return EQUALS.value; + } +} diff --git a/dmn-generator/src/main/java/de/uniko/bks/dom/dfl/rule/RuleArrow.java b/dmn-generator/src/main/java/de/uniko/bks/dom/dfl/rule/RuleArrow.java new file mode 100644 index 0000000000000000000000000000000000000000..2725b3b41aaf0e91d6943f52a611e348f73c81f8 --- /dev/null +++ b/dmn-generator/src/main/java/de/uniko/bks/dom/dfl/rule/RuleArrow.java @@ -0,0 +1,38 @@ +/* + * Developed by Hans-Henning Ramberger as part of a master thesis. + * Copyright (c) 2018. + */ + +package de.uniko.bks.dom.dfl.rule; + +/** Enum containing all supported arrows used in a DFL rule */ +public enum RuleArrow { + /** Arrow for strict rules */ + STRICT_RULE("->"), + /** Arrow for defeasible rules */ + DEFEASIBLE_RULE("=>"), + /** Arrow for facts */ + FACT(">>"), + /** Arrow for superiorities */ + SUPERIORITY(">"); + /** Actual value of the enum */ + private String value; + + /** + * Constructor to set the value of each enum entry + * + * @param value String value of the enum + */ + RuleArrow(String value) { + this.value = value; + } + + /** + * Getter for the (string) value of one enum entry + * + * @return The value of the enum entry + */ + public String getValue() { + return value; + } +} diff --git a/dmn-generator/src/main/java/de/uniko/bks/dom/dfl/rule/RuleDelimiter.java b/dmn-generator/src/main/java/de/uniko/bks/dom/dfl/rule/RuleDelimiter.java new file mode 100644 index 0000000000000000000000000000000000000000..6c39ef4d614ad2dde7c3b45d0d1f1be1456c43ae --- /dev/null +++ b/dmn-generator/src/main/java/de/uniko/bks/dom/dfl/rule/RuleDelimiter.java @@ -0,0 +1,34 @@ +/* + * Developed by Hans-Henning Ramberger as part of a master thesis. + * Copyright (c) 2018. + */ + +package de.uniko.bks.dom.dfl.rule; + +/** Enum containing all delimiters used in a DFL rule */ +public enum RuleDelimiter { + /** Delimiter between rule id and rule literals */ + ID(":"), + /** Delimiter replacing whitespaces in dfl rules */ + WHITESPACE("_"); + /** Actual value of the enum */ + private String value; + + /** + * Constructor to set the value of each enum entry + * + * @param value String value of the enum + */ + RuleDelimiter(String value) { + this.value = value; + } + + /** + * Getter for the (string) value of one enum entry + * + * @return The value of the enum entry + */ + public String getValue() { + return value; + } +} diff --git a/dmn-generator/src/main/java/de/uniko/bks/dom/dfl/rule/RuleId.java b/dmn-generator/src/main/java/de/uniko/bks/dom/dfl/rule/RuleId.java new file mode 100644 index 0000000000000000000000000000000000000000..bfc4398926c196e8c2d9cfeb7b063df7d64fee8e --- /dev/null +++ b/dmn-generator/src/main/java/de/uniko/bks/dom/dfl/rule/RuleId.java @@ -0,0 +1,127 @@ +/* + * Developed by Hans-Henning Ramberger as part of a master thesis. + * Copyright (c) 2018. + */ + +package de.uniko.bks.dom.dfl.rule; + +import de.uniko.bks.util.StringSanitizer; +import java.util.Comparator; + +/** Class representing a DFL rule identifier */ +public class RuleId { + /** Delimiter between two parts of the ruleId */ + private static final String PART_DELIMITER = + RuleDelimiter.WHITESPACE.getValue() + + RuleDelimiter.WHITESPACE.getValue() + + RuleDelimiter.WHITESPACE.getValue(); + + /** XML id of the rule in the DMN */ + private String xmlId; + /** Id of the decision in the DMN */ + private String decisionId; + /** Number of the rule */ + private int number; + /** Subnumber of the rule (only important on split up rules) */ + private int subNumber; + /** Priority of the rule */ + private int priority; + + /** + * Constructor to completely initialize a RuleId + * + * @param xmlId XML id of the rule in the DMN + * @param decisionId Id of the decision in the DMN + * @param number Number of the rule + * @param subNumber Subnumber of the rule (only important on split up rules) + * @param priority Priority of the rule + */ + public RuleId(String xmlId, String decisionId, int number, int subNumber, int priority) { + // Replace illegal characters + this.xmlId = StringSanitizer.sanitize(xmlId, true, false, false, true); + this.decisionId = decisionId; + this.number = number; + this.subNumber = subNumber; + this.priority = priority; + } + + /** + * Getter for the XML id of the rule in the DMN + * + * @return The XML id of the rule in the DMN + */ + public String getXmlId() { + return xmlId; + } + + /** + * Getter for the id of the decision in the DMN + * + * @return The id of the decision in the DMN + */ + public String getDecisionId() { + return decisionId; + } + + /** + * Getter for the number of the rule + * + * @return The number of the rule + */ + public int getNumber() { + return number; + } + + /** + * Getter for the subnumber of the rule (only important on split up rules) + * + * @return The subnumber of the rule + */ + public int getSubNumber() { + return subNumber; + } + + /** + * Getter for the priority of the rule + * + * @return The priority of the rule + */ + public int getPriority() { + return priority; + } + + /** + * Getter for the delimiter between two parts of the ruleId + * + * @return The delimiter between two parts of the ruleId + */ + public static String getPartDelimiter() { + return PART_DELIMITER; + } + + /** Define the comparator to sort the ruleIds by their number */ + public static Comparator<RuleId> COMPARE_BY_NUMBER = Comparator.comparingInt(one -> one.number); + + /** Define the comparator to sort th ruleIds by their priority */ + public static Comparator<RuleId> COMPARE_BY_PRIORITY = + Comparator.comparingInt(one -> one.priority); + + @Override + public String toString() { + // Do not render the xml id on facts + if (xmlId.equals("")) { + return this.decisionId + PART_DELIMITER + this.number + RuleDelimiter.ID.getValue(); + } + + return this.xmlId + + PART_DELIMITER + + this.decisionId + + PART_DELIMITER + + this.number + + PART_DELIMITER + + this.subNumber + + PART_DELIMITER + + this.priority + + RuleDelimiter.ID.getValue(); + } +} diff --git a/dmn-generator/src/main/java/de/uniko/bks/dom/dfl/rule/RuleType.java b/dmn-generator/src/main/java/de/uniko/bks/dom/dfl/rule/RuleType.java new file mode 100644 index 0000000000000000000000000000000000000000..63625718131bfcd65851f54ad528724d02ca2d48 --- /dev/null +++ b/dmn-generator/src/main/java/de/uniko/bks/dom/dfl/rule/RuleType.java @@ -0,0 +1,42 @@ +/* + * Developed by Hans-Henning Ramberger as part of a master thesis. + * Copyright (c) 2018. + */ + +package de.uniko.bks.dom.dfl.rule; + +/** Enum containing available rule types */ +public enum RuleType { + /** Rule type for booleans */ + BOOLEAN("boolean"), + /** Rule type for double */ + DOUBLE("double"), + /** Rule type for dates */ + DATE("date"), + /** Rule type for integers */ + INT("integer"), + /** Rule type for longs */ + LONG("long"), + /** Rule type for integers */ + STRING("string"); + /** Actual value of the enum */ + private String value; + + /** + * Constructor to set the value of each enum entry + * + * @param value String value of the enum + */ + RuleType(String value) { + this.value = value; + } + + /** + * Getter for the (string) value of one enum entry + * + * @return The value of the enum entry + */ + public String getValue() { + return value; + } +} diff --git a/dmn-generator/src/main/java/de/uniko/bks/dom/dfl/rule/RuleVariable.java b/dmn-generator/src/main/java/de/uniko/bks/dom/dfl/rule/RuleVariable.java new file mode 100644 index 0000000000000000000000000000000000000000..716dfe6cc2ca112e6d99bc972f42ce1349cd8523 --- /dev/null +++ b/dmn-generator/src/main/java/de/uniko/bks/dom/dfl/rule/RuleVariable.java @@ -0,0 +1,36 @@ +/* + * Developed by Hans-Henning Ramberger as part of a master thesis. + * Copyright (c) 2018. + */ + +package de.uniko.bks.dom.dfl.rule; + +/** Enum containing all delimiters used in variables of a DLF rule */ +public enum RuleVariable { + /** Sign for DFL variables */ + VAR_SIGN("@"), + /** Set expression for DFL variables */ + SET("set "), + /** Allocation of DFL expressions */ + ALLOCATION("="); + /** Actual value of the enum */ + private String value; + + /** + * Constructor to set the value of each enum entry + * + * @param value String value of the enum + */ + RuleVariable(String value) { + this.value = value; + } + + /** + * Getter for the (string) value of one enum entry + * + * @return The value of the enum entry + */ + public String getValue() { + return value; + } +} diff --git a/dmn-generator/src/main/java/de/uniko/bks/dom/dmn/Decision.java b/dmn-generator/src/main/java/de/uniko/bks/dom/dmn/Decision.java new file mode 100644 index 0000000000000000000000000000000000000000..98916f0bac8ea30d32567f5f99cb53f73bdc330f --- /dev/null +++ b/dmn-generator/src/main/java/de/uniko/bks/dom/dmn/Decision.java @@ -0,0 +1,81 @@ +/* + * Developed by Hans-Henning Ramberger as part of a master thesis. + * Copyright (c) 2018. + */ + +package de.uniko.bks.dom.dmn; + +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +/** Class specifying a dmn decision */ +@XmlRootElement(name = "decision") +public class Decision { + /** XML id of the Decision */ + private String id; + /** XML name of the Decision */ + private String name; + /** DecisionTable contained in the Decision */ + private DecisionTable dmnTable; + + /** Constructor of an empty Decision */ + public Decision() {} + + /** + * Getter for the XML id of the Decision + * + * @return The XML id of the Decision + */ + @XmlAttribute(name = "id") + public String getId() { + return id; + } + + /** + * Setter for the XML id of the Decision + * + * @param id XML id of the Decision + */ + public void setId(String id) { + this.id = id; + } + + /** + * Getter for the XML name of the Decision + * + * @return The XML name of the Decision + */ + @XmlAttribute(name = "name") + public String getName() { + return name; + } + + /** + * Setter for the XML name of the Decision + * + * @param name The XML name of the Decision + */ + public void setName(String name) { + this.name = name; + } + + /** + * Getter for the DecisionTable contained int the Decision + * + * @return The DecisionTable contained in the Decision + */ + @XmlElement(name = "decisionTable") + public DecisionTable getDmnTable() { + return dmnTable; + } + + /** + * Setter for the DecisionTable contained int the Decision + * + * @param dmnTable The DecisionTable contained in the Decision + */ + public void setDmnTable(DecisionTable dmnTable) { + this.dmnTable = dmnTable; + } +} diff --git a/dmn-generator/src/main/java/de/uniko/bks/dom/dmn/DecisionTable.java b/dmn-generator/src/main/java/de/uniko/bks/dom/dmn/DecisionTable.java new file mode 100644 index 0000000000000000000000000000000000000000..6e382603cab7aa60db334b04442a3bd679cb1a6a --- /dev/null +++ b/dmn-generator/src/main/java/de/uniko/bks/dom/dmn/DecisionTable.java @@ -0,0 +1,124 @@ +/* + * Developed by Hans-Henning Ramberger as part of a master thesis. + * Copyright (c) 2018. + */ + +package de.uniko.bks.dom.dmn; + +import de.uniko.bks.dom.dmn.header.OutputHeader; +import java.util.ArrayList; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlType; + +/** Class specifying a dmn DecisionTable */ +@XmlType(propOrder = {"input", "outputHeader", "dmnRule"}) +public class DecisionTable { + /** XML id of the decision table */ + private String id; + /** HitPolicy of the table */ + private String hitPolicy; + /** XML input elements of the table */ + private ArrayList<Input> input; + /** OutputHeader (meta-information about the output) of the table */ + private OutputHeader outputHeader; + /** Rules contained in the table */ + private ArrayList<DmnRule> dmnRule; + + /** Constructor to initialize an empty DecisionTable */ + public DecisionTable() {} + + /** + * Getter for the XML id of the decision table + * + * @return The XML id of the decision table + */ + @XmlAttribute + public String getId() { + return id; + } + + /** + * Setter for the XML id of the decision table + * + * @param id The XML id of the decision table + */ + public void setId(String id) { + this.id = id; + } + + /** + * Getter for the HitPolicy of a table + * + * @return The HitPolicy of the table + */ + @XmlAttribute + public String getHitPolicy() { + return hitPolicy; + } + + /** + * Setter for the HitPolicy of a table + * + * @param hitPolicy The HitPolicy of the table + */ + public void setHitPolicy(String hitPolicy) { + this.hitPolicy = hitPolicy; + } + + /** + * Getter for the XML input elements of the table + * + * @return The XML input elements of the table + */ + @XmlElement(name = "input") + public ArrayList<Input> getInput() { + return input; + } + + /** + * Getter for the OutputHeader (meta-information about the output) of the table + * + * @return The OutputHeader (meta-information about the output) of the table + */ + @XmlElement(name = "output") + public OutputHeader getOutputHeader() { + return outputHeader; + } + /** + * Setter for the XML input elements of the table + * + * @param input The XML input elements of the table + */ + public void setInput(ArrayList<Input> input) { + this.input = input; + } + + /** + * Getter for the rules contained in the table + * + * @return The rules contained in the table + */ + @XmlElement(name = "rule") + public ArrayList<DmnRule> getDmnRule() { + return dmnRule; + } + + /** + * Setter for the rules contained in the table + * + * @param dmnRule The rules contained in the table + */ + public void setDmnRule(ArrayList<DmnRule> dmnRule) { + this.dmnRule = dmnRule; + } + + /** + * Setter for the OutputHeader (meta-information about the output) of the table + * + * @param outputHeader The OutputHeader (meta-information about the output) of the table + */ + public void setOutputHeader(OutputHeader outputHeader) { + this.outputHeader = outputHeader; + } +} diff --git a/dmn-generator/src/main/java/de/uniko/bks/dom/dmn/DmnRule.java b/dmn-generator/src/main/java/de/uniko/bks/dom/dmn/DmnRule.java new file mode 100644 index 0000000000000000000000000000000000000000..478ee5e98b36757456daf39826170cc63edff753 --- /dev/null +++ b/dmn-generator/src/main/java/de/uniko/bks/dom/dmn/DmnRule.java @@ -0,0 +1,79 @@ +/* + * Developed by Hans-Henning Ramberger as part of a master thesis. + * Copyright (c) 2018. + */ + +package de.uniko.bks.dom.dmn; + +import de.uniko.bks.dom.dmn.entry.InputEntry; +import de.uniko.bks.dom.dmn.entry.OutputEntry; +import java.util.ArrayList; +import javax.xml.bind.annotation.XmlAttribute; + +/** Class specifying a single DMNRule */ +public class DmnRule { + /** XML id of the rule */ + private String id; + /** List of inputEntries of the rule */ + private ArrayList<InputEntry> inputEntry; + /** OutputEntry of the rule */ + private OutputEntry outputEntry; + + /** Constructor to initialize an empty DmnRule */ + public DmnRule() {} + + /** + * Getter for the XML id of the rule + * + * @return The XML id of the rule + */ + @XmlAttribute + public String getId() { + return id; + } + + /** + * Setter for the XML id of the rule + * + * @param id The XML id of the rule + */ + public void setId(String id) { + this.id = id; + } + + /** + * Getter for the List of inputEntries of the rule + * + * @return The List of inputEntries of the rule + */ + public ArrayList<InputEntry> getInputEntry() { + return inputEntry; + } + + /** + * Setter for the List of inputEntries of the rule + * + * @param inputEntry The List of inputEntries of the rule + */ + public void setInputEntry(ArrayList<InputEntry> inputEntry) { + this.inputEntry = inputEntry; + } + + /** + * Getter for OutputEntry of the rule + * + * @return The OutputEntry of the rule + */ + public OutputEntry getOutputEntry() { + return outputEntry; + } + + /** + * Setter for OutputEntry of the rule + * + * @param outputEntry The OutputEntry of the rule + */ + public void setOutputEntry(OutputEntry outputEntry) { + this.outputEntry = outputEntry; + } +} diff --git a/dmn-generator/src/main/java/de/uniko/bks/dom/dmn/HitPolicy.java b/dmn-generator/src/main/java/de/uniko/bks/dom/dmn/HitPolicy.java new file mode 100644 index 0000000000000000000000000000000000000000..090fd2034b6f3658c7c70f64a6eed6479862ea61 --- /dev/null +++ b/dmn-generator/src/main/java/de/uniko/bks/dom/dmn/HitPolicy.java @@ -0,0 +1,53 @@ +/* + * Developed by Hans-Henning Ramberger as part of a master thesis. + * Copyright (c) 2018. + */ + +package de.uniko.bks.dom.dmn; + +/** Enum containing all supported hit policies for DMN decision tables */ +public enum HitPolicy { + /** Entry for the ANY hit policy */ + ANY("ANY"), + /** Entry for the COLLECT hit policy */ + COLLECT("COLLECT"), + /** Entry for the FIRST hit policy */ + FIRST("FIRST"), + /** Entry for the OUTPUT ORDER hit policy */ + OUTPUT_ORDER("OUTPUT ORDER"), + /** Entry for the PRIORITY hit policy */ + PRIORITY("PRIORITY"), + /** Entry for the RULE ORDER hit policy */ + RULE_ORDER("RULE ORDER"), + /** Entry for the UNIQUE hit policy */ + UNIQUE("UNIQUE"); + /** Actual value of the enum */ + private String value; + + /** + * Constructor to set the value of each enum entry + * + * @param value String value of the enum + */ + HitPolicy(String value) { + this.value = value; + } + + /** + * Getter for the (string) value of one enum entry + * + * @return The value of the enum entry + */ + public String getValue() { + return value; + } + + /** + * Getter for the (string) value of the default enum entry + * + * @return The value of the default enum entry + */ + public static String getDefault() { + return UNIQUE.value; + } +} diff --git a/dmn-generator/src/main/java/de/uniko/bks/dom/dmn/Input.java b/dmn-generator/src/main/java/de/uniko/bks/dom/dmn/Input.java new file mode 100644 index 0000000000000000000000000000000000000000..6b93ab801d2857a5616546f926d4ca972d90a527 --- /dev/null +++ b/dmn-generator/src/main/java/de/uniko/bks/dom/dmn/Input.java @@ -0,0 +1,83 @@ +/* + * Developed by Hans-Henning Ramberger as part of a master thesis. + * Copyright (c) 2018. + */ + +package de.uniko.bks.dom.dmn; + +import de.uniko.bks.dom.dmn.header.InputHeader; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlElement; + +/** Class specifying a DMN Input */ +public class Input { + /** XML id of the Input */ + private String id; + /** XML label of the Input */ + private String label; + /** InputHeader of the Input element */ + private InputHeader inputHeader; + + /** Constructor to initialize an empty Input */ + public Input() {} + + /** + * Constructor to initialize a completely set Input + * + * @param id The XML id of the Input + * @param label The XML label of the Input + * @param inputHeader The InputHeader of the Input element + */ + public Input(String id, String label, InputHeader inputHeader) { + this.id = id; + this.label = label; + this.inputHeader = inputHeader; + } + + @XmlAttribute + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + /** + * Getter for the XML label of the Input + * + * @return The XML label of the Input + */ + @XmlAttribute + public String getLabel() { + return label; + } + + /** + * Setter for the XML label of the Input + * + * @param label The XML label of the Input + */ + public void setLabel(String label) { + this.label = label; + } + + /** + * Getter for the InputHeader of the Input element + * + * @return The InputHeader of the Input element + */ + @XmlElement(name = "inputExpression") + public InputHeader getInputHeader() { + return inputHeader; + } + + /** + * Setter for the InputHeader of the Input element + * + * @param inputHeader The InputHeader of the Input element + */ + public void setInputHeader(InputHeader inputHeader) { + this.inputHeader = inputHeader; + } +} diff --git a/dmn-generator/src/main/java/de/uniko/bks/dom/dmn/entry/InputEntry.java b/dmn-generator/src/main/java/de/uniko/bks/dom/dmn/entry/InputEntry.java new file mode 100644 index 0000000000000000000000000000000000000000..93083551fa5095afb1b6789392e7e529ea868b53 --- /dev/null +++ b/dmn-generator/src/main/java/de/uniko/bks/dom/dmn/entry/InputEntry.java @@ -0,0 +1,69 @@ +/* + * Developed by Hans-Henning Ramberger as part of a master thesis. + * Copyright (c) 2018. + */ + +package de.uniko.bks.dom.dmn.entry; + +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlElement; + +/** Class specifying a dmn InputEntry */ +public class InputEntry { + /** XML id of the InputEntry */ + private String id; + /** Text/value of the InputEntry */ + private String text; + + /** Constructor for an empty InputEntry */ + public InputEntry() {} + + /** + * Constructor for a completely initialized InputEntry + * + * @param id XML id of the InputEntry + * @param text Text/value of the InputEntry + */ + public InputEntry(String id, String text) { + this.id = id; + this.text = text; + } + + /** + * Getter for the InputEntry id + * + * @return The id of the InputEntry + */ + @XmlAttribute + public String getId() { + return id; + } + + /** + * Setter for the InputEntry id + * + * @param id The id of the InputEntry + */ + public void setId(String id) { + this.id = id; + } + + /** + * Getter for the InputEntry text/value + * + * @return The text/value of the InputEntry + */ + @XmlElement + public String getText() { + return text; + } + + /** + * Setter for the InputEntry text/value + * + * @param text The text/value of the InputEntry + */ + public void setText(String text) { + this.text = text; + } +} diff --git a/dmn-generator/src/main/java/de/uniko/bks/dom/dmn/entry/OutputEntry.java b/dmn-generator/src/main/java/de/uniko/bks/dom/dmn/entry/OutputEntry.java new file mode 100644 index 0000000000000000000000000000000000000000..c849d0c533d4c81e04bdbdb5e4a5792c5220360d --- /dev/null +++ b/dmn-generator/src/main/java/de/uniko/bks/dom/dmn/entry/OutputEntry.java @@ -0,0 +1,65 @@ +/* + * Developed by Hans-Henning Ramberger as part of a master thesis. + * Copyright (c) 2018. + */ + +package de.uniko.bks.dom.dmn.entry; + +import javax.xml.bind.annotation.XmlAttribute; + +/** Class specifying a dmn OutputEntry */ +public class OutputEntry { + /** XML id of the OutputEntry */ + private String id; + /** Text/value of the OutputEntry */ + private String text; + + /** Constructor for an empty OutputEntry */ + public OutputEntry() {} + + /** + * Constructor for an OutputEntry with an initial text/value + * + * @param text Text/value of the OutputEntry + */ + public OutputEntry(String text) { + this.text = text; + } + + /** + * Getter for the OutputEntry id + * + * @return The id of the OutputEntry + */ + @XmlAttribute + public String getId() { + return id; + } + + /** + * Setter for the OutputEntry id + * + * @param id The id of the OutputEntry + */ + public void setId(String id) { + this.id = id; + } + + /** + * Getter for the OutputEntry text/value + * + * @return The text/value of the OutputEntry + */ + public String getText() { + return text; + } + + /** + * Setter for the OutputEntry text/value + * + * @param text The text/value of the OutputEntry + */ + public void setText(String text) { + this.text = text; + } +} diff --git a/dmn-generator/src/main/java/de/uniko/bks/dom/dmn/entry/RuleEntry.java b/dmn-generator/src/main/java/de/uniko/bks/dom/dmn/entry/RuleEntry.java new file mode 100644 index 0000000000000000000000000000000000000000..a12357666f7e2c9624436b7953b99bd923673663 --- /dev/null +++ b/dmn-generator/src/main/java/de/uniko/bks/dom/dmn/entry/RuleEntry.java @@ -0,0 +1,138 @@ +/* + * Developed by Hans-Henning Ramberger as part of a master thesis. + * Copyright (c) 2018. + */ + +package de.uniko.bks.dom.dmn.entry; + +/** + * Class specifying a common transfer object encapsulating a rule entry + * + * @param <T> Type (java class) of the RuleEntry value + */ +public class RuleEntry<T> { + /** Type (java class) of the value */ + private String type; + /** Value of the RuleEntry */ + private T value; + /** Name (i.e. DMN table header) of the RuleEntry */ + private String name; + /** Whether the values is negated in a logical sense or not */ + private boolean negated; + /** + * Whether the RuleEntry is an output entry (this property is needed to avoid issues with + * SPINdle's way of handling head literals) + */ + private boolean output; + + /** + * Constructor for a simple non-negated RuleEntry + * + * @param type Type (java class) of the value + * @param value Value of the RuleEntry + * @param headerName Name (i.e. DMN table header) of the RuleEntry + */ + public RuleEntry(String type, T value, String headerName) { + this.type = type; + this.value = value; + this.name = headerName; + this.negated = false; + this.output = false; + } + + /** + * Constructor for a possible negated RuleEntry + * + * @param type Type (java class) of the value + * @param value Value of the RuleEntry + * @param headerName Name (i.e. DMN table header) of the RuleEntry + * @param negated Whether the values is negated in a logical sense or not + */ + public RuleEntry(String type, T value, String headerName, boolean negated) { + this.type = type; + this.value = value; + this.name = headerName; + this.negated = negated; + this.output = false; + } + + /** + * Constructor for an RuleEntry which is an dmn output + * + * @param type Type (java class) of the value + * @param value Value of the RuleEntry + * @param headerName Name (i.e. DMN table header) of the RuleEntry + * @param negated Whether the values is negated in a logical sense or not + * @param output Whether the RuleEntry is an output entry + */ + public RuleEntry(String type, T value, String headerName, boolean negated, boolean output) { + this.type = type; + this.value = value; + this.name = headerName; + this.negated = negated; + this.output = output; + } + + /** + * Getter for the RuleEntry type + * + * @return The type of the RuleEntry + */ + public String getType() { + return type; + } + + /** + * Getter for the RuleEntry value + * + * @return The value of the RuleEntry + */ + public T getValue() { + return value; + } + + /** + * Setter for the RuleEntry value + * + * @param value The value of the RuleEntry + */ + public void setValue(T value) { + this.value = value; + } + + /** + * Getter for the RuleEntry name + * + * @return The name of the RuleEntry + */ + public String getName() { + return name; + } + + /** + * Getter for the RuleEntry negated property + * + * @return Whether or not the RuleEntry is negated + */ + public boolean isNegated() { + return negated; + } + + /** + * Getter for the RuleEntry output property + * + * @return Whether or not the RuleEntry is an output + */ + public boolean isOutput() { + return output; + } + + /** + * Setter for the RuleEntry output property + * + * @param output Whether or not the RuleEntry is an output + */ + public void setOutput(boolean output) { + this.output = output; + } +} diff --git a/dmn-generator/src/main/java/de/uniko/bks/dom/dmn/header/Header.java b/dmn-generator/src/main/java/de/uniko/bks/dom/dmn/header/Header.java new file mode 100644 index 0000000000000000000000000000000000000000..ff46ea74257cb8e6cd4921b567054eb0040377cd --- /dev/null +++ b/dmn-generator/src/main/java/de/uniko/bks/dom/dmn/header/Header.java @@ -0,0 +1,56 @@ +/* + * Developed by Hans-Henning Ramberger as part of a master thesis. + * Copyright (c) 2018. + */ + +package de.uniko.bks.dom.dmn.header; + +import javax.xml.bind.annotation.XmlAttribute; + +/** Interface specifying which functions a header has to have */ +public abstract class Header { + /** The XML id of the Header */ + String id; + /** The type of the Header */ + String type; + /** The text/value of the Header */ + String text; + + /** + * Getter for the XML id of the Header + * + * @return The XML id of the Header + */ + @XmlAttribute + public String getId() { + return id; + } + + /** + * Setter for the XML id of the Header + * + * @param id The XML id of the Header + */ + public void setId(String id) { + this.id = id; + } + + /** + * Getter for the Header type + * + * @return The type of the Header + */ + @XmlAttribute(name = "typeRef") + public String getType() { + return type; + } + + /** + * Setter for the Header type + * + * @param type The type of the Header + */ + public void setType(String type) { + this.type = type; + } +} diff --git a/dmn-generator/src/main/java/de/uniko/bks/dom/dmn/header/InputHeader.java b/dmn-generator/src/main/java/de/uniko/bks/dom/dmn/header/InputHeader.java new file mode 100644 index 0000000000000000000000000000000000000000..13e989a043890afc56c156b77926a9b48f32d2f2 --- /dev/null +++ b/dmn-generator/src/main/java/de/uniko/bks/dom/dmn/header/InputHeader.java @@ -0,0 +1,46 @@ +/* + * Developed by Hans-Henning Ramberger as part of a master thesis. + * Copyright (c) 2018. + */ + +package de.uniko.bks.dom.dmn.header; + +import javax.xml.bind.annotation.XmlElement; + +/** Class specifying a dmn InputHeader */ +public class InputHeader extends Header { + /** Constructor of an empty InputHeader */ + public InputHeader() {} + + /** + * Constructor for a completely initialized InputHeader + * + * @param id The XML id of the Header + * @param type The type of the Header + * @param text The text/value of the Header + */ + public InputHeader(String id, String type, String text) { + this.id = id; + this.type = type; + this.text = text; + } + + /** + * Getter for the Header text/value + * + * @return The text/value of the Header + */ + @XmlElement + public String getText() { + return text; + } + + /** + * Setter for the Header text/value + * + * @param text The text/value of the Header + */ + public void setText(String text) { + this.text = text; + } +} diff --git a/dmn-generator/src/main/java/de/uniko/bks/dom/dmn/header/OutputHeader.java b/dmn-generator/src/main/java/de/uniko/bks/dom/dmn/header/OutputHeader.java new file mode 100644 index 0000000000000000000000000000000000000000..a146522427ebce4c75afbfd87e57dd59cdc52a07 --- /dev/null +++ b/dmn-generator/src/main/java/de/uniko/bks/dom/dmn/header/OutputHeader.java @@ -0,0 +1,56 @@ +/* + * Developed by Hans-Henning Ramberger as part of a master thesis. + * Copyright (c) 2018. + */ + +package de.uniko.bks.dom.dmn.header; + +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlElement; + +/** Class specifying a dmn OutputHeader */ +public class OutputHeader extends Header { + /** The outputValues (priority of the output elements/output order) */ + private OutputValues outputValues; + + /** Constructor of an empty OutputHeader */ + public OutputHeader() {} + + /** + * Getter for the Header name attribute + * + * @return The name attribute of the Header + */ + @XmlAttribute(name = "name") + public String getText() { + return text; + } + + /** + * Setter for the Header name attribute + * + * @param text The name attribute of the Header + */ + public void setText(String text) { + this.text = text; + } + + /** + * Getter for the outputValues (priority of the output elements/output order) + * + * @return The outputValues of the dmn table header + */ + @XmlElement(name = "outputValues") + public OutputValues getOutputValues() { + return outputValues; + } + + /** + * Setter for the outputValues (priority of the output elements/output order) + * + * @param outputValues The outputValues of the dmn table header + */ + public void setOutputValues(OutputValues outputValues) { + this.outputValues = outputValues; + } +} diff --git a/dmn-generator/src/main/java/de/uniko/bks/dom/dmn/header/OutputValues.java b/dmn-generator/src/main/java/de/uniko/bks/dom/dmn/header/OutputValues.java new file mode 100644 index 0000000000000000000000000000000000000000..060084045b1cb44ced16a07601acb87719bd69d5 --- /dev/null +++ b/dmn-generator/src/main/java/de/uniko/bks/dom/dmn/header/OutputValues.java @@ -0,0 +1,70 @@ +/* + * Developed by Hans-Henning Ramberger as part of a master thesis. + * Copyright (c) 2018. + */ + +package de.uniko.bks.dom.dmn.header; + +import de.uniko.bks.util.StringSanitizer; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlElement; + +/** Class specifying the OutputValues of a dmn table header */ +public class OutputValues { + /** The XML id of the OutputValues */ + private String id; + /** The text/value of the OutputValues as comma separated list */ + private String text; + + /** Constructor for empty OutputValues */ + public OutputValues() {} + + /** + * Constructor for completely initialized OutputValues + * + * @param id The XML id of the OutputValues + * @param text The text/value of the OutputValues as comma separated list + */ + public OutputValues(String id, String text) { + this.id = id; + this.text = text; + } + + /** + * Getter for the XML id of the OutputValues + * + * @return The XML id of the OutputValues + */ + @XmlAttribute + public String getId() { + return id; + } + + /** + * Setter for the XML id of the OutputValues + * + * @param id The XML id of the OutputValues + */ + public void setId(String id) { + this.id = id; + } + + /** + * Getter for the text/value of the OutputValues + * + * @return The text/value of the OutputValues + */ + @XmlElement + public String getText() { + return text; + } + + /** + * Setter for the text/value of the OutputValues + * + * @param text The text/value of the OutputValues + */ + public void setText(String text) { + this.text = StringSanitizer.sanitize(text, false, true); + } +} diff --git a/dmn-generator/src/main/java/de/uniko/bks/generator/DmnGenerator.java b/dmn-generator/src/main/java/de/uniko/bks/generator/DmnGenerator.java new file mode 100644 index 0000000000000000000000000000000000000000..9e38dc75b319053470089342f42877f25e9689e9 --- /dev/null +++ b/dmn-generator/src/main/java/de/uniko/bks/generator/DmnGenerator.java @@ -0,0 +1,121 @@ +/* + * Developed by Hans-Henning Ramberger as part of a master thesis. + * Copyright (c) 2018. + */ + +package de.uniko.bks.generator; + +import de.uniko.bks.dom.dmn.Decision; +import de.uniko.bks.dom.dmn.DecisionTable; +import de.uniko.bks.dom.dmn.DmnRule; +import de.uniko.bks.dom.dmn.entry.InputEntry; +import de.uniko.bks.generator.entry.InputEntryGenerator; +import de.uniko.bks.generator.entry.OutputEntryGenerator; +import de.uniko.bks.generator.entry.OutputEntryList; +import de.uniko.bks.generator.header.InputHeaderGenerator; +import de.uniko.bks.generator.header.OutputHeaderGenerator; +import de.uniko.bks.xml.FileHandler; +import de.uniko.bks.xml.XmlMarshaller; +import java.io.IOException; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.UUID; +import javax.xml.bind.JAXBException; + +/** Main class of the generator which supervises the DMN generation */ +public class DmnGenerator { + /** Identifier suffix for all ids in the DMN decision */ + private static String identifier = UUID.randomUUID().toString().substring(0, 4); + + /** + * Function which generates a DMN rule list + * + * @param columnCount Number of columns in the DMN table + * @param rowCount Number of rows in the DMN table + * @param outputEntryList List of output entries used in the dmn file (note the negations are + * created automatically) + * @return The DMN rule list + */ + private static ArrayList<DmnRule> generateDmnRuleList( + int columnCount, int rowCount, OutputEntryList outputEntryList) { + ArrayList<DmnRule> dmnRuleList = new ArrayList<>(); + // Generate a rule for each row + for (int i = 0; i < rowCount; i++) { + DmnRule dmnRule = new DmnRule(); + dmnRule.setId("dmnRule_" + identifier + "_" + i); + // Generate literals for a rule + ArrayList<InputEntry> inputEntryList = new ArrayList<>(); + for (int j = 0; j < columnCount; j++) { + inputEntryList.add(InputEntryGenerator.generateInputEntry(identifier, j, i)); + } + dmnRule.setInputEntry(inputEntryList); + dmnRule.setOutputEntry( + OutputEntryGenerator.generateOutputEntry(identifier, i, outputEntryList)); + dmnRuleList.add(dmnRule); + } + + return dmnRuleList; + } + + /** + * Function which generates a DMN decsion table + * + * @param columnCount Number of columns in the DMN table + * @param rowCount Number of rows in the DMN table + * @param hitPolicy Hit policy for the DMN table + * @param outputEntryList List of output entries used in the dmn file (note the negations are + * created automatically) + * @return The generated DMN decision table + */ + private static DecisionTable generateDecisionTable( + int columnCount, int rowCount, String hitPolicy, OutputEntryList outputEntryList) { + DecisionTable decisionTable = new DecisionTable(); + decisionTable.setId("decisionTable_" + identifier); + decisionTable.setHitPolicy(hitPolicy); + decisionTable.setInput(InputHeaderGenerator.generateInputList(identifier, columnCount)); + decisionTable.setOutputHeader( + OutputHeaderGenerator.generateOutputHeader(identifier, hitPolicy, outputEntryList)); + decisionTable.setDmnRule(generateDmnRuleList(columnCount, rowCount, outputEntryList)); + return decisionTable; + } + + /** + * Function which generates a DMN file + * + * @param columnCount Number of columns in the DMN table + * @param rowCount Number of rows in the DMN table + * @param hitPolicy Hit policy for the DMN table + * @param outputEntryList List of output entries used in the dmn file (note the negations are + * created automatically) + * @return The path of the newly created file + * @throws JAXBException Root exception for all JAXB exceptions + * @throws IOException Signals that an I/O exception of some sort has occurred + */ + public static Path generateDmnFile( + int columnCount, int rowCount, String hitPolicy, OutputEntryList outputEntryList) + throws JAXBException, IOException { + Decision decision = new Decision(); + decision.setId("decision_" + identifier); + decision.setName("decision_" + identifier); + decision.setDmnTable(generateDecisionTable(columnCount, rowCount, hitPolicy, outputEntryList)); + + // Generate the XML fragment + System.out.println( + "Generating DMN file with " + + columnCount + + " columns, " + + rowCount + + " rows and the " + + hitPolicy + + " hit policy"); + String xmlResult = XmlMarshaller.marshalDmnTable(decision); + // Get the template for an empty DMN model + String dmnTemplate = FileHandler.read("EmptyDmn.xml"); + // Replace the placeholder with the generated content + String fileContent = dmnTemplate.replace("<!--replace-me-->", xmlResult); + // Write the content into a new file + Path filePath = FileHandler.write(identifier, columnCount, rowCount, hitPolicy, fileContent); + System.out.println("Created new DMN file successfully with the path " + filePath.toString()); + return filePath; + } +} diff --git a/dmn-generator/src/main/java/de/uniko/bks/generator/entry/InputEntryGenerator.java b/dmn-generator/src/main/java/de/uniko/bks/generator/entry/InputEntryGenerator.java new file mode 100644 index 0000000000000000000000000000000000000000..ecc1b7159995d24b52b612db7593003facb59b47 --- /dev/null +++ b/dmn-generator/src/main/java/de/uniko/bks/generator/entry/InputEntryGenerator.java @@ -0,0 +1,47 @@ +/* + * Developed by Hans-Henning Ramberger as part of a master thesis. + * Copyright (c) 2018. + */ + +package de.uniko.bks.generator.entry; + +import de.uniko.bks.dom.dmn.entry.InputEntry; +import java.util.Random; + +/** Class which generates a single input entry for the DMN */ +public class InputEntryGenerator { + + private static String generateInputRange() { + String out = ""; + if (new Random().nextInt(1000) < 10) { + out += new Random().nextBoolean() ? "<" : "<="; + out += new Random().nextInt(1500) - 1000; + } else if (new Random().nextInt(1000) < 10) { + out += new Random().nextBoolean() ? ">" : "="; + out += new Random().nextInt(1500) + 50; + } else { + // Value between 0 and 15 + int lowerLimit = new Random().nextInt(2000) - 1500; + // Value between 15 and 45 + int upperLimit = lowerLimit + new Random().nextInt(1000) + 20; + + out += new Random().nextBoolean() ? "[" : "]"; + out += lowerLimit + ".." + upperLimit; + out += new Random().nextBoolean() ? "[" : "]"; + } + return out; + } + + /** + * Function which generates an integer input entry range + * + * @param identifier Identifier suffix for all ids in the DMN decision + * @param columnCount Number of the current column which is created + * @param rowCount Number of the current row which is created + * @return The generated input entry + */ + public static InputEntry generateInputEntry(String identifier, int columnCount, int rowCount) { + return new InputEntry( + "inputEntry_" + identifier + "_" + rowCount + "_" + columnCount, generateInputRange()); + } +} diff --git a/dmn-generator/src/main/java/de/uniko/bks/generator/entry/OutputEntryGenerator.java b/dmn-generator/src/main/java/de/uniko/bks/generator/entry/OutputEntryGenerator.java new file mode 100644 index 0000000000000000000000000000000000000000..36d32d44c13550024737f46baf60a6175c503ea6 --- /dev/null +++ b/dmn-generator/src/main/java/de/uniko/bks/generator/entry/OutputEntryGenerator.java @@ -0,0 +1,46 @@ +/* + * Developed by Hans-Henning Ramberger as part of a master thesis. + * Copyright (c) 2018. + */ + +package de.uniko.bks.generator.entry; + +import de.uniko.bks.dom.dmn.entry.OutputEntry; +import java.util.Random; + +/** Class which generates a single output entry for the DMN */ +public class OutputEntryGenerator { + /** + * Function which negates a given value + * + * @param outputEntry Value which should be negated + * @return THe negated value + */ + private static String negateOutputEntry(String outputEntry) { + return "not(" + outputEntry + ")"; + } + + /** + * Function which generates a random output entry using the items from the OutputEntryList + * + * @param identifier Identifier suffix for all ids in the DMN decision + * @param rowCount Number of the current row which is created + * @param outputEntryList List of output entries used in the dmn file (note the negations are + * created automatically) + * @return A random output entry + */ + public static OutputEntry generateOutputEntry( + String identifier, int rowCount, OutputEntryList outputEntryList) { + OutputEntry outputEntry = new OutputEntry(); + outputEntry.setId("outputEntry_" + identifier + "_" + rowCount); + String outputEntryValue = outputEntryList.getRandomValue(); + // Check if the value should be negated or not + if (new Random().nextBoolean()) { + // outputEntryValue = negateOutputEntry(outputEntryValue); + } + + // Set the value of the output entry + outputEntry.setText(outputEntryValue); + return outputEntry; + } +} diff --git a/dmn-generator/src/main/java/de/uniko/bks/generator/entry/OutputEntryList.java b/dmn-generator/src/main/java/de/uniko/bks/generator/entry/OutputEntryList.java new file mode 100644 index 0000000000000000000000000000000000000000..cd685ea516a6e1a3b0b2433706f6d0a034126409 --- /dev/null +++ b/dmn-generator/src/main/java/de/uniko/bks/generator/entry/OutputEntryList.java @@ -0,0 +1,35 @@ +/* + * Developed by Hans-Henning Ramberger as part of a master thesis. + * Copyright (c) 2018. + */ + +package de.uniko.bks.generator.entry; + +import java.util.Random; + +public class OutputEntryList { + /** The array containing the output values */ + private String[] values = {"a", "b", "c", "d", "e", "f", "g", "h"}; + + public String[] getValues() { + return this.values; + } + + public void setValues(String[] values) { + // Check if the given outputEntryList is empty + if (values != null && values.length > 0) { + // Set the list from the parameter + this.values = values; + } + } + + /** + * Function which gets one random item of the values array + * + * @return The random array entry + */ + String getRandomValue() { + int randomIndex = new Random().nextInt(this.values.length); + return this.values[randomIndex]; + } +} diff --git a/dmn-generator/src/main/java/de/uniko/bks/generator/header/InputHeaderGenerator.java b/dmn-generator/src/main/java/de/uniko/bks/generator/header/InputHeaderGenerator.java new file mode 100644 index 0000000000000000000000000000000000000000..7ead4340e33dde93b65b9185204263de1ff9783e --- /dev/null +++ b/dmn-generator/src/main/java/de/uniko/bks/generator/header/InputHeaderGenerator.java @@ -0,0 +1,41 @@ +/* + * Developed by Hans-Henning Ramberger as part of a master thesis. + * Copyright (c) 2018. + */ + +package de.uniko.bks.generator.header; + +import de.uniko.bks.dom.dfl.rule.RuleType; +import de.uniko.bks.dom.dmn.Input; +import de.uniko.bks.dom.dmn.header.InputHeader; +import java.util.ArrayList; + +/** Class which generates a list of input headers for the DMN */ +public class InputHeaderGenerator { + /** + * Function which generates the input list (list of input headers) + * + * @param identifier Identifier suffix for all ids in the DMN decision + * @param columnCount Number of columns in the DMN table + * @return The generated input list + */ + public static ArrayList<Input> generateInputList(String identifier, int columnCount) { + ArrayList<Input> inputList = new ArrayList<>(); + // Generate an integer input header for each column + for (int i = 0; i < columnCount; i++) { + Input input = new Input(); + input.setId("input_" + identifier + "_" + i); + input.setLabel("input_" + identifier + "_" + i); + // Create a new input header + InputHeader inputHeader = + new InputHeader( + "inputHeader_" + identifier + "_" + i, + RuleType.INT.getValue(), + "inputHeader_" + identifier + "_" + i); + input.setInputHeader(inputHeader); + inputList.add(input); + } + + return inputList; + } +} diff --git a/dmn-generator/src/main/java/de/uniko/bks/generator/header/OutputHeaderGenerator.java b/dmn-generator/src/main/java/de/uniko/bks/generator/header/OutputHeaderGenerator.java new file mode 100644 index 0000000000000000000000000000000000000000..11ce7a3395b868c172e2a5a9fe9be20ff2d81470 --- /dev/null +++ b/dmn-generator/src/main/java/de/uniko/bks/generator/header/OutputHeaderGenerator.java @@ -0,0 +1,48 @@ +/* + * Developed by Hans-Henning Ramberger as part of a master thesis. + * Copyright (c) 2018. + */ + +package de.uniko.bks.generator.header; + +import de.uniko.bks.dom.dfl.rule.RuleType; +import de.uniko.bks.dom.dmn.HitPolicy; +import de.uniko.bks.dom.dmn.header.OutputHeader; +import de.uniko.bks.dom.dmn.header.OutputValues; +import de.uniko.bks.generator.entry.OutputEntryList; + +/** Class which generates a single output header for the DMN */ +public class OutputHeaderGenerator { + /** + * Function which generates a DMN output header + * + * @param identifier Identifier suffix for all ids in the DMN decision + * @param hitPolicy The HitPolicy of the table + * @param outputEntryList List of output entries used in the dmn file (note the negations are + * created automatically) + * @return The DMN output header + */ + public static OutputHeader generateOutputHeader( + String identifier, String hitPolicy, OutputEntryList outputEntryList) { + OutputHeader outputHeader = new OutputHeader(); + outputHeader.setId("output_" + identifier); + outputHeader.setType(RuleType.STRING.getValue()); + outputHeader.setText("output_" + identifier); + // Set the rule order if it is needed + if (hitPolicy.equals(HitPolicy.PRIORITY.getValue()) + || hitPolicy.equals(HitPolicy.OUTPUT_ORDER.getValue())) { + StringBuilder stringBuilder = new StringBuilder(); + for (int i = 0; i < outputEntryList.getValues().length; i++) { + stringBuilder.append("\""); + stringBuilder.append(outputEntryList.getValues()[i]); + stringBuilder.append("\""); + // Check if it is the last item + if (i < outputEntryList.getValues().length - 1) { + stringBuilder.append(","); + } + } + outputHeader.setOutputValues(new OutputValues(identifier, stringBuilder.toString())); + } + return outputHeader; + } +} diff --git a/dmn-generator/src/main/java/de/uniko/bks/statistic/DmnDate.java b/dmn-generator/src/main/java/de/uniko/bks/statistic/DmnDate.java new file mode 100644 index 0000000000000000000000000000000000000000..18068b8e56f8679e147bddf7f4d3b4560c4640e5 --- /dev/null +++ b/dmn-generator/src/main/java/de/uniko/bks/statistic/DmnDate.java @@ -0,0 +1,161 @@ +package de.uniko.bks.statistic; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; + +class DmnDate { + + private Date date; + private Date endDate; + private boolean isLowerThan = false; + private boolean isGreaterThan = false; + private boolean itDoesntMatter = false; + + boolean isItDoesntMatter() { + return itDoesntMatter; + } + + private Date getDate() { + return date; + } + + private Date getEndDate() { + return endDate; + } + + private boolean isLowerThan() { + return isLowerThan; + } + + private boolean isGreaterThan() { + return isGreaterThan; + } + + private boolean isRange() { + return isRange; + } + + private boolean isRange = false; + + DmnDate(String dateString) { + parseString(dateString); + } + + private void parseString(String dateString) { + if (dateString.length() == 0) { + this.itDoesntMatter = true; + } else { + String first = dateString.substring(0, 1); + if (first.contains("[")) { + isRange = true; + dateString = dateString.replace("..", "#"); + String modDateString = dateString; + modDateString = modDateString.replace("[", ""); + modDateString = modDateString.replace("]", ""); + String[] dates = modDateString.split("#"); + this.date = parseStringToDate(dates[0]); + this.endDate = parseStringToDate(dates[1]); + } else { + if (dateString.contains("<") || dateString.contains("<")) { + dateString = dateString.replace("<", ""); + dateString = dateString.replace("<", ""); + this.isLowerThan = true; + } + if (dateString.contains(">") || dateString.contains(">")) { + dateString = dateString.replace(">", ""); + dateString = dateString.replace(">", ""); + this.isGreaterThan = true; + } + this.date = parseStringToDate(dateString); + this.endDate = this.date; + } + } + } + + private Date parseStringToDate(String dateString) { + SimpleDateFormat parser = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); + dateString = dateString.replace("date and time(\"", ""); + dateString = dateString.replace("\")", ""); + dateString = dateString.replace(" ", ""); + Date date1; + try { + date1 = parser.parse(dateString); + return date1; + } catch (ParseException e) { + date1 = new Date(); + e.printStackTrace(); + return date1; + } + } + + boolean isOverlapping(DmnDate dateToCheck) { + if (this.itDoesntMatter || dateToCheck.isItDoesntMatter()) { + return true; + } + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); + if (this.isRange) { + if (dateToCheck.isRange) { + if ((this.date.after(dateToCheck.getDate()) && this.date.before(dateToCheck.getEndDate())) + || (this.endDate.after(dateToCheck.getDate()) + && this.endDate.before(dateToCheck.getEndDate())) + || (dateToCheck.getDate().after(this.date) + && dateToCheck.getDate().before(this.endDate)) + || (dateToCheck.getEndDate().after(this.date) + && dateToCheck.getEndDate().before(this.endDate))) { + return true; + } + } + if (dateToCheck.isLowerThan()) { + if (this.date.before(dateToCheck.getDate())) { + return true; + } + } else if (dateToCheck.isGreaterThan()) { + if (this.endDate.after(dateToCheck.getDate())) { + return true; + } + } + if (dateToCheck.getDate().after(this.date) && dateToCheck.getDate().before(this.endDate)) { + return true; + } + } else if (this.isLowerThan) { + if (dateToCheck.isRange()) { + if (dateToCheck.date.before(this.date)) { + return true; + } + } + if (dateToCheck.isLowerThan()) { + if ((this.date.before(dateToCheck.getDate())) + || (dateToCheck.getDate().before(this.date))) { + return true; + } + } else if (dateToCheck.isGreaterThan()) { + if ((this.date.after(dateToCheck.getDate())) || (dateToCheck.getDate().before(this.date))) { + return true; + } + } + if (dateToCheck.getDate().before(this.date)) { + return true; + } + } else if (this.isGreaterThan()) { + if (dateToCheck.isRange()) { + if (dateToCheck.endDate.after(this.date)) { + return true; + } + } + if (dateToCheck.isLowerThan()) { + if ((this.date.before(dateToCheck.getDate())) || (dateToCheck.getDate().after(this.date))) { + return true; + } + } else if (dateToCheck.isGreaterThan()) { + if ((this.date.after(dateToCheck.getDate())) || (dateToCheck.getDate().after(this.date))) { + return true; + } + } + if (dateToCheck.getDate().after(this.date)) { + return true; + } + } + return format.format(this.date).equals(format.format(dateToCheck.getDate())); + } +} diff --git a/dmn-generator/src/main/java/de/uniko/bks/statistic/DmnFile.java b/dmn-generator/src/main/java/de/uniko/bks/statistic/DmnFile.java new file mode 100644 index 0000000000000000000000000000000000000000..356810368d5cc8065d7706a3d08fd9896dfe8f22 --- /dev/null +++ b/dmn-generator/src/main/java/de/uniko/bks/statistic/DmnFile.java @@ -0,0 +1,43 @@ +/* + * Developed by Hans-Henning Ramberger as part of a master thesis. + * Copyright (c) 2018. + */ + +package de.uniko.bks.statistic; + +import java.nio.file.Path; + +/** Class wrapping meta information about a generated DMN file */ +public class DmnFile { + /** HitPolicy of the DMN file */ + private String hitPolicy; + /** Path where the file is located */ + private Path path; + /** Number of columns in the DMN table */ + private int columnCount; + /** Number of the rows in the DMN table */ + private int rowCount; + + public DmnFile(String hitPolicy, Path path, int columnCount, int rowCount) { + this.hitPolicy = hitPolicy; + this.path = path; + this.columnCount = columnCount; + this.rowCount = rowCount; + } + + public String getHitPolicy() { + return hitPolicy; + } + + public Path getPath() { + return path; + } + + public int getColumnCount() { + return columnCount; + } + + public int getRowCount() { + return rowCount; + } +} diff --git a/dmn-generator/src/main/java/de/uniko/bks/statistic/NumberLine.java b/dmn-generator/src/main/java/de/uniko/bks/statistic/NumberLine.java new file mode 100644 index 0000000000000000000000000000000000000000..347f12ec867d474800152af7a2b70363f8014a34 --- /dev/null +++ b/dmn-generator/src/main/java/de/uniko/bks/statistic/NumberLine.java @@ -0,0 +1,191 @@ +package de.uniko.bks.statistic; + +import com.google.common.collect.Multimap; +import com.google.common.collect.MultimapBuilder; +import java.util.*; + +public class NumberLine { + private ArrayList<Map<Integer, Set<dmnInt>>> line = new ArrayList<>(); + private Set<Integer> isInLine = new HashSet<>(); + private Set<dmnInt> beginning = new HashSet<>(); + private Set<dmnInt> end = new HashSet<>(); + private Set<dmnInt> all = new HashSet<>(); + private Map<dmnInt, Set<dmnInt>> numberOverlaps = new HashMap<>(); + private boolean somethingChanged = false; + private boolean overlapsAreComputed = false; + private Set<dmnInt> forSureOverlaps = new HashSet<>(); + + private Map<dmnInt, String> mapping = new HashMap<>(); + + public ArrayList<Map<Integer, Set<dmnInt>>> getLine() { + return line; + } + + public Set<dmnInt> computeOverlapping(dmnInt number, Set<dmnInt> setToCompute) { + HashSet<dmnInt> result = new HashSet<>(); + for (dmnInt numberToCheck : setToCompute) { + if (number.compare(numberToCheck)) { + result.add(numberToCheck); + } + } + return result; + } + + public Set<Set<String>> getRuleOverlaps() { + Set<Set<String>> result = new HashSet<>(); + if (somethingChanged || !overlapsAreComputed) { + numberOverlaps = getOverlappingNumbers(); + } + for (dmnInt number : numberOverlaps.keySet()) { + String numberRule = mapping.get(number); + for (dmnInt overlapNumber : numberOverlaps.get(number)) { + HashSet<String> overlap = new HashSet<>(); + overlap.add(numberRule); + overlap.add(mapping.get(overlapNumber)); + result.add(overlap); + } + } + return result; + } + + public Set<Set<String>> getRuleOverlaps(Set<Set<String>> currentIncons) { + Set<Set<String>> result = getRuleOverlaps(); + result.retainAll(currentIncons); + return result; + } + + public Map<dmnInt, Set<dmnInt>> getOverlappingNumbers() { + HashSet<dmnInt> allBefore = new HashSet<>(); + HashSet<dmnInt> allAfter = new HashSet<>(all); + Multimap<dmnInt, dmnInt> numbersToProcess = MultimapBuilder.hashKeys().hashSetValues().build(); + Map<dmnInt, Set<dmnInt>> result = new HashMap<>(); + + Iterator<Map<Integer, Set<dmnInt>>> iter = line.iterator(); + while (iter.hasNext()) { + Map<Integer, Set<dmnInt>> currentmap = iter.next(); + if (currentmap.size() != 1) { + System.out.println("something went wrong"); + continue; + } + allAfter.removeAll(currentmap.get(currentmap.keySet().iterator().next())); + for (dmnInt currentNumber : currentmap.get(currentmap.keySet().iterator().next())) { + + if (currentNumber.isDoesntMatter()) { + HashSet<dmnInt> allMinusItself = new HashSet<>(all); + allMinusItself.remove(currentNumber); + result.put(currentNumber, allMinusItself); + continue; + } + HashSet<dmnInt> forCurrentNumber = new HashSet<>(); + forCurrentNumber.addAll( + computeOverlapping( + currentNumber, currentmap.get(currentmap.keySet().iterator().next()))); + forCurrentNumber.addAll(forSureOverlaps); + if (currentNumber.isRange()) { + if ((numbersToProcess).containsKey(currentNumber)) { + forCurrentNumber.addAll(numbersToProcess.get(currentNumber)); + numbersToProcess.removeAll(currentmap); + } else { + computeOverlapping(currentNumber, currentmap.get(currentmap.keySet().iterator().next())) + .forEach(number -> numbersToProcess.put(currentNumber, number)); + } + } + if (currentNumber.isLower()) { + forCurrentNumber.addAll(allBefore); + forCurrentNumber.addAll(beginning); + } + if (currentNumber.isGreater()) { + forCurrentNumber.addAll(allAfter); + forCurrentNumber.addAll(end); + } + if (!forCurrentNumber.isEmpty()) { + if (forCurrentNumber.contains(currentNumber)) { + forCurrentNumber.remove(currentNumber); + } + result.put(currentNumber, forCurrentNumber); + } + } + allBefore.addAll(currentmap.get(currentmap.keySet().iterator().next())); + numbersToProcess + .keySet() + .forEach( + key -> + numbersToProcess.putAll( + key, currentmap.get(currentmap.keySet().iterator().next()))); + } + numberOverlaps = result; + somethingChanged = false; + overlapsAreComputed = true; + return result; + } + + public void addToLine(dmnInt number, String ruleName) { + somethingChanged = true; + mapping.put(number, ruleName); + all.add(number); + number.setRuleName(ruleName); + if (number.isDoesntMatter()) { + forSureOverlaps.add(number); + return; + } + + if (number.isRange()) { + addToLine(number.getLowerBoundInt(), number); + addToLine(number.getUpperBoundInt(), number); + return; + } + if (number.isLower()) { + beginning.add(number); + } + if (number.isGreater()) { + end.add(number); + } + addToLine(number.getNumberInt(), number); + } + + private void addToLine(Integer number, dmnInt num) { + if (line.isEmpty()) { + Set<dmnInt> currentSet = new HashSet<>(); + currentSet.add(num); + Map<Integer, Set<dmnInt>> newMap = new HashMap<>(); + newMap.put(number, currentSet); + line.add(newMap); + } + ArrayList<Map<Integer, Set<dmnInt>>> newLine = new ArrayList<>(line); + Iterator<Map<Integer, Set<dmnInt>>> iter = newLine.iterator(); + + while (iter.hasNext()) { + Map<Integer, Set<dmnInt>> currentmap = iter.next(); + if (currentmap.size() != 1) { + System.out.println("something went wrong"); + continue; + } + int currentNumber = currentmap.keySet().iterator().next(); + if (number < currentNumber) { + Set<dmnInt> currentSet = new HashSet<>(); + currentSet.add(num); + Map<Integer, Set<dmnInt>> newMap = new HashMap<>(); + newMap.put(number, currentSet); + int index = line.indexOf(currentmap); + line.add(index, newMap); + } else if (number == currentNumber) { + Set<dmnInt> currentSet = currentmap.get(number); + currentSet.add(num); + Map<Integer, Set<dmnInt>> newMap = new HashMap<>(); + newMap.put(number, currentSet); + int index = line.indexOf(currentmap); + line.remove(currentmap); + line.add(index, newMap); + break; + } else if ((number > currentNumber) && iter.hasNext()) { + continue; + } else if (!iter.hasNext()) { + Set<dmnInt> currentSet = new HashSet<>(); + currentSet.add(num); + Map<Integer, Set<dmnInt>> newMap = new HashMap<>(); + newMap.put(number, currentSet); + line.add(newMap); + } + } + } +} diff --git a/dmn-generator/src/main/java/de/uniko/bks/statistic/NumberLineDouble.java b/dmn-generator/src/main/java/de/uniko/bks/statistic/NumberLineDouble.java new file mode 100644 index 0000000000000000000000000000000000000000..92eeffc29630a3a49e1b01578a1e4b039f3cf749 --- /dev/null +++ b/dmn-generator/src/main/java/de/uniko/bks/statistic/NumberLineDouble.java @@ -0,0 +1,185 @@ +package de.uniko.bks.statistic; + +import com.google.common.collect.Multimap; +import com.google.common.collect.MultimapBuilder; +import java.util.*; + +public class NumberLineDouble { + private ArrayList<Map<Double, Set<dmnInt>>> line = new ArrayList<>(); + private Set<Double> isInLine = new HashSet<>(); + private Set<dmnInt> beginning = new HashSet<>(); + private Set<dmnInt> end = new HashSet<>(); + private Set<dmnInt> all = new HashSet<>(); + private Map<dmnInt, Set<dmnInt>> numberOverlaps = new HashMap<>(); + private boolean somethingChanged = false; + private boolean overlapsAreComputed = false; + private Set<dmnInt> forSureOverlaps = new HashSet<>(); + + private Map<dmnInt, String> mapping = new HashMap<>(); + + public ArrayList<Map<Double, Set<dmnInt>>> getLine() { + return line; + } + + public Set<dmnInt> computeOverlapping(dmnInt number, Set<dmnInt> setToCompute) { + HashSet<dmnInt> result = new HashSet<>(); + for (dmnInt numberToCheck : setToCompute) { + if (number.compare(numberToCheck)) { + result.add(numberToCheck); + } + } + return result; + } + + public Set<Set<String>> getRuleOverlaps() { + Set<Set<String>> result = new HashSet<>(); + if (somethingChanged || !overlapsAreComputed) { + numberOverlaps = getOverlappingNumbers(); + } + for (dmnInt number : numberOverlaps.keySet()) { + String numberRule = mapping.get(number); + for (dmnInt overlapNumber : numberOverlaps.get(number)) { + HashSet<String> overlap = new HashSet<>(); + overlap.add(numberRule); + overlap.add(mapping.get(overlapNumber)); + result.add(overlap); + } + } + return result; + } + + public Map<dmnInt, Set<dmnInt>> getOverlappingNumbers() { + HashSet<dmnInt> allBefore = new HashSet<>(); + HashSet<dmnInt> allAfter = new HashSet<>(all); + Multimap<dmnInt, dmnInt> numbersToProcess = MultimapBuilder.hashKeys().hashSetValues().build(); + Map<dmnInt, Set<dmnInt>> result = new HashMap<>(); + + Iterator<Map<Double, Set<dmnInt>>> iter = line.iterator(); + while (iter.hasNext()) { + Map<Double, Set<dmnInt>> currentmap = iter.next(); + if (currentmap.size() != 1) { + System.out.println("something went wrong"); + continue; + } + allAfter.removeAll(currentmap.get(currentmap.keySet().iterator().next())); + for (dmnInt currentNumber : currentmap.get(currentmap.keySet().iterator().next())) { + + if (currentNumber.isDoesntMatter()) { + HashSet<dmnInt> allMinusItself = new HashSet<>(all); + allMinusItself.remove(currentNumber); + result.put(currentNumber, allMinusItself); + continue; + } + HashSet<dmnInt> forCurrentNumber = new HashSet<>(); + forCurrentNumber.addAll( + computeOverlapping( + currentNumber, currentmap.get(currentmap.keySet().iterator().next()))); + forCurrentNumber.addAll(forSureOverlaps); + if (currentNumber.isRange()) { + if ((numbersToProcess).containsKey(currentNumber)) { + forCurrentNumber.addAll(numbersToProcess.get(currentNumber)); + numbersToProcess.removeAll(currentmap); + } else { + computeOverlapping(currentNumber, currentmap.get(currentmap.keySet().iterator().next())) + .forEach(number -> numbersToProcess.put(currentNumber, number)); + } + } + if (currentNumber.isLower()) { + forCurrentNumber.addAll(allBefore); + forCurrentNumber.addAll(beginning); + } + if (currentNumber.isGreater()) { + forCurrentNumber.addAll(allAfter); + forCurrentNumber.addAll(end); + } + if (!forCurrentNumber.isEmpty()) { + if (forCurrentNumber.contains(currentNumber)) { + forCurrentNumber.remove(currentNumber); + } + result.put(currentNumber, forCurrentNumber); + } + } + allBefore.addAll(currentmap.get(currentmap.keySet().iterator().next())); + numbersToProcess + .keySet() + .forEach( + key -> + numbersToProcess.putAll( + key, currentmap.get(currentmap.keySet().iterator().next()))); + } + numberOverlaps = result; + somethingChanged = false; + overlapsAreComputed = true; + return result; + } + + public void addToLine(dmnInt number, String ruleName) { + somethingChanged = true; + mapping.put(number, ruleName); + all.add(number); + number.setRuleName(ruleName); + if (number.isDoesntMatter()) { + forSureOverlaps.add(number); + return; + } + + if (number.isRange()) { + addToLine(number.getLowerBoundDouble(), number); + addToLine(number.getUpperBoundDouble(), number); + return; + } + if (number.isLower()) { + beginning.add(number); + } + if (number.isGreater()) { + end.add(number); + } + addToLine(number.getNumberDouble(), number); + } + + private void addToLine(Double number, dmnInt num) { + if (line.isEmpty()) { + Set<dmnInt> currentSet = new HashSet<>(); + currentSet.add(num); + Map<Double, Set<dmnInt>> newMap = new HashMap<>(); + newMap.put(number, currentSet); + line.add(newMap); + } + ArrayList<Map<Double, Set<dmnInt>>> newLine = new ArrayList<>(line); + Iterator<Map<Double, Set<dmnInt>>> iter = newLine.iterator(); + + while (iter.hasNext()) { + Map<Double, Set<dmnInt>> currentmap = iter.next(); + if (currentmap.size() != 1) { + System.out.println("something went wrong"); + continue; + } + double currentNumber = currentmap.keySet().iterator().next(); + if (number < currentNumber) { + Set<dmnInt> currentSet = new HashSet<>(); + currentSet.add(num); + Map<Double, Set<dmnInt>> newMap = new HashMap<>(); + newMap.put(number, currentSet); + int index = line.indexOf(currentmap); + line.add(index, newMap); + } else if (number == currentNumber) { + Set<dmnInt> currentSet = currentmap.get(number); + currentSet.add(num); + Map<Double, Set<dmnInt>> newMap = new HashMap<>(); + newMap.put(number, currentSet); + int index = line.indexOf(currentmap); + line.remove(currentmap); + line.add(index, newMap); + break; + } else if ((number > currentNumber) && iter.hasNext()) { + continue; + } else if (!iter.hasNext()) { + Set<dmnInt> currentSet = new HashSet<>(); + currentSet.add(num); + Map<Double, Set<dmnInt>> newMap = new HashMap<>(); + newMap.put(number, currentSet); + line.add(newMap); + } + } + } +} diff --git a/dmn-generator/src/main/java/de/uniko/bks/statistic/NumberLineLong.java b/dmn-generator/src/main/java/de/uniko/bks/statistic/NumberLineLong.java new file mode 100644 index 0000000000000000000000000000000000000000..36863b4434d599ff02c1f708bed93b230098946b --- /dev/null +++ b/dmn-generator/src/main/java/de/uniko/bks/statistic/NumberLineLong.java @@ -0,0 +1,185 @@ +package de.uniko.bks.statistic; + +import com.google.common.collect.Multimap; +import com.google.common.collect.MultimapBuilder; +import java.util.*; + +public class NumberLineLong { + private ArrayList<Map<Long, Set<dmnInt>>> line = new ArrayList<>(); + private Set<Long> isInLine = new HashSet<>(); + private Set<dmnInt> beginning = new HashSet<>(); + private Set<dmnInt> end = new HashSet<>(); + private Set<dmnInt> all = new HashSet<>(); + private Map<dmnInt, Set<dmnInt>> numberOverlaps = new HashMap<>(); + private boolean somethingChanged = false; + private boolean overlapsAreComputed = false; + private Set<dmnInt> forSureOverlaps = new HashSet<>(); + + private Map<dmnInt, String> mapping = new HashMap<>(); + + public ArrayList<Map<Long, Set<dmnInt>>> getLine() { + return line; + } + + public Set<dmnInt> computeOverlapping(dmnInt number, Set<dmnInt> setToCompute) { + HashSet<dmnInt> result = new HashSet<>(); + for (dmnInt numberToCheck : setToCompute) { + if (number.compare(numberToCheck)) { + result.add(numberToCheck); + } + } + return result; + } + + public Set<Set<String>> getRuleOverlaps() { + Set<Set<String>> result = new HashSet<>(); + if (somethingChanged || !overlapsAreComputed) { + numberOverlaps = getOverlappingNumbers(); + } + for (dmnInt number : numberOverlaps.keySet()) { + String numberRule = mapping.get(number); + for (dmnInt overlapNumber : numberOverlaps.get(number)) { + HashSet<String> overlap = new HashSet<>(); + overlap.add(numberRule); + overlap.add(mapping.get(overlapNumber)); + result.add(overlap); + } + } + return result; + } + + public Map<dmnInt, Set<dmnInt>> getOverlappingNumbers() { + HashSet<dmnInt> allBefore = new HashSet<>(); + HashSet<dmnInt> allAfter = new HashSet<>(all); + Multimap<dmnInt, dmnInt> numbersToProcess = MultimapBuilder.hashKeys().hashSetValues().build(); + Map<dmnInt, Set<dmnInt>> result = new HashMap<>(); + + Iterator<Map<Long, Set<dmnInt>>> iter = line.iterator(); + while (iter.hasNext()) { + Map<Long, Set<dmnInt>> currentmap = iter.next(); + if (currentmap.size() != 1) { + System.out.println("something went wrong"); + continue; + } + allAfter.removeAll(currentmap.get(currentmap.keySet().iterator().next())); + for (dmnInt currentNumber : currentmap.get(currentmap.keySet().iterator().next())) { + + if (currentNumber.isDoesntMatter()) { + HashSet<dmnInt> allMinusItself = new HashSet<>(all); + allMinusItself.remove(currentNumber); + result.put(currentNumber, allMinusItself); + continue; + } + HashSet<dmnInt> forCurrentNumber = new HashSet<>(); + forCurrentNumber.addAll( + computeOverlapping( + currentNumber, currentmap.get(currentmap.keySet().iterator().next()))); + forCurrentNumber.addAll(forSureOverlaps); + if (currentNumber.isRange()) { + if ((numbersToProcess).containsKey(currentNumber)) { + forCurrentNumber.addAll(numbersToProcess.get(currentNumber)); + numbersToProcess.removeAll(currentmap); + } else { + computeOverlapping(currentNumber, currentmap.get(currentmap.keySet().iterator().next())) + .forEach(number -> numbersToProcess.put(currentNumber, number)); + } + } + if (currentNumber.isLower()) { + forCurrentNumber.addAll(allBefore); + forCurrentNumber.addAll(beginning); + } + if (currentNumber.isGreater()) { + forCurrentNumber.addAll(allAfter); + forCurrentNumber.addAll(end); + } + if (!forCurrentNumber.isEmpty()) { + if (forCurrentNumber.contains(currentNumber)) { + forCurrentNumber.remove(currentNumber); + } + result.put(currentNumber, forCurrentNumber); + } + } + allBefore.addAll(currentmap.get(currentmap.keySet().iterator().next())); + numbersToProcess + .keySet() + .forEach( + key -> + numbersToProcess.putAll( + key, currentmap.get(currentmap.keySet().iterator().next()))); + } + numberOverlaps = result; + somethingChanged = false; + overlapsAreComputed = true; + return result; + } + + public void addToLine(dmnInt number, String ruleName) { + somethingChanged = true; + mapping.put(number, ruleName); + all.add(number); + number.setRuleName(ruleName); + if (number.isDoesntMatter()) { + forSureOverlaps.add(number); + return; + } + + if (number.isRange()) { + addToLine(number.getLowerBoundLong(), number); + addToLine(number.getUpperBoundLong(), number); + return; + } + if (number.isLower()) { + beginning.add(number); + } + if (number.isGreater()) { + end.add(number); + } + addToLine(number.getNumberLong(), number); + } + + private void addToLine(Long number, dmnInt num) { + if (line.isEmpty()) { + Set<dmnInt> currentSet = new HashSet<>(); + currentSet.add(num); + Map<Long, Set<dmnInt>> newMap = new HashMap<>(); + newMap.put(number, currentSet); + line.add(newMap); + } + ArrayList<Map<Long, Set<dmnInt>>> newLine = new ArrayList<>(line); + Iterator<Map<Long, Set<dmnInt>>> iter = newLine.iterator(); + + while (iter.hasNext()) { + Map<Long, Set<dmnInt>> currentmap = iter.next(); + if (currentmap.size() != 1) { + System.out.println("something went wrong"); + continue; + } + long currentNumber = currentmap.keySet().iterator().next(); + if (number < currentNumber) { + Set<dmnInt> currentSet = new HashSet<>(); + currentSet.add(num); + Map<Long, Set<dmnInt>> newMap = new HashMap<>(); + newMap.put(number, currentSet); + int index = line.indexOf(currentmap); + line.add(index, newMap); + } else if (number == currentNumber) { + Set<dmnInt> currentSet = currentmap.get(number); + currentSet.add(num); + Map<Long, Set<dmnInt>> newMap = new HashMap<>(); + newMap.put(number, currentSet); + int index = line.indexOf(currentmap); + line.remove(currentmap); + line.add(index, newMap); + break; + } else if ((number > currentNumber) && iter.hasNext()) { + continue; + } else if (!iter.hasNext()) { + Set<dmnInt> currentSet = new HashSet<>(); + currentSet.add(num); + Map<Long, Set<dmnInt>> newMap = new HashMap<>(); + newMap.put(number, currentSet); + line.add(newMap); + } + } + } +} diff --git a/dmn-generator/src/main/java/de/uniko/bks/statistic/ParserStatistic.java b/dmn-generator/src/main/java/de/uniko/bks/statistic/ParserStatistic.java new file mode 100644 index 0000000000000000000000000000000000000000..a9071d88db0e3a8277e397706ae8a9e6c43a92d4 --- /dev/null +++ b/dmn-generator/src/main/java/de/uniko/bks/statistic/ParserStatistic.java @@ -0,0 +1,52 @@ +/* + * Developed by Hans-Henning Ramberger as part of a master thesis. + * Copyright (c) 2018. + */ + +package de.uniko.bks.statistic; + +/** Class representing a single statistic item */ +public class ParserStatistic { + private String hitPolicy; + private int columnCount; + private int rowCount; + private double averageRunTime; + + public ParserStatistic(String hitPolicy, int columnCount, int rowCount, double averageRunTime) { + this.hitPolicy = hitPolicy; + this.columnCount = columnCount; + this.rowCount = rowCount; + this.averageRunTime = averageRunTime; + } + + public String getHitPolicy() { + return hitPolicy; + } + + public int getColumnCount() { + return columnCount; + } + + public int getRowCount() { + return rowCount; + } + + public double getAverageRunTime() { + return averageRunTime; + } + + @Override + public String toString() { + return "ParserStatistic{" + + "hitPolicy='" + + hitPolicy + + '\'' + + ", columnCount=" + + columnCount + + ", rowCount=" + + rowCount + + ", averageRunTime=" + + averageRunTime + + '}'; + } +} diff --git a/dmn-generator/src/main/java/de/uniko/bks/statistic/PerformanceTestConfiguration.java b/dmn-generator/src/main/java/de/uniko/bks/statistic/PerformanceTestConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..83faa0be8ee51a0ef640be7151167e13ee10de20 --- /dev/null +++ b/dmn-generator/src/main/java/de/uniko/bks/statistic/PerformanceTestConfiguration.java @@ -0,0 +1,46 @@ +/* + * Developed by Hans-Henning Ramberger as part of a master thesis. + * Copyright (c) 2018. + */ + +package de.uniko.bks.statistic; + +/** Class containing the default configuration for performance tests */ +public class PerformanceTestConfiguration { + /** Amount (square root) of different data points (files) created */ + private static final int NUMBER_OF_DATA_POINTS = 10; + /** Number of runs for each file to calculate the average run-time */ + private static final int NUMBER_OF_TESTS = 3; + /** Whether or not the test files should be deleted after the statistics are generated */ + private static final boolean DELETE_TEST_FILES = true; + /** Difference in rows in each test dmn file */ + private static final int ROW_NUMBER_STEPS = 50; + /** Number of columns the generator starts with */ + private static final int INITIAL_COLUMN_COUNT = 1; + /** Number of rows the generator starts with */ + private static final int INITIAL_ROW_COUNT = 1; + + public static int getNumberOfDataPoints() { + return NUMBER_OF_DATA_POINTS; + } + + public static int getNumberOfTests() { + return NUMBER_OF_TESTS; + } + + public static boolean isDeleteTestFiles() { + return DELETE_TEST_FILES; + } + + public static int getRowNumberSteps() { + return ROW_NUMBER_STEPS; + } + + public static int getInitialColumnCount() { + return INITIAL_COLUMN_COUNT; + } + + public static int getInitialRowCount() { + return INITIAL_ROW_COUNT; + } +} diff --git a/dmn-generator/src/main/java/de/uniko/bks/statistic/PerformanceTester.java b/dmn-generator/src/main/java/de/uniko/bks/statistic/PerformanceTester.java new file mode 100644 index 0000000000000000000000000000000000000000..191776d9689c66e6eb212d8e7b7c802d22ce0643 --- /dev/null +++ b/dmn-generator/src/main/java/de/uniko/bks/statistic/PerformanceTester.java @@ -0,0 +1,221 @@ +/* + * Developed by Hans-Henning Ramberger as part of a master thesis. + * Copyright (c) 2018. + */ + +package de.uniko.bks.statistic; + +import de.uniko.bks.cli.CliArgument; +import de.uniko.bks.generator.DmnGenerator; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import javax.xml.bind.JAXBException; + +/** Class which processes automatic performance tests of the converter */ +public class PerformanceTester { + + private static CliArgument cliArgument; + + /** + * Function which calculates the average run-time of the conversion for one file + * + * @param runTimeList List of run-times for one test run + * @return The average run-time for the conversion of one file in milliseconds + */ + private static double calculateAverageRunTime(ArrayList<Long> runTimeList) { + double totalRunTime = 0; + // Add upt the run-time from each run + for (Long runTime : runTimeList) { + totalRunTime += runTime; + } + // Calculate the average of the runtime + return (totalRunTime / cliArgument.getNumberOfTestRuns()); + } + + /** + * Function which runs the performance tests + * + * @param dmnFileList List of DMN files to process + * @return The average run-time of a conversion of one file + * @throws IOException Signals that an I/O exception of some sort has occurred + */ + private static ArrayList<ArrayList<ParserStatistic>> runPerformanceTests( + ArrayList<DmnFile> dmnFileList) throws IOException { + ArrayList<ArrayList<ParserStatistic>> parserStatisticList = new ArrayList<>(); + + ArrayList<ParserStatistic> statistics = new ArrayList<>(); + ArrayList<ParserStatistic> verifierStatistics = new ArrayList<>(); + int size = dmnFileList.size() * cliArgument.getNumberOfTestRuns(); + int j = 1; + for (DmnFile dmnFile : dmnFileList) { + ArrayList<Long> runTimeList = new ArrayList<>(); + ArrayList<Long> verificationsFound = new ArrayList<>(); + for (int i = 0; i < cliArgument.getNumberOfTestRuns(); i++) { + TestDMN testDMN = new TestDMN(); + // Add the run-time of the current execution to the list of run-times for the file + testDMN.testDmn(dmnFile.getPath().toString()); + verificationsFound.add( + testDMN + .getVerifcation() + .getVerifierResults() + .stream() + .map(verifierResult -> verifierResult.getVerifications().size()) + .mapToLong(Long::valueOf) + .sum()); + runTimeList.add(testDMN.getExecTime()); + System.out.println( + "DMN " + j + " von " + size + " verified in " + testDMN.getExecTime() + "ms"); + j++; + } + statistics.add( + new ParserStatistic( + dmnFile.getHitPolicy(), + dmnFile.getColumnCount(), + dmnFile.getRowCount(), + // Calculate the average run-time for the current file + calculateAverageRunTime(runTimeList))); + verifierStatistics.add( + new ParserStatistic( + "Count verifications found", + dmnFile.getColumnCount(), + dmnFile.getRowCount(), + // Calculate the average run-time for the current file + calculateAverageRunTime(verificationsFound))); + } + parserStatisticList.add(statistics); + parserStatisticList.add(verifierStatistics); + return parserStatisticList; + } + + private static String toTikz(ArrayList<ParserStatistic> parserStatisticList) { + String newline = System.getProperty("line.separator"); + StringBuilder tikzCode = new StringBuilder(); + tikzCode.append("\\begin{figure}[h!]").append(newline); + tikzCode.append(" \\centering").append(newline); + tikzCode.append(" \\begin{tikzpicture}").append(newline); + tikzCode + .append(" \\begin{axis}") + .append( + "[xlabel=Number of columns, ylabel=Number of rows, zlabel=Run-time in milliseconds, xlabel style={sloped like x axis}, ylabel style={sloped}, view/az=45, zmin=0, grid=major]") + .append(newline); + tikzCode.append(" \\addplot3[surf, shader=faceted]").append(newline); + tikzCode.append(" coordinates {").append(newline); + + // Add the coordinates (x=columnCount, y=rowCount, z=averageRunTime) + int i = 1; + for (ParserStatistic parserStatistic : parserStatisticList) { + // Create a matrix NUMBER_OF_DATA_POINTS X NUMBER_OF_DATA_POINTS + if (i == 1) { + tikzCode.append(" "); + } + tikzCode.append("("); + tikzCode.append(parserStatistic.getColumnCount()).append(", "); + tikzCode.append(parserStatistic.getRowCount()).append(", "); + tikzCode.append(parserStatistic.getAverageRunTime()).append(") "); + + if (i % cliArgument.getNumberOfDataPoints() == 0 && i < parserStatisticList.size()) { + tikzCode.append(newline).append(newline).append(" "); + } + i++; + } + + tikzCode.append(newline).append(" };").append(newline); + tikzCode.append(" \\end{axis}").append(newline); + tikzCode.append(" \\end{tikzpicture}").append(newline); + tikzCode + .append("\\caption{Run-time statistics for the ") + .append(parserStatisticList.get(0).getHitPolicy()) + .append(" hit policy} ") + .append(newline); + tikzCode + .append("\\label{fig:statistic-") + .append(parserStatisticList.get(0).getHitPolicy().toLowerCase().replace(" ", "-")) + .append("}") + .append(newline); + tikzCode.append("\\end{figure}").append(newline); + + return tikzCode.toString(); + } + + /** + * Function which generates the DMN files analyzed for performance + * + * @param hitPolicy Hit policy for the DMN table + * @return List of DMN files which should be tested + * @throws JAXBException Root exception for all JAXB exceptions + * @throws IOException Signals that an I/O exception of some sort has occurred + */ + private static ArrayList<DmnFile> generateTestFiles(String hitPolicy, int rowNumberSteps) + throws JAXBException, IOException { + ArrayList<DmnFile> dmnFileList = new ArrayList<>(); + // Create the given amount of files + for (int i = 1; i <= cliArgument.getNumberOfDataPoints(); i++) { + // Create the file + for (int j = 1; j <= cliArgument.getNumberOfDataPoints(); j++) { + Path filePath = + (DmnGenerator.generateDmnFile( + i + cliArgument.getInitialColumnCount(), + (rowNumberSteps * j) + cliArgument.getInitialRowCount(), + hitPolicy, + cliArgument.getOutputEntryList())); + + // Add the file to the list of generated files + dmnFileList.add( + new DmnFile( + hitPolicy, + filePath, + i + cliArgument.getInitialColumnCount(), + (cliArgument.getRowSteps() * j) + cliArgument.getInitialRowCount())); + } + } + return dmnFileList; + } + + /** + * Function which deletes a list of DMN files + * + * @param dmnFileList List of DMN files which should be deleted + * @throws IOException Signals that an I/O exception of some sort has occurred + */ + private static void deleteTestFiles(ArrayList<DmnFile> dmnFileList) throws IOException { + for (DmnFile dmnFile : dmnFileList) { + Files.deleteIfExists(dmnFile.getPath()); + } + // Add an information to the cli + System.out.println("Deleted " + dmnFileList.size() + " files successfully"); + } + + public static void main(String argv[]) throws JAXBException, IOException { + cliArgument = new CliArgument(argv); + // Measure the start time + RunTimePrinter runTimePrinter = new RunTimePrinter(System.currentTimeMillis()); + + ArrayList<ArrayList<DmnFile>> dmnFileLists = new ArrayList<>(); + ArrayList<String> tikzPictureList = new ArrayList<>(); + // Generate the DMN files + dmnFileLists.add(generateTestFiles("COLLECT", cliArgument.getRowSteps())); + // Run the performance tests + for (ArrayList<DmnFile> dmnFileList : dmnFileLists) { + ArrayList<ArrayList<ParserStatistic>> parserStatisticList = runPerformanceTests(dmnFileList); + // Generate tikz code + for (ArrayList<ParserStatistic> set : parserStatisticList) { + tikzPictureList.add(toTikz(set)); + } + + // Delete the generated test files if needed + if (PerformanceTestConfiguration.isDeleteTestFiles()) { + deleteTestFiles(dmnFileList); + } + } + + // Print the tikz code of the statistics + tikzPictureList.forEach(System.out::println); + + // Measure the end time + runTimePrinter.setEndTime(System.currentTimeMillis()); + // Print the total execution time + System.out.println(runTimePrinter.toString()); + } +} diff --git a/dmn-generator/src/main/java/de/uniko/bks/statistic/RunTimePrinter.java b/dmn-generator/src/main/java/de/uniko/bks/statistic/RunTimePrinter.java new file mode 100644 index 0000000000000000000000000000000000000000..dfd97a214e0021a54edf287ec8485f0e577fb2f1 --- /dev/null +++ b/dmn-generator/src/main/java/de/uniko/bks/statistic/RunTimePrinter.java @@ -0,0 +1,34 @@ +/* + * Developed by Hans-Henning Ramberger as part of a master thesis. + * Copyright (c) 2018. + */ + +package de.uniko.bks.statistic; + +import java.util.concurrent.TimeUnit; + +/** Class which handles the printing/formatting of the statistic run-time */ +public class RunTimePrinter { + private long startTime; + private long endTime; + private static final String PRINTER_TEXT = + "\u001B[32mThe generation of statistics took %d minutes and %d seconds\u001B[0m"; + + RunTimePrinter(long startTime) { + this.startTime = startTime; + } + + void setEndTime(long endTime) { + this.endTime = endTime; + } + + @Override + public String toString() { + long executionTime = this.endTime - this.startTime; + return String.format( + PRINTER_TEXT, + TimeUnit.MILLISECONDS.toMinutes(executionTime), + TimeUnit.MILLISECONDS.toSeconds(executionTime) + - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(executionTime))); + } +} diff --git a/dmn-generator/src/main/java/de/uniko/bks/statistic/TestDMN.java b/dmn-generator/src/main/java/de/uniko/bks/statistic/TestDMN.java new file mode 100644 index 0000000000000000000000000000000000000000..a1031c5ddb2f6b2efbb5d2ee4cb6df12c0446e19 --- /dev/null +++ b/dmn-generator/src/main/java/de/uniko/bks/statistic/TestDMN.java @@ -0,0 +1,44 @@ +package de.uniko.bks.statistic; + +import de.unikoblenz.fgbks.dmn.core.models.VerifierCollectionResult; +import de.unikoblenz.fgbks.dmn.core.models.VerifierType; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.util.List; +import org.camunda.bpm.dmn.engine.DmnDecision; +import org.camunda.bpm.dmn.engine.DmnEngineConfiguration; + +public class TestDMN { + + // Multimap<String, String> outputMap = MultimapBuilder.hashKeys().hashSetValues().build(); + private List<DmnDecision> dmnDecisions = null; + private VerifierCollectionResult verifcation; + private long execTime = 0; + + public List<DmnDecision> testDmn(String filePath) throws FileNotFoundException { + InputStream inputStream = new FileInputStream(filePath); + this.dmnDecisions = + DmnEngineConfiguration.createDefaultDmnEngineConfiguration() + .buildEngine() + .parseDecisions(inputStream); + + execTime = System.currentTimeMillis(); + checkAllTables(); + execTime = System.currentTimeMillis() - execTime; + // checkTest(); + return this.dmnDecisions; + } + + public VerifierCollectionResult getVerifcation() { + return verifcation; + } + + public long getExecTime() { + return execTime; + } + + private void checkAllTables() { + verifcation = VerifierType.getAllResults(this.dmnDecisions); + } +} diff --git a/dmn-generator/src/main/java/de/uniko/bks/statistic/dmnInt.java b/dmn-generator/src/main/java/de/uniko/bks/statistic/dmnInt.java new file mode 100644 index 0000000000000000000000000000000000000000..3a97131c0cc0306dfc5a4afe3e99a0b4249ee1d3 --- /dev/null +++ b/dmn-generator/src/main/java/de/uniko/bks/statistic/dmnInt.java @@ -0,0 +1,463 @@ +package de.uniko.bks.statistic; + +public class dmnInt { + private boolean isRange = false; + private boolean isLower = false; + private boolean isGreater = false; + private boolean isEqual = false; + private boolean isInt = false; + private boolean isLong = false; + private boolean isDouble = false; + private int lowerBoundInt; + private int upperBoundInt; + private int numberInt; + private double numberDouble; + private double lowerBoundDouble; + private double upperBoundDouble; + private long numberLong; + private long lowerBoundLong; + private long upperBoundLong; + private String ruleName = ""; + private String originalText = ""; + private boolean isDoesntMatter = false; + final char eq = "=".charAt(0); + final char lt = "<".charAt(0); + final char gt = ">".charAt(0); + + void setRuleName(String ruleName) { + this.ruleName = ruleName; + } + + public String getRuleName() { + return ruleName; + } + + public boolean isRange() { + return isRange; + } + + public boolean isLower() { + return isLower; + } + + public boolean isGreater() { + return isGreater; + } + + public boolean isEqual() { + return isEqual; + } + + public int getLowerBoundInt() { + return lowerBoundInt; + } + + public int getUpperBoundInt() { + return upperBoundInt; + } + + public int getNumberInt() { + return numberInt; + } + + public double getNumberDouble() { + return numberDouble; + } + + public double getLowerBoundDouble() { + return lowerBoundDouble; + } + + public double getUpperBoundDouble() { + return upperBoundDouble; + } + + public long getNumberLong() { + return numberLong; + } + + public long getLowerBoundLong() { + return lowerBoundLong; + } + + public long getUpperBoundLong() { + return upperBoundLong; + } + + public boolean isInt() { + return isInt; + } + + public boolean isLong() { + return isLong; + } + + public boolean isDouble() { + return isDouble; + } + + public dmnInt(int number) { + this.isInt = true; + this.numberInt = number; + this.lowerBoundInt = number; + this.upperBoundInt = numberInt; + this.isEqual = true; + } + + public dmnInt(double number) { + this.numberDouble = number; + this.lowerBoundDouble = number; + this.upperBoundDouble = number; + this.isDouble = true; + } + + public dmnInt(long number) { + this.numberLong = number; + this.lowerBoundLong = number; + this.upperBoundLong = number; + } + + public dmnInt(int lowerBoundInt, int upperBoundInt) { + this.upperBoundInt = upperBoundInt; + this.lowerBoundInt = lowerBoundInt; + this.isRange = true; + this.isInt = true; + } + + public dmnInt(double lowerBoundDouble, double upperBoundDouble) { + this.upperBoundDouble = upperBoundDouble; + this.lowerBoundDouble = lowerBoundDouble; + this.isRange = true; + this.isDouble = true; + } + + public dmnInt(long lowerBoundLong, long upperBoundLong) { + this.lowerBoundLong = lowerBoundLong; + this.upperBoundLong = upperBoundLong; + this.isRange = true; + } + + public dmnInt(int number, boolean isLower, boolean isGreater, boolean isEqual) { + if (isLower && isGreater) { + System.out.println("ERROR: Number can't be lower and greater..."); + } + this.isInt = true; + this.isEqual = isEqual; + this.isGreater = isGreater; + this.isLower = isLower; + this.numberInt = number; + } + + public dmnInt(boolean itDoesntMatter) { + this.isDoesntMatter = true; + } + + public dmnInt(String term, String type) { + String modTerm = term; + + char c = term.charAt(0); + + if (lt == c) { + this.isLower = true; + modTerm = modTerm.replace("<", ""); + } else if (gt == c) { + this.isGreater = true; + modTerm = modTerm.replace(">", ""); + } + c = term.charAt(1); + if (eq == c) { + this.isEqual = true; + modTerm = modTerm.replace("=", ""); + } + + modTerm = modTerm.replace(" ", ""); + + switch (type) { + case "integer": + this.numberInt = Integer.parseInt(modTerm); + this.isInt = true; + case "double": + this.numberDouble = Double.parseDouble(modTerm); + this.isDouble = true; + case "long": + this.numberLong = Long.parseLong(modTerm); + this.isLong = true; + } + if (!this.isLower && !this.isGreater && !this.isEqual) { + this.isEqual = true; + } + } + + public dmnInt(double number, boolean isLower, boolean isGreater, boolean isEqual) { + if (isLower && isGreater) { + System.out.println("ERROR: Number can't be lower and greater..."); + } + this.isDouble = true; + this.isEqual = isEqual; + this.isGreater = isGreater; + this.isLower = isLower; + this.numberDouble = number; + } + + public dmnInt(long number, boolean isLower, boolean isGreater, boolean isEqual) { + if (isLower && isGreater) { + System.out.println("ERROR: Number can't be lower and greater..."); + } + + this.isEqual = isEqual; + this.isGreater = isGreater; + this.isLower = isLower; + this.numberLong = number; + } + + public boolean compare(dmnInt sndNum) { + if (this.isDoesntMatter || sndNum.isDoesntMatter()) { + return true; + } + if (this.isRange) { + return range(this, sndNum); + } + if (sndNum.isRange) { + return range(sndNum, this); + } + return bothNumbers(this, sndNum); + } + + public boolean bothNumbers(dmnInt x, dmnInt y) { + if (x.isLower) { + if (y.isLower) { + if (y.isInt && x.isInt) { + if (x.numberInt < y.numberInt || y.numberInt < x.numberInt) { + return true; + } + } else if (y.isDouble && x.isDouble) { + if (x.numberDouble < y.numberDouble || y.numberDouble < x.numberDouble) { + return true; + } + } else if (y.isLong && x.isLong) { + if (x.numberLong < y.numberLong || y.numberLong < x.numberLong) { + return true; + } + } + } + if (y.isEqual) { + if (y.isInt && x.isInt) { + if (x.numberInt > y.numberInt) { + return true; + } + } else if (y.isDouble && x.isDouble) { + if (x.numberDouble > y.numberDouble) { + return true; + } + } else if (y.isLong && x.isLong) { + if (x.numberLong > y.numberLong) { + return true; + } + } + } + if (y.isGreater) { + if (y.isInt && x.isInt) { + if ((x.numberInt != y.numberInt) + && (y.numberInt < x.numberInt || x.numberInt > y.numberInt)) { + return true; + } + } else if (y.isDouble && x.isDouble) { + if ((x.numberDouble != y.numberDouble) + && (y.numberDouble < x.numberDouble || x.numberDouble > y.numberDouble)) { + return true; + } + } else if (y.isLong && x.isLong) { + if ((x.numberLong != y.numberLong) + && (y.numberLong < x.numberLong || x.numberLong > y.numberLong)) { + return true; + } + } + } + } + if (x.isEqual) { + if (y.isLower) { + if (y.isInt && x.isInt) { + if (x.numberInt < y.numberInt) { + return true; + } + } else if (y.isDouble && x.isDouble) { + if (x.numberDouble < y.numberDouble) { + return true; + } + } else if (y.isLong && x.isLong) { + if (x.numberLong < y.numberLong) { + return true; + } + } + } + if (y.isEqual) { + if (y.isInt && x.isInt) { + if (x.numberInt == y.numberInt) { + return true; + } + } else if (y.isDouble && x.isDouble) { + if (x.numberDouble == y.numberDouble) { + return true; + } + } else if (y.isLong && x.isLong) { + if (x.numberLong == y.numberLong) { + return true; + } + } + } + if (y.isGreater) { + if (y.isInt && x.isInt) { + if (x.numberInt > y.numberInt) { + return true; + } + } else if (y.isDouble && x.isDouble) { + if (x.numberDouble > y.numberDouble) { + return true; + } + } else if (y.isLong && x.isLong) { + if (x.numberLong > y.numberLong) { + return true; + } + } + } + } + if (x.isGreater) { + if (y.isLower) { + if (y.isInt && x.isInt) { + if ((x.numberInt != y.numberInt) + && (y.numberInt > x.numberInt || x.numberInt < y.numberInt)) { + return true; + } + } else if (y.isDouble && x.isDouble) { + if ((x.numberDouble != y.numberDouble) + && (y.numberDouble > x.numberDouble || x.numberDouble < y.numberDouble)) { + return true; + } + } else if (y.isLong && x.isLong) { + if ((x.numberLong != y.numberLong) + && (y.numberLong > x.numberLong || x.numberLong < y.numberLong)) { + return true; + } + } + } + } + return false; + } + + public boolean rangeTest(int number, int lower, int upper) { + if ((number > lower && number < upper) || (number == lower) || (number == upper)) { + return true; + } + return false; + } + + public boolean rangeTest(double number, double lower, double upper) { + if (((number > lower && number < upper) || (number == lower) || (number == upper))) { + return true; + } + return false; + } + + public boolean rangeTest(long number, long lower, long upper) { + if ((number > lower && number < upper) || (number == lower) || (number == upper)) { + return true; + } + return false; + } + + public boolean range(dmnInt x, dmnInt y) { + if (x.isRange) { + if (y.isRange) { + if (y.isInt && x.isInt) { + // Case if both is int range + if (rangeTest(x.lowerBoundInt, y.lowerBoundInt, y.upperBoundInt) + || rangeTest(x.upperBoundInt, y.lowerBoundInt, y.upperBoundInt) + || rangeTest(y.lowerBoundInt, x.lowerBoundInt, x.upperBoundInt) + || rangeTest(y.upperBoundInt, x.lowerBoundInt, x.upperBoundInt)) { + return true; + } + return false; + + } else if (y.isDouble && x.isDouble) { + if (rangeTest(x.lowerBoundDouble, y.lowerBoundDouble, y.upperBoundDouble) + || rangeTest(x.upperBoundDouble, y.lowerBoundDouble, y.upperBoundDouble) + || rangeTest(y.lowerBoundDouble, x.lowerBoundDouble, x.upperBoundDouble) + || rangeTest(y.upperBoundDouble, x.lowerBoundDouble, x.upperBoundDouble)) { + return true; + } + return false; + } else if (y.isLong && x.isLong) { + if (rangeTest(x.lowerBoundLong, y.lowerBoundLong, y.upperBoundLong) + || rangeTest(x.upperBoundLong, y.lowerBoundLong, y.upperBoundLong) + || rangeTest(y.lowerBoundLong, x.lowerBoundLong, x.upperBoundLong) + || rangeTest(y.upperBoundLong, x.lowerBoundLong, x.upperBoundLong)) { + return true; + } + return false; + } + } else { + if (y.isInt && x.isInt) { + // Case if both is int range + if (y.isEqual) { + if (rangeTest(y.numberInt, x.lowerBoundInt, x.upperBoundInt)) { + return true; + } + } + if (y.isLower) { + if (x.lowerBoundInt < y.numberInt) { + return true; + } + return false; + } + if (y.isGreater) { + if (x.upperBoundInt > y.numberInt) { + return true; + } + return false; + } + } else if (y.isDouble && x.isDouble) { + if (y.isEqual) { + if (rangeTest(y.numberDouble, x.lowerBoundDouble, x.upperBoundDouble)) { + return true; + } + } + if (y.isLower) { + if (x.lowerBoundDouble < y.numberDouble) { + return true; + } + return false; + } + if (y.isGreater) { + if (x.upperBoundDouble > y.numberDouble) { + return true; + } + return false; + } + } else if (y.isLong && x.isLong) { + if (y.isEqual) { + if (rangeTest(y.numberLong, x.lowerBoundLong, x.upperBoundLong)) { + return true; + } + } + if (y.isLower) { + if (x.lowerBoundLong < y.numberLong) { + return true; + } + return false; + } + if (y.isGreater) { + if (x.upperBoundLong > y.numberLong) { + return true; + } + return false; + } + } + } + } + return false; + } + + public boolean isDoesntMatter() { + return isDoesntMatter; + } +} diff --git a/dmn-generator/src/main/java/de/uniko/bks/util/StringSanitizer.java b/dmn-generator/src/main/java/de/uniko/bks/util/StringSanitizer.java new file mode 100644 index 0000000000000000000000000000000000000000..0900bd733655b084e2c087bae02c2b4fd7874b15 --- /dev/null +++ b/dmn-generator/src/main/java/de/uniko/bks/util/StringSanitizer.java @@ -0,0 +1,150 @@ +/* + * Developed by Hans-Henning Ramberger as part of a master thesis. + * Copyright (c) 2018. + */ + +package de.uniko.bks.util; + +import de.uniko.bks.dom.dfl.rule.Comparator; +import de.uniko.bks.dom.dfl.rule.RuleDelimiter; + +/** Class providing functions to sanitize strings */ +public class StringSanitizer { + /** + * Function which removes whitespaces from a given text + * + * @param text Text in which all whitespaces should be removed + * @return The sanitized text + */ + private static String replaceWhitespace(String text) { + text = text.replace(Comparator.SMALLER.getValue() + " ", Comparator.SMALLER.getValue()); + text = + text.replace( + Comparator.SMALLER_OR_EQUALS.getValue() + " ", Comparator.SMALLER_OR_EQUALS.getValue()); + text = + text.replace( + Comparator.GREATER_OR_EQUALS.getValue() + " ", Comparator.GREATER_OR_EQUALS.getValue()); + text = text.replace(Comparator.GREATER.getValue() + " ", Comparator.GREATER.getValue()); + + return text.replace(" ", RuleDelimiter.WHITESPACE.getValue()); + } + + /** + * Function which removes double quotes from a given text + * + * @param text Text in which all double quotes should be removed + * @return The sanitized text + */ + private static String replaceQuotes(String text) { + return text.replace("\"", ""); + } + + /** + * Function which removes colons from a given text + * + * @param text Text in which all colons should be removed + * @return The sanitized text + */ + private static String replaceColon(String text) { + return text.replace(":", ""); + } + + /** + * Function which removes dashes from a given text + * + * @param text Text in which all dashes should be removed + * @return The sanitized text + */ + private static String replaceDash(String text) { + return text.replace( + "-", RuleDelimiter.WHITESPACE.getValue() + RuleDelimiter.WHITESPACE.getValue()); + } + + /** + * Function which sanitizes whitespaces + * + * @param text Text in which all whitespaces should be sanitized + * @return The sanitized text + */ + public static String sanitize(String text) { + return sanitize(text, true, true, true, true); + } + + /** + * Function which sanitizes whitespaces + * + * @param text Text in which all whitespaces should be sanitized + * @param replaceWhitespace Whether or not whitespaces should be sanitized + * @return The sanitized text + */ + public static String sanitize(String text, boolean replaceWhitespace) { + return sanitize(text, replaceWhitespace, false, false, false); + } + + /** + * Function which sanitizes whitespaces and double quotes + * + * @param text Text which should be sanitized + * @param replaceWhitespace Whether or not whitespaces should be sanitized + * @param replaceQuotes Whether or not double quotes should be sanitized + * @return The sanitized text + */ + public static String sanitize(String text, boolean replaceWhitespace, boolean replaceQuotes) { + return sanitize(text, replaceWhitespace, replaceQuotes, false, false); + } + + /** + * Function which sanitizes whitespaces, double quotes and colons + * + * @param text Text which should be sanitized + * @param replaceWhitespace Whether or not whitespaces should be sanitized + * @param replaceQuotes Whether or not double quotes should be sanitized + * @param replaceColon Whether or not colons should be sanitized + * @return The sanitized text + */ + public static String sanitize( + String text, boolean replaceWhitespace, boolean replaceQuotes, boolean replaceColon) { + return sanitize(text, replaceWhitespace, replaceQuotes, replaceColon, false); + } + + /** + * Function which sanitizes whitespaces, double quotes and colons + * + * @param text Text which should be sanitized + * @param replaceWhitespace Whether or not whitespaces should be sanitized + * @param replaceQuotes Whether or not double quotes should be sanitized + * @param replaceColon Whether or not colons should be sanitized + * @param replaceDash Whether or not dashes should be sanitized + * @return The sanitized text + */ + public static String sanitize( + String text, + boolean replaceWhitespace, + boolean replaceQuotes, + boolean replaceColon, + boolean replaceDash) { + if (replaceWhitespace) { + text = replaceWhitespace(text); + } + if (replaceQuotes) { + text = replaceQuotes(text); + } + if (replaceColon) { + text = replaceColon(text); + } + if (replaceDash) { + text = replaceDash(text); + } + return text; + } + + /** + * Function removes whitespaces after commas + * + * @param text Text in which all whitespaces after commas should be removed + * @return The text without whitespaces after commas + */ + public static String removeWhitespacesAfterComma(String text) { + return text.replaceAll(",\\s+", ","); + } +} diff --git a/dmn-generator/src/main/java/de/uniko/bks/xml/FileHandler.java b/dmn-generator/src/main/java/de/uniko/bks/xml/FileHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..e0bf0b654453bfaf4b58eb89d1e4ef6c53d67a34 --- /dev/null +++ b/dmn-generator/src/main/java/de/uniko/bks/xml/FileHandler.java @@ -0,0 +1,89 @@ +/* + * Developed by Hans-Henning Ramberger as part of a master thesis. + * Copyright (c) 2018. + */ + +package de.uniko.bks.xml; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.stream.Collectors; + +/** Class which provides basic functionalities for handling files */ +public class FileHandler { + private static final String FILE_EXTENSION = ".xml"; + + /** + * Function which reads a file into a variable + * + * @param fileName Name of the file which should be read + * @return The content of the given file + */ + public static String read(String fileName) { + // Get the class loader to load the resource + ClassLoader classLoader = FileHandler.class.getClassLoader(); + InputStream inputStream = classLoader.getResourceAsStream(fileName); + + // Convert the stream into a String + return new BufferedReader(new InputStreamReader(inputStream)) + .lines() + .collect(Collectors.joining("\n")); + } + + /** + * Function which reads a file into a variable + * + * @param filePath Complete path of the file which should be read + * @return The content of the given file + * @throws IOException Signals that an I/O exception of some sort has occurred + */ + public static String read(Path filePath) throws IOException { + return new String(Files.readAllBytes(filePath), StandardCharsets.UTF_8); + } + + /** + * Function which generates a name for the DMN file + * + * @param identifier Identifier suffix for all ids in the DMN decision + * @param columnCount Number of columns in the DMN table + * @param rowCount Number of rows in the DMN table + * @param hitPolicy Hit policy for the DMN table + * @return The name of the generated DMN file + */ + private static String generateFileName( + String identifier, int columnCount, int rowCount, String hitPolicy) { + return "generated_dmn_c" + + columnCount + + "_r" + + rowCount + + "_h" + + hitPolicy.replace(" ", "_") + + "_" + + identifier; + } + + /** + * Function which writes a given content into a new file + * + * @param identifier Identifier suffix for all ids in the DMN decision + * @param columnCount Number of columns in the DMN table + * @param rowCount Number of rows in the DMN table + * @param content The content which is written into the new file + * @param hitPolicy Hit policy for the DMN table + * @return The path of the new file + * @throws IOException Signals that an I/O exception of some sort has occurred + */ + public static Path write( + String identifier, int columnCount, int rowCount, String hitPolicy, String content) + throws IOException { + // Write the file to the temporary directory of the os + File file = + File.createTempFile( + generateFileName(identifier, columnCount, rowCount, hitPolicy), FILE_EXTENSION); + + // Fill the generated file with content + return Files.write(file.toPath(), content.getBytes()); + } +} diff --git a/dmn-generator/src/main/java/de/uniko/bks/xml/XmlMarshaller.java b/dmn-generator/src/main/java/de/uniko/bks/xml/XmlMarshaller.java new file mode 100644 index 0000000000000000000000000000000000000000..c42947f616375253a6eac496d43252175d1ede12 --- /dev/null +++ b/dmn-generator/src/main/java/de/uniko/bks/xml/XmlMarshaller.java @@ -0,0 +1,34 @@ +/* + * Developed by Hans-Henning Ramberger as part of a master thesis. + * Copyright (c) 2018. + */ + +package de.uniko.bks.xml; + +import de.uniko.bks.dom.dmn.Decision; +import java.io.StringWriter; +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Marshaller; + +/** Class marshalling a given Java Decision into XML */ +public class XmlMarshaller { + public static String marshalDmnTable(Decision decision) throws JAXBException { + JAXBContext jaxbContext = JAXBContext.newInstance(Decision.class); + Marshaller marshaller = jaxbContext.createMarshaller(); + + // Format the resulting XML + marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); + // Tell the marshaller to only write the XML fragment without the header + marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true); + // Set the correct encoding + marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8"); + + // Create a variable to store the marshalling result + StringWriter stringWriter = new StringWriter(); + // Marshal the decision into a variable + marshaller.marshal(decision, stringWriter); + + return stringWriter.toString(); + } +} diff --git a/dmn-generator/src/main/resources/EmptyDmn.xml b/dmn-generator/src/main/resources/EmptyDmn.xml new file mode 100644 index 0000000000000000000000000000000000000000..a2d65c7749cda2cc0545c6f33539ec00daf701e6 --- /dev/null +++ b/dmn-generator/src/main/resources/EmptyDmn.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> +<definitions xmlns="http://www.omg.org/spec/DMN/20151101/dmn.xsd" xmlns:biodi="http://bpmn.io/schema/dmn/biodi/1.0" id="Definitions_1epahzp" name="DRD" namespace="http://camunda.org/schema/1.0/dmn"> + <extensionElements> + <biodi:bounds x="150" y="150" width="180" height="80" /> + </extensionElements> + <!--replace-me--> +</definitions> \ No newline at end of file diff --git a/dmn-generator/src/test/java/PerformanceTest.java b/dmn-generator/src/test/java/PerformanceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..9f50961f419e3e4cac2f7adb324e3ee2034e5a0e --- /dev/null +++ b/dmn-generator/src/test/java/PerformanceTest.java @@ -0,0 +1,17 @@ +import de.uniko.bks.statistic.PerformanceTester; +import java.io.IOException; +import javax.xml.bind.JAXBException; +import org.junit.jupiter.api.Test; + +public class PerformanceTest { + @Test + public void perfTest() { + try { + PerformanceTester.main(new String[] {""}); + } catch (JAXBException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/dmn-verifier-app/src/main/java/de/unikoblenz/fgbks/dmn/core/DmnService.java b/dmn-verifier-app/src/main/java/de/unikoblenz/fgbks/dmn/core/DmnService.java index cfca8a3e1b0311487335761090cdb6eb4c6b8fc5..511694da1f7a49b32b4edad3f5b1e84e4b01c29b 100644 --- a/dmn-verifier-app/src/main/java/de/unikoblenz/fgbks/dmn/core/DmnService.java +++ b/dmn-verifier-app/src/main/java/de/unikoblenz/fgbks/dmn/core/DmnService.java @@ -43,7 +43,7 @@ public class DmnService { } public VerifierResult getVerifierFromType(VerifierType verifierType) { - return verifierType.getResult(dmnDecisions, null).orElse(null); + return verifierType.getResult(dmnDecisions, null, null).orElse(null); } public VerifierCollectionResult getAllVerifier() { diff --git a/dmn-verifier-app/src/main/java/de/unikoblenz/fgbks/dmn/core/models/VerifierCollectionResult.java b/dmn-verifier-app/src/main/java/de/unikoblenz/fgbks/dmn/core/models/VerifierCollectionResult.java index 07492e90a5c12164bedb1e9962bfc38379d3a295..f1737831619fc4e6cb2fd8ac5d4ace4629b31a99 100644 --- a/dmn-verifier-app/src/main/java/de/unikoblenz/fgbks/dmn/core/models/VerifierCollectionResult.java +++ b/dmn-verifier-app/src/main/java/de/unikoblenz/fgbks/dmn/core/models/VerifierCollectionResult.java @@ -5,6 +5,7 @@ import java.util.ArrayList; import java.util.Comparator; import java.util.List; import java.util.Objects; +import java.util.concurrent.CopyOnWriteArrayList; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; @@ -15,7 +16,7 @@ public class VerifierCollectionResult { private List<VerifierResult> verifierResults; private VerifierCollectionResult() { - verifierResults = new ArrayList<>(); + verifierResults = new CopyOnWriteArrayList<>(); } public List<VerifierResult> getVerifierResults() { @@ -28,7 +29,7 @@ public class VerifierCollectionResult { public class Builder extends DefaultBuilder<VerifierCollectionResult> { - public VerifierCollectionResult.Builder addVerification(VerifierResult verificationResult) { + public VerifierCollectionResult.Builder addVerifierResult(VerifierResult verificationResult) { value.verifierResults.add(Objects.requireNonNull(verificationResult)); return this; } 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 56cdd1442031c1c15887a939c5bf3cd03d48a6e7..9e2d47b1ce709d223f46484d4fe4c81033593ca3 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 @@ -6,37 +6,54 @@ import de.unikoblenz.fgbks.dmn.core.verifier.helper.RuleMap; import java.util.Arrays; import java.util.List; import java.util.Optional; +import java.util.concurrent.ConcurrentLinkedQueue; import org.camunda.bpm.dmn.engine.DmnDecision; import org.slf4j.LoggerFactory; public enum VerifierType { - Identical("Checking for identical rules.", IdenticalRules.class), - Subsumption("Checking for rules, which subsume other rules.", SubsumptionRules.class), - Equivalent("Checking for synonyms in columns.", EquivalentRules.class), - Overlap("Checking for overlapping rules.", OverlappingRules.class), - Missing("Checking for missing rules.", MissingRules.class), + Identical("Checking for identical rules.", IdenticalRules.class, true), + Subsumption("Checking for rules, which subsume other rules.", SubsumptionRules.class, true), + Equivalent("Checking for synonyms in columns.", EquivalentRules.class, true), + Overlap("Checking for overlapping rules.", OverlappingRules.class, true), + Missing("Checking for missing rules.", MissingRules.class, true), PartialReduction( - "Checking for partial reduction of rules (combination).", PartialReductionRules.class); + "Checking for partial reduction of rules (combination).", PartialReductionRules.class, true), + Interdeterminism( + "Identical, Overlapping or Subsumption with different conclusions.", + InterdeterminismRules.class, + false); private final Class<? extends AbstractVerifier> verifierClass; private String description; + private boolean autoExecutable; - VerifierType(String description, Class<? extends AbstractVerifier> verifierClass) { + VerifierType( + String description, Class<? extends AbstractVerifier> verifierClass, boolean autoExecutable) { this.description = description; this.verifierClass = verifierClass; + this.autoExecutable = autoExecutable; } public String getDescription() { return description; } - public Optional<VerifierResult> getResult(List<DmnDecision> dmnDecisionList, RuleMap ruleMap) { + private boolean isAutoExecutable() { + return autoExecutable; + } + + public Optional<VerifierResult> getResult( + List<DmnDecision> dmnDecisionList, + RuleMap ruleMap, + ConcurrentLinkedQueue<VerificationResult> interdeterminismRules) { if (ruleMap == null) { ruleMap = RuleMap.createMapFromDmn(dmnDecisionList); } try { return Optional.of( - verifierClass.newInstance().findValidationErrors(dmnDecisionList, ruleMap)); + verifierClass + .newInstance() + .findValidationErrors(dmnDecisionList, ruleMap, interdeterminismRules)); } catch (Exception ex) { LoggerFactory.getLogger(VerifierType.class).warn(ex.getMessage()); ex.printStackTrace(); @@ -46,14 +63,18 @@ public enum VerifierType { public static VerifierCollectionResult getAllResults(List<DmnDecision> dmnDecisionList) { RuleMap ruleMap = RuleMap.createMapFromDmn(dmnDecisionList); + ConcurrentLinkedQueue<VerificationResult> interdeterminismRules = new ConcurrentLinkedQueue<>(); Builder builder = VerifierCollectionResult.getBuilder(); Arrays.asList(values()) .parallelStream() + .filter(VerifierType::isAutoExecutable) .forEach( verifierType -> verifierType - .getResult(dmnDecisionList, ruleMap) - .ifPresent(builder::addVerification)); + .getResult(dmnDecisionList, ruleMap, interdeterminismRules) + .ifPresent(builder::addVerifierResult)); + Interdeterminism.getResult(dmnDecisionList, ruleMap, interdeterminismRules) + .ifPresent(builder::addVerifierResult); return builder.build(); } } diff --git a/dmn-verifier-app/src/main/java/de/unikoblenz/fgbks/dmn/core/verifier/AbstractVerifier.java b/dmn-verifier-app/src/main/java/de/unikoblenz/fgbks/dmn/core/verifier/AbstractVerifier.java index 8e15bc88477306c95eddf6ce17e2c5729c2a4e6e..4fa153e6fe078b5891be4cb3d5d90fd6fb63c3d1 100644 --- a/dmn-verifier-app/src/main/java/de/unikoblenz/fgbks/dmn/core/verifier/AbstractVerifier.java +++ b/dmn-verifier-app/src/main/java/de/unikoblenz/fgbks/dmn/core/verifier/AbstractVerifier.java @@ -8,8 +8,10 @@ import de.unikoblenz.fgbks.dmn.core.verifier.helper.RuleMap; 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.LinkedList; import java.util.List; import java.util.Objects; +import java.util.Queue; import java.util.logging.Logger; import org.camunda.bpm.dmn.engine.DmnDecision; @@ -21,17 +23,24 @@ public abstract class AbstractVerifier { private VerifierResult.Builder verifierResultBuilder; protected List<DmnDecision> dmnDecisionList; protected RuleMap ruleMap; + protected Queue<VerificationResult> interdeterminismRules; protected AbstractVerifier(VerifierType verifierType) { this.verifierType = Objects.requireNonNull(verifierType); } public final VerifierResult findValidationErrors( - final List<DmnDecision> dmnDecisionList, final RuleMap ruleMap) { - LOGGER.info("Start verifyDecision " + this.getClass().getSimpleName()); + final List<DmnDecision> dmnDecisionList, + final RuleMap ruleMap, + Queue<VerificationResult> interdeterminismRules) { + // LOGGER.info("Start verifyDecision " + this.getClass().getSimpleName()); long start = System.currentTimeMillis(); this.dmnDecisionList = new ArrayList<>(dmnDecisionList); this.ruleMap = ruleMap; + if (interdeterminismRules == null) { + interdeterminismRules = new LinkedList<>(); + } + this.interdeterminismRules = interdeterminismRules; verifierResultBuilder = VerifierResult.getBuilder().withVerifierType(verifierType); beforeVerifyDecision(); for (DmnDecision d : dmnDecisionList) { @@ -39,12 +48,14 @@ public abstract class AbstractVerifier { } afterVerifyDecision(); long fin = System.currentTimeMillis(); + /* LOGGER.info( "Finish verifyDecision " + this.getClass().getSimpleName() + ". " + "Time: " + (fin - start)); + */ return verifierResultBuilder.build(); } diff --git a/dmn-verifier-app/src/main/java/de/unikoblenz/fgbks/dmn/core/verifier/EquivalentRules.java b/dmn-verifier-app/src/main/java/de/unikoblenz/fgbks/dmn/core/verifier/EquivalentRules.java index f61c8916f694eff1d5cf8e2e92d48de5395aae44..20a196e32e53ba6cd9468a0f19ec9cfd9772f5ce 100644 --- a/dmn-verifier-app/src/main/java/de/unikoblenz/fgbks/dmn/core/verifier/EquivalentRules.java +++ b/dmn-verifier-app/src/main/java/de/unikoblenz/fgbks/dmn/core/verifier/EquivalentRules.java @@ -22,7 +22,9 @@ public class EquivalentRules extends AbstractVerifier { } @Override - protected void beforeVerifyDecision() {} + protected void beforeVerifyDecision() { + // Nothing + } @Override protected void verifyDecision(DmnDecision d) { @@ -67,5 +69,7 @@ public class EquivalentRules extends AbstractVerifier { } @Override - protected void afterVerifyDecision() {} + protected void afterVerifyDecision() { + // Nothing + } } diff --git a/dmn-verifier-app/src/main/java/de/unikoblenz/fgbks/dmn/core/verifier/IdenticalRules.java b/dmn-verifier-app/src/main/java/de/unikoblenz/fgbks/dmn/core/verifier/IdenticalRules.java index c3abfa77f97d3cb477d4b7ee5bf7a9067551d8dc..4c9347ded6edabc2575f48d860888a4dcee47b9d 100644 --- a/dmn-verifier-app/src/main/java/de/unikoblenz/fgbks/dmn/core/verifier/IdenticalRules.java +++ b/dmn-verifier-app/src/main/java/de/unikoblenz/fgbks/dmn/core/verifier/IdenticalRules.java @@ -18,7 +18,9 @@ public class IdenticalRules extends AbstractVerifier { } @Override - protected void beforeVerifyDecision() {} + protected void beforeVerifyDecision() { + // Nothing + } @Override protected void verifyDecision(DmnDecision d) { @@ -31,15 +33,18 @@ public class IdenticalRules extends AbstractVerifier { private void checkForIdenticalRules( List<Type> inputs, int i, List<RuleIdentifier> currentRuleIdentifiers) { if (i == inputs.size()) { + boolean differentConclusions = + checkDifferentConclusion( + currentRuleIdentifiers.get(0).getDecisionKey(), currentRuleIdentifiers); VerificationResult.Builder vBuilder = VerificationResult.getBuilder() - .withMessage( - getMessageText( - currentRuleIdentifiers, - checkDifferentConclusion( - currentRuleIdentifiers.get(0).getDecisionKey(), currentRuleIdentifiers))); + .withMessage(getMessageText(currentRuleIdentifiers, differentConclusions)); vBuilder.addRules(currentRuleIdentifiers); - addVerification(vBuilder.build()); + VerificationResult vResult = vBuilder.build(); + if (differentConclusions) { + interdeterminismRules.add(vResult); + } + addVerification(vResult); } else { List<Value> currentBounds = new ArrayList<>(); List<Value> sortedBounds = @@ -76,9 +81,6 @@ public class IdenticalRules extends AbstractVerifier { } } - @Override - protected void afterVerifyDecision() {} - private String getMessageText( List<RuleIdentifier> currentRuleIdentifiers, boolean isDifferentConclusion) { StringBuilder sb = new StringBuilder("Rules "); @@ -97,4 +99,9 @@ public class IdenticalRules extends AbstractVerifier { } return sb.toString(); } + + @Override + protected void afterVerifyDecision() { + // Nothing} + } } diff --git a/dmn-verifier-app/src/main/java/de/unikoblenz/fgbks/dmn/core/verifier/InterdeterminismRules.java b/dmn-verifier-app/src/main/java/de/unikoblenz/fgbks/dmn/core/verifier/InterdeterminismRules.java new file mode 100644 index 0000000000000000000000000000000000000000..7694d0731daf2ffa14304b12b8761616357698cd --- /dev/null +++ b/dmn-verifier-app/src/main/java/de/unikoblenz/fgbks/dmn/core/verifier/InterdeterminismRules.java @@ -0,0 +1,31 @@ +package de.unikoblenz.fgbks.dmn.core.verifier; + +import static de.unikoblenz.fgbks.dmn.core.models.VerifierType.Interdeterminism; + +import de.unikoblenz.fgbks.dmn.core.models.VerificationResult; +import org.camunda.bpm.dmn.engine.DmnDecision; + +public class InterdeterminismRules extends AbstractVerifier { + + public InterdeterminismRules() { + super(Interdeterminism); + } + + @Override + protected void beforeVerifyDecision() { + // Nothing + } + + @Override + protected void verifyDecision(DmnDecision d) { + // Nothing + } + + @Override + protected void afterVerifyDecision() { + // just add the rules prev found in overlapping, identical or subsumption + for (VerificationResult verificationResult : interdeterminismRules) { + addVerification(verificationResult); + } + } +} 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 66fe182a13a3354b57485ce7982f1f96061e7732..e617d9f452959b925e8e837534982cd7279f360c 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 @@ -22,7 +22,9 @@ public class MissingRules extends AbstractVerifier { } @Override - protected void beforeVerifyDecision() {} + protected void beforeVerifyDecision() { + // Nothing + } @Override protected void verifyDecision(DmnDecision d) { @@ -184,5 +186,7 @@ public class MissingRules extends AbstractVerifier { } @Override - protected void afterVerifyDecision() {} + protected void afterVerifyDecision() { + // Nothing + } } 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 eaced682b1f9fc8b471d7f52b8280977c31e4259..1d74781270a7d9dbaf39624f55572e769c677bc5 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 @@ -38,16 +38,18 @@ public class OverlappingRules extends AbstractVerifier { if (i == inputs.size()) { // do nothing, if there was no real overlap found prev.. if (hasOverlap) { + boolean differentConclusions = + checkDifferentConclusion( + currentRuleIdentifiers.get(0).getDecisionKey(), currentRuleIdentifiers); VerificationResult.Builder vBuilder = VerificationResult.getBuilder() - .withMessage( - getMessageText( - currentRuleIdentifiers, - checkDifferentConclusion( - currentRuleIdentifiers.get(0).getDecisionKey(), - currentRuleIdentifiers))); + .withMessage(getMessageText(currentRuleIdentifiers, differentConclusions)); vBuilder.addRules(currentRuleIdentifiers); - addVerification(vBuilder.build()); + VerificationResult vResult = vBuilder.build(); + if (differentConclusions) { + interdeterminismRules.add(vResult); + } + addVerification(vResult); } } else { List<Value> currentBounds = new ArrayList<>(); @@ -142,7 +144,9 @@ public class OverlappingRules extends AbstractVerifier { } @Override - protected void afterVerifyDecision() {} + protected void afterVerifyDecision() { + // Nothing + } private String getMessageText( List<RuleIdentifier> currentRuleIdentifiers, boolean isDifferentConclusion) { diff --git a/dmn-verifier-app/src/main/java/de/unikoblenz/fgbks/dmn/core/verifier/PartialReductionRules.java b/dmn-verifier-app/src/main/java/de/unikoblenz/fgbks/dmn/core/verifier/PartialReductionRules.java index 15da25ef0e73cb1cb4f95bf416830d55ade54a75..83bae0c96a091bbefdc70dbb2d7d86c4fd6117ce 100644 --- a/dmn-verifier-app/src/main/java/de/unikoblenz/fgbks/dmn/core/verifier/PartialReductionRules.java +++ b/dmn-verifier-app/src/main/java/de/unikoblenz/fgbks/dmn/core/verifier/PartialReductionRules.java @@ -18,7 +18,9 @@ public class PartialReductionRules extends AbstractVerifier { } @Override - protected void beforeVerifyDecision() {} + protected void beforeVerifyDecision() { + // Nothing + } @Override protected void verifyDecision(DmnDecision d) { @@ -157,5 +159,7 @@ public class PartialReductionRules extends AbstractVerifier { } @Override - protected void afterVerifyDecision() {} + protected void afterVerifyDecision() { + // Nothing + } } diff --git a/dmn-verifier-app/src/main/java/de/unikoblenz/fgbks/dmn/core/verifier/SubsumptionRules.java b/dmn-verifier-app/src/main/java/de/unikoblenz/fgbks/dmn/core/verifier/SubsumptionRules.java index b5f219670da39634f8e39f89aee0869e58d9eeca..c003c28b1b67e7575028dcd7901b7197601c733e 100644 --- a/dmn-verifier-app/src/main/java/de/unikoblenz/fgbks/dmn/core/verifier/SubsumptionRules.java +++ b/dmn-verifier-app/src/main/java/de/unikoblenz/fgbks/dmn/core/verifier/SubsumptionRules.java @@ -39,20 +39,23 @@ public class SubsumptionRules extends AbstractVerifier { if (i == inputs.size()) { // finish, if subsumption was found previously, than add the rules if (hasSubsumption && currentRuleIdentifiers.size() > currentRootSubsumptionElements.size()) { + boolean differentConclusions = + checkDifferentConclusion( + currentRootSubsumptionElements.get(0).getRuleIdentifier().getDecisionKey(), + currentRuleIdentifiers); VerificationResult.Builder vBuilder = VerificationResult.getBuilder() .withMessage( getMessageText( currentRuleIdentifiers, currentRootSubsumptionElements, - checkDifferentConclusion( - currentRootSubsumptionElements - .get(0) - .getRuleIdentifier() - .getDecisionKey(), - currentRuleIdentifiers))); + differentConclusions)); vBuilder.addRules(currentRuleIdentifiers); - addVerification(vBuilder.build()); + VerificationResult vResult = vBuilder.build(); + if (differentConclusions) { + interdeterminismRules.add(vResult); + } + addVerification(vResult); } } else { // get all input values from the current column, and filter with the prev. iteration