diff --git a/bundles/cnf/releaserepo/index.xml.sha b/bundles/cnf/releaserepo/index.xml.sha deleted file mode 100644 index 07ba11363..000000000 --- a/bundles/cnf/releaserepo/index.xml.sha +++ /dev/null @@ -1 +0,0 @@ -0400d6060920df51e4069b81cd760256a0e891a20f37e9dc0a06c4d05a97367b diff --git a/bundles/specmate-cause-effect-patterns/.classpath b/bundles/specmate-cause-effect-patterns/.classpath index 2d7348660..c790fdc43 100644 --- a/bundles/specmate-cause-effect-patterns/.classpath +++ b/bundles/specmate-cause-effect-patterns/.classpath @@ -1,11 +1,11 @@ - - - - - - - - - - - + + + + + + + + + + + diff --git a/bundles/specmate-integration-test/.classpath b/bundles/specmate-integration-test/.classpath index cce7ccc85..59c129156 100644 --- a/bundles/specmate-integration-test/.classpath +++ b/bundles/specmate-integration-test/.classpath @@ -1,12 +1,12 @@ - - - - - - - - - - - - + + + + + + + + + + + + diff --git a/bundles/specmate-integration-test/resources/test_rules.objectif b/bundles/specmate-integration-test/resources/test_rules.objectif index 6b969e482..9e51fa68c 100644 --- a/bundles/specmate-integration-test/resources/test_rules.objectif +++ b/bundles/specmate-integration-test/resources/test_rules.objectif @@ -1,4 +1,5 @@ -WENN A=5 UND B=3 DANN C=7 ENDE-WENN +WENN A=5 UND X=9 DANN C=7 ENDE-WENN WENN X = Z ODER Y>4 DANN F="Test" ENDE-WENN WENN A=1 DANN WENN B=1 DANN C=1 ENDE-WENN ENDE-WENN -WENN A=1 DANN B=1 SONST C=2 ENDE-WENN \ No newline at end of file +WENN A=1 DANN B=1 SONST C=2 ENDE-WENN +WENN A=1 DANN WENN B=1 DANN C=1 SONST C=2 ENDE-WENN ENDE-WENN \ No newline at end of file diff --git a/bundles/specmate-integration-test/src/com/specmate/test/integration/ObjectifTest.java b/bundles/specmate-integration-test/src/com/specmate/test/integration/ObjectifTest.java index 597ba4e44..96c17052e 100644 --- a/bundles/specmate-integration-test/src/com/specmate/test/integration/ObjectifTest.java +++ b/bundles/specmate-integration-test/src/com/specmate/test/integration/ObjectifTest.java @@ -7,9 +7,14 @@ import java.util.List; import org.eclipse.emf.common.util.URI; +import org.json.JSONArray; import org.osgi.framework.Bundle; import org.osgi.framework.FrameworkUtil; +import com.specmate.model.requirements.CEGModel; +import com.specmate.model.requirements.CEGNode; +import com.specmate.model.requirements.NodeType; +import com.specmate.model.requirements.RequirementsFactory; import com.specmate.objectif.resolve.BusinessRuleUtil; import com.specmate.objectif.resolve.rule.AndNode; import com.specmate.objectif.resolve.rule.BusinessRuleNode; @@ -18,95 +23,267 @@ import com.specmate.objectif.resolve.rule.OrNode; import com.specmate.xtext.XTextException; -public class ObjectifTest { +// Two types of tests: +// 1) tests for checking the functionality of the xtext parsing +// 2) tests for checking the functionality of the pseudo code translator +public class ObjectifTest extends ModelGenerationTestBase { + + public ObjectifTest() throws Exception { + super(); + } + + // Unit tests for the Xtext Parsing @Test public void testLoadRules() throws URISyntaxException, XTextException { List rules = loadRules("/resources/test_rules.objectif"); - Assert.assertTrue(rules.size() == 4); + Assert.assertTrue(rules.size() == 5); } - + @Test public void testAND() throws URISyntaxException, XTextException { List rules = loadRules("/resources/test_rules.objectif"); BusinessRuleNode rule = rules.get(0); ObjectifNode cause = rule.getCause(); Assert.assertTrue(cause instanceof AndNode); - - ObjectifNode causeA = ((AndNode)cause).getLeft(); - ObjectifNode causeB = ((AndNode)cause).getRight(); + + ObjectifNode causeA = ((AndNode) cause).getLeft(); + ObjectifNode causeB = ((AndNode) cause).getRight(); Assert.assertTrue(causeA instanceof LiteralNode); - Assert.assertTrue(((LiteralNode)causeA).getContent().equals("A=5")); + Assert.assertTrue(((LiteralNode) causeA).getContent().equals("A=5")); Assert.assertTrue(causeB instanceof LiteralNode); - Assert.assertTrue(((LiteralNode)causeB).getContent().equals("B=3")); - + Assert.assertTrue(((LiteralNode) causeB).getContent().equals("X=9")); + ObjectifNode effect = rule.getEffect(); Assert.assertTrue(effect instanceof LiteralNode); - Assert.assertTrue(((LiteralNode)effect).getContent().equals("C=7")); + Assert.assertTrue(((LiteralNode) effect).getContent().equals("C=7")); } - + @Test public void testOR() throws URISyntaxException, XTextException { List rules = loadRules("/resources/test_rules.objectif"); BusinessRuleNode rule = rules.get(1); ObjectifNode cause = rule.getCause(); Assert.assertTrue(cause instanceof OrNode); - - ObjectifNode causeA = ((OrNode)cause).getLeft(); - ObjectifNode causeB = ((OrNode)cause).getRight(); + + ObjectifNode causeA = ((OrNode) cause).getLeft(); + ObjectifNode causeB = ((OrNode) cause).getRight(); Assert.assertTrue(causeA instanceof LiteralNode); - Assert.assertTrue(((LiteralNode)causeA).getContent().equals("X = Z")); + Assert.assertTrue(((LiteralNode) causeA).getContent().equals("X = Z")); Assert.assertTrue(causeB instanceof LiteralNode); - Assert.assertTrue(((LiteralNode)causeB).getContent().equals("Y>4")); - + Assert.assertTrue(((LiteralNode) causeB).getContent().equals("Y>4")); + ObjectifNode effect = rule.getEffect(); Assert.assertTrue(effect instanceof LiteralNode); - Assert.assertTrue(((LiteralNode)effect).getContent().equals("F=\"Test\"")); + Assert.assertTrue(((LiteralNode) effect).getContent().equals("F=\"Test\"")); } - + @Test public void testNesting() throws URISyntaxException, XTextException { List rules = loadRules("/resources/test_rules.objectif"); BusinessRuleNode rule = rules.get(2); ObjectifNode cause = rule.getCause(); Assert.assertTrue(cause instanceof LiteralNode); - Assert.assertTrue(((LiteralNode)cause).getContent().equals("A=1")); - + Assert.assertTrue(((LiteralNode) cause).getContent().equals("A=1")); + ObjectifNode effect = rule.getEffect(); Assert.assertTrue(effect instanceof BusinessRuleNode); - - BusinessRuleNode ruleB = (BusinessRuleNode) effect; - + + BusinessRuleNode ruleB = (BusinessRuleNode) effect; + ObjectifNode causeB = ruleB.getCause(); Assert.assertTrue(causeB instanceof LiteralNode); - Assert.assertTrue(((LiteralNode)causeB).getContent().equals("B=1")); - + Assert.assertTrue(((LiteralNode) causeB).getContent().equals("B=1")); + ObjectifNode effectB = ruleB.getEffect(); Assert.assertTrue(effectB instanceof LiteralNode); - Assert.assertTrue(((LiteralNode)effectB).getContent().equals("C=1")); + Assert.assertTrue(((LiteralNode) effectB).getContent().equals("C=1")); } - + @Test public void testAlternative() throws URISyntaxException, XTextException { List rules = loadRules("/resources/test_rules.objectif"); BusinessRuleNode rule = rules.get(3); ObjectifNode cause = rule.getCause(); Assert.assertTrue(cause instanceof LiteralNode); - Assert.assertTrue(((LiteralNode)cause).getContent().equals("A=1")); + Assert.assertTrue(((LiteralNode) cause).getContent().equals("A=1")); ObjectifNode effect = rule.getEffect(); Assert.assertTrue(effect instanceof LiteralNode); - Assert.assertTrue(((LiteralNode)effect).getContent().equals("B=1")); - + Assert.assertTrue(((LiteralNode) effect).getContent().equals("B=1")); + Assert.assertTrue(rule.hasAlternative()); ObjectifNode alternative = rule.getAlternative(); Assert.assertTrue(alternative instanceof LiteralNode); - Assert.assertTrue(((LiteralNode)alternative).getContent().equals("C=2")); + Assert.assertTrue(((LiteralNode) alternative).getContent().equals("C=2")); + } + + @Test + public void testAlternativeCombinedWithNesting() throws URISyntaxException, XTextException { + List rules = loadRules("/resources/test_rules.objectif"); + BusinessRuleNode rule = rules.get(4); + ObjectifNode cause = rule.getCause(); + Assert.assertTrue(cause instanceof LiteralNode); + Assert.assertTrue(((LiteralNode) cause).getContent().equals("A=1")); + ObjectifNode effect = rule.getEffect(); + Assert.assertTrue(effect instanceof BusinessRuleNode); + BusinessRuleNode ruleB = (BusinessRuleNode) effect; + + ObjectifNode causeB = ruleB.getCause(); + Assert.assertTrue(causeB instanceof LiteralNode); + Assert.assertTrue(((LiteralNode) causeB).getContent().equals("B=1")); + + ObjectifNode effectB = ruleB.getEffect(); + Assert.assertTrue(effectB instanceof LiteralNode); + Assert.assertTrue(((LiteralNode) effectB).getContent().equals("C=1")); + + Assert.assertTrue(ruleB.hasAlternative()); + ObjectifNode alternative = ruleB.getAlternative(); + Assert.assertTrue(alternative instanceof LiteralNode); + Assert.assertTrue(((LiteralNode) alternative).getContent().equals("C=2")); + + } + + // Unit tests for the pseudo code translation + @Test + public void testModelGenerationSimplePseudoCode() { + String text = "WENN x DANN y ENDE-WENN"; + RequirementsFactory f = RequirementsFactory.eINSTANCE; + CEGModel model = f.createCEGModel(); + CEGNode node1 = createNode(model, "BR1", "is present", NodeType.OR); + CEGNode node2 = createNode(model, "x", "is present", null); + CEGNode node3 = createNode(model, "y", "is present", null); + createConnection(model, node1, node3, false); + createConnection(model, node2, node1, false); + JSONArray generated = generateCEGWithModelRequirementsText(text); + checkResultingModel(generated, model); + } + + @Test + public void testModelGenerationPseudoCodeWithInterconnectedCauses() { + String text = "WENN x UND y UND z ODER w DANN effekt ENDE-WENN"; + RequirementsFactory f = RequirementsFactory.eINSTANCE; + CEGModel model = f.createCEGModel(); + CEGNode node1 = createNode(model, "BR1", "is present", NodeType.OR); + CEGNode node2 = createNode(model, "x", "is present", null); + CEGNode node3 = createNode(model, "y", "is present", null); + CEGNode node4 = createNode(model, "z", "is present", null); + CEGNode node5 = createNode(model, "w", "is present", null); + CEGNode node6 = createNode(model, "effekt", "is present", null); + CEGNode node7 = createNode(model, "Intermediate Node1", "is present", NodeType.AND); + createConnection(model, node2, node7, false); + createConnection(model, node3, node7, false); + createConnection(model, node4, node7, false); + createConnection(model, node5, node1, false); + createConnection(model, node7, node1, false); + createConnection(model, node1, node6, false); + JSONArray generated = generateCEGWithModelRequirementsText(text); + checkResultingModel(generated, model); + } + + @Test + public void testModelGenerationPseudoCodeWithInterconnectedCausesAndSingleNesting() { + String text = "WENN x UND y UND z ODER w DANN WENN xB ODER yB DANN effekt ENDE-WENN ENDE-WENN"; + RequirementsFactory f = RequirementsFactory.eINSTANCE; + CEGModel model = f.createCEGModel(); + CEGNode node1 = createNode(model, "BR1", "is present", NodeType.OR); + CEGNode node2 = createNode(model, "x", "is present", null); + CEGNode node3 = createNode(model, "y", "is present", null); + CEGNode node4 = createNode(model, "z", "is present", null); + CEGNode node5 = createNode(model, "w", "is present", null); + CEGNode node7 = createNode(model, "Intermediate Node1", "is present", NodeType.AND); + + CEGNode node8 = createNode(model, "BR2", "is present", NodeType.AND); + CEGNode node9 = createNode(model, "xB", "is present", null); + CEGNode node10 = createNode(model, "yB", "is present", null); + CEGNode node6 = createNode(model, "effekt", "is present", null); + CEGNode node11 = createNode(model, "Intermediate Node2", "is present", NodeType.OR); + + createConnection(model, node2, node7, false); + createConnection(model, node3, node7, false); + createConnection(model, node4, node7, false); + createConnection(model, node5, node1, false); + createConnection(model, node7, node1, false); + createConnection(model, node1, node8, false); + createConnection(model, node9, node11, false); + createConnection(model, node10, node11, false); + createConnection(model, node11, node8, false); + createConnection(model, node8, node6, false); + JSONArray generated = generateCEGWithModelRequirementsText(text); + checkResultingModel(generated, model); + } + + @Test + public void testModelGenerationPseudoCodeWithMultipleNesting() { + String text = "WENN x UND y DANN WENN xB ODER yB DANN WENN xC UND yC DANN effekt ENDE-WENN ENDE-WENN ENDE-WENN"; + RequirementsFactory f = RequirementsFactory.eINSTANCE; + CEGModel model = f.createCEGModel(); + CEGNode node1 = createNode(model, "BR1", "is present", NodeType.AND); + CEGNode node2 = createNode(model, "x", "is present", null); + CEGNode node3 = createNode(model, "y", "is present", null); + + CEGNode node4 = createNode(model, "BR2", "is present", NodeType.AND); + CEGNode node5 = createNode(model, "xB", "is present", null); + CEGNode node6 = createNode(model, "yB", "is present", null); + CEGNode node7 = createNode(model, "Intermediate Node1", "is present", NodeType.OR); + + CEGNode node8 = createNode(model, "BR3", "is present", NodeType.AND); + CEGNode node9 = createNode(model, "xC", "is present", null); + CEGNode node10 = createNode(model, "yC", "is present", null); + CEGNode node11 = createNode(model, "effekt", "is present", null); + + createConnection(model, node2, node1, false); + createConnection(model, node3, node1, false); + createConnection(model, node1, node4, false); + createConnection(model, node5, node7, false); + createConnection(model, node6, node7, false); + createConnection(model, node7, node4, false); + createConnection(model, node4, node8, false); + createConnection(model, node9, node8, false); + createConnection(model, node10, node8, false); + createConnection(model, node8, node11, false); + JSONArray generated = generateCEGWithModelRequirementsText(text); + checkResultingModel(generated, model); } + @Test + public void testModelGenerationPseudoCodeWithMultipleNestingAndAlternativeEffect() { + String text = "WENN x UND y DANN WENN xB ODER yB DANN WENN xC UND yC DANN effekt SONST aEffekt ENDE-WENN ENDE-WENN ENDE-WENN"; + RequirementsFactory f = RequirementsFactory.eINSTANCE; + CEGModel model = f.createCEGModel(); + CEGNode node1 = createNode(model, "BR1", "is present", NodeType.AND); + CEGNode node2 = createNode(model, "x", "is present", null); + CEGNode node3 = createNode(model, "y", "is present", null); + + CEGNode node4 = createNode(model, "BR2", "is present", NodeType.AND); + CEGNode node5 = createNode(model, "xB", "is present", null); + CEGNode node6 = createNode(model, "yB", "is present", null); + CEGNode node7 = createNode(model, "Intermediate Node1", "is present", NodeType.OR); + + CEGNode node8 = createNode(model, "BR3", "is present", NodeType.AND); + CEGNode node9 = createNode(model, "xC", "is present", null); + CEGNode node10 = createNode(model, "yC", "is present", null); + CEGNode node11 = createNode(model, "effekt", "is present", null); + CEGNode node12 = createNode(model, "aEffekt", "is present", null); + + createConnection(model, node2, node1, false); + createConnection(model, node3, node1, false); + createConnection(model, node1, node4, false); + createConnection(model, node5, node7, false); + createConnection(model, node6, node7, false); + createConnection(model, node7, node4, false); + createConnection(model, node4, node8, false); + createConnection(model, node9, node8, false); + createConnection(model, node10, node8, false); + createConnection(model, node8, node11, false); + createConnection(model, node8, node12, true); + JSONArray generated = generateCEGWithModelRequirementsText(text); + checkResultingModel(generated, model); + } + private List loadRules(String mainFile) throws URISyntaxException, XTextException { URI main = getLocalFile(mainFile); return new BusinessRuleUtil().loadXTextResources(main); } - + private URI getLocalFile(String fileName) throws URISyntaxException { Bundle bundle = FrameworkUtil.getBundle(NLPServiceTest.class); return URI.createURI(bundle.getResource(fileName).toURI().toString()); diff --git a/bundles/specmate-model-generation/bnd.bnd b/bundles/specmate-model-generation/bnd.bnd index 12df0f4a1..05bdfae5d 100644 --- a/bundles/specmate-model-generation/bnd.bnd +++ b/bundles/specmate-model-generation/bnd.bnd @@ -20,7 +20,8 @@ specmate-metrics;version=latest,\ specmate-cause-effect-patterns,\ specmate-config-api;version=latest,\ - specmate-xtext;version=latest + specmate-xtext;version=latest,\ + specmate-objectif Private-Package: \ com.specmate.modelgeneration,\ com.specmate.modelgeneration.legacy diff --git a/bundles/specmate-model-generation/generated/buildfiles b/bundles/specmate-model-generation/generated/buildfiles new file mode 100644 index 000000000..d354c9596 --- /dev/null +++ b/bundles/specmate-model-generation/generated/buildfiles @@ -0,0 +1 @@ +A:/specmate/bundles/specmate-model-generation/generated/specmate-model-generation.jar diff --git a/bundles/specmate-model-generation/src/com/specmate/modelgeneration/GenerateModelFromPseudoCode.java b/bundles/specmate-model-generation/src/com/specmate/modelgeneration/GenerateModelFromPseudoCode.java new file mode 100644 index 000000000..cf0108888 --- /dev/null +++ b/bundles/specmate-model-generation/src/com/specmate/modelgeneration/GenerateModelFromPseudoCode.java @@ -0,0 +1,378 @@ +package com.specmate.modelgeneration; + +import java.util.ArrayList; +import java.util.List; + +import com.specmate.common.exception.SpecmateException; +import com.specmate.common.exception.SpecmateInternalException; +import com.specmate.model.administration.ErrorCode; +import com.specmate.model.requirements.CEGModel; +import com.specmate.model.requirements.CEGNode; +import com.specmate.model.requirements.NodeType; +import com.specmate.objectif.resolve.BusinessRuleUtil; +import com.specmate.objectif.resolve.rule.AndNode; +import com.specmate.objectif.resolve.rule.BusinessRuleNode; +import com.specmate.objectif.resolve.rule.LiteralNode; +import com.specmate.objectif.resolve.rule.ObjectifNode; +import com.specmate.objectif.resolve.rule.OrNode; +import com.specmate.xtext.XTextException; + +// This class transforms pseudo code into a CEG. +// The pseudo code must conform with the implemented XText grammar. +public class GenerateModelFromPseudoCode implements ICEGFromRequirementGenerator { + + // Our factory to create the elements of the CEG (nodes and connections). + CEGCreation creation = new CEGCreation(); + + // Counter for numbering of intermediate nodes as well as business rule nodes. + int intermediatenum = 1; + int brnum = 1; + + @Override + public CEGModel createModel(CEGModel model, String text) throws SpecmateException { + List rules; + // First, we need to load all pseudo code lines from a document / input. + try { + rules = new BusinessRuleUtil().parseXTextResource(text); + } catch (XTextException e) { + throw new SpecmateInternalException(ErrorCode.INTERNAL_PROBLEM, + "Unfortunately, the XText parsing of your inserted pseudo code went wrong." + e.getMessage()); + } + + BusinessRuleNode rule = rules.get(0); + return createCEGModelForEachSection(rule, model, null, false); + } + + // This method creates a CEG model for each "WENN" --> "DANN" --> "ENDE-WENN" + // clause. + private CEGModel createCEGModelForEachSection(BusinessRuleNode rule, CEGModel model, CEGNode brNode, + boolean nesting) { + + // The type of a BR node should be always "OR". Exception: The model consists + // only of conjunctive causes. + CEGNode cegNodeBR; + + // If the brNode is null, we start with a new model. + if (brNode == null) { + cegNodeBR = creation.createNode(model, "BR" + (brnum++), "is present", 0, 0, NodeType.OR); + } else { + // Otherwise, we are within a recursive loop (i.e. the clause is nested) and the + // brNode is our superior + // business rule. + cegNodeBR = brNode; + } + + // Check for nesting: + // The rootEffect contains all effects of a rule. If the rootEffect is a + // "BusinessRuleNode", the rule is nested and consists of multiple rules. + // Here we check if we need to do a recursion again or if we reached the + // business rule at the lowest depth (i.e. the business rule which does not + // contains another one). + ObjectifNode rootEffect = rule.getEffect(); + if (rootEffect instanceof BusinessRuleNode) { + // Case: nested + // Basic idea: Split the whole tree in subtrees (i.e. split the tree into + // individual business rules) and connect first the causes to these business + // rules and then simply connect superior rules to inferior rules. + CEGNode cegNodeBRNested = creation.createNode(model, "BR" + (brnum++), "is present", 0, 0, NodeType.AND); + List> andCausesGroups = new ArrayList>(); + List singleOrCauses = new ArrayList(); + + ObjectifNode rootCause = rule.getCause(); + identifyCauses(rootCause, singleOrCauses, andCausesGroups); + + // superior rule + if (nesting == false) { + connectCausesToBR(cegNodeBR, singleOrCauses, andCausesGroups, model); + } else { + // inferior rule + connectCausesToNestedBR(cegNodeBR, singleOrCauses, andCausesGroups, model); + } + + // We do not need to identify the effects of the business rule since we know + // that the rule contains another one and is therefore a condition for the + // nested rule. We simply connect the rule with the inferior rule. + creation.createConnection(model, cegNodeBR, cegNodeBRNested, false); + + // Start with the recursion. We update the root and do the whole procedure for + // the inferior rule again. Until we reach a rule which is not nested anymore. + createCEGModelForEachSection((BusinessRuleNode) rootEffect, model, cegNodeBRNested, true); + + } else { + // Case: not nested + // RootCause contains all causes of a rule. + ObjectifNode rootCause = rule.getCause(); + List effects = new ArrayList(); + List alternativeEffects = new ArrayList(); + + identifyEffects(rootEffect, effects); + + if (rule.hasAlternative()) { + rootEffect = rule.getAlternative(); + identifyEffects(rootEffect, alternativeEffects); + } + + // 1. Case = The rule contains only one cause. This is the case if the rootCause + // is a "LiteralNode". + if (rootCause instanceof LiteralNode) { + // Create a node with the content of the rootCause and connect the node with the + // rule node by means of identity connection. + CEGNode cegNode = creation.createNode(model, ((LiteralNode) rootCause).getContent(), "is present", 0, 0, + null); + + creation.createConnection(model, cegNode, cegNodeBR, false); + // Search for effects of the rule. + //identifyEffects(rootEffect, effects); + +// if (rule.hasAlternative()) { +// rootEffect = rule.getAlternative(); +// identifyEffects(rootEffect, alternativeEffects); +// } + + // Connect the rule node with all identified effects. + for (ObjectifNode effect : effects) { + CEGNode cegNodeEffect = creation.createNode(model, ((LiteralNode) effect).getContent(), + "is present", 0, 0, null); + creation.createConnection(model, cegNodeBR, cegNodeEffect, false); + } + + // Connect the rule node with its alternative effects + for (ObjectifNode effect : alternativeEffects) { + CEGNode cegNodeEffect = creation.createNode(model, ((LiteralNode) effect).getContent(), + "is present", 0, 0, null); + creation.createConnection(model, cegNodeBR, cegNodeEffect, true); + } + } else { + + // 2. Case = The rule contains multiple causes which are interconnected + // (conjunctions or disjunctions). + List> andCausesGroups = new ArrayList>(); + List singleOrCauses = new ArrayList(); + + identifyCauses(rootCause, singleOrCauses, andCausesGroups); + //identifyEffects(rootEffect, effects); + + if (nesting == false) { + connectCausesToBR(cegNodeBR, singleOrCauses, andCausesGroups, model); + } else { + connectCausesToNestedBR(cegNodeBR, singleOrCauses, andCausesGroups, model); + } + connectBRToEffects(cegNodeBR, effects, alternativeEffects, model); + } + } + return model; + } + + private void connectBRToEffects(CEGNode cegNodeBR, List effects, + List alternativeEffects, CEGModel model) { + // Iterate over all effects and connect the business rule node with these + // effects. + for (ObjectifNode effect : effects) { + CEGNode cegNode = creation.createNode(model, ((LiteralNode) effect).getContent(), "is present", 0, 0, null); + creation.createConnection(model, cegNodeBR, cegNode, false); + } + for (ObjectifNode effect : alternativeEffects) { + CEGNode cegNode = creation.createNode(model, ((LiteralNode) effect).getContent(), "is present", 0, 0, null); + creation.createConnection(model, cegNodeBR, cegNode, true); + } + } + + // Implementation for non nested BRs + private void connectCausesToBR(CEGNode cegNodeBR, List singleOrCauses, + List> andCausesGroups, CEGModel model) { + + for (List andCauses : andCausesGroups) { + + CEGNode intermediateNode = null; + + if (andCausesGroups.size() > 0 && singleOrCauses.size() != 0) { + intermediateNode = creation.createNode(model, "Intermediate Node" + (intermediatenum++), "is present", + 0, 0, NodeType.AND); + + creation.createConnection(model, intermediateNode, cegNodeBR, false); + } + + for (ObjectifNode cause : andCauses) { + CEGNode cegNode = creation.createNode(model, ((LiteralNode) cause).getContent(), "is present", 0, 0, + null); + + // There are two cases: + // a) there is only one set of conjunctive causes and no single / disjunctive + // causes --> just connect the causes with the rule without intermediate node. + if (andCausesGroups.size() == 1 && singleOrCauses.size() == 0) { + creation.createConnection(model, cegNode, cegNodeBR, false); + cegNodeBR.setType(NodeType.AND); + } else { + // b) otherwise we need an intermediate node and connect conjunctive causes to + // these nodes. All related causes share the same intermediate node! + creation.createConnection(model, cegNode, intermediateNode, false); + } + } + } + // We iterate over all single causes and connect them with the BR by means of a + // disjunction. + for (ObjectifNode cause : singleOrCauses) { + CEGNode cegNode = creation.createNode(model, ((LiteralNode) cause).getContent(), "is present", 0, 0, null); + creation.createConnection(model, cegNode, cegNodeBR, false); + } + } + + // Implementation for nested BRs: + // It is important to differentiate between non nested and nested BR since + // nested BRs have always the NodeType "AND" (because superior BRs are always a + // condition for the nested ones). As a result, here we need intermediate nodes + // for the disjunctive causes and no intermediate nodes for the conjunction + // since they can be directly connected to the BR. + private void connectCausesToNestedBR(CEGNode cegNodeBR, List singleOrCauses, + List> andCausesGroups, CEGModel model) { + + CEGNode intermediateNodeNested = null; + + if (singleOrCauses.size() > 1) { + intermediateNodeNested = creation.createNode(model, "Intermediate Node" + (intermediatenum++), "is present", + 0, 0, NodeType.OR); + creation.createConnection(model, intermediateNodeNested, cegNodeBR, false); + } + + for (List andCauses : andCausesGroups) { + + CEGNode intermediateNode = null; + + if (andCausesGroups.size() > 0 && singleOrCauses.size() != 0) { + intermediateNode = creation.createNode(model, "Intermediate Node" + (intermediatenum++), "is present", + 0, 0, NodeType.AND); + creation.createConnection(model, intermediateNode, intermediateNodeNested, false); + } + + for (ObjectifNode cause : andCauses) { + CEGNode cegNode = creation.createNode(model, ((LiteralNode) cause).getContent(), "is present", 0, 0, + null); + + if (singleOrCauses.size() == 1 || andCausesGroups.size() == 1 && singleOrCauses.size() == 0) { + creation.createConnection(model, cegNode, cegNodeBR, false); + } else { + creation.createConnection(model, cegNode, intermediateNode, false); + } + } + } + + for (ObjectifNode cause : singleOrCauses) { + CEGNode cegNode = creation.createNode(model, ((LiteralNode) cause).getContent(), "is present", 0, 0, null); + if (singleOrCauses.size() == 1) { + creation.createConnection(model, cegNode, cegNodeBR, false); + } else { + creation.createConnection(model, cegNode, intermediateNodeNested, false); + } + } + } + + private void identifyEffects(ObjectifNode rootEffect, List effects) { + // 1. Case: There is only a single effect + if (rootEffect instanceof LiteralNode) { + effects.add(rootEffect); + } + + // 2. Case: The effects are interconnected. + if (rootEffect instanceof AndNode) { + + if (rootEffect.getLeft() instanceof LiteralNode) { + effects.add(rootEffect.getLeft()); + } + + if (rootEffect.getRight() instanceof LiteralNode) { + effects.add(rootEffect.getRight()); + } + + if (rootEffect.getLeft() instanceof AndNode) { + connectAndCauses(rootEffect.getLeft(), effects); + } + + if (rootEffect.getRight() instanceof AndNode) { + connectAndCauses(rootEffect.getRight(), effects); + } + } + } + + // Here we connect causes which are interconnected using conjunctions. + // Related causes are saved within individual lists. + private void connectAndCauses(ObjectifNode rootCause, List andCauses) { + if (rootCause instanceof AndNode) { + + if (rootCause.getLeft() instanceof LiteralNode) { + // add cause to the list. + andCauses.add(rootCause.getLeft()); + } + + if (rootCause.getRight() instanceof LiteralNode) { + // add cause to the list. + andCauses.add(rootCause.getRight()); + } + + if (rootCause.getLeft() instanceof AndNode) { + // recursive call of the method but with updated rootcause. + // Here we do not (!) create a new list because this is connection of multiple + // conjunctions. So we build a large list of multiple interconnected causes. + connectAndCauses(rootCause.getLeft(), andCauses); + } + + if (rootCause.getRight() instanceof AndNode) { + // recursive call of the method but with updated rootcause. + // See explanation above. + connectAndCauses(rootCause.getRight(), andCauses); + } + } + } + + // Here we separate between isolated causes (disjunctive causes) and related + // causes (conjunctive causes). + private void identifyCauses(ObjectifNode rootCause, List orCauses, + List> causesGroups) { + if (rootCause instanceof OrNode) { + // 1. Case: A single "or" connection + if (rootCause.getLeft() instanceof LiteralNode) { + orCauses.add(rootCause.getLeft()); + } + + // belongs to case 1 + if (rootCause.getRight() instanceof LiteralNode) { + orCauses.add(rootCause.getRight()); + } + + if (rootCause.getLeft() instanceof AndNode) { + // Here we identify a conjunction. We create a list of Objectif nodes for each + // identified conjunction. + List andCauses = new ArrayList(); + connectAndCauses(rootCause.getLeft(), andCauses); + causesGroups.add(andCauses); + } + + if (rootCause.getRight() instanceof AndNode) { + // Here we identify also a conjunction. Same procedure as described above. + List andCauses = new ArrayList(); + connectAndCauses(rootCause.getRight(), andCauses); + causesGroups.add(andCauses); + } + + if (rootCause.getRight() instanceof OrNode) { + // recursive call of the method with updated rootCause + identifyCauses(rootCause.getRight(), orCauses, causesGroups); + } + + if (rootCause.getLeft() instanceof OrNode) { + // same here + identifyCauses(rootCause.getLeft(), orCauses, causesGroups); + } + + } else if (rootCause instanceof AndNode) { + // Exceptional case if rootCause is a "AndNode". + // Here we also need to create a new list of conjunctive nodes. + List andCauses = new ArrayList(); + connectAndCauses(rootCause, andCauses); + causesGroups.add(andCauses); + + } else if (rootCause instanceof LiteralNode) { + // This is the case if the rule consists of only one cause. + orCauses.add(rootCause); + } + } +} diff --git a/bundles/specmate-model-generation/src/com/specmate/modelgeneration/GenerateModelFromRequirementService.java b/bundles/specmate-model-generation/src/com/specmate/modelgeneration/GenerateModelFromRequirementService.java index 2531babb9..0b5fb75be 100644 --- a/bundles/specmate-model-generation/src/com/specmate/modelgeneration/GenerateModelFromRequirementService.java +++ b/bundles/specmate-model-generation/src/com/specmate/modelgeneration/GenerateModelFromRequirementService.java @@ -89,7 +89,13 @@ private CEGModel generateModelFromDescription(CEGModel model) throws SpecmateExc ELanguage lang = NLPUtil.detectLanguage(text); ICEGFromRequirementGenerator generator; - generator = new PatternbasedCEGGenerator(lang, tagger, this.configService); + + if(lang == ELanguage.PSEUDO) { + generator = new GenerateModelFromPseudoCode(); + } else { + generator = new PatternbasedCEGGenerator(lang, tagger, this.configService); + } + try { generator.createModel(model, text); diff --git a/bundles/specmate-model-generators-typescript/bin/.gitignore b/bundles/specmate-model-generators-typescript/bin/.gitignore deleted file mode 100644 index c2d9872a1..000000000 --- a/bundles/specmate-model-generators-typescript/bin/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/com/ diff --git a/bundles/specmate-model-generators-typescript/bin/com/specmate/model/generators/typescript/generateAngular.mtl b/bundles/specmate-model-generators-typescript/bin/com/specmate/model/generators/typescript/generateAngular.mtl new file mode 100644 index 000000000..7a6cbc508 --- /dev/null +++ b/bundles/specmate-model-generators-typescript/bin/com/specmate/model/generators/typescript/generateAngular.mtl @@ -0,0 +1,144 @@ +[comment encoding = UTF-8 /] +[module generateAngular('http://www.eclipse.org/emf/2002/Ecore')] + +[template public generateElement(aClass : EClass)] +[comment @main/] + +[file (aClass.name.concat('.ts'), false)] + import './support/gentypes'; + import { Proxy } from './support/proxy'; + + [comment imports for referenced classes /] + [if ((aClass.ePackage.name='history') or (aClass.ePackage.name='batch'))] + [for (r: EString | aClass.eAllReferences.eType.name->asSet())] + import { [r /] } from './[r/]'; + [/for] + [/if] + + [if (aClass.interface)] + export interface [aClass.name.toUpperFirst()/] { + [else] + export class [aClass.name.toUpperFirst()/] { + [/if] + + ___nsuri: string[if not (aClass.interface)] = "[aClass.ePackage.nsURI /]"[/if]; + [if not (aClass.interface)]public[/if] url: string; + [if not (aClass.interface)]public[/if] className: string[if not (aClass.interface)] = "[aClass.name.toUpperFirst()/]"[/if]; + [if not (aClass.interface)]public static className: string = "[aClass.name.toUpperFirst()/]";[/if] + + // Attributes + [for (a: EAttribute | aClass.eAllAttributes)] + [if not (aClass.interface)]public[/if] [a.name/]: [a.eType.name/][if (a.many)]['['/][']'/][/if]; + [/for] + + // References + [for (r: EReference | aClass.eAllReferences)] + [if not (r.containment)][if (not (aClass.interface))]public[/if] [r.name/]: Proxy[if (r.many)]['['/][']'/][/if];[/if] + [/for] + + // Containment + [comment Containment for history package /] + [if ((aClass.ePackage.name='history') or (aClass.ePackage.name='batch'))] + [for (r: EReference | aClass.eAllReferences)] + [if (r.containment)][if (not (aClass.interface))]public[/if] [r.name/]: [r.eType.name/][if (r.many)]['['/][']'/][/if];[/if] + [/for] + [/if] + + [comment Fetch icon string from model annotation /] + [if (aClass.eAnnotations.details->exists(entry|entry.key='icon'))] + // Icon + public iconClass:string = "[aClass.getEAnnotation('http://specmate.com/gen').details + ->select(entry|entry.key='icon')->first().value/]"; + [/if] + + } + + [/file] +[/template] + +[template public generateEnum(aEnum: EEnum)] +[comment @main/] +[file (aEnum.name.concat('.ts'), false)] +export enum [aEnum.name/] { + [for (el: EEnumLiteral | aEnum.eLiterals) separator(',\n')][el.literal.toUpperCase()/] = [el.value/][/for] +} +[/file] +[/template] + +[template public generateMeta(aPackage: EPackage)] +[comment @main/] +[if (aPackage.name.equalsIgnoreCase('model'))] + [file ('meta/field-meta.ts', false)] + +export class FieldMetaItem { + public name: string; + public shortDesc: string; + public longDesc: string; + public type: string; + public required?: boolean; + public values?: string; + public rows?: string; + public position?: string; + public allowedPattern?: string; +} + +export class MetaInfo { + [for (aClass: EClass | aPackage.eSubpackages.eClassifiers->filter(EClass))] + public static [aClass.name/]: FieldMetaItem['['/][']'/] = ['['/] + [for (attribute: EAttribute | + aClass.eAllAttributes->select(attr: EAttribute | not attr.eAnnotations.getEAnnotation('http://specmate.com/form_meta')->isEmpty() and + aClass.eAnnotations.details->select(detail: EStringToStringMapEntry | detail.key.startsWith('disabled') and detail.value.equalsIgnoreCase(attr.name))->isEmpty()) + ) separator(',')] + { + name: "[attribute.name/]", + [for (detail: EStringToStringMapEntry | attribute.eAnnotations.details) separator(',\n')] + [detail.key/]: [if (not (detail.value.equalsIgnoreCase('true') or detail.value.equalsIgnoreCase('false')))]'[/if][detail.value.replaceAll('\'', '\\\\' + '\'')/][if (not (detail.value.equalsIgnoreCase('true') or detail.value.equalsIgnoreCase('false')))]'[/if][/for] + + }[/for] + [']'/]; + [/for] +} + + [/file] +[/if] +[/template] + +[template public generateGentypes(aPackage: EPackage)] +[comment @main/] + + [if (aPackage.eContainer() = null)] + [file ('support/gentypes.ts', false)] +type EInt = number; +type EDouble = number; +type EString = string; +type EBoolean = boolean; +type NodeType = string; +type OperationType = string; +type EDate = Date; +type ParameterType = string; +type AccessRights = string; +type ErrorCode = string; +type ELong = number; + [/file] + + [file ('support/proxy.ts',false)] + export class Proxy { + public ___proxy = true; + public url:string; + } + [/file] + [/if] + +[/template] + + +[query public default(type: String) : String = + if type='EString' then '""' + else if type='EInt' then 'Number(0)' + else if type='EBoolean' then 'false' + else 'undefined' + endif + endif + endif +/] + diff --git a/bundles/specmate-nlp/src/com/specmate/nlp/api/ELanguage.java b/bundles/specmate-nlp/src/com/specmate/nlp/api/ELanguage.java index 54ee5761c..35f5be429 100644 --- a/bundles/specmate-nlp/src/com/specmate/nlp/api/ELanguage.java +++ b/bundles/specmate-nlp/src/com/specmate/nlp/api/ELanguage.java @@ -1,7 +1,7 @@ package com.specmate.nlp.api; public enum ELanguage { - DE("de", "SUBJ"), EN("en", "subj"); + DE("de", "SUBJ"), EN("en", "subj"), PSEUDO("pseudo", "subj"); private String language; private String subjectDependencyType; diff --git a/bundles/specmate-nlp/src/com/specmate/nlp/util/NLPUtil.java b/bundles/specmate-nlp/src/com/specmate/nlp/util/NLPUtil.java index 6e97be35c..d2b444a45 100644 --- a/bundles/specmate-nlp/src/com/specmate/nlp/util/NLPUtil.java +++ b/bundles/specmate-nlp/src/com/specmate/nlp/util/NLPUtil.java @@ -7,6 +7,7 @@ import java.util.Optional; import java.util.Set; import java.util.StringJoiner; +import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -242,9 +243,14 @@ public static List findDependencies(Collection dependenc private static String DE_Pattern = "\\b(der|die|das|ein|eine|einen)\\b"; public static ELanguage detectLanguage(String text) { + Pattern pattern= Pattern.compile(".*WENN.*ENDE-WENN.*", Pattern.DOTALL); + + if (pattern.matcher(text).matches()) { + return ELanguage.PSEUDO; + } if (text.matches("(?i)(.*)"+DE_Pattern+"(.*)")) { return ELanguage.DE; - } + } return ELanguage.EN; } diff --git a/bundles/specmate-objectif/src/com/specmate/objectif/resolve/rule/ObjectifNode.java b/bundles/specmate-objectif/src/com/specmate/objectif/resolve/rule/ObjectifNode.java index dbe8ae26a..75ebe3004 100644 --- a/bundles/specmate-objectif/src/com/specmate/objectif/resolve/rule/ObjectifNode.java +++ b/bundles/specmate-objectif/src/com/specmate/objectif/resolve/rule/ObjectifNode.java @@ -2,4 +2,11 @@ public abstract class ObjectifNode { abstract void generateCEG(); + public ObjectifNode getLeft() { + return null; + } + + public ObjectifNode getRight() { + return null; + } } diff --git a/bundles/specmate-std-env/dev-specmate-all.bndrun b/bundles/specmate-std-env/dev-specmate-all.bndrun index 9c67c2c15..75688de9e 100644 --- a/bundles/specmate-std-env/dev-specmate-all.bndrun +++ b/bundles/specmate-std-env/dev-specmate-all.bndrun @@ -1,4 +1,4 @@ --runfw: org.eclipse.osgi;version='[3.13.300.v20190218-1622,3.13.300.v20190218-1622]' +-runfw: org.eclipse.osgi;version='[3.13.300.v20190218-1622,3.13.300.v20190218-1622]' -runrequires: \ osgi.identity;filter:='(osgi.identity=specmate-cdo-server)',\ osgi.identity;filter:='(osgi.identity=org.apache.felix.gogo.command)',\ @@ -47,7 +47,8 @@ osgi.identity;filter:='(osgi.identity=specmate-jira-connector)',\ osgi.identity;filter:='(osgi.identity=specmate-nlp)',\ osgi.identity;filter:='(osgi.identity=specmate-model-generation)',\ - bnd.identity;id='log4j' + bnd.identity;id='log4j',\ + bnd.identity;id='specmate-objectif' -runbundles: \ javassist;version='[3.18.1,3.18.2)',\ javax.annotation-api;version='[1.2.0,1.2.1)',\ @@ -222,19 +223,21 @@ org.eclipse.xtext.xbase.lib;version='[2.10.0,2.10.1)',\ specmate-cause-effect-patterns;version=snapshot,\ specmate-xtext;version=snapshot,\ - jaxb-api;version='[2.3.1,2.3.2)' - --runproperties: \ - jetty.http.port=8080,\ - jetty.etc.config.urls='etc/jetty.xml,etc/jetty-http.xml,etc/jetty-deployer.xml,etc/jetty-rewrite.xml',\ - osgi.console=,\ - jetty.home.bundle=specmate-jettystarter,\ - osgi.compatibility.bootdelegation=true,\ - equinox.log.history.max=1000,\ - org.osgi.service.log.admin.loglevel=DEBUG --runrepos: \ - Workspace,\ - Local --runvm: -Xmx6000m\n\ - -Djdk.crypto.KeyAgreement.legacyKDF=true --resolve: auto \ No newline at end of file + jaxb-api;version='[2.3.0,2.3.1)',\ + specmate-objectif;version=snapshot + +-runproperties: \ + jetty.http.port=8080,\ + jetty.etc.config.urls='etc/jetty.xml,etc/jetty-http.xml,etc/jetty-deployer.xml,etc/jetty-rewrite.xml',\ + osgi.console=,\ + jetty.home.bundle=specmate-jettystarter,\ + osgi.compatibility.bootdelegation=true,\ + equinox.log.history.max=1000,\ + org.osgi.service.log.admin.loglevel=DEBUG +-runrepos: \ + Workspace,\ + Local +-runvm: -Xmx6000m\n\ + -Djdk.crypto.KeyAgreement.legacyKDF=true +-resolve: auto +-runee: JavaSE-11 \ No newline at end of file