Attachment 'uml2ascii-2004.py'
Download 1 #format python
2 """
3 MoinMoin Processor to create Simple ASCII UML Diagrams
4 release 0.2.20040202
5 Senya Basin, 1998-2004
6 """
7 import re,sys,string,pprint
8 USAGE =""" UML2ASCII Usage:
9 {{{#!uml2ascii [Options]
10 Diagram DDL
11 }}}
12 -----------------------------------------------------------
13 DDL Sample:
14 [Box 1] -> [Box2] -(3,1)- [Box 3]
15 [Box 4|...two liner...] -> --(2,)-- [Box 5.hvc=///]
16
17 Box format: [pos_x, pos_y, text, attr]
18 pos_x - column position of the box (optional, auto-incremented by default)
19 pos_y - row position of the box (optional, auto-incremented with new-line by default)
20 text - text to display in the box. Required. Use "|" for multi-line text. Use "|-|" for divider.
21 attr - Optional attributes:
22 hvc=... : use alternative characters: Horizontal, Vertical and Corner.
23 Box samples: [Box1] [Class Point|-|int getX()|int getY()|void setX(int)|void setY(int)] [1,5,Box at cell 1:5]
24
25 Link format: START_CHAR -* FROM_TO -* END_CHAR
26 START_CHAR - starting character. Required.
27 END_CHAR - ending character. Required.
28 FROM_TO - (id_from, id_to). Optional. From previous to next by default.
29 id_from - Box ID from which the links starts. Optional. Previous box by default.
30 id_to - Box ID to which the link goes. Optional. Next box by default.
31 Link samples: -> <-> -- <> -(2,5)-> -(,7)->
32
33 Options:
34 fg:color - diagram color
35 dx:value - minimum horizontal gap
36 dy:value - minimum vertical gap
37 nn: - print box IDs
38 """
39 H_CHAR = '-'
40 V_CHAR = '|'
41 C_CHAR = '+'
42 class UMLAsciiDiagram:
43 def __init__(self):
44 self.pos_x = 0
45 self.pos_y = 0
46 self.obj_id = 0
47 self.tab_nx = 0
48 self.tab_ny = 0
49 self.boxes = {}
50 self.links_from = {}
51 self.cols_width = {}
52 self.rows_height = {}
53 self.cols_start = []
54 self.rows_start = []
55 self.DEBUG = 0
56 self.fgcolor = 'black'
57 self.DX = 5
58 self.DY = 3
59 self.NN = 0
60
61 def parse_params(s, args):
62 m = re.match('.*(fgcolor|fg):(\S+).*', args, re.I)
63 if m:
64 s.fgcolor = m.group(2)
65 m = re.match('.*DY:(\d+)\s.*', args, re.I)
66 if m:
67 s.DY = int(m.group(1))
68 m = re.match('.*DX:(\d+)\s.*', args, re.I)
69 if m:
70 s.DX = int(m.group(1))
71 if re.match('.*NN:.*', args, re.I):
72 s.NN = 1
73 if re.match('.*DEBUG:.*', args, re.I):
74 s.DEBUG = 1
75
76 def pdebug(s, *args):
77 if s.DEBUG: print 'DEBUG: ', args, '<br>'
78
79 def parse_ddl(s, args):
80 re_box = re.compile(r'\[(\d*),?(\d*),?"?(.*?)\s*?"?,?(\w*?=.*?)?\]')
81 re_link = re.compile(r'(\s?[<-])[-(]*(\d*),?(\d*)[-)]*([->])\s?')
82
83 if re.match('^\s*$', args):
84 return 0
85
86 rc = 0
87 s.pos_y += 1
88 s.pos_x = 0
89
90 for tok in re.findall('\[.*?\]|[<-].*?[->]\s', args):
91 #s.pdebug("parsed TOKEN:", tok)
92 if re.match('\[\]', tok):
93 s.pos_x += 1
94 continue
95
96 m = re_box.match(tok)
97 if m:
98 (x,y,text,attr) = m.group(1,2,3,4)
99 s.add_box(x, y, text, attr)
100 rc = 1
101
102 m = re_link.match(tok)
103 if m:
104 (fr,to,start,end) = m.group(2,3,1,4)
105 s.add_link(fr, to, start.strip(), end.strip())
106
107 return rc
108
109 def add_box(s, x='', y='', text='', attr='', id=None):
110
111 s.obj_id = parse_int(id, s.obj_id + 1)
112 s.pos_x = parse_int(x, s.pos_x + 1)
113 s.pos_y = parse_int(y, s.pos_y)
114
115 hvc = ''
116 if attr:
117 m = re.match('hvc=(...)', attr)
118 if m: hvc = m.group(1)
119 m = re.match('id=(\d+)', attr)
120 if m: s.obj_id = m.group(1)
121
122 s.pdebug('BOX: x=', s.pos_x, '; y=', s.pos_y,
123 "; text='",text,"'; hvc=",hvc,"; id=",s.obj_id)
124
125 (width, lines) = make_textbox(text, hvc)
126 height = len(lines)
127 s.boxes[s.obj_id] = (s.obj_id, s.pos_x, s.pos_y, text, attr, width, height, lines)
128
129 if not s.cols_width.has_key(s.pos_x) or width > s.cols_width[s.pos_x]:
130 s.cols_width[s.pos_x] = width
131
132 if not s.rows_height.has_key(s.pos_y) or height > s.rows_height[s.pos_y]:
133 s.rows_height[s.pos_y] = height
134
135 def add_link(s, from_id='', to_id='', start=H_CHAR, end=H_CHAR, attr=None):
136 from_id = parse_int(from_id, s.obj_id)
137 to_id = parse_int(to_id, s.obj_id + 1)
138
139 if not s.links_from.has_key(from_id):
140 s.links_from[from_id] = []
141
142 links = s.links_from[from_id]
143 links.append([from_id, to_id, start, end, attr])
144 s.pdebug ("LINK: from=",from_id," to=",to_id," link=\""+start+end+"\" attr=",attr)
145
146 def define_geometry(s):
147 s.tab_nx = len(s.cols_width.keys())
148 s.tab_ny = len(s.rows_height.keys())
149
150 for i in range(s.tab_nx + 1):
151 s.cols_start.append(0)
152
153 for i in range(2, s.tab_nx + 1):
154 s.cols_start[i] = s.cols_start[i-1] + s.cols_width[i-1] + s.DX;
155
156 for i in range(s.tab_ny + 1):
157 s.rows_start.append(0)
158
159 for i in range(2, s.tab_ny + 1):
160 s.rows_start[i] = s.rows_start[i-1] + s.rows_height[i-1] + s.DY;
161
162 return s.tab_nx * s.tab_ny
163
164 def draw_box(s, screen, box_id):
165 (box_id, tx, ty, text, attr, width, height, lines) = s.boxes[box_id]
166 for j in range(height):
167 yy = s.rows_start[ty] + j
168 for i in range(width):
169 xx = s.cols_start[tx] + i
170 screen[yy][xx] = lines[j][i]
171 if s.NN:
172 sid = repr(box_id)
173 for i in range(len(sid)):
174 screen[s.rows_start[ty]][s.cols_start[tx] + i] = sid[i]
175
176 def draw_hline (s, screen, x1, x2, y, c_start, c_end):
177 for x in range(x1, x2):
178 screen[y][x] = H_CHAR
179
180 if c_start: screen[y][x1] = c_start
181 if c_end: screen[y][x2-1] = c_end
182
183 def draw_h2line (s, screen, x1, x2, y, c_start, c_end):
184 s.draw_hline(screen, x1, x2+2, y+1, c_start, c_end)
185 screen[y][x1] = V_CHAR
186 screen[y+1][x1] = C_CHAR
187 screen[y][x2+1] = V_CHAR
188 screen[y+1][x2+1] = C_CHAR
189
190 def draw_vline (s, screen, x, y1, y2, c_vert=V_CHAR):
191 for y in range(y1, y2):
192 screen[y][x] = c_vert
193
194 def draw_link(s, screen, link):
195 (fr, to, start, end, attr) = link
196
197 (box_id1, tx1, ty1, undef, undef, w1, h1, undef) = s.boxes[fr]
198 if not box_id1:
199 return
200
201 (box_id2, tx2, ty2, undef, undef, w2, h2, undef) = s.boxes[to]
202 if not box_id2:
203 return
204
205 if ( (ty1 - ty2) == 0):
206 if (abs(tx2 - tx1) == 1):
207 if (tx1 < tx2):
208 x1 = s.cols_start[tx1] + w1
209 x2 = s.cols_start[tx2]
210 else:
211 x1 = s.cols_start[tx2] + w1
212 x2 = s.cols_start[tx1]
213
214 h = h2//2
215 if h1 < h2: h = h1//2
216 s.draw_hline(screen, x1, x2, s.rows_start[ty1] + h, start, end)
217
218 elif (abs(tx2 - tx1) > 1):
219 if (tx1 < tx2):
220 x1 = s.cols_start[tx1] + w1//2
221 x2 = s.cols_start[tx2] + w2//2 - 1
222 else:
223 x1 = s.cols_start[tx2] + w2//2
224 x2 = s.cols_start[tx1] + w1//2 - 1
225
226 h = h1
227 if h1 < h2: h2
228 s.draw_h2line(screen, x1, x2, s.rows_start[ty1] + h, start, end)
229
230 elif ((tx1 - tx2)==0 and abs(ty2 - ty1)==1):
231 if (ty1 < ty2):
232 y1 = s.rows_start[ty1] + s.rows_height[ty1]
233 y2 = s.rows_start[ty2]
234 else:
235 y1 = s.rows_start[ty2] + s.rows_height[ty2]
236 y2 = s.rows_start[ty1]
237
238 w = w2//2
239 if w1 < w2: w = w1//2
240 s.draw_vline(screen, s.cols_start[tx1] + w, y1, y2)
241
242 else:
243 if (tx1 < tx2):
244 x1 = s.cols_start[tx1] + w1
245 x2 = s.cols_start[tx2]
246 else:
247 x1 = s.cols_start[tx2] + w1
248 x2 = s.cols_start[tx1]
249
250 if (ty1 < ty2):
251 y1 = s.rows_start[ty1] + h1//2
252 y2 = s.rows_start[ty2] + h2//2
253 else:
254 y1 = s.rows_start[ty2] + h2//2
255 y2 = s.rows_start[ty1] + h1//2
256
257 w = s.DX//2
258 s.draw_hline(screen, x1, x1 + w, y1, start, H_CHAR)
259 s.draw_vline(screen, x1 + w, y1, y2)
260 s.draw_hline(screen, x1 + w, x2, y2, H_CHAR, end)
261
262 def draw_diagram(s):
263 screen_w = s.cols_start[s.tab_nx] + s.cols_width[s.tab_nx]
264 screen_h = s.rows_start[s.tab_ny] + s.rows_height[s.tab_ny]
265 screen = []
266 while screen_h > 0:
267 screen.append(map(lambda x: ' ', range(screen_w)))
268 screen_h -= 1
269
270 for links in s.links_from.values():
271 for link in links:
272 s.draw_link(screen, link)
273
274 for box_id in s.boxes.keys():
275 s.draw_box(screen, box_id)
276
277 return print_screen(screen)
278
279 #################################################################
280 def make_line(l, hch=H_CHAR, cch=C_CHAR):
281 sline = cch
282 while l > 2:
283 l, sline = l-1, sline + hch
284 return sline + cch
285
286 def frame_text (text, vch=V_CHAR):
287
288 lines = text.split(V_CHAR);
289
290 smax = 0
291 for line in lines:
292 if len(line) > smax: smax = len(line)
293
294 for i in range(len(lines)):
295 ltmp = vch + ' ' + lines[i].ljust(smax) + ' ' + vch
296 if re.match('\s*\-+\s*', lines[i]):
297 lines[i] = ltmp.replace(' ','-')
298 else:
299 lines[i] = ltmp
300
301 return (smax + 4, lines)
302
303 def make_textbox (text, symbols=''):
304 if not symbols: symbols = H_CHAR + V_CHAR + C_CHAR
305 hchar, vchar, cchar = symbols
306
307 (width, lines) = frame_text(text, vchar);
308
309 sline = make_line(width, hchar, cchar);
310 lines.insert(0, sline)
311 lines.append(sline)
312 return (width, lines);
313
314
315 def parse_int(val, dflt):
316 if val:
317 return int(val)
318 else:
319 return int(dflt)
320
321 def print_screen(screen):
322 rc = ''
323 for j in range(len(screen)):
324 for i in range(len(screen[j])):
325 rc += screen[j][i]
326 rc += '\n'
327 return rc
328
329 #################################################################
330 def process(request, formatter, lines):
331 try:
332 ad = UMLAsciiDiagram()
333 ad.parse_params(lines[0])
334 del lines[0]
335
336 rc = 0
337 for line in lines:
338 rc += ad.parse_ddl(line)
339
340 if (rc > 0):
341 ad.define_geometry()
342 sb = ad.draw_diagram()
343 else:
344 sb = string.join(lines, '\n')
345 sb = USAGE + sb
346 except:
347 raise
348
349 sys.stdout.write(formatter.rawHTML('<font color=\"' + ad.fgcolor + '\">\n'))
350 sys.stdout.write(formatter.rawHTML('<PRE class=code>'))
351 sys.stdout.write(formatter.rawHTML(sb))
352 sys.stdout.write(formatter.rawHTML('</PRE></font>'))
353 return
Attached Files
To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.You are not allowed to attach a file to this page.