Changeset 1109

Show
Ignore:
Timestamp:
12/14/06 14:08:36 (2 years ago)
Author:
arjanmol
Message:

Added Fork/Join and Decision/Merge node connectors. Added ElementFactory?.swap_element()

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • gaphor/branches/new-canvas/gaphor/UML/__init__.py

    r1057 r1109  
    3636    _default_element_factory.flush() 
    3737 
     38def swap_element(element, new_class): 
     39    """Swap the class for an element 
     40    """ 
     41    _default_element_factory.swap_element(element, new_class) 
     42 
    3843if 0 and __debug__:  
    3944    # Keep track of all model elements that are created 
  • gaphor/branches/new-canvas/gaphor/UML/elementfactory.py

    r1106 r1109  
    1010from gaphor.UML.event import CreateElementEvent, RemoveElementEvent, \ 
    1111                             FlushFactoryEvent, ModelFactoryEvent 
    12  
    13 # TODO: create an ElementFactory method that allows to swap instances of 
    14 #       one type to another (e.g. ForkNode <-> DescisionNode) 
    1512 
    1613 
     
    167164    def swap_element(self, element, new_class): 
    168165        assert element in self._elements.values() 
    169         element.__class__ = new_class 
    170         self.notify(element, '__class__') 
     166        if element.__class__ is not new_class: 
     167            element.__class__ = new_class 
     168            self.notify(element, '__class__') 
    171169 
    172170    def connect(self, callback, *data): 
  • gaphor/branches/new-canvas/gaphor/adapters/connectors.py

    r1104 r1109  
    966966        edge (incoming or outgoing). 
    967967        """ 
     968        opposite = self.line.opposite(handle) 
    968969        line = self.line 
    969970        element = self.element 
    970971        subject = element.subject 
     972        connected_to = opposite.connected_to 
     973 
     974        # Element can not connect back to itself 
     975        if connected_to is element: 
     976            return None 
     977 
     978        # Same goes for subjects: 
     979        if connected_to and \ 
     980                (not (connected_to.subject or element.subject)) \ 
     981                 and connected_to.subject is element.subject: 
     982            return None 
    971983 
    972984        # If one side of self.element has more than one edge, the 
    973985        # type of node is determined (either join or fork). 
    974986        if handle is line.head and len(subject.incoming) > 1 and len(subject.outgoing) > 0: 
    975             print 'head: type determined' 
    976987            return None 
    977988 
    978989        if handle is line.tail and len(subject.incoming) > 0 and len(subject.outgoing) > 1: 
    979             print 'tail: type determined' 
    980990            return None 
    981991 
     
    984994    def connect_subject(self): 
    985995        super(FlowForkNodeConnect, self).connect_subject() 
     996 
    986997        # Switch class for self.element Join/Fork depending on the number 
    987998        # of incoming/outgoing edges. 
     999        subject = self.element.subject 
     1000        if len(subject.incoming) > 1 and len(subject.outgoing) < 2: 
     1001            UML.swap_element(subject, UML.JoinNode) 
     1002        elif len(subject.incoming) < 2 and len(subject.outgoing) > 1: 
     1003            UML.swap_element(subject, UML.ForkNode) 
     1004        elif len(subject.incoming) > 1 and len(subject.outgoing) > 1: 
     1005            raise RuntimeError, 'Inconsistent state' 
    9881006 
    9891007component.provideAdapter(FlowForkNodeConnect) 
    9901008 
    9911009 
     1010class FlowDecisionNodeConnect(FlowConnect): 
     1011    """ 
     1012    Connect Flow to a ForkNode 
     1013    """ 
     1014    component.adapts(items.DecisionNodeItem, items.FlowItem) 
     1015 
     1016    def glue(self, handle, x, y): 
     1017        """ 
     1018        In addition to the normal check, one end should have at most one 
     1019        edge (incoming or outgoing). 
     1020        """ 
     1021        opposite = self.line.opposite(handle) 
     1022        line = self.line 
     1023        element = self.element 
     1024        subject = element.subject 
     1025        connected_to = opposite.connected_to 
     1026 
     1027        # Element can not connect back to itself 
     1028        if connected_to is element: 
     1029            return None 
     1030 
     1031        # Same goes for subjects: 
     1032        if connected_to and \ 
     1033                (not (connected_to.subject or element.subject)) \ 
     1034                 and connected_to.subject is element.subject: 
     1035            return None 
     1036 
     1037        # If one side of self.element has more than one edge, the 
     1038        # type of node is determined (either join or fork). 
     1039        if handle is line.head and len(subject.incoming) > 1 and len(subject.outgoing) > 0: 
     1040            return None 
     1041 
     1042        if handle is line.tail and len(subject.incoming) > 0 and len(subject.outgoing) > 1: 
     1043            return None 
     1044 
     1045        return super(FlowDecisionNodeConnect, self).glue(handle, x, y) 
     1046 
     1047    def connect_subject(self): 
     1048        super(FlowDecisionNodeConnect, self).connect_subject() 
     1049 
     1050        # Switch class for self.element Join/Fork depending on the number 
     1051        # of incoming/outgoing edges. 
     1052        subject = self.element.subject 
     1053        if len(subject.incoming) > 1 and len(subject.outgoing) < 2: 
     1054            UML.swap_element(subject, UML.MergeNode) 
     1055        elif len(subject.incoming) < 2 and len(subject.outgoing) > 1: 
     1056            UML.swap_element(subject, UML.DecisionNode) 
     1057        elif len(subject.incoming) > 1 and len(subject.outgoing) > 1: 
     1058            raise RuntimeError, 'Inconsistent state' 
     1059 
     1060component.provideAdapter(FlowDecisionNodeConnect) 
     1061 
     1062 
    9921063# vim:sw=4:et:ai 
  • gaphor/branches/new-canvas/gaphor/adapters/tests/test_connector.py

    r1104 r1109  
    588588        assert flow1.subject.source is a2.subject 
    589589 
    590     def test_flow_fork_decision(self): 
     590    def test_flow_fork_decision(self, itemClass=items.ForkNodeItem, forkNodeClass=UML.ForkNode, joinNodeClass=UML.JoinNode): 
    591591        """ 
    592592        Test fork/decision behaviour. 
     593         [1] A join node has one outgoing edge. 
     594             self.outgoing->size() = 1 
     595         [2] If a join node has an incoming object flow, it must have an 
     596             outgoing object flow, otherwise, it must have an outgoing control 
     597             flow. 
     598 
     599         [1] A fork node has one incoming edge. 
     600         [2] The edges coming into and out of a fork node must be either all 
     601             object flows or all control flows. 
    593602        """ 
    594603        assert len(list(UML.select())) == 0 
     
    597606        flow1 = diagram.create(items.FlowItem) 
    598607        flow2 = diagram.create(items.FlowItem) 
     608        flow3 = diagram.create(items.FlowItem) 
     609        flow4 = diagram.create(items.FlowItem) 
    599610        a1 = diagram.create(items.ActionItem, subject=UML.create(UML.Action)) 
    600611        a2 = diagram.create(items.ActionItem, subject=UML.create(UML.Action)) 
    601         f1 = diagram.create(items.ForkNodeItem, subject=UML.create(UML.JoinNode)) 
    602  
    603         assert len(UML.lselect()) == 5, UML.lselect() 
     612        a3 = diagram.create(items.ActionItem, subject=UML.create(UML.Action)) 
     613        f1 = diagram.create(itemClass, subject=UML.create(joinNodeClass)) 
     614 
     615        #assert len(UML.lselect()) == 6, UML.lselect() 
    604616 
    605617        # Connect between two actions (ControlFlow) 
     618        # Connecting line this: 
     619        #                  |--flow2-->[ a2 ] 
     620        # [ a1 ] --flow1-->| 
     621        #                  |--flow3-->[ a3 ] 
     622 
     623        # First connect the Actions: 
     624 
    606625        adapter = component.queryMultiAdapter((a1, flow1), IConnect) 
    607626        assert adapter 
    608627        adapter.connect(flow1.head, flow1.head.x, flow1.head.y) 
    609628 
     629        adapter = component.queryMultiAdapter((a2, flow2), IConnect) 
     630        assert adapter 
     631        adapter.connect(flow2.tail, flow2.tail.x, flow2.tail.y) 
     632 
     633        adapter = component.queryMultiAdapter((a3, flow3), IConnect) 
     634        assert adapter 
     635        adapter.connect(flow3.tail, flow3.tail.x, flow3.tail.y) 
     636 
     637        # Now connect to ForkNode: 
     638 
    610639        adapter = component.queryMultiAdapter((f1, flow1), IConnect) 
    611640        assert adapter 
    612641        adapter.connect(flow1.tail, flow1.tail.x, flow1.tail.y) 
     642        assert flow1.tail.connected_to is f1 
    613643        assert flow1.subject 
    614644        assert flow1.subject.target is f1.subject 
    615         assert type(f1.subject) is UML.JoinNode 
    616  
    617         # [1] A join node has one outgoing edge. 
    618         #     self.outgoing->size() = 1 
    619         # [2] If a join node has an incoming object flow, it must have an 
    620         #     outgoing object flow, otherwise, it must have an outgoing control 
    621         #     flow. 
    622  
    623         # [1] A fork node has one incoming edge. 
    624         # [2] The edges coming into and out of a fork node must be either all 
    625         #     object flows or all control flows. 
     645        assert flow1.subject in f1.subject.incoming 
     646        assert type(f1.subject) is joinNodeClass 
     647 
     648        adapter = component.queryMultiAdapter((f1, flow2), IConnect) 
     649        assert adapter 
     650        adapter.connect(flow2.head, flow2.head.x, flow2.head.y) 
     651        assert flow2.head.connected_to is f1 
     652        assert flow2.subject.source is f1.subject 
     653        assert flow2.subject in f1.subject.outgoing 
     654        assert type(f1.subject) is joinNodeClass 
     655 
     656        adapter = component.queryMultiAdapter((f1, flow3), IConnect) 
     657        assert adapter 
     658        adapter.connect(flow3.head, flow3.head.x, flow3.head.y) 
     659        assert flow3.head.connected_to is f1 
     660        assert flow3.subject.source is f1.subject 
     661        assert flow3.subject in f1.subject.outgoing 
     662 
     663        assert type(f1.subject) is forkNodeClass, f1.subject 
     664 
     665        # flow4 can't be an incoming flow: 
     666 
     667        adapter = component.queryMultiAdapter((f1, flow4), IConnect) 
     668        assert adapter 
     669        adapter.connect(flow4.tail, flow4.tail.x, flow4.tail.y) 
     670        assert flow4.tail.connected_to is None 
     671 
     672        # flow4 can be connected as outgoing flow though: 
     673 
     674        adapter = component.queryMultiAdapter((f1, flow4), IConnect) 
     675        assert adapter 
     676        adapter.connect(flow4.head, flow4.head.x, flow4.head.y) 
     677        assert flow4.head.connected_to is f1 
     678 
     679        adapter.disconnect(flow4.head) 
     680        assert flow4.head.connected_to is None 
     681 
     682        # Now change the ForkNode back into a JoinNode by moving flow2 
     683        # to the opposite side: 
     684        # [ a1 ]--flow1-->| 
     685        #                 |--flow3-->[ a3 ] 
     686        # [ a2 ]--flow2-->| 
     687 
     688        adapter = component.queryMultiAdapter((a2, flow2), IConnect) 
     689        adapter.disconnect(flow2.tail) 
     690        assert len(a2.subject.incoming) == 0 
     691 
     692        # Let's try if we can connect both ends of flow2 to the ForkNode: 
     693        adapter = component.queryMultiAdapter((f1, flow2), IConnect) 
     694        adapter.connect(flow2.tail, flow2.tail.x, flow2.tail.y) 
     695        assert flow2.tail.connected_to is None, flow2.tail.connected_to 
     696 
     697        adapter.disconnect(flow2.head) 
     698        assert len(f1.subject.incoming) == 1 
     699        assert len(f1.subject.outgoing) == 1 
     700 
     701        adapter = component.queryMultiAdapter((a2, flow2), IConnect) 
     702        adapter.connect(flow2.head, flow2.head.x, flow2.head.y) 
     703        assert len(a2.subject.outgoing) == 0 
     704 
     705        adapter = component.queryMultiAdapter((f1, flow2), IConnect) 
     706        adapter.connect(flow2.tail, flow2.tail.x, flow2.tail.y) 
     707        assert len(a2.subject.outgoing) == 1 
     708        assert len(f1.subject.incoming) == 2 
     709        assert len(f1.subject.outgoing) == 1 
     710        assert type(f1.subject) is joinNodeClass, f1.subject 
     711 
     712        # And of course I can't add another outgoing edge: 
     713        adapter = component.queryMultiAdapter((f1, flow4), IConnect) 
     714        assert adapter 
     715        adapter.connect(flow4.head, flow4.head.x, flow4.head.y) 
     716        assert flow4.head.connected_to is None 
     717 
     718 
     719    def test_flow_decision_merge(self): 
     720        """ 
     721        Decision/Merge node is basically the same as Fork/Join node. 
     722        """ 
     723        self.test_flow_fork_decision(itemClass=items.DecisionNodeItem, 
     724                                     forkNodeClass=UML.DecisionNode, 
     725                                     joinNodeClass=UML.MergeNode) 
    626726 
    627727# vim:sw=4:et:ai