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 prev
.link_to(op
, exit
)
94 if op_node
.localName
== 'block':
95 # Block nodes have a special failure child
96 for x
in op_node
.childNodes
:
97 if x
.localName
== 'fail':
101 # If the new node has children then they are the failure case
102 _load(op_node
, op
, 'fail')
104 link
= op_node
.getAttributeNS(None, 'target_fail')
106 to_link
.append((op
, 'fail', link
))
107 link
= op_node
.getAttributeNS(None, 'target_next')
109 to_link
.append((op
, 'next', link
))
110 _load(node
, block
.start
, 'next')
111 for (op
, exit
, child
) in to_link
:
115 print "**** Not adding link to unknown ID ****"
120 def load_dome_program(prog
, ns
):
121 "prog should be a DOM 'dome-program' node. ns will be updated"
123 assert isinstance(ns
, Namespaces
.Namespaces
)
124 #print "Loading", prog
125 if prog
.localName
!= 'dome-program':
126 raise Exception('Not a DOME program: %s!' % prog
)
128 new
= Program(str(prog
.getAttributeNS(None, 'name')))
130 #print "Loading '%s'..." % new.name
133 for node
in prog
.childNodes
:
134 if node
.localName
== 'node' and not done_update
:
135 print "*** Converting from old format ***"
136 new
.code
= load(prog
, new
, ns
)
138 if node
.localName
== 'block':
139 assert not done_update
140 new
.code
= load(node
, new
, ns
)
141 if node
.localName
== 'dome-program':
142 new
.add_sub(load_dome_program(node
, ns
))
148 "A program contains a code Block and any number of sub-programs."
149 def __init__(self
, name
):
150 assert '/' not in name
152 self
.code
= Block(self
)
155 self
.subprograms
= {}
164 path
= p
.name
+ '/' + path
168 def changed(self
, op
= None):
171 self
.parent
.changed(op
)
173 for w
in self
.watchers
:
174 w
.program_changed(op
)
176 def tree_changed(self
):
179 self
.parent
.tree_changed()
181 for w
in self
.watchers
:
182 w
.prog_tree_changed()
184 def add_sub(self
, prog
):
186 raise Exception('%s already has a parent program!' % prog
.name
)
187 if self
.subprograms
.has_key(prog
.name
):
188 raise Exception('%s already has a child called %s!' %
189 (self
.name
, prog
.name
))
191 self
.subprograms
[prog
.name
] = prog
194 def remove_sub(self
, prog
):
195 if prog
.parent
!= self
:
196 raise Exception('%s is no child of mime!' % prog
)
198 del self
.subprograms
[prog
.name
]
201 def rename(self
, name
):
204 if p
.subprograms
.has_key(name
):
205 raise Exception('%s already has a child called %s!' % (p
.name
, name
))
213 def to_xml(self
, doc
):
214 node
= doc
.createElementNS(DOME_NS
, 'dome:dome-program')
215 node
.setAttributeNS(None, 'name', self
.name
)
217 node
.appendChild(self
.code
.to_xml(doc
))
219 # Keep them in the same order to help with diffs...
220 progs
= self
.subprograms
.keys()
224 p
= self
.subprograms
[name
]
225 node
.appendChild(p
.to_xml(doc
))
230 return "Program(%s)" % self
.name
233 "Each node in a chain is an Op. There is no graphical stuff in here."
235 def __init__(self
, action
= None):
236 "Creates a new node (can be linked into another node later)"
240 action
= list(action
)
246 self
.dx
, self
.dy
= (0, 0)
248 def set_parent(self
, parent
):
249 if self
.parent
== parent
:
251 if parent
and self
.parent
:
252 raise Exception('Already got a parent!')
254 self
.next
.set_parent(parent
)
256 self
.fail
.set_parent(parent
)
259 def changed(self
, op
= None):
260 if hasattr(self
, 'cached_code'):
262 print "(remove cached code)"
263 self
.parent
.changed(op
or self
)
266 assert self
.action
[0] != 'Start'
267 self
.next
, self
.fail
= (self
.fail
, self
.next
)
270 def link_to(self
, child
, exit
):
271 # Create a link from this exit to this child Op
272 # Can't link both exits to the same node (bad for tree-walking code in List)
273 assert self
.action
[0] != 'Start' or exit
== 'next'
274 assert child
.action
[0] != 'Start'
275 assert child
is not self
276 assert child
.prev
is None
278 if (exit
== 'next' and self
.fail
== child
) or \
279 (exit
== 'fail' and self
.next
== child
):
280 raise Exception("Can't link both exits (of %s) to the same node!" % self
)
282 #print "Link %s:%s -> %s" % (self, exit, child)
284 if child
.parent
and child
.parent
is not self
.parent
:
285 raise Exception('%s is from a different parent (%s vs %s)!' %
286 (child
, child
.parent
, self
.parent
))
287 # If we already have something on this exit, and the new node has a
288 # clear next exit, move the rest of the chain there.
289 child
.set_parent(self
.parent
)
290 current
= getattr(self
, exit
)
293 raise Exception('%s already has a next exit' % child
)
294 self
.unlink(exit
, may_delete
= 0)
295 child
.link_to(current
, 'next')
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
is not child
.prev
:
311 raise Exception('Internal error: %s not my child!' % child
)
314 setattr(self
, exit
, None)
317 # There is no way to reach this child now, so unlink its children.
320 child
._unlink
('next')
322 child
._unlink
('fail')
325 """Remove this node. It there is exactly one out-going arc and one incoming one,
326 join them together. Error if:"
327 - There are multiple out-going arcs
328 - There is a single out-going arc but multiple parents"""
329 if self
.next
and self
.fail
:
330 raise Exception("Can't delete a node with both fail and next exits in use.")
332 raise Exception("Can't delete a Start node!")
336 # Find the chain to preserve (can't have both set here)
346 preserve
= getattr(self
, exit
)
347 self
.unlink(exit
, may_delete
= 0)
348 if prev
.next
== self
:
353 # Remove all links to us
354 if self
.prev
.next
== self
:
355 self
.prev
.unlink('next')
357 self
.prev
.unlink('fail')
359 # Exit is now our parent's exit that leads to us...
361 # Relink following nodes to our (single) parent
362 prev
.link_to(preserve
, exit
)
375 from Ft
.Xml
.cDomlette
import implementation
376 doc
= implementation
.createDocument(DOME_NS
, 'dome:dome-program', None)
377 self
.to_xml_int(doc
.documentElement
)
380 def to_xml(self
, doc
):
381 node
= doc
.createElementNS(DOME_NS
, 'dome:node')
382 node
.setAttributeNS(None, 'action', `self
.action`
)
383 node
.setAttributeNS(None, 'dx', str(self
.dx
))
384 node
.setAttributeNS(None, 'dy', str(self
.dy
))
387 def to_xml_int(self
, parent
):
388 """Adds a chain of <Node> elements to 'parent'. Links only followed when node is
390 node
= self
.to_xml(parent
.ownerDocument
)
391 parent
.appendChild(node
)
393 def add_link(op
, parent
):
394 node
= parent
.ownerDocument
.createElementNS(DOME_NS
, 'dome:link')
395 parent
.appendChild(node
)
396 node
.setAttributeNS(None, 'target', str(id(op
)))
399 if isinstance(self
, Block
):
400 fail
= parent
.ownerDocument
.createElementNS(DOME_NS
, 'dome:fail')
401 self
.fail
.to_xml_int(fail
)
402 node
.appendChild(fail
)
404 self
.fail
.to_xml_int(node
)
406 self
.next
.to_xml_int(parent
)
409 return "{" + `self
.action`
+ "}"
412 return "{" + `self
.action`
+ "}"
414 def get_program(self
):
416 while p
and not isinstance(p
, Program
):
421 """A Block is an Op which contains a group of Ops."""
423 def __init__(self
, parent
):
424 Op
.__init
__(self
, action
= ['Block'])
427 self
.start
.parent
= self
433 def set_start(self
, start
):
434 assert not start
.prev
436 start
.set_parent(self
)
440 def is_toplevel(self
):
441 return not isinstance(self
.parent
, Block
)
443 def link_to(self
, child
, exit
):
444 assert not self
.is_toplevel()
445 Op
.link_to(self
, child
, exit
)
447 def to_xml(self
, doc
):
448 node
= doc
.createElementNS(DOME_NS
, 'dome:block')
449 node
.setAttributeNS(None, 'foreach', str(self
.foreach
))
450 node
.setAttributeNS(None, 'enter', str(self
.enter
))
451 node
.setAttributeNS(None, 'restore', str(self
.restore
))
452 node
.setAttributeNS(None, 'comment', str(self
.comment
))
453 assert not self
.start
.fail
455 self
.start
.next
.to_xml_int(node
)
458 def toggle_restore(self
):
459 self
.restore
= not self
.restore
462 def toggle_enter(self
):
463 self
.enter
= not self
.enter
466 def toggle_foreach(self
):
467 self
.foreach
= not self
.foreach
470 def set_comment(self
, comment
):
471 self
.comment
= comment