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
, ns
):
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':
54 elif action
[0] == 'do_search' and action
[2] != 'unused':
55 print "Converting search namespaces..."
56 for p
, u
in action
[2].iteritems():
58 new_prefix
= ns
.ensure_ns(p
, u
)
59 action
[1] = action
[1].replace(p
+ ':',
63 elif op_node
.localName
== 'block':
64 op
= load(op_node
, block
)
66 if op_node
.nodeType
== Node
.ELEMENT_NODE
and op_node
.localName
!= 'fail':
67 print "** WARNING ** Unknown op:", op_node
71 dx
= int(float(op_node
.getAttributeNS(None, 'dx')))
74 dy
= int(float(op_node
.getAttributeNS(None, 'dy')))
80 node_id
= op_node
.getAttributeNS(None, 'id')
84 prev
.link_to(op
, exit
)
88 if op_node
.localName
== 'block':
89 # Block nodes have a special failure child
90 for x
in op_node
.childNodes
:
91 if x
.localName
== 'fail':
95 # If the new node has children then they are the failure case
96 _load(op_node
, op
, 'fail')
98 link
= op_node
.getAttributeNS(None, 'target_fail')
100 to_link
.append((op
, 'fail', link
))
101 link
= op_node
.getAttributeNS(None, 'target_next')
103 to_link
.append((op
, 'next', link
))
104 _load(node
, block
.start
, 'next')
105 for (op
, exit
, child
) in to_link
:
109 print "**** Not adding link to unknown ID ****"
114 def load_dome_program(prog
, ns
):
115 "prog should be a DOM 'dome-program' node. ns will be updated"
117 assert isinstance(ns
, Namespaces
.Namespaces
)
118 #print "Loading", prog
119 if prog
.localName
!= 'dome-program':
120 raise Exception('Not a DOME program: %s!' % prog
)
122 new
= Program(str(prog
.getAttributeNS(None, 'name')))
124 #print "Loading '%s'..." % new.name
127 for node
in prog
.childNodes
:
128 if node
.localName
== 'node' and not done_update
:
129 print "*** Converting from old format ***"
130 new
.code
= load(prog
, new
)
132 if node
.localName
== 'block':
133 assert not done_update
134 new
.code
= load(node
, new
, ns
)
135 if node
.localName
== 'dome-program':
136 new
.add_sub(load_dome_program(node
))
142 "A program contains a code Block and any number of sub-programs."
143 def __init__(self
, name
):
144 assert '/' not in name
146 self
.code
= Block(self
)
149 self
.subprograms
= {}
158 path
= p
.name
+ '/' + path
162 def changed(self
, op
= None):
165 self
.parent
.changed(op
)
167 for w
in self
.watchers
:
168 w
.program_changed(op
)
170 def tree_changed(self
):
173 self
.parent
.tree_changed()
175 for w
in self
.watchers
:
176 w
.prog_tree_changed()
178 def add_sub(self
, prog
):
180 raise Exception('%s already has a parent program!' % prog
.name
)
181 if self
.subprograms
.has_key(prog
.name
):
182 raise Exception('%s already has a child called %s!' %
183 (self
.name
, prog
.name
))
185 self
.subprograms
[prog
.name
] = prog
188 def remove_sub(self
, prog
):
189 if prog
.parent
!= self
:
190 raise Exception('%s is no child of mime!' % prog
)
192 del self
.subprograms
[prog
.name
]
195 def rename(self
, name
):
198 if p
.subprograms
.has_key(name
):
199 raise Exception('%s already has a child called %s!' % (p
.name
, name
))
207 def to_xml(self
, doc
):
208 node
= doc
.createElementNS(DOME_NS
, 'dome:dome-program')
209 node
.setAttributeNS(None, 'name', self
.name
)
211 node
.appendChild(self
.code
.to_xml(doc
))
213 # Keep them in the same order to help with diffs...
214 progs
= self
.subprograms
.keys()
218 p
= self
.subprograms
[name
]
219 node
.appendChild(p
.to_xml(doc
))
224 return "Program(%s)" % self
.name
227 "Each node in a chain is an Op. There is no graphical stuff in here."
229 def __init__(self
, action
= None):
230 "Creates a new node (can be linked into another node later)"
234 action
= list(action
)
239 self
.prev
= [] # First parent is used for rendering as a tree
240 self
.dx
, self
.dy
= (0, 0)
242 def set_parent(self
, parent
):
243 if self
.parent
== parent
:
245 if parent
and self
.parent
:
246 raise Exception('Already got a parent!')
247 nearby
= self
.prev
[:]
249 nearby
.append(self
.next
)
251 nearby
.append(self
.fail
)
253 [x
.set_parent(parent
) for x
in nearby
if x
.parent
is not parent
]
255 def changed(self
, op
= None):
256 if hasattr(self
, 'cached_code'):
258 print "(remove cached code)"
259 self
.parent
.changed(op
or self
)
262 assert self
.action
[0] != 'Start'
263 self
.next
, self
.fail
= (self
.fail
, self
.next
)
266 def link_to(self
, child
, exit
):
267 # Create a link from this exit to this child Op
268 # Can't link both exits to the same node (bad for tree-walking code in List)
269 assert self
.action
[0] != 'Start' or exit
== 'next'
270 assert child
.action
[0] != 'Start'
271 assert child
is not self
273 if (exit
== 'next' and self
.fail
== child
) or \
274 (exit
== 'fail' and self
.next
== child
):
275 raise Exception("Can't link both exits (of %s) to the same node!" % self
)
277 #print "Link %s:%s -> %s" % (self, exit, child)
279 if child
.parent
and child
.parent
is not self
.parent
:
280 raise Exception('%s is from a different parent (%s vs %s)!' %
281 (child
, child
.parent
, self
.parent
))
282 # If we already have something on this exit, and the new node has a
283 # clear next exit, move the rest of the chain there.
284 child
.set_parent(self
.parent
)
285 current
= getattr(self
, exit
)
288 raise Exception('%s already has a next exit' % child
)
289 primary
= current
.prev
[0] == self
290 self
.unlink(exit
, may_delete
= 0)
291 child
.link_to(current
, 'next')
293 # We were the primary parent, so we must be again...
294 current
.prev
.remove(child
)
295 current
.prev
.insert(0, child
)
296 child
.prev
.append(self
)
297 setattr(self
, exit
, child
)
300 def unlink(self
, exit
, may_delete
= 1):
301 "Remove link from us to child"
302 assert exit
in ['next', 'fail']
303 self
._unlink
(exit
, may_delete
)
306 def _unlink(self
, exit
, may_delete
= 1):
307 child
= getattr(self
, exit
)
309 raise Exception('%s has no child on exit %s' % (self
, exit
))
310 if self
not in child
.prev
:
311 raise Exception('Internal error: %s not my child!' % child
)
313 child
.prev
.remove(self
) # Only remove one copy
314 setattr(self
, exit
, None)
316 for x
in child
.prev
: print x
318 if may_delete
and not child
.prev
:
319 # There is no way to reach this child now, so unlink its children.
322 child
._unlink
('next')
324 child
._unlink
('fail')
327 """Remove this node. It there is exactly one out-going arc and one incoming one,
328 join them together. Error if:"
329 - There are multiple out-going arcs
330 - There is a single out-going arc but multiple parents"""
331 if self
.next
and self
.fail
:
332 raise Exception("Can't delete a node with both fail and next exits in use.")
334 raise Exception("Can't delete a Start node!")
338 # Find the chain to preserve (can't have both set here)
347 if len(self
.prev
) != 1:
348 raise Exception("Deleted node-chain must have a single link in")
350 preserve
= getattr(self
, exit
)
351 self
.unlink(exit
, may_delete
= 0)
352 if prev
.next
== self
:
357 # Remove all links to us
358 for p
in self
.prev
[:]:
364 # Exit is now our parent's exit that leads to us...
366 # Relink following nodes to our (single) parent
367 prev
.link_to(preserve
, exit
)
380 from Ft
.Xml
.cDomlette
import implementation
381 doc
= implementation
.createDocument(DOME_NS
, 'dome:dome-program', None)
382 self
.to_xml_int(doc
.documentElement
)
385 def to_xml(self
, doc
):
386 node
= doc
.createElementNS(DOME_NS
, 'dome:node')
387 node
.setAttributeNS(None, 'action', `self
.action`
)
388 node
.setAttributeNS(None, 'dx', str(self
.dx
))
389 node
.setAttributeNS(None, 'dy', str(self
.dy
))
392 def to_xml_int(self
, parent
):
393 """Adds a chain of <Node> elements to 'parent'. Links only followed when node is
395 node
= self
.to_xml(parent
.ownerDocument
)
396 parent
.appendChild(node
)
398 if len(self
.prev
) > 1:
399 node
.setAttributeNS(None, 'id', str(id(self
)))
401 def add_link(op
, parent
):
402 node
= parent
.ownerDocument
.createElementNS(DOME_NS
, 'dome:link')
403 parent
.appendChild(node
)
404 node
.setAttributeNS(None, 'target', str(id(op
)))
407 if self
.fail
.prev
[0] == self
:
408 if isinstance(self
, Block
):
409 fail
= parent
.ownerDocument
.createElementNS(DOME_NS
, 'dome:fail')
410 self
.fail
.to_xml_int(fail
)
411 node
.appendChild(fail
)
413 self
.fail
.to_xml_int(node
)
415 node
.setAttributeNS(None, 'target_fail', str(id(self
.fail
)))
417 if self
.next
.prev
[0] == self
:
418 self
.next
.to_xml_int(parent
)
420 node
.setAttributeNS(None, 'target_next', str(id(self
.next
)))
423 return "{" + `self
.action`
+ "}"
425 def get_program(self
):
427 while p
and not isinstance(p
, Program
):
432 """A Block is an Op which contains a group of Ops."""
434 def __init__(self
, parent
):
435 Op
.__init
__(self
, action
= ['Block'])
438 self
.start
.parent
= self
444 def set_start(self
, start
):
445 assert not start
.prev
447 start
.set_parent(self
)
451 def is_toplevel(self
):
452 return not isinstance(self
.parent
, Block
)
454 def link_to(self
, child
, exit
):
455 assert not self
.is_toplevel()
456 Op
.link_to(self
, child
, exit
)
458 def to_xml(self
, doc
):
459 node
= doc
.createElementNS(DOME_NS
, 'dome:block')
460 node
.setAttributeNS(None, 'foreach', str(self
.foreach
))
461 node
.setAttributeNS(None, 'enter', str(self
.enter
))
462 node
.setAttributeNS(None, 'restore', str(self
.restore
))
463 node
.setAttributeNS(None, 'comment', str(self
.comment
))
464 assert not self
.start
.fail
466 self
.start
.next
.to_xml_int(node
)
469 def toggle_restore(self
):
470 self
.restore
= not self
.restore
473 def toggle_enter(self
):
474 self
.enter
= not self
.enter
477 def toggle_foreach(self
):
478 self
.foreach
= not self
.foreach
481 def set_comment(self
, comment
):
482 self
.comment
= comment