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'
18 if int(node
.getAttributeNS(None, 'foreach')):
19 block
.toggle_foreach()
20 if int(node
.getAttributeNS(None, 'enter')):
26 id_hash
= {} # id from file -> Op
28 def _load(chain
, prev
, exit
):
29 for op_node
in chain
.childNodes
:
30 if str(op_node
.localName
) == 'node':
31 attr
= op_node
.getAttributeNS(None, 'action')
32 action
= eval(str(attr
))
33 if action
[0] == 'Start':
34 print "Skipping Start"
36 if action
[0] == 'chroot':
38 elif action
[0] == 'unchroot':
40 #elif action[0] == 'set_attrib':
42 # action = ('add_attrib', action[1], action[2])
44 # action = ('set_attrib', action[3])
45 elif action
[0] == 'playback':
47 elif action
[0] == 'add_attrib':
50 elif op_node
.localName
== 'block':
51 op
= load(op_node
, block
)
53 if op_node
.nodeType
== Node
.ELEMENT_NODE
:
54 print "** WARNING ** Unknown op:", op_node
58 dx
= int(float(op_node
.getAttributeNS(None, 'dx')))
61 dy
= int(float(op_node
.getAttributeNS(None, 'dy')))
67 node_id
= op_node
.getAttributeNS(None, 'id')
71 prev
.link_to(op
, exit
)
75 if op_node
.nodeName
== 'block':
76 # Block nodes have a special failure child
77 for x
in op_node
.childNodes
:
78 if x
.localName
== 'fail':
82 # If the new node has children then they are the failure case
83 _load(op_node
, op
, 'fail')
85 link
= op_node
.getAttributeNS(None, 'target_fail')
87 to_link
.append((op
, 'fail', link
))
88 link
= op_node
.getAttributeNS(None, 'target_next')
90 to_link
.append((op
, 'next', link
))
91 _load(node
, block
.start
, 'next')
92 for (op
, exit
, child
) in to_link
:
96 print "**** Not adding link to unknown ID ****"
101 def load_dome_program(prog
):
102 "prog should be a DOM 'dome-program' node."
103 #print "Loading", prog
104 if prog
.localName
!= 'dome-program':
105 raise Exception('Not a DOME program: %s!' % prog
)
107 new
= Program(str(prog
.getAttributeNS(None, 'name')))
109 #print "Loading '%s'..." % new.name
112 for node
in prog
.childNodes
:
113 if node
.localName
== 'node' and not done_update
:
114 print "*** Converting from old format ***"
115 new
.code
= load(prog
, new
)
117 if node
.localName
== 'block':
118 assert not done_update
119 new
.code
= load(node
, new
)
120 if node
.localName
== 'dome-program':
121 new
.add_sub(load_dome_program(node
))
126 "A program contains a code Block and any number of sub-programs."
127 def __init__(self
, name
):
128 assert '/' not in name
130 self
.code
= Block(self
)
133 self
.subprograms
= {}
141 path
= p
.name
+ '/' + path
145 def changed(self
, op
= None):
147 self
.parent
.changed(op
)
149 for w
in self
.watchers
:
150 w
.program_changed(op
)
152 def tree_changed(self
):
154 self
.parent
.tree_changed()
156 for w
in self
.watchers
:
157 w
.prog_tree_changed()
159 def add_sub(self
, prog
):
161 raise Exception('%s already has a parent program!' % prog
.name
)
162 if self
.subprograms
.has_key(prog
.name
):
163 raise Exception('%s already has a child called %s!' %
164 (self
.name
, prog
.name
))
166 self
.subprograms
[prog
.name
] = prog
169 def remove_sub(self
, prog
):
170 if prog
.parent
!= self
:
171 raise Exception('%s is no child of mime!' % prog
)
173 del self
.subprograms
[prog
.name
]
176 def rename(self
, name
):
179 if p
.subprograms
.has_key(name
):
180 raise Exception('%s already has a child called %s!' % (p
.name
, name
))
188 def to_xml(self
, doc
):
189 node
= doc
.createElementNS(DOME_NS
, 'dome-program')
190 node
.setAttributeNS(None, 'name', self
.name
)
192 node
.appendChild(self
.code
.to_xml(doc
))
194 # Keep them in the same order to help with diffs...
195 progs
= self
.subprograms
.keys()
199 p
= self
.subprograms
[name
]
200 node
.appendChild(p
.to_xml(doc
))
205 return "Program(%s)" % self
.name
208 "Each node in a chain is an Op. There is no graphical stuff in here."
210 def __init__(self
, action
= None):
211 "Creates a new node (can be linked into another node later)"
218 self
.prev
= [] # First parent is used for rendering as a tree
219 self
.dx
, self
.dy
= (0, 0)
221 def set_parent(self
, parent
):
222 if self
.parent
== parent
:
224 if parent
and self
.parent
:
225 raise Exception('Already got a parent!')
226 nearby
= self
.prev
[:]
228 nearby
.append(self
.next
)
230 nearby
.append(self
.fail
)
232 [x
.set_parent(parent
) for x
in nearby
if x
.parent
is not parent
]
234 def changed(self
, op
= None):
235 self
.parent
.changed(op
or self
)
238 assert self
.action
[0] != 'Start'
239 self
.next
, self
.fail
= (self
.fail
, self
.next
)
242 def link_to(self
, child
, exit
):
243 # Create a link from this exit to this child Op
244 assert self
.action
[0] != 'Start' or exit
== 'next'
245 assert child
.action
[0] != 'Start'
247 print "Link %s:%s -> %s" % (self
, exit
, child
)
249 if child
.parent
and child
.parent
is not self
.parent
:
250 raise Exception('%s is from a different parent (%s vs %s)!' %
251 (child
, child
.parent
, self
.parent
))
252 # If we already have something on this exit, and the new node has a
253 # clear next exit, move the rest of the chain there.
254 child
.set_parent(self
.parent
)
255 current
= getattr(self
, exit
)
258 raise Exception('%s already has a next exit' % child
)
259 self
.unlink(exit
, may_delete
= 0)
260 child
.link_to(current
, 'next')
261 child
.prev
.append(self
)
262 setattr(self
, exit
, child
)
265 def unlink(self
, exit
, may_delete
= 1):
266 "Remove link from us to child"
267 assert exit
in ['next', 'fail']
268 self
._unlink
(exit
, may_delete
)
271 def _unlink(self
, exit
, may_delete
= 1):
272 child
= getattr(self
, exit
)
274 raise Exception('%s has no child on exit %s' % (self
, exit
))
275 if self
not in child
.prev
:
276 raise Exception('Internal error: %s not my child!' % child
)
278 child
.prev
.remove(self
) # Only remove one copy
279 setattr(self
, exit
, None)
281 for x
in child
.prev
: print x
283 if may_delete
and not child
.prev
:
284 # There is no way to reach this child now, so unlink its children.
287 child
._unlink
('next')
289 child
._unlink
('fail')
292 """Remove this node. It there is exactly one out-going arc and one incoming one,
293 join them together. Error if:"
294 - There are multiple out-going arcs
295 - There is a single out-going arc but multiple parents"""
296 if self
.next
and self
.fail
:
297 raise Exception("Can't delete a node with both fail and next exits in use.")
299 raise Exception("Can't delete a Start node!")
303 # Find the chain to preserve (can't have both set here)
312 if len(self
.prev
) != 1:
313 raise Exception("Deleted node-chain must have a single link in")
315 preserve
= getattr(self
, exit
)
316 self
.unlink(exit
, may_delete
= 0)
318 # Remove all links to us
326 # Relink following nodes to our (single) parent
327 prev
.link_to(preserve
, exit
)
340 from xml
.dom
import implementation
341 doc
= implementation
.createDocument(DOME_NS
, 'dome-program', None)
342 self
.to_xml_int(doc
.documentElement
)
345 def to_xml(self
, doc
):
346 node
= doc
.createElementNS(DOME_NS
, 'node')
347 node
.setAttributeNS(None, 'action', `self
.action`
)
348 node
.setAttributeNS(None, 'dx', str(self
.dx
))
349 node
.setAttributeNS(None, 'dy', str(self
.dy
))
352 def to_xml_int(self
, parent
):
353 """Adds a chain of <Node> elements to 'parent'. Links only followed when node is
355 node
= self
.to_xml(parent
.ownerDocument
)
356 parent
.appendChild(node
)
358 if len(self
.prev
) > 1:
359 node
.setAttributeNS(None, 'id', str(id(self
)))
361 def add_link(op
, parent
):
362 node
= parent
.ownerDocument
.createElementNS(DOME_NS
, 'link')
363 parent
.appendChild(node
)
364 node
.setAttributeNS(None, 'target', str(id(op
)))
367 if self
.fail
.prev
[0] == self
:
368 if isinstance(self
, Block
):
369 fail
= parent
.ownerDocument
.createElementNS(DOME_NS
, 'fail')
370 self
.fail
.to_xml_int(fail
)
371 node
.appendChild(fail
)
373 self
.fail
.to_xml_int(node
)
375 node
.setAttributeNS(None, 'target_fail', str(id(self
.fail
)))
377 if self
.next
.prev
[0] == self
:
378 self
.next
.to_xml_int(parent
)
380 node
.setAttributeNS(None, 'target_next', str(id(self
.next
)))
383 return "{" + `self
.action`
+ "}"
385 def get_program(self
):
387 while not isinstance(p
, Program
):
392 """A Block is an Op which contains a group of Ops."""
394 def __init__(self
, parent
):
395 Op
.__init
__(self
, action
= ['Block'])
398 self
.start
.parent
= self
402 def set_start(self
, start
):
403 assert not start
.prev
405 start
.set_parent(self
)
409 def is_toplevel(self
):
410 return not isinstance(self
.parent
, Block
)
412 def link_to(self
, child
, exit
):
413 assert not self
.is_toplevel()
414 Op
.link_to(self
, child
, exit
)
416 def to_xml(self
, doc
):
417 node
= doc
.createElementNS(DOME_NS
, 'block')
418 node
.setAttributeNS(None, 'foreach', str(self
.foreach
))
419 node
.setAttributeNS(None, 'enter', str(self
.enter
))
420 assert not self
.start
.fail
422 self
.start
.next
.to_xml_int(node
)
425 def toggle_enter(self
):
426 self
.enter
= not self
.enter
429 def toggle_foreach(self
):
430 self
.foreach
= not self
.foreach