Changeset 1109
- Timestamp:
- 12/14/06 14:08:36 (2 years ago)
- Files:
-
- gaphor/branches/new-canvas/gaphor/UML/__init__.py (modified) (1 diff)
- gaphor/branches/new-canvas/gaphor/UML/elementfactory.py (modified) (2 diffs)
- gaphor/branches/new-canvas/gaphor/adapters/connectors.py (modified) (2 diffs)
- gaphor/branches/new-canvas/gaphor/adapters/tests/test_connector.py (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
gaphor/branches/new-canvas/gaphor/UML/__init__.py
r1057 r1109 36 36 _default_element_factory.flush() 37 37 38 def swap_element(element, new_class): 39 """Swap the class for an element 40 """ 41 _default_element_factory.swap_element(element, new_class) 42 38 43 if 0 and __debug__: 39 44 # Keep track of all model elements that are created gaphor/branches/new-canvas/gaphor/UML/elementfactory.py
r1106 r1109 10 10 from gaphor.UML.event import CreateElementEvent, RemoveElementEvent, \ 11 11 FlushFactoryEvent, ModelFactoryEvent 12 13 # TODO: create an ElementFactory method that allows to swap instances of14 # one type to another (e.g. ForkNode <-> DescisionNode)15 12 16 13 … … 167 164 def swap_element(self, element, new_class): 168 165 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__') 171 169 172 170 def connect(self, callback, *data): gaphor/branches/new-canvas/gaphor/adapters/connectors.py
r1104 r1109 966 966 edge (incoming or outgoing). 967 967 """ 968 opposite = self.line.opposite(handle) 968 969 line = self.line 969 970 element = self.element 970 971 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 971 983 972 984 # If one side of self.element has more than one edge, the 973 985 # type of node is determined (either join or fork). 974 986 if handle is line.head and len(subject.incoming) > 1 and len(subject.outgoing) > 0: 975 print 'head: type determined'976 987 return None 977 988 978 989 if handle is line.tail and len(subject.incoming) > 0 and len(subject.outgoing) > 1: 979 print 'tail: type determined'980 990 return None 981 991 … … 984 994 def connect_subject(self): 985 995 super(FlowForkNodeConnect, self).connect_subject() 996 986 997 # Switch class for self.element Join/Fork depending on the number 987 998 # 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' 988 1006 989 1007 component.provideAdapter(FlowForkNodeConnect) 990 1008 991 1009 1010 class 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 1060 component.provideAdapter(FlowDecisionNodeConnect) 1061 1062 992 1063 # vim:sw=4:et:ai gaphor/branches/new-canvas/gaphor/adapters/tests/test_connector.py
r1104 r1109 588 588 assert flow1.subject.source is a2.subject 589 589 590 def test_flow_fork_decision(self ):590 def test_flow_fork_decision(self, itemClass=items.ForkNodeItem, forkNodeClass=UML.ForkNode, joinNodeClass=UML.JoinNode): 591 591 """ 592 592 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. 593 602 """ 594 603 assert len(list(UML.select())) == 0 … … 597 606 flow1 = diagram.create(items.FlowItem) 598 607 flow2 = diagram.create(items.FlowItem) 608 flow3 = diagram.create(items.FlowItem) 609 flow4 = diagram.create(items.FlowItem) 599 610 a1 = diagram.create(items.ActionItem, subject=UML.create(UML.Action)) 600 611 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() 604 616 605 617 # 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 606 625 adapter = component.queryMultiAdapter((a1, flow1), IConnect) 607 626 assert adapter 608 627 adapter.connect(flow1.head, flow1.head.x, flow1.head.y) 609 628 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 610 639 adapter = component.queryMultiAdapter((f1, flow1), IConnect) 611 640 assert adapter 612 641 adapter.connect(flow1.tail, flow1.tail.x, flow1.tail.y) 642 assert flow1.tail.connected_to is f1 613 643 assert flow1.subject 614 644 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) 626 726 627 727 # vim:sw=4:et:ai
