浏览代码

maintain conformance when adding nodes and deleting attributes

Lucas Heer 7 年之前
父节点
当前提交
5b14d5fdf3
共有 7 个文件被更改,包括 72 次插入24 次删除
  1. 13 0
      commons.py
  2. 17 1
      evolution/attribute_ops.py
  3. 1 0
      evolution/edge_ops.py
  4. 21 13
      evolution/node_ops.py
  5. 17 7
      sketchUI/exm_mainwindow.py
  6. 2 2
      sketchUI/exm_scene.py
  7. 1 1
      wrappers/modelverse_SCCD.py

+ 13 - 0
commons.py

@@ -220,3 +220,16 @@ def new_example_model():
     mv.attr_assign(exm, mid, "is_example", True)
     mv.attr_assign(exm, mid, "is_example", True)
     mv.attr_assign(exm, mid, "descr", "")
     mv.attr_assign(exm, mid, "descr", "")
     return exm
     return exm
+
+def get_nodes_with_attribute(model, attr_key, node_type):
+    """
+    Returns all nodes which have an attribute identified by attr_key and are typed by node_type
+    """
+    ret = []
+    typed_nodes = all_nodes_with_type(model, node_type)
+    for node_id in typed_nodes:
+        attrs = get_attributes_of_node(model, node_id)
+        for attr in attrs:
+            if attr.key == attr_key:
+                ret.append(node_id)
+    return ret

+ 17 - 1
evolution/attribute_ops.py

@@ -25,6 +25,7 @@ class AttributeAdd(object):
         if local:
         if local:
             mv.transformation_execute_MANUAL("graph_ops/add_attribute", {"gm":model}, {"gm":model},
             mv.transformation_execute_MANUAL("graph_ops/add_attribute", {"gm":model}, {"gm":model},
                                              callback=self._callback)
                                              callback=self._callback)
+            # TODO: local add can make attribute mandatory and break conformance relationship of instance models
         else:
         else:
             for m in commons.all_models():
             for m in commons.all_models():
                 nodes = commons.all_nodes_with_type(m, self._node_type)
                 nodes = commons.all_nodes_with_type(m, self._node_type)
@@ -44,7 +45,7 @@ class AttributeDelete(object):
         self._node_id = ""
         self._node_id = ""
         self._node_type = ""
         self._node_type = ""
 
 
-    def execute(self, model, node_id, key, local):
+    def execute(self, model, node_id, key, local, check_if_last=False):
         """
         """
         Deletes an attribute identified by its key from node_id in model.
         Deletes an attribute identified by its key from node_id in model.
         """
         """
@@ -57,6 +58,21 @@ class AttributeDelete(object):
         if local:
         if local:
             mv.transformation_execute_MANUAL("graph_ops/del_attribute", {"gm":model}, {"gm":model},
             mv.transformation_execute_MANUAL("graph_ops/del_attribute", {"gm":model}, {"gm":model},
                                              callback=self._callback)
                                              callback=self._callback)
+        if check_if_last:
+            # a local attribute delete can break the conformance for instance models if the attribute was the last in
+            # all example models -> check and correct this if necessary
+            for exm in commons.all_example_models():
+                all_attrs = commons.get_all_attributes_of_type(exm, self._node_type)
+                for attr in all_attrs:
+                    if attr.key == key:
+                        # wasn't the last, we're done here
+                        return
+            # it was the last -> delete this attribute in all instance models to maintain conformance
+            print("Attribute {} was the last, deleting it from all instance models to maintain conformance ...".format(key))
+            for im in commons.all_instance_models():
+                nodes = commons.get_nodes_with_attribute(im, key, self._node_type)
+                for node in nodes:
+                    self.execute(im, node, key, local=True, check_if_last=False)
         else:
         else:
             # delete all attributes with key
             # delete all attributes with key
             for m in commons.all_models():
             for m in commons.all_models():

+ 1 - 0
evolution/edge_ops.py

@@ -16,6 +16,7 @@ class EdgeAdd(object):
         if local:
         if local:
             mv.transformation_execute_MANUAL("graph_ops/add_edge", {"gm":model}, {"gm":model},
             mv.transformation_execute_MANUAL("graph_ops/add_edge", {"gm":model}, {"gm":model},
                                              callback=self._callback)
                                              callback=self._callback)
+            # TODO: local edge add can make an edge mandatory and break the conformance relationship of instance models
         else:
         else:
             from_type = get_node_type(model, self._from_node)
             from_type = get_node_type(model, self._from_node)
             to_type = get_node_type(model, self._to_node)
             to_type = get_node_type(model, self._to_node)

+ 21 - 13
evolution/node_ops.py

