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 collections import defaultdict1718from blockdiag.utils import XY, unquote1920from rackdiag import parser21from rackdiag.elements import Diagram, Rack, RackItem222324class DiagramTreeBuilder:25 def build(self, tree):26 self.diagram = Diagram()27 self.instantiate(self.diagram.racks[0], tree)2829 for rack in self.diagram.racks[:]:30 if len(rack.nodes) == 0:31 self.diagram.racks.remove(rack)3233 return self.diagram3435 def instantiate(self, rack, tree):36 for stmt in tree.stmts:37 if isinstance(stmt, parser.Attr):38 try:39 rack.set_attribute(stmt)40 except AttributeError:41 self.diagram.set_attribute(stmt)42 elif isinstance(stmt, parser.RackItem):43 if stmt.number is None:44 if len(rack.nodes) > 0:45 item = rack.nodes[-1]46 number = item.number + item.colheight47 else:48 number = 149 else:50 number = stmt.number5152 item = RackItem(number, unquote(stmt.label))53 item.set_attributes(stmt.attrs)54 rack.nodes.append(item)55 elif isinstance(stmt, parser.Rack):56 _rack = Rack()57 self.diagram.racks.append(_rack)58 self.instantiate(_rack, stmt)59 elif isinstance(stmt, parser.AttrPlugin):60 self.diagram.set_plugin(stmt.name, stmt.attrs)616263class DiagramLayoutManager:64 def __init__(self, diagram):65 self.diagram = diagram6667 def run(self):68 x = 069 for rack in self.diagram.racks:70 self.layout_rack(rack)71 rack.xy = XY(x, 0)72 rack.fixiate()7374 x += rack.colwidth7576 self.diagram.fixiate()7778 def layout_rack(self, rack):79 usage = defaultdict(bool)8081 for item in rack.nodes:82 item.xy = XY(-1, -1)8384 for item in rack.nodes:85 if rack.descending:86 y = rack.colheight - item.number - item.colheight + 187 else:88 y = item.number - 18990 for x in range(255):91 r = range(y, y + item.colheight)92 if len([_ for _ in r if usage[(x, _)]]) == 0:93 break9495 item.xy = XY(x, y)96 for dy in range(y, y + item.colheight):97 usage[(x, dy)] = 198 self.validate_rack(rack, item)99100 def validate_rack(self, rack, item):101 if item.xy.y < 0:102 msg = "Rack %d is layouted to underground!\n" % item.number103 raise AttributeError(msg)104 elif rack.colheight < item.xy.y + item.colheight:105 msg = "Rack %d is oversized to rack-height\n" % item.number106 raise AttributeError(msg)107108109class ScreenNodeBuilder:110111 def build(cls, tree):112 Rack.clear()113 RackItem.clear()114 Diagram.clear()115116 return cls(tree).run()117118 def __init__(self, tree):119 self.diagram = DiagramTreeBuilder().build(tree)120121 def run(self):122 DiagramLayoutManager(self.diagram).run()123 return self.diagram