Attachment 'umlsequence.py'

Download

   1 #format python
   2 
   3 """
   4 UML sequence diagram parser for MoinMoin.
   5 
   6 
   7 Copyright (C) 2007 by Pascal Bauermeister
   8 
   9 This module is free software; you can redistribute it and/or modify
  10 it under the terms of the GNU General Public License as published by
  11 the Free Software Foundation; either version 2, or (at your option)
  12 any later version.
  13 
  14 This parser passes its input to umlgraph. There is also a more "visual"
  15 alternate syntax.
  16 
  17 -- #---------------------------------------------------------------------------
  18 
  19 Usage:
  20   {{{#!umlsequence [debug=1|2] [help=1] [percent=N]
  21     PROCEDURAL_STATMENTS or ALTERNATE_SYNTAX
  22   }}}
  23 
  24 Procedural syntax (a.k.a. Spinellis syntax) see:
  25   http://www.spinellis.gr/sw/umlgraph/doc/index.html
  26   http://www.spinellis.gr/sw/umlgraph/doc/seq-ops.html
  27 
  28   Examples:
  29     http://www.spinellis.gr/sw/umlgraph/doc/uml-appa.html (then click next)
  30 
  31 Alternate ("easy") syntax:
  32   See http://moinmoin.wikiwikiweb.de/UmlSequence
  33 
  34 See also:
  35   Umlgraph: http://www.spinellis.gr/sw/umlgraph/
  36 
  37 -- #---------------------------------------------------------------------------
  38 
  39 ChangeLog:
  40 
  41 Pascal Bauermeister <pascal DOT bauermeister AT gmail DOT com> 2007-05-13:
  42   * Initial version
  43 
  44 -- #---------------------------------------------------------------------------
  45 """
  46 
  47 
  48 # Parser's name
  49 NAME = __name__.split(".")[-1]
  50 
  51 Dependencies = []
  52 
  53 import os, re, sha
  54 import StringIO, string
  55 from MoinMoin.action import AttachFile
  56 from MoinMoin.Page import Page
  57 from MoinMoin import wikiutil, config
  58 
  59 def get_sequence_pic ():
  60     return """
  61 #/usr/bin/pic2plot -Tps
  62 #
  63 # Pic macros for drawing UML sequence diagrams
  64 #
  65 # (C) Copyright 2004-2005 Diomidis Spinellis.
  66 #
  67 # Permission to use, copy, and distribute this software and its
  68 # documentation for any purpose and without fee is hereby granted,
  69 # provided that the above copyright notice appear in all copies and that
  70 # both that copyright notice and this permission notice appear in
  71 # supporting documentation.
  72 #
  73 # THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
  74 # WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
  75 # MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  76 #
  77 # $Id: sequence.pic,v 1.10 2005/10/19 18:36:08 dds Exp $
  78 #
  79 
  80 
  81 # Default parameters (can be redefined)
  82 
  83 # Spacing between messages
  84 spacing = 0.25;
  85 # Active box width
  86 awid = .1;
  87 # Box height
  88 boxht = 0.3;
  89 # Commend folding
  90 corner_fold=awid
  91 # Comment distance
  92 define comment_default_move {up 0.25 right 0.25};
  93 # Comment height
  94 comment_default_ht=0.5;
  95 # Comment width
  96 comment_default_wid=1;
  97 
  98 
  99 # Create a new object(name,label)
 100 define object {
 101 	$1: box $2; move;
 102 	# Could also underline text with \\mk\\ul\\ul\\ul...\\rt
 103 	{
 104 		line from $1.w + (.1, -.07) to $1.e + (-.1, -.07);
 105 	}
 106 	move to $1.e;
 107 	move right;
 108 	# Active is the level of activations of the object
 109 	# 0 : inactive : draw thin line swimlane
 110 	# 1 : active : draw thick swimlane
 111 	# > 1: nested : draw nested swimlane
 112 	active_$1 = 0;
 113 	lifestart_$1 = $1.s.y;
 114 }
 115 
 116 # Create a new external actor(name,label)
 117 define actor {
 118 	$1: [
 119 		XSEQC: circle rad 0.06;
 120 		XSEQL: line from XSEQC.s down .12;
 121 		line from XSEQL.start - (.15,.02) to XSEQL.start + (.15,-.02);
 122 		XSEQL1: line from XSEQL.end left .08 down .15;
 123 		XSEQL2: line from XSEQL.end right .08 down .15;
 124 		line at XSEQC.n invis "" "" "" $2;
 125 	]
 126 	move to $1.e;
 127 	move right;
 128 	active_$1 = 0;
 129 	lifestart_$1 = $1.s.y - .05;
 130 }
 131 
 132 # Create a new placeholder object(name)
 133 define placeholder_object {
 134 	$1: box invisible;
 135 	move;
 136 	move to $1.e;
 137 	move right;
 138 	active_$1 = 0;
 139 	lifestart_$1 = $1.s.y;
 140 }
 141 
 142 define pobject {
 143 	placeholder_object($1);
 144 }
 145 
 146 define extend_lifeline {
 147 	if (active_$1 > 0) then {
 148                 # draw the left edges of the boxes
 149 		move to ($1.x - awid/2, Here.y);
 150 		for level = 1 to active_$1 do {
 151 			line from (Here.x, lifestart_$1) to Here;
 152 			move right awid/2
 153 		}
 154 
 155                 # draw the right edge of the innermost box
 156 		move right awid/2;
 157 		line from (Here.x, lifestart_$1) to Here;
 158 	} else {
 159 		line from ($1.x, lifestart_$1) to ($1.x, Here.y) dashed;
 160 	}
 161 	lifestart_$1 = Here.y;
 162 }
 163 
 164 # complete(name)
 165 # Complete the lifeline of the object with the given name
 166 define complete {
 167 	extend_lifeline($1)
 168 	if (active_$1) then {
 169 		# draw bottom of all active boxes
 170 		line right ((active_$1 + 1) * awid/2) from ($1.x - awid/2, Here.y);
 171 	}
 172 }
 173 
 174 # Draw a message(from_object,to_object,label)
 175 define message {
 176 	down;
 177 	move spacing;
 178 	# Adjust so that lines and arrows do not fall into the
 179 	# active box.  Should be .5, but the arrow heads tend to
 180 	# overshoot.
 181 	if ($1.x <= $2.x) then {
 182 		off_from = awid * .6;
 183 		off_to = -awid * .6;
 184 	} else {
 185 		off_from = -awid * .6;
 186 		off_to = awid * .6;
 187 	}
 188 
 189         # add half a box width for each level of nesting
 190         if (active_$1 > 1) then {
 191                 off_from = off_from + (active_$1 - 1) * awid/2;
 192         }
 193 
 194         # add half a box width for each level of nesting
 195         if (active_$2 > 1) then {
 196                 off_to = off_to + (active_$2 - 1) * awid/2;
 197         }
 198 
 199 	if ($1.x == $2.x) then {
 200 		arrow from ($1.x + off_from, Here.y) right then down .25 then left $3 ljust " " " " " " ;
 201 	} else {
 202 		arrow from ($1.x + off_from, Here.y) to ($2.x + off_to, Here.y) $3 " ";
 203 	}
 204 }
 205 
 206 # Display a lifeline constraint(object,label)
 207 define lifeline_constraint {
 208         off_from = awid;
 209         # add half a box width for each level of nesting
 210         if (active_$1 > 1) then {
 211                 off_from = off_from + (active_$1 - 1) * awid/2;
 212         }
 213 
 214 	box at ($1.x + off_from, Here.y) invis $2 ljust " " ;
 215 }
 216 
 217 define lconstraint {
 218 	lifeline_constraint($1,$2);
 219 }
 220 
 221 # Display an object constraint(label)
 222 # for the last object drawn
 223 define object_constraint {
 224 	{ box invis with .s at last box .nw $1 ljust; }
 225 }
 226 
 227 define oconstraint {
 228 	object_constraint($1);
 229 }
 230 
 231 # Draw a creation message(from_object,to_object,object_label)
 232 define create_message {
 233 	down;
 234 	move spacing;
 235 	if ($1.x <= $2.x) then {
 236 		off_from = awid * .6;
 237 		off_to = -boxwid * .51;
 238 	} else {
 239 		off_from = -awid * .6;
 240 		off_to = boxwid * .51;
 241 	}
 242 
 243         # add half a box width for each level of nesting
 244         if (active_$1 > 1) then {
 245                 off_from = off_from + (active_$1 - 1) * awid/2;
 246         }
 247 
 248 	# See comment in destroy_message
 249 	XSEQA: arrow from ($1.x + off_from, Here.y) to ($2.x + off_to, Here.y) "«create»" " ";
 250 	if ($1.x <= $2.x) then {
 251 		{ XSEQB: box $3 with .w at XSEQA.end; }
 252 	} else {
 253 		{ XSEQB: box $3 with .e at XSEQA.end; }
 254 	}
 255 	{
 256 		line from XSEQB.w + (.1, -.07) to XSEQB.e + (-.1, -.07);
 257 	}
 258 	lifestart_$2 = XSEQB.s.y;
 259 	move (spacing + boxht) / 2;
 260 }
 261 
 262 define cmessage {
 263 	create_message($1,$2,$3);
 264 }
 265 
 266 # Draw an X for a given object
 267 define drawx {
 268 	{
 269 	line from($1.x - awid, lifestart_$1 - awid) to ($1.x + awid, lifestart_$1 + awid);
 270 	line from($1.x - awid, lifestart_$1 + awid) to ($1.x + awid, lifestart_$1 - awid);
 271 	}
 272 }
 273 
 274 # Draw a destroy message(from_object,to_object)
 275 define destroy_message {
 276 	down;
 277 	move spacing;
 278 	# The troff code is \\(Fo \\(Fc
 279 	# The groff code is also \\[Fo] \\[Fc]
 280 	# The pic2plot code is \\Fo \\Fc
 281 	# See http://www.delorie.com/gnu/docs/plotutils/plotutils_71.html
 282 	# To stay compatible with all we have to hardcode the characters
 283 	message($1,$2,"«destroy»");
 284 	complete($2);
 285 	drawx($2);
 286 }
 287 
 288 define dmessage {
 289 	destroy_message($1,$2);
 290 }
 291 
 292 # An object deletes itself: delete(object)
 293 define delete {
 294 	complete($1);
 295 	lifestart_$1 = lifestart_$1 - awid;
 296 	drawx($1);
 297 }
 298 
 299 # Draw a message return(from_object,to_object,label)
 300 define return_message {
 301 	down;
 302 	move spacing;
 303 	# See comment in message
 304 	if ($1.x <= $2.x) then {
 305 		off_from = awid * .6;
 306 		off_to = -awid * .6;
 307 	} else {
 308 		off_from = -awid * .6;
 309 		off_to = awid * .6;
 310 	}
 311 
 312         # add half a box width for each level of nesting
 313         if (active_$1 > 1) then {
 314                 off_from = off_from + (active_$1 - 1) * awid/2;
 315         }
 316 
 317         # add half a box width for each level of nesting
 318         if (active_$2 > 1) then {
 319                 off_to = off_to + (active_$2 - 1) * awid/2;
 320         }
 321 
 322 	arrow from  ($1.x + off_from, Here.y) to ($2.x + off_to, Here.y) dashed $3 " ";
 323 }
 324 
 325 define rmessage {
 326 	return_message($1,$2,$3);
 327 }
 328 
 329 # Object becomes active
 330 # Can be nested to show recursion
 331 define active {
 332 	extend_lifeline($1);
 333 	# draw top of new active box
 334 	line right awid from ($1.x + (active_$1 - 1) * awid/2, Here.y);
 335 	active_$1 = active_$1 + 1;
 336 }
 337 
 338 # Object becomes inactive
 339 # Can be nested to show recursion
 340 define inactive {
 341 	extend_lifeline($1);
 342 	active_$1 = active_$1 - 1;
 343 	# draw bottom of innermost active box
 344 	line right awid from ($1.x + (active_$1 - 1) * awid/2, Here.y);
 345 }
 346 
 347 # Time step
 348 # Useful at the beginning and the end
 349 # to show object states
 350 define step {
 351 	down;
 352 	move spacing;
 353 }
 354 
 355 # Switch to asynchronous messages
 356 define async {
 357 	arrowhead = 0;
 358 	arrowwid = arrowwid * 2;
 359 }
 360 
 361 # Switch to synchronous messages
 362 define sync {
 363 	arrowhead = 1;
 364 	arrowwid = arrowwid / 2;
 365 }
 366 
 367 # same as lifeline_constraint, but Text and empty string are exchanged.
 368 define lconstraint_below{
 369         off_from = awid;
 370         # add half a box width for each level of nesting
 371         if (active_$1 > 1) then {
 372                 off_from = off_from + (active_$1 - 1) * awid/2;
 373         }
 374 
 375 	box at ($1.x + off_from, Here.y) invis "" $2 ljust;
 376 }
 377 
 378 # begin_frame(left_object,name,label_text);
 379 define begin_frame {
 380 	# The lifeline will be cut here
 381 	extend_lifeline($1);
 382 	# draw the frame-label
 383 	$2: box $3 invis with .n at ($1.x, Here.y);
 384 	d = $2.e.y - $2.se.y;
 385 	line from $2.ne to $2.e then down d left d then to $2.sw;
 386 	# continue the lifeline below the frame-label
 387 	move to $2.s;
 388 	lifestart_$1 = Here.y;
 389 }
 390 
 391 # end_frame(right_object,name);
 392 define end_frame {
 393 	# dummy-box for the lower right corner:
 394 	box invis "" with .s at ($1.x, Here.y);
 395 	# draw the frame
 396 	frame_wid = last box.se.x - $2.nw.x
 397 	frame_ht = - last box.se.y + $2.nw.y
 398 	box with .nw at $2.nw wid frame_wid ht frame_ht;
 399 	# restore Here.y
 400 	move to last box.s;
 401 }
 402 
 403 # comment(object,[name],[line_movement], [box_size] text);
 404 define comment {
 405 	old_y = Here.y
 406 	# draw the first connecting line, at which's end the box wil be positioned
 407 	move to ($1.x, Here.y)
 408 	if "$3" == "" then {
 409 		line comment_default_move() dashed;
 410 	} else {
 411 		line $3 dashed;
 412 	}
 413 
 414 	# draw the box, use comment_default_xx if no explicit
 415 	# size is given together with the text in parameter 4
 416 	old_boxht=boxht;
 417 	old_boxwid=boxwid;
 418 	boxht=comment_default_ht;
 419 	boxwid=comment_default_wid;
 420 	if "$2" == "" then {
 421 		box invis $4;
 422 	} else {
 423 		$2: box invis $4;
 424 	}
 425 	boxht=old_boxht;
 426 	boxwid=old_boxwid;
 427 
 428 	# draw the frame of the comment
 429 	line from       last box.nw \\
 430 		to          last box.ne - (corner_fold, 0) \\
 431 		then to last box.ne - (0, corner_fold) \\
 432 		then to last box.se \\
 433 		then to last box.sw \\
 434 		then to last box.nw ;
 435 	line from       last box.ne - (corner_fold, 0) \\
 436 		to          last box.ne - (corner_fold, corner_fold) \\
 437 		then to last box.ne - (0, corner_fold) ;
 438 
 439 	# restore Here.y
 440 	move to ($1.x, old_y)
 441 }
 442 
 443 # connect_to_comment(object,name);
 444 define connect_to_comment {
 445 	old_y = Here.y
 446 	# start at the object
 447 	move to ($1.x, Here.y)
 448 	# find the best connection-point of the comment to use as line-end
 449 	if $1.x < $2.w.x then {
 450 		line to $2.w dashed;
 451 	} else {
 452 		if $1.x > $2.e.x then {
 453 			line to $2.e dashed;
 454 		} else {
 455 			if Here.y < $2.s.y then {
 456 				line to $2.s dashed;
 457 			} else {
 458 				if Here.y > $2.n.y then {
 459 					line to $2.n dashed;
 460 				}
 461 			}
 462 		}
 463 	}
 464 	# restore Here.y
 465 	move to ($1.x, old_y)
 466 }
 467         """
 468 
 469 
 470 ###############################################################################
 471 
 472 import os
 473 def try_remove (path):
 474     try: os.remove (path)
 475     except: pass
 476 
 477 ###############################################################################
 478 class Parser:
 479     import re
 480     EXPRE1 = re.compile (
 481         "([A-Za-z_0-9]+?)([\+\-\#~!]?)"
 482         " *"
 483         "(\??-+>"
 484         "|<-+\??"
 485         "|\??=+>\??"
 486         "|\??<=+\??"
 487         "|\??#+>"
 488         "|<#+\??"
 489         "|\??:+>"
 490         "|<:+\??"
 491         "|//"
 492         "|:"
 493         "|>"
 494         "|\*"
 495         "|\["
 496         "|\]"
 497         "|\{_?\}"
 498         ")"
 499         " *"
 500         "([#A-Za-z_0-9]*)([\+\-\#~!]?)"
 501         " *"
 502         "(.*)")
 503 
 504     EXPRE1_call = re.compile ("([A-Za-z_0-9]+) *(=) *(.*)")
 505     EXPRE1_call = re.compile ("(.*?) *(=) *(.*)")
 506 
 507     EXPRE2 = re.compile ("([A-Za-z_0-9]+[\+\-\#~!]|:)+")
 508 
 509     EXPRE3 = re.compile ("([A-Za-z_0-9]*)([\+\-\#~!]?) *(_?)\{(.*)\}")
 510     
 511     extensions = ['.dot']
 512 
 513     def __init__ (self, raw, request, **kw):
 514         # save call arguments for later use in format()
 515         self.raw = raw.encode ('utf-8')
 516         self.request = request
 517 
 518         self.attrs, msg = wikiutil.parseAttributes (request,
 519                                                    kw.get ('format_args', ''))
 520         return
 521 
 522     def _usage (self, full=False):
 523 
 524         """Return the interesting part of the module's doc"""
 525 
 526         if full: return __doc__
 527 
 528         lines = __doc__.splitlines ()
 529         start = 0
 530         end = len (lines)
 531         for i in range (end):
 532             if lines [i].strip ().lower () == "usage:":
 533                 start = i
 534                 break
 535         for i in range (start, end):
 536             if lines [i].startswith ('--'):
 537                 end = i
 538                 break
 539         return '\n'.join (lines [start:end])
 540 
 541 
 542     def _convert_from_alternate_1 (self, lines):
 543         newlines = []
 544         
 545         self.has_first_step = False
 546         self.objects = []
 547 
 548         def add_obj (name):
 549             if name not in self.objects: self.objects.append (name)
 550 
 551         def rem_obj (name):
 552             if name in self.objects: self.objects.remove (name)
 553             
 554         def add (txt):
 555             txt = txt.strip ()
 556             if not txt : return
 557             ini = "object", "pobject", "placeholder_object", "actor"
 558             obj = max ([txt.startswith (i) for i in ini])
 559             if not obj and not self.has_first_step :
 560                 newlines.append ("step();")
 561                 self.has_first_step = True
 562             newlines.append (txt)
 563 
 564         def nl2str (text):
 565             texts = text.split ("\\n")
 566             texts = [t.strip () for t in texts]
 567             maxlen = max ( [len (t) for t in texts])
 568             text = ' " " '.join (texts)
 569             nlines = len (texts)
 570             return text, nlines, maxlen
 571             
 572         def do_line (line):
 573             oline = line
 574             
 575             line = line.strip ()
 576             if not line: return
 577 
 578             if line.startswith ("#"):
 579                 newlines.append (line)
 580                 return
 581 
 582             # try to match: OBJECT[OP] OP OBJECT[OP] MORE
 583             terms = Parser.EXPRE1.findall (line)
 584             if terms:
 585                 l, lop, op, r, rop, edge = terms [0]
 586                 newlines.append (' '*50 + "##1## "+oline)
 587                 ##newlines.append (' '*50 + "# "+ `(l,op,r,edge)`)
 588 
 589                 async_head = False
 590                 async_tail = False
 591 
 592                 if len (op) > 1:
 593                     # trim '?'
 594                     if op [0] == "?":
 595                         op = op [1:]
 596                         if op.startswith ("<"):
 597                             async_head = True
 598                         else:
 599                             async_tail = True
 600 
 601                     if op [-1] == "?":
 602                         op = op [:-1]
 603                         if op.endswith (">"):
 604                             async_head = True
 605                         else:
 606                             async_tail = True                            
 607 
 608                     if async_tail:
 609                         add ('async();')
 610                     
 611                     # trim long arrows
 612                     if op [0] == "<": op = op [:2]
 613                     elif op [-1] == ">": op = op [-2:]
 614 
 615                 # transform left arrow to right arrow
 616                 if op [0] == "<":
 617                     op = op [1:] + ">"
 618                     l, r = r, l
 619                     lop, rop = rop, lop
 620                 ##newlines.append (' '*50 + "# "+ `(l,op,r,edge)`)
 621                 
 622                 r2 = ' '.join ( (r, edge)).strip ()
 623 
 624                 # escape quotes
 625                 edge = edge.replace ('"','\\"')
 626                 r2 = r2.replace ('"','\\"')
 627 
 628                 # implement primitives
 629                 # http://www.spinellis.gr/sw/umlgraph/doc/seq-ops.html
 630 
 631                 if op == ":"  and r2 and not r2.startswith ("#"):
 632                     add ('object(%s," %s ");' % (l, r2))
 633                     add_obj (l)
 634 
 635                 elif op == ":" :
 636                     add ('pobject(%s);' % (l))
 637 
 638                 elif op == "*" :
 639                     if r2.startswith ("#"): r2 = ""
 640                     add ('actor(%s," %s ");' % (l, r2))
 641                     add_obj (l)
 642                     
 643                 elif op == "->" :
 644                     edge, nlines, maxlen = nl2str (edge)
 645                     if edge.startswith ("<(>"):
 646                         add ('message(%s,%s,"");' % (l, r))
 647                         edge = edge [3:].strip ()
 648                         add ('lconstraint(%s," %s ");' % (l, edge))
 649                     elif edge.startswith ("<)>"):
 650                         add ('message(%s,%s,"");' % (l, r))
 651                         edge = edge [3:].strip ()
 652                         add ('lconstraint(%s," %s ");' % (r, edge))
 653                     else:
 654                         add ('message(%s,%s," %s ");' % (l, r, edge))
 655                     if lop: do_line (l+lop)
 656                     if rop: do_line (r+rop)
 657 
 658                 elif op == ">" :
 659                     r2, nlines, maxlen = nl2str (r2)
 660                     add ('active(%s);' % l)
 661                     add ('step();')
 662                     add ('message(%s,%s," %s ");' % (l, l, r2))
 663                     add ('inactive(%s);' % l)
 664                     if lop: do_line (l+lop)
 665                     
 666                 elif op == ":>" :
 667                     add ('cmessage(%s,%s," %s "," ");' % (l, r, edge))
 668                     add_obj (r)
 669                     if lop: do_line (l+lop)
 670                     if rop: do_line (r+rop)
 671 
 672                 elif op == "=>" :
 673                     subterms = Parser.EXPRE1_call.findall (edge)
 674                     if subterms: subterms = subterms [0]
 675                     if subterms and len (subterms)==3 and subterms [1] == "=" :
 676                         # short hand for request+result
 677                         res, op, edge = subterms
 678                         add ('message(%s,%s," %s ");' % (l, r, edge))
 679                         add ('active(%s);' % r)
 680 
 681                         if lop: do_line (l+lop)
 682                         
 683                         # sync/async for arrow head end:
 684                         if async_tail and not async_head:
 685                             add ('sync();')
 686                         elif not async_tail and async_head:
 687                             add ('async();')
 688 
 689                         add ('rmessage(%s,%s," %s ");' % (r, l, res))
 690                         add ('inactive(%s);' % r)
 691                     else:
 692                         # result only
 693                         add ('rmessage(%s,%s," %s ");' % (l, r, edge))
 694                         if lop: do_line (l+lop)
 695                     if rop: do_line (r+rop)
 696 
 697                 elif op == "#>" :
 698                     add ('dmessage(%s,%s);' % (l, r))
 699                     rem_obj (r)
 700                     if lop: do_line (l+lop)
 701                     if rop: do_line (r+rop)
 702                     
 703                 elif op == "//" :
 704                     r2 = (r + " " + edge).strip ()
 705                     if r2.startswith ("["):
 706                         opts, text = r2 [1:].split ("]",1) # opts passed
 707                     else: opts, text = "", r2              # no opts passed
 708                     opts = (opts+",,").split (",") [:3]    # insure 3 elements
 709                     text = text.strip ()
 710 
 711                     if not text:
 712                         add ('connect_to_comment(%s,%s);' % (l, opts [0]))
 713                     else:
 714                         text, nlines, maxlen = nl2str (text)
 715                         if not opts [1]: # auto-calculate box pos
 716                             opts [1] = "down 0 right"
 717                         if not opts [2]: # auto-calculate box size
 718                             w = 0.10 + float (maxlen) / 13.0
 719                             h = 0.10 + 0.16 * nlines
 720                             opts [2] = "wid %.2f ht %.2f" % (w, h)
 721                         opts = ",".join (opts)
 722                         add ('comment(%s,%s "%s");' % (l, opts, text))
 723 
 724                     if lop: do_line (l+lop)
 725 
 726                 elif op == "[" or op == "]":
 727                     # Possible syntax:
 728                     #   frame_name [ object frame_title
 729                     #           ... activity ...
 730                     #                object ] frame_name
 731                     #
 732                     # Possible syntax:
 733                     #                object ] frame_name frame_title
 734                     #           ... activity ...
 735                     #   frame_name [ object
 736                     #
 737                     if edge:
 738                         if op == "]": r, l = l, r
 739                         add ('begin_frame(%s,%s," %s ");' % (r, l, edge))
 740                     else:
 741                         if op == "[": r, l = l, r
 742                         add ('end_frame(%s,%s);' % (l, r))
 743 
 744                 if async_head or async_tail:
 745                     add ('sync();')
 746                 return
 747 
 748             # try to match: OBJECT[OP] [OBJECT[OP] ...]
 749             terms = Parser.EXPRE2.findall (line)
 750             if terms == line.split ():
 751                 newlines.append (' '*50 + "##2## "+oline)
 752                 for term in terms :
 753                     l, op = term [:-1], term [-1]
 754                     if op == "+" :
 755                         add ('active(%s);' % l)
 756                     elif op == "-" :
 757                         add ('inactive(%s);' % l)
 758                     elif op == "!" :
 759                         add ('active(%s);' % l)
 760                         add ('step();')
 761                         add ('inactive(%s);' % l)
 762                     elif op == "#" :
 763                         add ('complete(%s);' % l)
 764                         rem_obj (l)
 765                     elif op == "~" :
 766                         add ('delete(%s);' % l)
 767                         rem_obj (l)
 768                 if ":" in terms :
 769                     add ('step();')
 770                 return
 771 
 772             # try to match: [OBJECT[OP]] [_]{CONSTRAINT}
 773             terms = Parser.EXPRE3.findall (line)
 774             if terms:
 775                 newlines.append (' '*50 + "##3## "+oline)
 776                 l, lop, below, r = terms [0]
 777                 r, nlines, maxlen = nl2str (r)
 778                 if not l:
 779                     add ('oconstraint(" %s ");' % (r))
 780                 elif not below:
 781                     add ('lconstraint(%s," %s ");' % (l, r))
 782                     if lop: do_line (l+lop)
 783                 else:
 784                     add ('lconstraint_below(%s," %s ");' % (l, r))
 785                     if lop: do_line (l+lop)
 786                 return
 787 
 788             # all attemps to match by RE failed => output as-is
 789             newlines.append (line)
 790 
 791         for line in lines: do_line (line)
 792 
 793         if self.objects:
 794             add ('step();')
 795             for o in self.objects: add ('complete(%s);' % o)
 796         return newlines
 797 
 798 
 799     def format (self, formatter):
 800         """The parser's entry point"""
 801 
 802         # parse bangpath for arguments
 803         opt_dbg  = False
 804         opt_help = None
 805         opt_percent = 100
 806         for (key, val) in self.attrs.items ():
 807             val = val [1:-1]
 808             if   key == 'debug':   opt_dbg  = int (val)
 809             elif key == 'help':    opt_help = val
 810             elif key == 'percent': opt_percent = int (val)
 811             else:
 812                 self.request.write (formatter.rawHTML ("""
 813                 <p><strong class="error">
 814                 Error: processor %s: invalid argument: %s
 815                 <pre>%s</pre></strong> </p>
 816                 """ % (NAME, self.attrs, self._usage ())))
 817                 return
 818 
 819         # help ?
 820         if opt_help is not None and opt_help != '0':
 821             self.request.write (formatter.rawHTML ("""
 822             <p>
 823             Processor %s usage:
 824             <pre>%s</pre></p>
 825             """ % (NAME, self._usage (opt_help == '2'))))
 826             return
 827 
 828         # useful
 829         pagename = formatter.page.page_name
 830         raw = self.raw
 831 
 832         # debug ? pre-print
 833         if opt_dbg==1:
 834             self.request.write (formatter.rawHTML (
 835                 "<pre>\n%s\n</pre>" % raw))
 836 
 837         # preprocess: join lines ending with '\' with the next one
 838         # (lazy way, TODO: use regex)
 839         while raw.find ("\\\n ")>=0:
 840             raw =raw.replace ("\\\n ", "\\\n")
 841         while raw.find (" \\\n")>=0:
 842             raw =raw.replace (" \\\n", "\\\n")
 843         raw = raw.replace ("\\\n", " ")
 844         lines = raw.split ('\n')
 845 
 846         # alternate syntax support
 847         lines = self._convert_from_alternate_1 (lines)
 848 
 849         # debug ? post-print
 850         if opt_dbg==2:
 851             self.request.write (formatter.rawHTML(
 852                 "<pre>\n%s\n</pre>" % '\n'.join (lines)))
 853 
 854         # go !
 855         all = '\n'.join (lines).strip ()
 856         name = 'autogenerated--' + NAME + '-parser--' + \
 857                sha.new (all+`self.attrs.items ()`).hexdigest ()
 858         pngname = name + '.png'
 859 
 860         attdir = AttachFile.getAttachDir (self.request, pagename, create=1) + '/'
 861         pngpath = attdir + pngname
 862 
 863         dm2ri = attdir + "delete.me.to.regenerate.images"
 864 
 865         # delete autogenerated attachments if debug or dm2ri
 866         # attachment does not exist
 867         if not os.path.isfile (dm2ri):
 868             # create dm2ri attachment
 869             open (dm2ri,'w').close ()
 870             # delete autogenerated attachments
 871             for root, dirs, files in os.walk (attdir, topdown=False):
 872                 for name in files:
 873                     if name.startswith ("autogenerated-"):
 874                         os.remove (os.path.join (root, name))
 875 
 876         pngdir = os.path.splitext (pngpath) [0] 
 877         ps = pngdir + ".ps"
 878         pic = pngdir + ".pic"
 879         err = pngdir + "-err.txt"
 880 
 881         want_png = opt_dbg>1 or \
 882                    not os.path.exists (pngpath) or \
 883                    os.path.exists (err)
 884         
 885         if want_png:
 886             all = ".PS\n%s\n%s\n.PE" % (get_sequence_pic () , all)
 887             open (pic, "w").write (all)
 888             
 889              # Launch pic2plot => postscript
 890             os.system ('pic2plot -T ps "%s" > "%s" 2>"%s"' % (pic, ps, err))
 891             if not os.stat (err).st_size:
 892                 try_remove (err)
 893             else:
 894                 # Error handling
 895 
 896                 # let us parse the 1st line of the error file, saying:
 897                 #   pic2plot:<PIC_FILENAME>:<LINE_NR>:<ERROR_MESSAGE>
 898                 err = open (err).readlines () [0].split (":")
 899                 if len (err) == 4:
 900                     lnr, msg = err [2:2+2]
 901                     ix = int (lnr)-1
 902                     snip = [l.rstrip () for l in open (pic).readlines ()]
 903                     snip, context = snip [ix], snip [max (ix-2,0):ix+1+2]
 904                     context = "\n".join (context)
 905                 else: # cannot parse it
 906                     msg = ":".join (err)
 907                     snip = ""
 908 
 909                 # print error message
 910                 self.request.write (formatter.rawHTML ("""
 911                 <p><strong class="error">
 912                 Error: parser '%s': %s""" % (NAME, msg)))
 913 
 914                 # print code snippet
 915                 if snip:
 916                     self.request.write (formatter.rawHTML("""
 917                     <br>Faulty line:
 918                     <pre>%s</pre>
 919                     Context:
 920                     <pre>%s</pre>
 921                     (for full source, specify debug=2 as parser's arg)
 922                     """ % (snip, context)))
 923 
 924                 # close error message and return
 925                 self.request.write (formatter.rawHTML ("""
 926                 </strong> </p>"""))
 927                 return
 928                 
 929             # Run the postprocessing/conversion chain
 930             os.system ('convert -density %dx%d "%s" "%s"' % \
 931                        (opt_percent, opt_percent, ps, pngpath))
 932 
 933             # Cleanup
 934             if opt_dbg<2: try_remove (pic)
 935             try_remove (ps)
 936 
 937         # Form the <img> tag
 938         url = AttachFile.getAttachUrl (pagename, pngname, self.request)
 939         self.request.write (formatter.image (src = url))
 940 
 941         # Done
 942         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.
  • [get | view] (2007-07-05 22:20:02, 28.8 KB) [[attachment:umlsequence.py]]
 All files | Selected Files: delete move to page copy to page

You are not allowed to attach a file to this page.