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 def bool_attr(node
, attr
):
13 return {"0": False, "1": True,
14 "False": False, "True": True}[node
.getAttributeNS(None, attr
)]
16 # Converts a DOM <block> node to a Block object.
17 def load(node
, parent
, ns
):
18 #assert node.localName == 'block'
23 if bool_attr(node
, 'foreach'):
24 block
.toggle_foreach()
25 if bool_attr(node
, 'enter'):
27 if node
.hasAttributeNS(None, 'restore'):
28 if bool_attr(node
, 'restore'):
29 block
.toggle_restore()
30 comment
= node
.getAttributeNS(None, 'comment')
31 block
.set_comment(comment
)
35 id_hash
= {} # id from file -> Op
37 def _load(chain
, prev
, exit
):
38 for op_node
in chain
.childNodes
:
39 if str(op_node
.localName
) == 'node':
40 attr
= op_node
.getAttributeNS(None, 'action')
41 action
= eval(str(attr
))
42 if action
[0] == 'Start':
43 print "Skipping Start"
45 if action
[0] == 'chroot':
47 elif action
[0] == 'unchroot':
49 #elif action[0] == 'set_attrib':
51 # action = ('add_attrib', action[1], action[2])
53 # action = ('set_attrib', action[3])
54 elif action
[0] == 'playback':
56 elif action
[0] == 'add_attrib':
58 elif action
[0] == 'do_search' and type(action
[-1]) is dict:
59 print "Converting search namespaces..."
60 for p
, u
in action
[-1].iteritems():
63 if p
.startswith('_'): p
= None
64 p
= ns
.ensure_ns(p
, u
)
65 action
[1] = action
[1].replace(old
+ ':',
69 elif op_node
.localName
== 'block':
70 op
= load(op_node
, block
, ns
)
72 if op_node
.nodeType
== Node
.ELEMENT_NODE
and op_node
.localName
!= 'fail':
73 print "** WARNING ** Unknown op:", op_node
77 dx
= int(float(op_node
.getAttributeNS(None, 'dx')))
80 dy
= int(float(op_node
.getAttributeNS(None, 'dy')))
86 node_id
= op_node
.getAttributeNS(None, 'id')
90 if op_node
.getAttributeNS(None, 'propagate_fail') == 'True':
91 op
.propagate_fail
= True
93 prev
.link_to(op
, exit
)
97 if op_node
.localName
== 'block':
98 # Block nodes have a special failure child
99 for x
in op_node
.childNodes
:
100 if x
.localName
== 'fail':
104 # If the new node has children then they are the failure case
105 _load(op_node
, op
, 'fail')
107 link
= op_node
.getAttributeNS(None, 'target_fail')
109 to_link
.append((op
, 'fail', link
))
110 link
= op_node
.getAttributeNS(None, 'target_next')
112 to_link
.append((op
, 'next', link
))
113 _load(node
, block
.start
, 'next')
114 for (op
, exit
, child
) in to_link
:
118 print "**** Not adding link to unknown ID ****"
123 def load_dome_program(prog
, ns
):
124 "prog should be a DOM 'dome-program' node. ns will be updated"
126 assert isinstance(ns
, Namespaces
.Namespaces
)
127 #print "Loading", prog
128 if prog
.localName
!= 'dome-program':
129 raise Exception('Not a DOME program: %s!' % prog
)
131 new
= Program(str(prog
.getAttributeNS(None, 'name')))
133 #print "Loading '%s'..." % new.name
136 for node
in prog
.childNodes
:
137 if node
.localName
== 'node' and not done_update
:
138 print "*** Converting from old format ***"
139 new
.code
= load(prog
, new
, ns
)
141 if node
.localName
== 'block':
142 assert not done_update
143 new
.code
= load(node
, new
, ns
)
144 if node
.localName
== 'dome-program':
145 new
.add_sub(load_dome_program(node
, ns
))
151 "A program contains a code Block and any number of sub-programs."
152 def __init__(self
, name
):
153 assert '/' not in name
155 self
.code
= Block(self
)
158 self
.subprograms
= {}
167 path
= p
.name
+ '/' + path
171 def changed(self
, op
= None):
174 self
.parent
.changed(op
)
176 for w
in self
.watchers
:
177 w
.program_changed(op
)
179 def tree_changed(self
):
182 self
.parent
.tree_changed()
184 for w
in self
.watchers
:
185 w
.prog_tree_changed()
187 def add_sub(self
, prog
):
189 raise Exception('%s already has a parent program!' % prog
.name
)
190 if self
.subprograms
.has_key(prog
.name
):
191 raise Exception('%s already has a child called %s!' %
192 (self
.name
, prog
.name
))
194 self
.subprograms
[prog
.name
] = prog
197 def remove_sub(self
, prog
):
198 if prog
.parent
!= self
:
199 raise Exception('%s is no child of mime!' % prog
)
201 del self
.subprograms
[prog
.name
]
204 def rename(self
, name
):
207 if p
.subprograms
.has_key(name
):
208 raise Exception('%s already has a child called %s!' % (p
.name
, name
))
216 def to_xml(self
, doc
):
217 node
= doc
.createElementNS(DOME_NS
, 'dome:dome-program')
218 node
.setAttributeNS(None, 'name', self
.name
)
220 node
.appendChild(self
.code
.to_xml(doc
))
222 # Keep them in the same order to help with diffs...
223 progs
= self
.subprograms
.keys()
227 p
= self
.subprograms
[name
]
228 node
.appendChild(p
.to_xml(doc
))
233 return "Program(%s)" % self
.name
236 "Each node in a chain is an Op. There is no graphical stuff in here."
238 def __init__(self
, action
= None):
239 "Creates a new node (can be linked into another node later)"
243 action
= list(action
)
249 self
.dx
, self
.dy
= (0, 0)
250 self
.propagate_fail
= False
252 def set_parent(self
, parent
):
253 if self
.parent
== parent
:
255 if parent
and self
.parent
:
256 raise Exception('Already got a parent!')
258 self
.next
.set_parent(parent
)
260 self
.fail
.set_parent(parent
)
263 def changed(self
, op
= None):
264 if hasattr(self
, 'cached_code'):
266 print "(remove cached code)"
267 self
.parent
.changed(op
or self
)
270 assert self
.action
[0] != 'Start'
271 self
.next
, self
.fail
= (self
.fail
, self
.next
)
274 def link_to(self
, child
, exit
):
275 # Create a link from this exit to this child Op
276 # Can't link both exits to the same node (bad for tree-walking code in List)
277 assert self
.action
[0] != 'Start' or exit
== 'next'
278 assert child
.action
[0] != 'Start'
279 assert child
is not self
280 assert child
.prev
is None
282 if (exit
== 'next' and self
.fail
== child
) or \
283 (exit
== 'fail' and self
.next
== child
):
284 raise Exception("Can't link both exits (of %s) to the same node!" % self
)
286 #print "Link %s:%s -> %s" % (self, exit, child)
288 if child
.parent
and child
.parent
is not self
.parent
:
289 raise Exception('%s is from a different parent (%s vs %s)!' %
290 (child
, child
.parent
, self
.parent
))
291 # If we already have something on this exit, and the new node has a
292 # clear next exit, move the rest of the chain there.
293 child
.set_parent(self
.parent
)
294 current
= getattr(self
, exit
)
297 raise Exception('%s already has a next exit' % child
)
298 self
.unlink(exit
, may_delete
= 0)
299 child
.link_to(current
, 'next')
301 setattr(self
, exit
, child
)
304 def unlink(self
, exit
, may_delete
= 1):
305 "Remove link from us to child"
306 assert exit
in ['next', 'fail']
307 self
._unlink
(exit
, may_delete
)
310 def _unlink(self
, exit
, may_delete
= 1):
311 child
= getattr(self
, exit
)
313 raise Exception('%s has no child on exit %s' % (self
, exit
))
314 if self
is not child
.prev
:
315 raise Exception('Internal error: %s not my child!' % child
)
318 setattr(self
, exit
, None)
321 # There is no way to reach this child now, so unlink its children.
324 child
._unlink
('next')
326 child
._unlink
('fail')
329 """Remove this node. It there is exactly one out-going arc and one incoming one,
330 join them together. Error if:"
331 - There are multiple out-going arcs
332 - There is a single out-going arc but multiple parents"""
333 if self
.next
and self
.fail
:
334 raise Exception("Can't delete a node with both fail and next exits in use.")
336 raise Exception("Can't delete a Start node!")
340 # Find the chain to preserve (can't have both set here)
350 preserve
= getattr(self
, exit
)
351 self
.unlink(exit
, may_delete
= 0)
352 if prev
.next
== self
:
357 # Remove all links to us
358 if self
.prev
.next
== self
:
359 self
.prev
.unlink('next')
361 self
.prev
.unlink('fail')
363 # Exit is now our parent's exit that leads to us...
365 # Relink following nodes to our (single) parent
366 prev
.link_to(preserve
, exit
)
379 from Ft
.Xml
.cDomlette
import implementation
380 doc
= implementation
.createDocument(DOME_NS
, 'dome:dome-program', None)
381 self
.to_xml_int(doc
.documentElement
)
384 def to_xml(self
, doc
):
385 node
= doc
.createElementNS(DOME_NS
, 'dome:node')
386 node
.setAttributeNS(None, 'action', `self
.action`
)
387 node
.setAttributeNS(None, 'dx', str(self
.dx
))
388 node
.setAttributeNS(None, 'dy', str(self
.dy
))
389 if self
.propagate_fail
:
390 node
.setAttributeNS(None, 'propagate_fail', 'True')
393 def to_xml_int(self
, parent
):
394 """Adds a chain of <Node> elements to 'parent'. Links only followed when node is
396 node
= self
.to_xml(parent
.ownerDocument
)
397 parent
.appendChild(node
)
399 def add_link(op
, parent
):
400 node
= parent
.ownerDocument
.createElementNS(DOME_NS
, 'dome:link')
401 parent
.appendChild(node
)
402 node
.setAttributeNS(None, 'target', str(id(op
)))
405 if isinstance(self
, Block
):
406 fail
= parent
.ownerDocument
.createElementNS(DOME_NS
, 'dome:fail')
407 self
.fail
.to_xml_int(fail
)
408 node
.appendChild(fail
)
410 self
.fail
.to_xml_int(node
)
412 self
.next
.to_xml_int(parent
)
415 return "{" + `self
.action`
+ "}"
418 return "{" + `self
.action`
+ "}"
420 def get_program(self
):
422 while p
and not isinstance(p
, Program
):
426 def set_propagate_fail(self
, propagate_fail
):
427 assert propagate_fail
in (True, False)
428 if self
.propagate_fail
!= propagate_fail
:
429 self
.propagate_fail
= propagate_fail
434 """A Block is an Op which contains a group of Ops."""
436 def __init__(self
, parent
):
437 Op
.__init
__(self
, action
= ['Block'])
440 self
.start
.parent
= self
446 def set_start(self
, start
):
447 assert not start
.prev
449 start
.set_parent(self
)
453 def is_toplevel(self
):
454 return not isinstance(self
.parent
, Block
)
456 def link_to(self
, child
, exit
):
457 assert not self
.is_toplevel()
458 Op
.link_to(self
, child
, exit
)
460 def to_xml(self
, doc
):
461 node
= doc
.createElementNS(DOME_NS
, 'dome:block')
462 node
.setAttributeNS(None, 'foreach', str(self
.foreach
))
463 node
.setAttributeNS(None, 'enter', str(self
.enter
))
464 node
.setAttributeNS(None, 'restore', str(self
.restore
))
465 node
.setAttributeNS(None, 'comment', str(self
.comment
))
466 assert not self
.start
.fail
468 self
.start
.next
.to_xml_int(node
)
471 def toggle_restore(self
):
472 self
.restore
= not self
.restore
475 def toggle_enter(self
):
476 self
.enter
= not self
.enter
479 def toggle_foreach(self
):
480 self
.foreach
= not self
.foreach
483 def set_comment(self
, comment
):
484 self
.comment
= comment