1 from __future__
import nested_scopes
3 from constants
import DOME_NS
4 from xml
.dom
import Node
6 def el_named(node
, name
):
7 for n
in node
.childNodes
:
8 if n
.localName
== name
:
12 # Converts a DOM <block> node to a Block object.
13 def load(node
, parent
):
14 #assert node.localName == 'block'
19 if int(node
.getAttributeNS(None, 'foreach')):
20 block
.toggle_foreach()
21 if int(node
.getAttributeNS(None, 'enter')):
23 if node
.hasAttributeNS(None, 'restore'):
24 if int(node
.getAttributeNS(None, 'restore')):
25 block
.toggle_restore()
26 comment
= node
.getAttributeNS(None, 'comment')
27 block
.set_comment(comment
)
31 id_hash
= {} # id from file -> Op
33 def _load(chain
, prev
, exit
):
34 for op_node
in chain
.childNodes
:
35 if str(op_node
.localName
) == 'node':
36 attr
= op_node
.getAttributeNS(None, 'action')
37 action
= eval(str(attr
))
38 if action
[0] == 'Start':
39 print "Skipping Start"
41 if action
[0] == 'chroot':
43 elif action
[0] == 'unchroot':
45 #elif action[0] == 'set_attrib':
47 # action = ('add_attrib', action[1], action[2])
49 # action = ('set_attrib', action[3])
50 elif action
[0] == 'playback':
52 elif action
[0] == 'add_attrib':
55 elif op_node
.localName
== 'block':
56 op
= load(op_node
, block
)
58 if op_node
.nodeType
== Node
.ELEMENT_NODE
and op_node
.localName
!= 'fail':
59 print "** WARNING ** Unknown op:", op_node
63 dx
= int(float(op_node
.getAttributeNS(None, 'dx')))
66 dy
= int(float(op_node
.getAttributeNS(None, 'dy')))
72 node_id
= op_node
.getAttributeNS(None, 'id')
76 prev
.link_to(op
, exit
)
80 if op_node
.localName
== 'block':
81 # Block nodes have a special failure child
82 for x
in op_node
.childNodes
:
83 if x
.localName
== 'fail':
87 # If the new node has children then they are the failure case
88 _load(op_node
, op
, 'fail')
90 link
= op_node
.getAttributeNS(None, 'target_fail')
92 to_link
.append((op
, 'fail', link
))
93 link
= op_node
.getAttributeNS(None, 'target_next')
95 to_link
.append((op
, 'next', link
))
96 _load(node
, block
.start
, 'next')
97 for (op
, exit
, child
) in to_link
:
101 print "**** Not adding link to unknown ID ****"
106 def load_dome_program(prog
):
107 "prog should be a DOM 'dome-program' node."
108 #print "Loading", prog
109 if prog
.localName
!= 'dome-program':
110 raise Exception('Not a DOME program: %s!' % prog
)
112 new
= Program(str(prog
.getAttributeNS(None, 'name')))
114 #print "Loading '%s'..." % new.name
117 for node
in prog
.childNodes
:
118 if node
.localName
== 'node' and not done_update
:
119 print "*** Converting from old format ***"
120 new
.code
= load(prog
, new
)
122 if node
.localName
== 'block':
123 assert not done_update
124 new
.code
= load(node
, new
)
125 if node
.localName
== 'dome-program':
126 new
.add_sub(load_dome_program(node
))
132 "A program contains a code Block and any number of sub-programs."
133 def __init__(self
, name
):
134 assert '/' not in name
136 self
.code
= Block(self
)
139 self
.subprograms
= {}
148 path
= p
.name
+ '/' + path
152 def changed(self
, op
= None):
155 self
.parent
.changed(op
)
157 for w
in self
.watchers
:
158 w
.program_changed(op
)
160 def tree_changed(self
):
163 self
.parent
.tree_changed()
165 for w
in self
.watchers
:
166 w
.prog_tree_changed()
168 def add_sub(self
, prog
):
170 raise Exception('%s already has a parent program!' % prog
.name
)
171 if self
.subprograms
.has_key(prog
.name
):
172 raise Exception('%s already has a child called %s!' %
173 (self
.name
, prog
.name
))
175 self
.subprograms
[prog
.name
] = prog
178 def remove_sub(self
, prog
):
179 if prog
.parent
!= self
:
180 raise Exception('%s is no child of mime!' % prog
)
182 del self
.subprograms
[prog
.name
]
185 def rename(self
, name
):
188 if p
.subprograms
.has_key(name
):
189 raise Exception('%s already has a child called %s!' % (p
.name
, name
))
197 def to_xml(self
, doc
):
198 node
= doc
.createElementNS(DOME_NS
, 'dome-program')
199 node
.setAttributeNS(None, 'name', self
.name
)
201 node
.appendChild(self
.code
.to_xml(doc
))
203 # Keep them in the same order to help with diffs...
204 progs
= self
.subprograms
.keys()
208 p
= self
.subprograms
[name
]
209 node
.appendChild(p
.to_xml(doc
))
214 return "Program(%s)" % self
.name
217 "Each node in a chain is an Op. There is no graphical stuff in here."
219 def __init__(self
, action
= None):
220 "Creates a new node (can be linked into another node later)"
224 action
= list(action
)
229 self
.prev
= [] # First parent is used for rendering as a tree
230 self
.dx
, self
.dy
= (0, 0)
232 def set_parent(self
, parent
):
233 if self
.parent
== parent
:
235 if parent
and self
.parent
:
236 raise Exception('Already got a parent!')
237 nearby
= self
.prev
[:]
239 nearby
.append(self
.next
)
241 nearby
.append(self
.fail
)
243 [x
.set_parent(parent
) for x
in nearby
if x
.parent
is not parent
]
245 def changed(self
, op
= None):
246 if hasattr(self
, 'cached_code'):
248 print "(remove cached code)"
249 self
.parent
.changed(op
or self
)
252 assert self
.action
[0] != 'Start'
253 self
.next
, self
.fail
= (self
.fail
, self
.next
)
256 def link_to(self
, child
, exit
):
257 # Create a link from this exit to this child Op
258 # Can't link both exits to the same node (bad for tree-walking code in List)
259 assert self
.action
[0] != 'Start' or exit
== 'next'
260 assert child
.action
[0] != 'Start'
261 assert child
is not self
263 if (exit
== 'next' and self
.fail
== child
) or \
264 (exit
== 'fail' and self
.next
== child
):
265 raise Exception("Can't link both exits (of %s) to the same node!" % self
)
267 #print "Link %s:%s -> %s" % (self, exit, child)
269 if child
.parent
and child
.parent
is not self
.parent
:
270 raise Exception('%s is from a different parent (%s vs %s)!' %
271 (child
, child
.parent
, self
.parent
))
272 # If we already have something on this exit, and the new node has a
273 # clear next exit, move the rest of the chain there.
274 child
.set_parent(self
.parent
)
275 current
= getattr(self
, exit
)
278 raise Exception('%s already has a next exit' % child
)
279 primary
= current
.prev
[0] == self
280 self
.unlink(exit
, may_delete
= 0)
281 child
.link_to(current
, 'next')
283 # We were the primary parent, so we must be again...
284 current
.prev
.remove(child
)
285 current
.prev
.insert(0, child
)
286 child
.prev
.append(self
)
287 setattr(self
, exit
, child
)
290 def unlink(self
, exit
, may_delete
= 1):
291 "Remove link from us to child"
292 assert exit
in ['next', 'fail']
293 self
._unlink
(exit
, may_delete
)
296 def _unlink(self
, exit
, may_delete
= 1):
297 child
= getattr(self
, exit
)
299 raise Exception('%s has no child on exit %s' % (self
, exit
))
300 if self
not in child
.prev
:
301 raise Exception('Internal error: %s not my child!' % child
)
303 child
.prev
.remove(self
) # Only remove one copy
304 setattr(self
, exit
, None)
306 for x
in child
.prev
: print x
308 if may_delete
and not child
.prev
:
309 # There is no way to reach this child now, so unlink its children.
312 child
._unlink
('next')
314 child
._unlink
('fail')
317 """Remove this node. It there is exactly one out-going arc and one incoming one,
318 join them together. Error if:"
319 - There are multiple out-going arcs
320 - There is a single out-going arc but multiple parents"""
321 if self
.next
and self
.fail
:
322 raise Exception("Can't delete a node with both fail and next exits in use.")
324 raise Exception("Can't delete a Start node!")
328 # Find the chain to preserve (can't have both set here)
337 if len(self
.prev
) != 1:
338 raise Exception("Deleted node-chain must have a single link in")
340 preserve
= getattr(self
, exit
)
341 self
.unlink(exit
, may_delete
= 0)
342 if prev
.next
== self
:
347 # Remove all links to us
348 for p
in self
.prev
[:]:
354 # Exit is now our parent's exit that leads to us...
356 # Relink following nodes to our (single) parent
357 prev
.link_to(preserve
, exit
)
370 from Ft
.Xml
.cDomlette
import implementation
371 doc
= implementation
.createDocument(DOME_NS
, 'dome-program', None)
372 self
.to_xml_int(doc
.documentElement
)
375 def to_xml(self
, doc
):
376 node
= doc
.createElementNS(DOME_NS
, 'node')
377 node
.setAttributeNS(None, 'action', `self
.action`
)
378 node
.setAttributeNS(None, 'dx', str(self
.dx
))
379 node
.setAttributeNS(None, 'dy', str(self
.dy
))
382 def to_xml_int(self
, parent
):
383 """Adds a chain of <Node> elements to 'parent'. Links only followed when node is
385 node
= self
.to_xml(parent
.ownerDocument
)
386 parent
.appendChild(node
)
388 if len(self
.prev
) > 1:
389 node
.setAttributeNS(None, 'id', str(id(self
)))
391 def add_link(op
, parent
):
392 node
= parent
.ownerDocument
.createElementNS(DOME_NS
, 'link')
393 parent
.appendChild(node
)
394 node
.setAttributeNS(None, 'target', str(id(op
)))
397 if self
.fail
.prev
[0] == self
:
398 if isinstance(self
, Block
):
399 fail
= parent
.ownerDocument
.createElementNS(DOME_NS
, 'fail')
400 self
.fail
.to_xml_int(fail
)
401 node
.appendChild(fail
)
403 self
.fail
.to_xml_int(node
)
405 node
.setAttributeNS(None, 'target_fail', str(id(self
.fail
)))
407 if self
.next
.prev
[0] == self
:
408 self
.next
.to_xml_int(parent
)
410 node
.setAttributeNS(None, 'target_next', str(id(self
.next
)))
413 return "{" + `self
.action`
+ "}"
415 def get_program(self
):
417 while p
and not isinstance(p
, Program
):
422 """A Block is an Op which contains a group of Ops."""
424 def __init__(self
, parent
):
425 Op
.__init
__(self
, action
= ['Block'])
428 self
.start
.parent
= self
434 def set_start(self
, start
):
435 assert not start
.prev
437 start
.set_parent(self
)
441 def is_toplevel(self
):
442 return not isinstance(self
.parent
, Block
)
444 def link_to(self
, child
, exit
):
445 assert not self
.is_toplevel()
446 Op
.link_to(self
, child
, exit
)
448 def to_xml(self
, doc
):
449 node
= doc
.createElementNS(DOME_NS
, 'block')
450 node
.setAttributeNS(None, 'foreach', str(self
.foreach
))
451 node
.setAttributeNS(None, 'enter', str(self
.enter
))
452 node
.setAttributeNS(None, 'restore', str(self
.restore
))
453 node
.setAttributeNS(None, 'comment', str(self
.comment
))
454 assert not self
.start
.fail
456 self
.start
.next
.to_xml_int(node
)
459 def toggle_restore(self
):
460 self
.restore
= not self
.restore
463 def toggle_enter(self
):
464 self
.enter
= not self
.enter
467 def toggle_foreach(self
):
468 self
.foreach
= not self
.foreach
471 def set_comment(self
, comment
):
472 self
.comment
= comment