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 type(action
[-1]) is dict:
55 print "Converting search namespaces..."
56 for p
, u
in action
[-1].iteritems():
59 if p
.startswith('_'): p
= None
60 p
= ns
.ensure_ns(p
, u
)
61 action
[1] = action
[1].replace(old
+ ':',
65 elif op_node
.localName
== 'block':
66 op
= load(op_node
, block
, ns
)
68 if op_node
.nodeType
== Node
.ELEMENT_NODE
and op_node
.localName
!= 'fail':
69 print "** WARNING ** Unknown op:", op_node
73 dx
= int(float(op_node
.getAttributeNS(None, 'dx')))
76 dy
= int(float(op_node
.getAttributeNS(None, 'dy')))
82 node_id
= op_node
.getAttributeNS(None, 'id')
86 prev
.link_to(op
, exit
)
90 if op_node
.localName
== 'block':
91 # Block nodes have a special failure child
92 for x
in op_node
.childNodes
:
93 if x
.localName
== 'fail':
97 # If the new node has children then they are the failure case
98 _load(op_node
, op
, 'fail')
100 link
= op_node
.getAttributeNS(None, 'target_fail')
102 to_link
.append((op
, 'fail', link
))
103 link
= op_node
.getAttributeNS(None, 'target_next')
105 to_link
.append((op
, 'next', link
))
106 _load(node
, block
.start
, 'next')
107 for (op
, exit
, child
) in to_link
:
111 print "**** Not adding link to unknown ID ****"
116 def load_dome_program(prog
, ns
):
117 "prog should be a DOM 'dome-program' node. ns will be updated"
119 assert isinstance(ns
, Namespaces
.Namespaces
)
120 #print "Loading", prog
121 if prog
.localName
!= 'dome-program':
122 raise Exception('Not a DOME program: %s!' % prog
)
124 new
= Program(str(prog
.getAttributeNS(None, 'name')))
126 #print "Loading '%s'..." % new.name
129 for node
in prog
.childNodes
:
130 if node
.localName
== 'node' and not done_update
:
131 print "*** Converting from old format ***"
132 new
.code
= load(prog
, new
, ns
)
134 if node
.localName
== 'block':
135 assert not done_update
136 new
.code
= load(node
, new
, ns
)
137 if node
.localName
== 'dome-program':
138 new
.add_sub(load_dome_program(node
, ns
))
144 "A program contains a code Block and any number of sub-programs."
145 def __init__(self
, name
):
146 assert '/' not in name
148 self
.code
= Block(self
)
151 self
.subprograms
= {}
160 path
= p
.name
+ '/' + path
164 def changed(self
, op
= None):
167 self
.parent
.changed(op
)
169 for w
in self
.watchers
:
170 w
.program_changed(op
)
172 def tree_changed(self
):
175 self
.parent
.tree_changed()
177 for w
in self
.watchers
:
178 w
.prog_tree_changed()
180 def add_sub(self
, prog
):
182 raise Exception('%s already has a parent program!' % prog
.name
)
183 if self
.subprograms
.has_key(prog
.name
):
184 raise Exception('%s already has a child called %s!' %
185 (self
.name
, prog
.name
))
187 self
.subprograms
[prog
.name
] = prog
190 def remove_sub(self
, prog
):
191 if prog
.parent
!= self
:
192 raise Exception('%s is no child of mime!' % prog
)
194 del self
.subprograms
[prog
.name
]
197 def rename(self
, name
):
200 if p
.subprograms
.has_key(name
):
201 raise Exception('%s already has a child called %s!' % (p
.name
, name
))
209 def to_xml(self
, doc
):
210 node
= doc
.createElementNS(DOME_NS
, 'dome:dome-program')
211 node
.setAttributeNS(None, 'name', self
.name
)
213 node
.appendChild(self
.code
.to_xml(doc
))
215 # Keep them in the same order to help with diffs...
216 progs
= self
.subprograms
.keys()
220 p
= self
.subprograms
[name
]
221 node
.appendChild(p
.to_xml(doc
))
226 return "Program(%s)" % self
.name
229 "Each node in a chain is an Op. There is no graphical stuff in here."
231 def __init__(self
, action
= None):
232 "Creates a new node (can be linked into another node later)"
236 action
= list(action
)
241 self
.prev
= [] # First parent is used for rendering as a tree
242 self
.dx
, self
.dy
= (0, 0)
244 def set_parent(self
, parent
):
245 if self
.parent
== parent
:
247 if parent
and self
.parent
:
248 raise Exception('Already got a parent!')
249 nearby
= self
.prev
[:]
251 nearby
.append(self
.next
)
253 nearby
.append(self
.fail
)
255 [x
.set_parent(parent
) for x
in nearby
if x
.parent
is not parent
]
257 def changed(self
, op
= None):
258 if hasattr(self
, 'cached_code'):
260 print "(remove cached code)"
261 self
.parent
.changed(op
or self
)
264 assert self
.action
[0] != 'Start'
265 self
.next
, self
.fail
= (self
.fail
, self
.next
)
268 def link_to(self
, child
, exit
):
269 # Create a link from this exit to this child Op
270 # Can't link both exits to the same node (bad for tree-walking code in List)
271 assert self
.action
[0] != 'Start' or exit
== 'next'
272 assert child
.action
[0] != 'Start'
273 assert child
is not self
275 if (exit
== 'next' and self
.fail
== child
) or \
276 (exit
== 'fail' and self
.next
== child
):
277 raise Exception("Can't link both exits (of %s) to the same node!" % self
)
279 #print "Link %s:%s -> %s" % (self, exit, child)
281 if child
.parent
and child
.parent
is not self
.parent
:
282 raise Exception('%s is from a different parent (%s vs %s)!' %
283 (child
, child
.parent
, self
.parent
))
284 # If we already have something on this exit, and the new node has a
285 # clear next exit, move the rest of the chain there.
286 child
.set_parent(self
.parent
)
287 current
= getattr(self
, exit
)
290 raise Exception('%s already has a next exit' % child
)
291 primary
= current
.prev
[0] == self
292 self
.unlink(exit
, may_delete
= 0)
293 child
.link_to(current
, 'next')
295 # We were the primary parent, so we must be again...
296 current
.prev
.remove(child
)
297 current
.prev
.insert(0, child
)
298 child
.prev
.append(self
)
299 setattr(self
, exit
, child
)
302 def unlink(self
, exit
, may_delete
= 1):
303 "Remove link from us to child"
304 assert exit
in ['next', 'fail']
305 self
._unlink
(exit
, may_delete
)
308 def _unlink(self
, exit
, may_delete
= 1):
309 child
= getattr(self
, exit
)
311 raise Exception('%s has no child on exit %s' % (self
, exit
))
312 if self
not in child
.prev
:
313 raise Exception('Internal error: %s not my child!' % child
)
315 child
.prev
.remove(self
) # Only remove one copy
316 setattr(self
, exit
, None)
318 for x
in child
.prev
: print x
320 if may_delete
and not child
.prev
:
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)
349 if len(self
.prev
) != 1:
350 raise Exception("Deleted node-chain must have a single link in")
352 preserve
= getattr(self
, exit
)
353 self
.unlink(exit
, may_delete
= 0)
354 if prev
.next
== self
:
359 # Remove all links to us
360 for p
in self
.prev
[:]:
366 # Exit is now our parent's exit that leads to us...
368 # Relink following nodes to our (single) parent
369 prev
.link_to(preserve
, exit
)
382 from Ft
.Xml
.cDomlette
import implementation
383 doc
= implementation
.createDocument(DOME_NS
, 'dome:dome-program', None)
384 self
.to_xml_int(doc
.documentElement
)
387 def to_xml(self
, doc
):
388 node
= doc
.createElementNS(DOME_NS
, 'dome:node')
389 node
.setAttributeNS(None, 'action', `self
.action`
)
390 node
.setAttributeNS(None, 'dx', str(self
.dx
))
391 node
.setAttributeNS(None, 'dy', str(self
.dy
))
394 def to_xml_int(self
, parent
):
395 """Adds a chain of <Node> elements to 'parent'. Links only followed when node is
397 node
= self
.to_xml(parent
.ownerDocument
)
398 parent
.appendChild(node
)
400 if len(self
.prev
) > 1:
401 node
.setAttributeNS(None, 'id', str(id(self
)))
403 def add_link(op
, parent
):
404 node
= parent
.ownerDocument
.createElementNS(DOME_NS
, 'dome:link')
405 parent
.appendChild(node
)
406 node
.setAttributeNS(None, 'target', str(id(op
)))
409 if self
.fail
.prev
[0] == self
:
410 if isinstance(self
, Block
):
411 fail
= parent
.ownerDocument
.createElementNS(DOME_NS
, 'dome:fail')
412 self
.fail
.to_xml_int(fail
)
413 node
.appendChild(fail
)
415 self
.fail
.to_xml_int(node
)
417 node
.setAttributeNS(None, 'target_fail', str(id(self
.fail
)))
419 if self
.next
.prev
[0] == self
:
420 self
.next
.to_xml_int(parent
)
422 node
.setAttributeNS(None, 'target_next', str(id(self
.next
)))
425 return "{" + `self
.action`
+ "}"
427 def get_program(self
):
429 while p
and not isinstance(p
, Program
):
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