@@ -1,14 +1,14 @@
 import sys
 import sys
 sys.path.append("../wrappers")
 sys.path.append("../wrappers")
 from wrappers import modelverse as mv
 from wrappers import modelverse as mv
-from commons import *
+import commons
 
 
 
 
 class NodeAdd(object):
 class NodeAdd(object):
     def __init__(self):
     def __init__(self):
         self._node_type = ""
         self._node_type = ""
 
 
-    def execute(self, model, node_type, local):
+    def execute(self, model, node_type, local, check_if_last=False):
         """
         """
         Add a new node with type node_type to model model.
         Add a new node with type node_type to model model.
         If local is true, the node is only added to the model.
         If local is true, the node is only added to the model.
@@ -18,8 +18,16 @@ class NodeAdd(object):
         if local:
         if local:
             mv.transformation_execute_MANUAL("graph_ops/add_node", {"gm":model}, {"gm":model},
             mv.transformation_execute_MANUAL("graph_ops/add_node", {"gm":model}, {"gm":model},
                                              callback=self._callback)
                                              callback=self._callback)
+            # Local add can make a type mandatory and therefore break conformance
+            if check_if_last:
+                if commons.is_type_mandatory(node_type):
+                    print("Type {} just became mandatory, adding it to instance models ...".format(node_type))
+                    # local add made type mandatory, so need to add to instance models
+                    for im in commons.all_instance_models():
+                        self.execute(im, node_type, local=True, check_if_last=False)
+
         else:
         else:
-            for m in all_models():
+            for m in commons.all_models():
                 self.execute(m, node_type, local=True)
                 self.execute(m, node_type, local=True)
 
 
     def _callback(self, model):
     def _callback(self, model):
@@ -35,7 +43,7 @@ class NodeDelete(object):
 
 
     def execute(self, model, node, local, check_if_last=False):
     def execute(self, model, node, local, check_if_last=False):
         self._node = node
         self._node = node
