1# -*- coding: utf-8 -*-2# Copyright 2011 Takeshi KOMIYA3#4# Licensed under the Apache License, Version 2.0 (the "License");5# you may not use this file except in compliance with the License.6# You may obtain a copy of the License at7#8# http://www.apache.org/licenses/LICENSE-2.09#10# Unless required by applicable law or agreed to in writing, software11# distributed under the License is distributed on an "AS IS" BASIS,12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.13# See the License for the specific language governing permissions and14# limitations under the License.1516from blockdiag.utils import XY, unquote1718from seqdiag import parser19from seqdiag.elements import (AltBlock, Diagram, DiagramEdge, DiagramNode,20 EdgeSeparator, NodeGroup)212223class DiagramTreeBuilder(object):24 def build(self, tree):25 self.diagram = Diagram()26 self.diagram = self.instantiate(self.diagram, None, tree)2728 self.update_node_order()29 self.update_edge_order()30 self.update_altblock_ylevel()31 self.diagram.colwidth = len(self.diagram.nodes)32 self.diagram.colheight = len(self.diagram.edges) + 13334 for sep in self.diagram.separators:35 self.diagram.edges.remove(sep)3637 if self.diagram.activation != 'none':38 self.create_activities()3940 if self.diagram.autonumber:41 self.update_label_numbered()4243 return self.diagram4445 def update_edge_order(self):46 for i, edge in enumerate(self.diagram.edges):47 edge.order = i4849 height = len(self.diagram.edges) + 150 for group in self.diagram.groups:51 group.colheight = height5253 def update_altblock_ylevel(self):54 altblocks = self.diagram.altblocks55 for i in range(len(self.diagram.edges) + 1):56 blocks = [b for b in altblocks if b.edges[0].order == i]57 for j, altblock in enumerate(reversed(blocks)):58 altblock.ylevel_top = j + 15960 blocks = [b for b in altblocks if b.edges[-1].order == i]61 for j, altblock in enumerate(reversed(blocks)):62 altblock.ylevel_bottom = j + 16364 def update_label_numbered(self):65 for i, edge in enumerate(self.diagram.edges):66 edge.label = "%d. %s" % (i + 1, edge.label or "")6768 def create_activities(self):69 if len(self.diagram.edges) == 0:70 return7172 first_node = self.diagram.edges[0].node173 active_nodes = {first_node: 1}74 for node in self.diagram.nodes:75 if node.activated:76 active_nodes[node] = 17778 edge_count = len(self.diagram.edges) + len(self.diagram.separators)79 for i in range(edge_count):80 match = [e for e in self.diagram.edges if e.order == i]81 if match:82 edge = match[0]83 if edge.activate is False:84 pass85 elif edge.dir == 'forward':86 if edge.node2 in active_nodes:87 active_nodes[edge.node2] += 188 else:89 active_nodes[edge.node2] = 190 elif edge.dir == 'back':91 if edge.node2 in active_nodes:92 active_nodes[edge.node2] -= 193 else:94 active_nodes[edge.node2] = 09596 for node in active_nodes:97 if active_nodes[node] > 0:98 for index in range(active_nodes[node]):99 node.activate(i, index)100101 for node in self.diagram.nodes:102 node.deactivate()103104 def update_node_order(self):105 x = 0106 uniq = []107108 for node in self.diagram.nodes:109 if node not in uniq:110 node.xy = XY(x, 0)111 uniq.append(node)112 x += 1113114 if node.group:115 for subnode in node.group.nodes:116 if subnode not in uniq:117 subnode.xy = XY(x, 0)118 uniq.append(subnode)119 x += 1120121 for group in self.diagram.groups:122 x = min(node.xy.x for node in group.nodes)123 group.xy = XY(x, 0)124 group.colwidth = len(group.nodes)125126 def append_node(self, node, group):127 if node not in self.diagram.nodes:128 self.diagram.nodes.append(node)129130 if isinstance(group, NodeGroup) and node not in group.nodes:131 if node.group:132 msg = "DiagramNode could not belong to two groups"133 raise RuntimeError(msg)134135 group.nodes.append(node)136 node.group = group137138 def instantiate(self, group, block, tree):139 for stmt in tree.stmts:140 if isinstance(stmt, parser.Node):141 node = DiagramNode.get(stmt.id)142 node.set_attributes(stmt.attrs)143 self.append_node(node, group)144145 elif isinstance(stmt, parser.Edge):146 self.instantiate_edge(group, block, stmt)147148 elif isinstance(stmt, parser.Group):149 node = NodeGroup.get(None)150 self.instantiate(node, block, stmt)151 self.diagram.groups.append(node)152153 elif isinstance(stmt, parser.Attr):154 if block:155 block.set_attribute(stmt)156 else:157 group.set_attribute(stmt)158159 elif isinstance(stmt, parser.Separator):160 sep = EdgeSeparator(stmt.type, unquote(stmt.value))161 sep.group = group162 self.diagram.separators.append(sep)163 group.edges.append(sep)164165 elif isinstance(stmt, parser.Fragment):166 subblock = AltBlock(stmt.type, stmt.id)167 if block:168 subblock.xlevel = block.xlevel + 1169 self.diagram.altblocks.append(subblock)170171 self.instantiate(group, subblock, stmt)172 if block:173 for edge in subblock.edges:174 block.edges.append(edge)175176 elif isinstance(stmt, parser.Extension):177 if stmt.type == 'class':178 name = unquote(stmt.name)179 Diagram.classes[name] = stmt180 elif stmt.type == 'plugin':181 self.diagram.set_plugin(stmt.name, stmt.attrs)182183 return group184185 def instantiate_edge(self, group, block, stmt):186 from_node = DiagramNode.get(stmt.from_node)187 self.append_node(from_node, group)188189 to_node = DiagramNode.get(stmt.to_node)190 self.append_node(to_node, group)191192 edge = DiagramEdge(from_node, to_node)193 edge.set_dir(stmt.edge_type)194 edge.set_attributes(stmt.attrs)195196 if edge.dir in ('forward', 'both'):197 forward = edge.duplicate()198 forward.dir = 'forward'199 group.edges.append(forward)200 if block:201 block.edges.append(forward)202203 if stmt.followers:204 followers = list(stmt.followers)205 next_edge_type, next_to_node = followers.pop(0)206 nested = parser.Edge(stmt.to_node, next_edge_type, next_to_node,207 followers, stmt.attrs, stmt.edge_block)208 self.instantiate_edge(group, block, nested)209 elif stmt.edge_block:210 self.instantiate(group, block, stmt.edge_block)211212 if edge.dir in ('back', 'both'):213 reverse = edge.duplicate()214 reverse.dir = 'back'215 if edge.dir == 'both':216 reverse.style = 'dashed'217 reverse.label = edge.return_label218 reverse.leftnote = None219 reverse.rightnote = None220221 group.edges.append(reverse)222 if block:223 block.edges.append(reverse)224225226class ScreenNodeBuilder(object):227228 def build(cls, tree):229 DiagramNode.clear()230 DiagramEdge.clear()231 NodeGroup.clear()232233 return DiagramTreeBuilder().build(tree)