-        self._node_type = get_node_type(model, node)
+        self._node_type = commons.get_node_type(model, node)
 
 
         if local:
         if local:
             mv.transformation_execute_MANUAL("graph_ops/del_node", {"gm":model}, {"gm":model},
             mv.transformation_execute_MANUAL("graph_ops/del_node", {"gm":model}, {"gm":model},
@@ -44,18 +52,18 @@ class NodeDelete(object):
                 # check if we just deleted the last instance of the node in all example models
                 # check if we just deleted the last instance of the node in all example models
                 # if yes, delete it in instance models as well to preserve their validity
                 # if yes, delete it in instance models as well to preserve their validity
                 remaining_instances = 0
                 remaining_instances = 0
-                for m in all_example_models():
-                    remaining_instances += len(all_nodes_with_type(m, self._node_type))
+                for m in commons.all_example_models():
+                    remaining_instances += len(commons.all_nodes_with_type(m, self._node_type))
                 if remaining_instances == 0:
                 if remaining_instances == 0:
                     # it was indeed the last one, delete from instance models
                     # it was indeed the last one, delete from instance models
                     self._was_last = True
                     self._was_last = True
-                    for m in all_instance_models():
-                        nodes_to_delete = all_nodes_with_type(m, self._node_type)
+                    for m in commons.all_instance_models():
+                        nodes_to_delete = commons.all_nodes_with_type(m, self._node_type)
                         for node in nodes_to_delete:
                         for node in nodes_to_delete:
                             self.execute(m, node, local=True)
                             self.execute(m, node, local=True)
         else:
         else:
-            for m in all_models():
-                nodes_to_delete = all_nodes_with_type(m, self._node_type)
+            for m in commons.all_models():
+                nodes_to_delete = commons.all_nodes_with_type(m, self._node_type)
                 for node in nodes_to_delete:
                 for node in nodes_to_delete:
                     self.execute(m, node, local=True)
                     self.execute(m, node, local=True)
 
 
@@ -83,9 +91,9 @@ class NodeRetype(object):
             mv.transformation_execute_MANUAL("graph_ops/retype_node", {"gm":model}, {"gm":model},
             mv.transformation_execute_MANUAL("graph_ops/retype_node", {"gm":model}, {"gm":model},
                                              callback=self._callback)
                                              callback=self._callback)
         else:
         else:
-            node_type = get_node_type(model, node)
-            for m in all_models():
-                for node in all_nodes_with_type(m, node_type):
+            node_type = commons.get_node_type(model, node)
+            for m in commons.all_models():
+                for node in commons.all_nodes_with_type(m, node_type):
                     self.execute(m, node, new_type, local=True)
                     self.execute(m, node, new_type, local=True)
 
 
     def _callback(self, model):
     def _callback(self, model):

+ 17 - 7
sketchUI/exm_mainwindow.py

@@ -10,7 +10,7 @@ from sketchUI.graphics_node_item import GraphicsNodeItem
 from sketchUI.graphics_edge_item import GraphicsEdgeItem
 from sketchUI.graphics_edge_item import GraphicsEdgeItem
 from wrappers.modelverse import element_list_nice
 from wrappers.modelverse import element_list_nice
 import commons
 import commons
-from evolution.node_ops import NodeDelete
+from evolution.node_ops import NodeDelete, NodeAdd
 from evolution.attribute_ops import AttributeDelete
 from evolution.attribute_ops import AttributeDelete
 
 
 
 
@@ -260,10 +260,20 @@ class EXMMainWindow(QMainWindow, Ui_MainWindow):
 
 
     def _on_list_item_clicked(self, event):
     def _on_list_item_clicked(self, event):
         # add node to model
         # add node to model
-        node_id = mvops.add_node(self._cur_model, event.text())
-        # render node
-        self._add_node_to_scene(node_id, event.text())
-        self.plainTextEdit.appendPlainText("Added node of type {} to model".format(event.text()))
+        self.plainTextEdit.appendPlainText("Adding node of type {} to model".format(event.text()))
+        add_handler = NodeAdd()
+
+        # ask if global or local delete
+        scope, ok = QInputDialog.getItem(self.parent(), "Select scope", "Scope", ["Local", "Global"])
+        if not ok:
+            return
+
+        if scope == "Global":
+            add_handler.execute(self._cur_model, event.text(), local=False, check_if_last=False)
+        else:
+            add_handler.execute(self._cur_model, event.text(), local=True, check_if_last=True)
+        # render node by reloading the whole model (since we cannot get the node id if the newly added model
+        self._load_model()
 
 
     def _on_attribute_edited(self, item):
     def _on_attribute_edited(self, item):
         # type: (QTableWidgetItem) -> None
         # type: (QTableWidgetItem) -> None
@@ -278,7 +288,7 @@ class EXMMainWindow(QMainWindow, Ui_MainWindow):
 
 
         if not attr_val:
         if not attr_val:
             # ask if global or local delete
             # ask if global or local delete
-            scope, ok = QInputDialog.getItem(self._parent, "Select scope", "Scope", ["Local", "Global"])
+            scope, ok = QInputDialog.getItem(self.parent(), "Select scope", "Scope", ["Local", "Global"])
             if not ok:
             if not ok:
                 return
                 return
             del_handler = AttributeDelete()
             del_handler = AttributeDelete()
@@ -287,7 +297,7 @@ class EXMMainWindow(QMainWindow, Ui_MainWindow):
             if scope == "Global":
             if scope == "Global":
                 del_handler.execute(self._cur_model, node.node_id, attr_key, local=False)
                 del_handler.execute(self._cur_model, node.node_id, attr_key, local=False)
             else:
             else:
-                del_handler.execute(self._cur_model, node.node_id, attr_key, local=True)
+                del_handler.execute(self._cur_model, node.node_id, attr_key, local=True, check_if_last=True)
 
 
             # update view
             # update view
             self.tableWidget.removeRow(row)
             self.tableWidget.removeRow(row)

+ 2 - 2
sketchUI/exm_scene.py

@@ -308,9 +308,9 @@ class SketchScene(QGraphicsScene):
         # add the node to the model
         # add the node to the model
         add_handler = NodeAdd()
         add_handler = NodeAdd()
         if scope == "Global":
         if scope == "Global":
-            add_handler.execute(self._cur_model, node_type, local=False)
+            add_handler.execute(self._cur_model, node_type, local=False, check_if_last=False)
         else:
         else:
-            add_handler.execute(self._cur_model, node_type, local=True)
+            add_handler.execute(self._cur_model, node_type, local=True, check_if_last=True)
         # Get node id of newly added node in current model
         # Get node id of newly added node in current model
         nodeid = commons.all_nodes_with_type(self._cur_model, node_type)[0]
         nodeid = commons.all_nodes_with_type(self._cur_model, node_type)[0]
 
 

+ 1 - 1
wrappers/modelverse_SCCD.py

@@ -1,7 +1,7 @@
 """
 """
 Generated by Statechart compiler by Glenn De Jonghe, Joeri Exelmans, Simon Van Mierlo, and Yentl Van Tendeloo (for the inspiration)
 Generated by Statechart compiler by Glenn De Jonghe, Joeri Exelmans, Simon Van Mierlo, and Yentl Van Tendeloo (for the inspiration)
 
 
-Date:   Sun May  6 11:42:36 2018
+Date:   Sun May  6 17:04:56 2018
 
 
 Model author: Yentl Van Tendeloo
 Model author: Yentl Van Tendeloo
 Model name:   MvK Server
 Model name:   MvK Server