Removed all the MD5 suck checking stuff.
[dom-editor.git] / Dome / View.py
blob684c438889e9b447648461fbf28107fe4b5f53aa
1 from __future__ import nested_scopes
3 try:
4 from rox import alert
5 except:
6 def alert(message):
7 print "***", message
9 import support
10 from support import *
11 from xml.dom import Node, XMLNS_NAMESPACE
12 from Ft.Xml import XPath
13 from Ft.Xml.XPath import FT_EXT_NAMESPACE, Context
14 from Ft.Xml.cDomlette import implementation
15 from Ft.Xml.Domlette import PrettyPrint
17 import re, string, sys
18 import urlparse
19 from StringIO import StringIO
21 from Program import Op, Block
22 from Beep import Beep
24 import time
25 import urllib, urllib2
26 import traceback
28 from constants import *
30 import re
32 #http://www.w3.org/2001/12/soap-envelope'
33 SOAPENV_NS = 'http://schemas.xmlsoap.org/soap/envelope/'
35 def elements(node):
36 out = []
37 for x in node.childNodes:
38 if x.nodeType == Node.ELEMENT_NODE:
39 out.append(x)
40 return out
42 normal_chars = string.letters + string.digits + "-"
44 fast_global = re.compile('//([-A-Za-z][-A-Za-z0-9]*:)?[-A-Za-z][-A-Za-z0-9]*$')
46 # An view contains:
47 # - A ref to a DOM document
48 # - A set of current nodes
49 # - A root node
50 # - A chroot stack
51 # It does not have any display code. It does contain code to perform actions
52 # (actions affect the document AND the view state).
54 # These actions can be repeated using '.'
55 record_again = [
56 "do_global",
57 "select_children",
58 "subst",
59 "python",
60 "xpath",
61 "ask",
62 "yank",
63 "shallow_yank",
64 "delete_node",
65 "delete_node_no_clipboard",
66 "delete_shallow",
67 "play",
68 "map",
69 "change_node",
70 "add_node",
71 "suck",
72 "http_post",
73 "put_before",
74 "put_after",
75 "put_replace",
76 "put_as_child",
77 "put_as_child_end",
78 "yank_value",
79 "yank_attribs",
80 "paste_attribs",
81 "compare",
82 "fail",
83 "do_pass",
84 "attribute",
85 "set_attrib",
86 "rename_attrib",
87 "add_attrib",
88 "soap_send",
89 "show_canvas",
90 "show_html",
91 "select_dups",
92 "select_region",
95 def same(a, b):
96 "Recursivly compare two nodes."
97 if a.nodeType != b.nodeType or a.nodeName != b.nodeName:
98 return FALSE
99 if a.nodeValue != b.nodeValue:
100 return FALSE
101 aks = a.childNodes
102 bks = b.childNodes
103 if len(aks) != len(bks):
104 return FALSE
105 for (ak, bk) in map(None, aks, bks):
106 if not same(ak, bk):
107 return FALSE
108 return TRUE
110 class InProgress(Exception):
111 "Throw this if the operation will complete later..."
112 class Done(Exception):
113 "Thrown when the chain is completed successfully"
115 class View:
116 def __init__(self, model, callback_handlers = None):
117 """callback_handlers is an (idle_add, idle_remove) tuple"""
118 self.root = None
119 self.displays = []
120 self.lists = []
121 self.single_step = 1 # 0 = Play 1 = Step-into 2 = Step-over
122 self.model = None
123 self.chroots = [] # (model, node, marked)
124 self.foreach_stack = [] # (block, [nodes], restore-nodes, restore-marks)
125 self.current_nodes = []
126 self.clipboard = None
127 self.current_attrib = None
128 self.marked = {}
130 if not callback_handlers:
131 from rox import g
132 self.idle_add, self.idle_remove = g.idle_add, g.idle_remove
133 else:
134 self.idle_add, self.idle_remove = callback_handlers
136 self.exec_point = None # None, or (Op, Exit)
137 self.rec_point = None # None, or (Op, Exit)
138 self.op_in_progress = None
139 self.idle_cb = 0
140 self.callback_on_return = None # Called when there are no more Ops...
141 self.in_callback = 0 # (not the above callback - this is the playback one)
142 self.innermost_failure = None
143 self.call_on_done = None # Called when there is nowhere to return to
144 self.exec_stack = [] # Ops we are inside (display use only)
146 self.breakpoints = {} # (op, exit) keys, values don't matter
147 self.current_nodes = []
148 self.set_model(model)
150 def get_current(self):
151 if len(self.current_nodes) == 1:
152 return self.current_nodes[0]
153 raise Exception('This operation required exactly one selected node!')
155 def set_model(self, model):
156 assert not self.marked
157 if self.root:
158 self.move_to([])
159 self.model.unlock(self.root)
160 self.root = None
161 if self.model:
162 self.model.remove_view(self)
163 self.model.root_program.watchers.remove(self)
164 self.model = model
165 self.model.root_program.watchers.append(self)
166 model.add_view(self)
167 self.set_display_root(self.model.get_root())
168 self.move_to(self.root)
170 def running(self):
171 return self.idle_cb != 0 or self.in_callback
173 def run_new(self, callback = None):
174 "Reset the playback system (stack, step-mode and point)."
175 "Call callback(exit) when execution finishes."
176 if self.idle_cb:
177 self.idle_remove(self.idle_cb)
178 self.idle_cb = 0
179 self.single_step = 0
180 self.innermost_failure = None
181 self.call_on_done = callback
182 self.callback_on_return = None
183 while self.exec_stack:
184 self.pop_stack()
185 self.reset_foreach_stack()
186 self.status_changed()
187 self.update_stack()
188 self.set_status(None)
190 def reset_foreach_stack(self):
191 for block, nodes, restore, mark in self.foreach_stack:
192 if mark:
193 print "reset_foreach_stack: unlocking %d nodes" % len(mark)
194 [self.model.unlock(x) for x in mark]
195 self.foreach_stack = []
197 def push_stack(self, op):
198 if not isinstance(op, Op):
199 raise Exception('push_stack: not an Op', op)
200 self.exec_stack.append(op)
201 self.update_stack(op)
203 def pop_stack(self):
204 op = self.exec_stack.pop()
205 self.update_stack(op)
207 def update_stack(self, op = None):
208 "Called when exec_stack or foreach_stack changes."
209 for l in self.lists:
210 l.update_stack(op)
212 def set_exec(self, pos):
213 if self.op_in_progress:
214 raise Exception("Operation in progress...")
215 if pos is not None:
216 assert isinstance(pos[0], Op)
217 assert pos[1] in ['next', 'fail']
218 self.exec_point = pos
219 #if pos:
220 #print "set_exec: %s:%s" % pos
221 for l in self.lists:
222 l.update_points()
224 def set_rec(self, pos):
225 self.rec_point = pos
226 for l in self.lists:
227 l.update_points()
228 self.status_changed()
230 def record_at_point(self):
231 if not self.exec_point:
232 alert("No current point!")
233 return
234 self.set_rec(self.exec_point)
235 self.set_exec(None)
237 def stop_recording(self):
238 if self.rec_point:
239 self.set_exec(self.rec_point)
240 self.set_rec(None)
241 else:
242 alert("Not recording!")
244 def may_record(self, action):
245 "Perform and, possibly, record this action"
246 rec = self.rec_point
248 if rec:
249 print "RECORD:", rec, action
250 (op, old_exit) = rec
251 if action == ['enter']:
252 new_op = Block(op.parent)
253 new_op.toggle_enter()
254 if len(self.current_nodes) > 1:
255 new_op.toggle_foreach()
256 else:
257 new_op = Op(action)
258 op.link_to(new_op, old_exit)
259 self.set_exec(rec)
260 try:
261 self.do_one_step()
262 except InProgress:
263 if isinstance(new_op, Block):
264 self.set_rec((new_op.start, 'next'))
265 else:
266 self.set_rec((new_op, 'next'))
267 return
268 play_op, exit = self.exec_point
269 # (do_one_step may have stopped recording)
270 if self.rec_point:
271 self.set_rec((new_op, exit))
272 self.set_exec(None)
273 return
275 exit = 'next'
276 try:
277 self.do_action(action)
278 except InProgress:
279 pass
280 except Beep:
281 from rox import g
282 g.gdk.beep()
283 (type, val, tb) = sys.exc_info()
284 #if not val.may_record:
285 # return 0
286 exit = 'fail'
287 except Done:
288 raise
289 except:
290 rox.report_exception()
292 def add_display(self, display):
293 "Calls move_from(old_node) when we move and update_all() on updates."
294 self.displays.append(display)
295 #print "Added:", self.displays
297 def remove_display(self, display):
298 self.displays.remove(display)
299 #print "Removed, now:", self.displays
300 if not self.displays:
301 self.delete()
303 def update_replace(self, old, new):
304 if old == self.root:
305 self.root = new
306 if old in self.current_nodes:
307 self.model.lock(new)
308 self.model.unlock(old)
309 self.current_nodes.remove(old)
310 self.current_nodes.append(new)
311 self.update_all(new.parentNode)
312 else:
313 self.update_all(new.parentNode)
315 def has_ancestor(self, node, ancestor):
316 while node != ancestor:
317 node = node.parentNode
318 if not node:
319 return FALSE
320 return TRUE
322 def update_all(self, node):
323 for display in self.displays:
324 display.update_all(node)
326 def delete(self):
327 #print "View deleted"
328 self.model.root_program.watchers.remove(self)
329 self.move_to([])
330 for l in self.lists:
331 l.destroy()
332 self.model.unlock(self.root)
333 self.root = None
334 self.model.remove_view(self)
335 self.model = None
337 # 'nodes' may be either a node or a list of nodes.
338 # (duplicates will be removed)
339 # If it's a single node, then an 'attrib' node may also be specified
340 def move_to(self, nodes, attrib = None):
341 if self.current_nodes == nodes:
342 return
344 if attrib and attrib.nodeType != Node.ATTRIBUTE_NODE:
345 raise Exception('attrib not of type ATTRIBUTE_NODE!')
347 if type(nodes) != list:
348 assert nodes
349 nodes = [nodes]
350 else:
351 for n in nodes: assert n.nodeType
353 if len(nodes) > 1:
354 # Remove duplicates
355 map = {}
356 old = nodes
357 nodes = []
358 for n in old:
359 if n not in map:
360 map[n] = None
361 nodes.append(n)
362 #if len(old) != len(nodes):
363 # print "(move_to: attempt to set duplicate nodes)"
365 old_nodes = self.current_nodes
366 self.current_nodes = nodes
368 for node in self.current_nodes:
369 self.model.lock(node)
370 for node in old_nodes:
371 self.model.unlock(node)
373 self.current_attrib = attrib
375 for display in self.displays:
376 display.move_from(old_nodes)
378 def move_prev_sib(self):
379 try:
380 new = [n.previousSibling or 1/0 for n in self.current_nodes]
381 except:
382 raise Beep
383 self.move_to(new)
385 def move_next_sib(self):
386 try:
387 new = [n.nextSibling or 1/0 for n in self.current_nodes]
388 except:
389 raise Beep
390 self.move_to(new)
392 def move_left(self):
393 new = []
394 for n in self.current_nodes:
395 if n == self.root:
396 raise Beep
397 p = n.parentNode
398 if p not in new:
399 new.append(p)
400 self.move_to(new)
402 def move_right(self):
403 new = []
404 for n in self.current_nodes:
405 kids = n.childNodes
406 if kids:
407 new.append(kids[0])
408 else:
409 raise Beep
410 self.move_to(new)
412 def move_home(self):
413 self.move_to(self.root)
415 def move_end(self):
416 if not self.get_current().childNodes:
417 raise Beep
418 node = self.get_current().childNodes[0]
419 while node.nextSibling:
420 node = node.nextSibling
421 self.move_to(node)
423 def set_display_root(self, root):
424 self.model.lock(root)
425 if self.root:
426 self.model.unlock(self.root)
427 self.root = root
428 self.update_all(root)
430 def enter(self):
431 """Change the display root to a COPY of the selected node.
432 Call Leave to check changes back in."""
433 node = self.get_current()
434 if node is self.root:
435 raise Beep # Locking problems if this happens...
436 if self.model.doc is not node.ownerDocument:
437 raise Exception('Current node not in view!')
438 self.move_to([])
439 self.set_marked([])
441 new_model = self.model.lock_and_copy(node)
442 self.chroots.append((self.model, node, self.marked))
443 self.set_model(new_model)
444 self.update_stack()
446 def leave(self):
447 """Undo the effect of the last chroot()."""
448 if not self.chroots:
449 raise Beep
451 self.set_marked([])
452 self.move_to([])
453 model = self.model
455 (old_model, old_node, old_marked) = self.chroots.pop()
456 self.update_stack()
458 copy = old_model.import_with_ns(self.model.get_root())
459 old_model.unlock(old_node)
460 old_model.replace_node(old_node, copy)
461 self.set_model(old_model)
462 self.move_to([copy])
463 self.set_marked(old_marked.keys())
465 if not model.views:
466 model.undo_stack = None
467 model.__dict__ = {}
468 del model
469 import gc
470 gc.collect()
472 def do_action(self, action):
473 "'action' is a tuple (function, arg1, arg2, ...)"
474 "Performs the action. Returns if action completes, or raises "
475 "InProgress if not (will call resume() later)."
476 if action[0] in record_again:
477 self.last_action = action
478 elif action[0] == 'again':
479 action = self.last_action
480 fn = getattr(self, action[0])
481 exit = 'next'
482 #print "DO:", action[0]
483 self.model.mark()
484 try:
485 new = apply(fn, action[1:])
486 except InProgress:
487 raise
488 except Beep:
489 if not self.op_in_progress:
490 raise
491 exit = 'fail'
492 new = None
493 except:
494 if not self.op_in_progress:
495 raise
496 traceback.print_exc()
497 exit = 'fail'
498 new = None
500 if self.op_in_progress:
501 op = self.op_in_progress
502 self.set_oip(None)
503 self.set_exec((op, exit))
504 if new:
505 self.move_to(new)
507 def breakpoint(self):
508 if self.breakpoints.has_key(self.exec_point):
509 return 1
510 op = self.exec_point[0]
511 if op.parent.start == op and op.next == None:
512 return 1 # Empty program
513 return 0
515 def do_one_step(self):
516 "Execute the next op after exec_point, then:"
517 "- position the point on one of the exits return."
518 "- if there is no op to perform, call callback_on_return() or raise Done."
519 "- if the operation is started but not complete, raise InProgress and "
520 " arrange to resume() later."
521 if self.op_in_progress:
522 alert("Already executing something.")
523 raise Done()
524 if not self.exec_point:
525 alert("No current playback point.")
526 raise Done()
527 (op, exit) = self.exec_point
529 if self.single_step == 0 and self.breakpoint():
530 print "Hit a breakpoint! At " + time.ctime(time.time())
531 if self.rec_point:
532 self.set_rec(None)
533 self.single_step = 1
534 for l in self.lists:
535 l.show_prog(op.get_program())
536 return
538 next = getattr(op, exit)
539 try:
540 if next:
541 self.set_oip(next)
542 self.do_action(next.action) # May raise InProgress
543 return
545 if exit == 'fail' and not self.innermost_failure:
546 #print "Setting innermost_failure on", op
547 self.innermost_failure = op
549 # If we're in a block, try exiting from it...
550 if isinstance(op.parent, Block):
551 if self.start_block_iteration(op.parent, continuing = exit):
552 return # Looping...
553 if not op.parent.is_toplevel():
554 self.set_exec((op.parent, exit))
555 return
556 except Done:
557 print "(skipped a whole program!)"
558 if self.callback_on_return:
559 cb = self.callback_on_return
560 self.callback_on_return = None
561 cb()
562 else:
563 raise Done()
565 def set_oip(self, op):
566 #print "set_oip:", self.exec_point
567 if op:
568 self.set_exec(None)
569 self.op_in_progress = op
570 for l in self.lists:
571 l.update_points()
573 def fast_global(self, name):
574 "Search for nodes with this name anywhere under the root (//name)"
575 #print "Fast global", name
576 if ':' in name:
577 (prefix, localName) = string.split(name, ':', 1)
578 else:
579 (prefix, localName) = (None, name)
580 if self.current_nodes:
581 src = self.current_nodes[-1]
582 else:
583 src = self.root
584 namespaceURI = self.model.prefix_to_namespace(src, prefix)
585 select = []
586 def add(node):
587 if node.nodeType != Node.ELEMENT_NODE:
588 return
589 if node.localName == localName and node.namespaceURI == namespaceURI:
590 select.append(node)
591 map(add, node.childNodes)
592 add(self.root)
593 self.move_to(select)
595 # Actions...
597 def do_global(self, pattern):
598 if len(self.current_nodes) != 1:
599 self.move_to(self.root)
600 if pattern[:2] == '//':
601 if fast_global.match(pattern):
602 self.fast_global(pattern[2:])
603 return
605 assert not self.op_in_progress or (self.op_in_progress.action[1] == pattern)
606 try:
607 code = self.op_in_progress.cached_code
608 except:
609 from Ft.Xml.XPath import XPathParser
610 code = XPathParser.new().parse(self.macro_pattern(pattern))
611 if self.op_in_progress and pattern.find('@CURRENT@') == -1:
612 self.op_in_progress.cached_code = code
614 ns = self.model.namespaces.uri
615 c = Context.Context(self.get_current(), processorNss = ns)
616 #print code
617 nodes = code.evaluate(c)
618 assert type(nodes) == list
620 #don't select the document itself!
621 #Also, don't select attributes (needed for XSLT stuff)
622 nodes = [n for n in nodes if n.parentNode]
624 #nodes = XPath.Evaluate(self.macro_pattern(pattern), contextNode = self.get_current())
625 #print "Found", nodes
626 self.move_to(nodes)
628 def select_children(self):
629 new = []
630 for n in self.current_nodes:
631 new.extend(n.childNodes)
632 self.move_to(new)
634 def select_region(self, path, ns = None):
635 if len(self.current_nodes) == 0:
636 raise Beep
637 src = self.current_nodes[-1]
639 ns = self.model.namespaces.uri
640 c = Context.Context(src, [src], processorNss = ns)
641 rt = XPath.Evaluate(path, context = c)
642 node = None
643 for x in rt:
644 if not self.has_ancestor(x, self.root):
645 print "[ skipping search result above root ]"
646 continue
647 if not node:
648 node = x
649 if not node:
650 print "*** Search for '%s' in select_region failed" % path
651 print " (namespaces were '%s')" % ns
652 raise Beep
653 if node.parentNode != src.parentNode:
654 print "Nodes must have same parent!"
655 raise Beep
656 on = 0
657 selected = []
658 for n in src.parentNode.childNodes:
659 was_on = on
660 if n is src or n is node:
661 on = not was_on
662 if on or was_on:
663 selected.append(n)
664 self.move_to(selected)
666 def macro_pattern(self, pattern):
667 """Do the @CURRENT@ substitution for an XPath"""
668 if len(self.current_nodes) != 1:
669 return pattern
670 node = self.get_current()
671 if node.nodeType == Node.TEXT_NODE:
672 current = node.data
673 else:
674 if self.current_attrib:
675 current = self.current_attrib.value
676 else:
677 current = node.nodeName
678 pattern = pattern.replace('@CURRENT@', current)
679 #print "Searching for", pattern
680 return pattern
682 def do_search(self, pattern, ns = None, toggle = FALSE):
683 if len(self.current_nodes) == 0:
684 src = self.root
685 else:
686 src = self.current_nodes[-1]
688 # May be from a text_search...
689 #assert not self.op_in_progress or (self.op_in_progress.action[1] == pattern)
690 try:
691 code = self.op_in_progress.cached_code
692 except:
693 from Ft.Xml.XPath import XPathParser
694 code = XPathParser.new().parse(self.macro_pattern(pattern))
695 if self.op_in_progress and pattern.find('@CURRENT@') == -1:
696 self.op_in_progress.cached_code = code
698 ns = self.model.namespaces.uri
699 c = Context.Context(src, [src], processorNss = ns)
701 rt = code.evaluate(c)
702 node = None
703 for x in rt:
704 if not self.has_ancestor(x, self.root):
705 print "[ skipping search result above root ]"
706 continue
707 if not node:
708 node = x
709 #if self.node_to_line[x] > self.current_line:
710 #node = x
711 #break
712 if not node:
713 #print "*** Search for '%s' failed" % pattern
714 #print " (namespaces were '%s')" % ns
715 raise Beep
716 if toggle:
717 new = self.current_nodes[:]
718 if node in new:
719 new.remove(node)
720 else:
721 new.append(node)
722 self.move_to(new)
723 else:
724 self.move_to(node)
726 def do_text_search(self, pattern):
727 pattern = self.macro_pattern(pattern)
728 return self.do_search("//text()[ext:match('%s')]" % pattern)
730 def subst(self, replace, with):
731 "re search and replace on the current node"
732 nodes = self.current_nodes[:]
733 check = len(nodes) == 1
734 a = self.current_attrib
735 if a:
736 new, num = re.subn(replace, with, a.value)
737 if not num:
738 raise Beep
739 a = self.model.set_attrib(nodes[0], a.name, new)
740 self.move_to(nodes[0], a)
741 else:
742 self.move_to([])
743 final = []
744 for n in nodes:
745 if n.nodeType == Node.TEXT_NODE:
746 old = n.data.replace('\n', ' ')
747 new, num = re.subn(replace, with, old)
748 if check and not num:
749 self.move_to(n)
750 raise Beep
751 self.model.set_data(n, new)
752 final.append(n)
753 elif n.nodeType == Node.ELEMENT_NODE:
754 old = str(n.nodeName)
755 new, num = re.subn(replace, with, old)
756 if check and not num:
757 self.move_to(n)
758 raise Beep
759 new_ns, x = self.model.split_qname(n, new)
760 final.append(self.model.set_name(n, new_ns, new))
761 else:
762 self.move_to(n)
763 raise Beep
764 self.move_to(final)
766 def xpath(self, expr):
767 "Put the result of 'expr' on the clipboard."
768 expr = 'string(%s)' % expr
769 if len(self.current_nodes) == 0:
770 src = self.root
771 else:
772 src = self.current_nodes[-1]
774 try:
775 code = self.op_in_progress.cached_code
776 except:
777 from Ft.Xml.XPath import XPathParser
778 code = XPathParser.new().parse(self.macro_pattern(expr))
779 if self.op_in_progress and expr.find('@CURRENT@') == -1:
780 self.op_in_progress.cached_code = code
782 ns = self.model.namespaces.uri
783 c = Context.Context(src, [src], processorNss = ns)
785 rt = code.evaluate(c)
787 self.clipboard = self.model.doc.createTextNode(rt)
788 print "Result is", self.clipboard
790 def python(self, expr):
791 "Replace node with result of expr(old_value)"
792 if self.get_current().nodeType == Node.TEXT_NODE:
793 vars = {'x': self.get_current().data, 're': re, 'sub': re.sub, 'string': string}
794 result = eval(expr, vars)
795 new = self.python_to_node(result)
796 node = self.get_current()
797 self.move_to([])
798 self.model.replace_node(node, new)
799 self.move_to(new)
800 else:
801 raise Beep
803 def resume(self, exit = 'next'):
804 "After raising InProgress, call this to start moving again."
805 if self.op_in_progress:
806 op = self.op_in_progress
807 self.set_oip(None)
808 self.set_exec((op, exit))
809 if not self.single_step:
810 self.sched()
811 self.status_changed()
812 else:
813 print "(nothing to resume)"
815 def ask(self, q):
816 def ask_cb(result, self = self):
817 if result is None:
818 exit = 'fail'
819 else:
820 self.clipboard = self.model.doc.createTextNode(result)
821 exit = 'next'
822 self.resume(exit)
823 from GetArg import GetArg
824 box = GetArg('Input:', ask_cb, [q], destroy_return = 1)
825 raise InProgress
827 def python_to_node(self, data):
828 "Convert a python data structure into a tree and return the root."
829 if type(data) == list:
830 nlist = self.model.doc.createElementNS(DOME_NS, 'dome:list')
831 nlist.setAttributeNS(XMLNS_NAMESPACE, 'xmlns:dome', DOME_NS)
832 for x in data:
833 nlist.appendChild(self.python_to_node(x))
834 return nlist
835 return self.model.doc.createTextNode(str(data))
837 def yank(self, deep = 1):
838 if self.current_attrib:
839 a = self.current_attrib
841 self.clipboard = self.model.doc.createElementNS(a.namespaceURI, a.nodeName)
842 self.clipboard.appendChild(self.model.doc.createTextNode(a.value))
843 else:
844 self.clipboard = self.model.doc.createDocumentFragment()
845 for n in self.current_nodes:
846 c = n.cloneNode(deep)
847 #print n, "->", c
848 self.clipboard.appendChild(c)
850 #print "Clip now", self.clipboard
852 def shallow_yank(self):
853 self.yank(0)
855 def delete_shallow(self):
856 nodes = self.current_nodes[:]
857 if not nodes:
858 return
859 if self.root in nodes:
860 raise Beep
861 self.shallow_yank()
862 self.move_to([])
863 for n in nodes:
864 self.model.delete_shallow(n)
865 self.move_home()
867 def delete_node_no_clipboard(self):
868 self.delete_node(yank = 0)
870 def delete_node(self, yank = 1):
871 nodes = self.current_nodes[:]
872 if not nodes:
873 return
874 if yank:
875 self.yank()
876 if self.current_attrib:
877 ca = self.current_attrib
878 self.current_attrib = None
879 self.model.set_attrib(self.get_current(), ca.name, None)
880 return
881 if self.root in nodes:
882 raise Beep
883 self.move_to([])
884 new = [x.parentNode for x in nodes]
885 self.move_to(new)
886 self.model.delete_nodes(nodes)
888 def undo(self):
889 nodes = self.current_nodes[:]
890 self.move_to([])
891 self.model.unlock(self.root)
892 try:
893 self.model.undo()
894 finally:
895 self.model.lock(self.root)
896 self.move_to(filter(lambda x: self.has_ancestor(x, self.root), nodes))
898 def redo(self):
899 nodes = self.current_nodes[:]
900 self.move_to([])
901 self.model.unlock(self.root)
902 try:
903 self.model.redo()
904 finally:
905 self.model.lock(self.root)
906 self.move_to(filter(lambda x: self.has_ancestor(x, self.root), nodes))
908 def default_done(self, exit):
909 "Called when execution of a program returns. op_in_progress has been "
910 "restored - move to the exit."
911 #print "default_done(%s)" % exit
912 if self.op_in_progress:
913 op = self.op_in_progress
914 self.set_oip(None)
915 self.set_exec((op, exit))
916 else:
917 print "No operation to return to!"
918 c = self.call_on_done
919 if c:
920 self.call_on_done = None
921 c(exit)
922 elif exit == 'fail':
923 self.jump_to_innermost_failure()
924 raise Done()
926 def jump_to_innermost_failure(self):
927 assert self.innermost_failure != None
929 print "Returning to innermost failure:", self.innermost_failure
930 self.set_exec((self.innermost_failure, 'fail'))
931 for l in self.lists:
932 if hasattr(l, 'set_innermost_failure'):
933 l.set_innermost_failure(self.innermost_failure)
935 def play(self, name, done = None):
936 "Play this macro. When it returns, restore the current op_in_progress (if any)"
937 "and call done(exit). Default for done() moves exec_point."
938 "done() is called from do_one_step() - usual rules apply."
940 prog = self.name_to_prog(name)
941 self.innermost_failure = None
943 if not done:
944 done = self.default_done
946 def cbor(self = self, op = self.op_in_progress, done = done,
947 name = name,
948 old_cbor = self.callback_on_return,
949 old_ss = self.single_step):
950 "We're in do_one_step..."
952 #print "Return from '%s'..." % name
954 if old_ss == 2 and self.single_step == 0:
955 self.single_step = old_ss
956 self.callback_on_return = old_cbor
958 o, exit = self.exec_point
959 if op:
960 #print "Resume op '%s' (%s)" % (op.program.name, op)
961 self.pop_stack()
962 self.set_oip(op)
963 return done(exit)
965 self.callback_on_return = cbor
967 if self.single_step == 2:
968 self.single_step = 0
970 if self.op_in_progress:
971 self.push_stack(self.op_in_progress)
972 self.set_oip(None)
973 self.play_block(prog.code)
974 self.sched()
975 self.status_changed()
976 raise InProgress
978 def start_block_iteration(self, block, continuing = None):
979 "True if we are going to run the block, False to exit the loop"
980 "Continuing is 'next' or 'fail' if we reached the end of the block."
981 #print "Start interation"
982 if not self.foreach_stack:
983 raise Done
984 stack_block, nodes_list, restore, old_mark = self.foreach_stack[-1]
985 if stack_block != block:
986 self.reset_foreach_stack()
987 self.update_stack()
988 raise Exception("Reached the end of a block we never entered")
990 if continuing:
991 if block.enter:
992 self.leave()
993 if block.foreach:
994 restore.extend(self.current_nodes)
995 if continuing == 'fail':
996 print "Error in block; exiting early in program", block.get_program()
997 if old_mark:
998 [self.model.unlock(x) for x in old_mark]
999 self.foreach_stack.pop()
1000 self.update_stack()
1001 return 0
1002 while nodes_list and nodes_list[0].parentNode == None:
1003 print "Skipping deleted node", nodes_list[0]
1004 del nodes_list[0]
1006 if not nodes_list:
1007 self.foreach_stack.pop()
1008 self.update_stack()
1009 if block.foreach:
1010 nodes = filter(lambda x: self.has_ancestor(x, self.root), restore)
1011 self.move_to(nodes)
1012 if old_mark is not None:
1013 self.set_marked(old_mark)
1014 [self.model.unlock(x) for x in old_mark]
1015 return 0 # Nothing left to do
1016 nodes = nodes_list[0]
1017 del nodes_list[0]
1018 self.move_to(nodes)
1020 if nodes_list:
1021 print "[ %d after this ]" % len(nodes_list),
1022 sys.stdout.flush()
1024 if block.enter:
1025 self.enter()
1026 self.set_exec((block.start, 'next'))
1027 return 1
1029 def play_block(self, block):
1030 assert isinstance(block, Block)
1031 #print "Enter Block!"
1032 if block.foreach:
1033 list = self.current_nodes[:]
1034 else:
1035 list = [self.current_nodes[:]] # List of one item, containing everything
1037 if block.restore:
1038 marks = self.marked.copy()
1039 [self.model.lock(x) for x in marks]
1040 else:
1041 marks = None
1042 self.foreach_stack.append((block, list, [], marks))
1044 self.update_stack()
1045 if not self.start_block_iteration(block):
1046 # No nodes selected...
1047 if not block.is_toplevel():
1048 self.set_exec((block, 'next'))
1049 else:
1050 self.set_oip(None)
1051 self.set_exec((block.start, 'next'))
1052 raise Done
1054 def Block(self):
1055 assert self.op_in_progress
1056 oip = self.op_in_progress
1057 self.set_oip(None)
1058 self.play_block(oip)
1059 if not self.single_step:
1060 self.sched()
1061 raise InProgress
1063 def sched(self):
1064 if self.op_in_progress:
1065 raise Exception("Operation in progress")
1066 if self.idle_cb:
1067 raise Exception("Already playing!")
1068 self.idle_cb = self.idle_add(self.play_callback)
1070 def play_callback(self):
1071 self.idle_remove(self.idle_cb)
1072 self.idle_cb = 0
1073 try:
1074 self.in_callback = 1
1075 try:
1076 self.do_one_step()
1077 finally:
1078 self.in_callback = 0
1079 except Done:
1080 (op, exit) = self.exec_point
1081 if exit == 'fail' and self.innermost_failure:
1082 self.jump_to_innermost_failure()
1083 print "Done, at " + time.ctime(time.time())
1084 self.run_new()
1085 return 0
1086 except InProgress:
1087 #print "InProgress"
1088 return 0
1089 except:
1090 type, val, tb = sys.exc_info()
1091 list = traceback.extract_tb(tb)
1092 stack = traceback.format_list(list[-2:])
1093 ex = traceback.format_exception_only(type, val) + ['\n\n'] + stack
1094 traceback.print_exception(type, val, tb)
1095 print "Error in do_one_step(): stopping playback"
1096 node = self.op_in_progress
1097 self.set_oip(None)
1098 if node:
1099 self.set_exec((node, 'fail'))
1100 self.status_changed()
1101 return 0
1102 if self.op_in_progress or self.single_step:
1103 self.status_changed()
1104 return 0
1105 self.sched()
1106 return 0
1108 def set_status(self, message = None):
1109 "Set the status bar message."
1110 for d in self.displays:
1111 if hasattr(d, 'set_status'): d.set_status(message)
1113 def status_changed(self):
1114 for display in self.displays:
1115 if hasattr(display, 'update_state'):
1116 display.update_state()
1118 def map(self, name):
1119 print "Map", name
1121 nodes = self.current_nodes[:]
1122 if not nodes:
1123 print "map of nothing: skipping..."
1124 return
1125 inp = [nodes, None] # Nodes, next
1126 def next(exit = exit, self = self, name = name, inp = inp):
1127 "This is called while in do_one_step() - normal rules apply."
1128 nodes, next = inp
1129 print "[ %d to go ]" % len(nodes),
1130 sys.stdout.flush()
1131 if exit == 'fail':
1132 print "Map: nodes remaining, but an error occurred..."
1133 return self.default_done(exit)
1134 while nodes and nodes[0].parentNode == None:
1135 print "Skipping deleted node", nodes[0]
1136 del nodes[0]
1137 if not nodes:
1138 return self.default_done(exit)
1139 self.move_to(nodes[0])
1140 del nodes[0]
1141 if not nodes:
1142 next = None
1143 #print "Map: calling play (%d after this)" % len(nodes)
1144 self.play(name, done = next) # Should raise InProgress
1145 if nodes is self.current_nodes:
1146 raise Exception("Slice failed!")
1147 inp[1] = next
1148 next('next')
1150 def name_to_prog(self, name):
1151 comps = string.split(name, '/')
1152 prog = self.model.root_program
1153 if prog.name != comps[0]:
1154 raise Exception("No such program as '%s'!" % name)
1155 del comps[0]
1156 while comps:
1157 prog = prog.subprograms[comps[0]]
1158 del comps[0]
1159 return prog
1161 def change_node(self, new_data):
1162 nodes = self.current_nodes
1163 if not nodes:
1164 return
1165 self.move_to([])
1166 if nodes[0].nodeType == Node.ELEMENT_NODE:
1167 # Slow, so do this here, even if vaguely incorrect...
1168 assert ' ' not in new_data
1169 if ':' in new_data:
1170 (prefix, localName) = string.split(new_data, ':', 1)
1171 else:
1172 (prefix, localName) = (None, new_data)
1173 namespaceURI = self.model.prefix_to_namespace(nodes[0], prefix)
1174 out = []
1175 for node in nodes:
1176 if node is self.root:
1177 self.model.unlock(self.root)
1178 new = self.model.set_name(node, namespaceURI, new_data)
1179 self.model.lock(new)
1180 self.root = new
1181 else:
1182 new = self.model.set_name(node, namespaceURI, new_data)
1183 out.append(new)
1184 self.move_to(out)
1185 else:
1186 for node in nodes:
1187 self.model.set_data(node, new_data)
1188 self.move_to(nodes)
1190 def add_node(self, where, data):
1191 cur = self.get_current()
1192 if where[1] == 'e':
1193 if ':' in data:
1194 (prefix, localName) = string.split(data, ':', 1)
1195 else:
1196 (prefix, localName) = (None, data)
1197 namespaceURI = self.model.prefix_to_namespace(self.get_current(), prefix)
1198 new = self.model.doc.createElementNS(namespaceURI, data)
1199 elif where[1] == 'a':
1200 self.add_attrib(None, data)
1201 return
1202 else:
1203 new = self.model.doc.createTextNode(data)
1205 try:
1206 if where[0] == 'i':
1207 self.model.insert_before(cur, new)
1208 elif where[0] == 'a':
1209 self.model.insert_after(cur, new)
1210 elif where[0] == 'e':
1211 self.model.insert_before(None, new, parent = cur)
1212 else:
1213 self.model.insert(cur, new)
1214 except:
1215 raise Beep
1217 self.move_to(new)
1219 def request_from_node(self, node, attrib):
1220 """Return a urllib2.Request object. If attrib is set then the URI is
1221 taken from that, otherwise search for a good attribute."""
1222 uri = None
1223 if node.nodeType == Node.TEXT_NODE:
1224 uri = node.nodeValue
1225 else:
1226 if attrib:
1227 uri = attrib.value
1228 elif node.hasAttributeNS(None, 'uri'):
1229 uri = node.getAttributeNS(None, 'uri')
1230 else:
1231 for attr in node.attributes.keys():
1232 a_node = node.attributes[attr]
1233 if a_node.namespaceURI == XMLNS_NAMESPACE:
1234 continue
1235 uri = a_node.value
1236 if uri.find('//') != -1 or uri.find('.htm') != -1:
1237 break
1238 if not uri:
1239 print "Can't suck", node, "(no uri attribute found)"
1240 raise Beep
1241 if uri.find('//') == -1:
1242 base = self.model.get_base_uri(node)
1243 if not ':' in base:
1244 base = os.path.dirname(base)
1245 print "Relative URI..."
1246 if base:
1247 print "Base URI is:", base, "add", uri
1248 if uri.startswith('/'):
1249 uri = urlparse.urljoin(base, uri)
1250 else:
1251 uri = base + '/' + uri
1252 print "Final URI is:", uri
1253 else:
1254 pass
1255 #print "Warning: Can't find 'uri' attribute!"
1256 if not ':' in uri:
1257 uri = 'file://' + uri
1258 request = urllib2.Request(uri)
1260 return request
1262 def http_post(self):
1263 node = self.get_current()
1264 attrs = node.attributes
1265 post = []
1266 request = self.request_from_node(node, self.current_attrib)
1267 for (ns,name) in attrs.keys():
1268 if ns is not None: continue
1269 value = str(attrs[(ns, name)].value)
1270 if name.startswith('header-'):
1271 request.add_header(str(name)[7:], value)
1272 else:
1273 post.append((str(name), value))
1275 request.add_data(urllib.urlencode(post))
1276 node = self.suck_node(node, request)
1277 if node:
1278 self.move_to(node)
1280 def suck(self, md5_only = 0):
1281 nodes = self.current_nodes[:]
1282 attrib = self.current_attrib
1283 self.move_to([])
1284 final = []
1285 for x in nodes:
1286 request = self.request_from_node(x, attrib)
1287 try:
1288 new = self.suck_node(x, request, md5_only = md5_only)
1289 final.append(new)
1290 finally:
1291 self.move_to(x)
1292 self.move_to(final)
1294 def suck_md5(self):
1295 self.suck(md5_only = 1)
1297 def suck_node(self, node, request, md5_only = 0):
1298 """Load the resource specified by request and replace 'node' with the
1299 sucked data."""
1300 uri = request.get_full_url()
1301 self.set_status("Fetching %s (connecting)..." % uri)
1302 try:
1303 if uri.startswith('file:///'):
1304 assert not request.has_data()
1305 stream = open(uri[7:])
1306 else:
1307 if request.has_data(): print "POSTING", request.get_data()
1308 stream = urllib2.urlopen(request)
1309 headers = stream.info().headers
1311 self.set_status("Fetching %s (downloading)..." % uri)
1312 data = stream.read()
1313 self.set_status("Fetching %s (parsing)..." % uri)
1315 if not data.startswith('<?xml'): data = support.to_html_doc(data)
1317 try:
1318 root = support.parse_data(data, uri)
1319 except:
1320 raise Beep
1322 new = self.model.import_with_ns(root.documentElement)
1323 new.setAttributeNS(None, 'uri', uri)
1325 self.move_to([])
1326 if node == self.root:
1327 self.model.unlock(self.root)
1328 self.model.replace_node(self.root, new)
1329 self.model.strip_space(new)
1330 self.model.lock(new)
1331 self.root = new
1332 else:
1333 self.model.replace_node(node, new)
1334 self.model.strip_space(new)
1336 finally:
1337 self.set_status()
1338 return new
1340 def put_before(self):
1341 node = self.get_current()
1342 if self.clipboard == None:
1343 raise Beep
1344 new = self.clipboard.cloneNode(1)
1345 try:
1346 self.model.insert_before(node, new)
1347 except:
1348 raise Beep
1350 def put_after(self):
1351 node = self.get_current()
1352 if self.clipboard == None:
1353 raise Beep
1354 new = self.clipboard.cloneNode(1)
1355 self.model.insert_after(node, new)
1357 def put_replace(self):
1358 node = self.get_current()
1359 if self.clipboard == None:
1360 print "No clipboard!"
1361 raise Beep
1362 if self.current_attrib:
1363 if self.clipboard.nodeType == Node.DOCUMENT_FRAGMENT_NODE:
1364 value = self.clipboard.childNodes[0].data
1365 else:
1366 value = self.clipboard.data
1367 a = self.current_attrib
1368 value = value.replace('\n', ' ')
1369 a = self.model.set_attrib(node, a.name, value)
1370 self.move_to(node, a)
1371 return
1372 if self.clipboard.nodeType == Node.DOCUMENT_FRAGMENT_NODE:
1373 if len(self.clipboard.childNodes) != 1:
1374 print "Multiple nodes in clipboard!"
1375 raise Beep
1376 new = self.clipboard.childNodes[0].cloneNode(1)
1377 else:
1378 new = self.clipboard.cloneNode(1)
1379 if new.nodeType != Node.ELEMENT_NODE:
1380 raise Beep
1381 self.move_to([])
1382 try:
1383 if node == self.root:
1384 self.model.unlock(self.root)
1385 try:
1386 self.model.replace_node(self.root, new)
1387 self.root = new
1388 finally:
1389 self.model.lock(self.root)
1390 else:
1391 self.model.replace_node(node, new)
1392 self.move_to(new)
1393 except:
1394 type, val, tb = sys.exc_info()
1395 traceback.print_exception(type, val, tb)
1396 print "Replace failed!"
1397 raise Beep
1399 def put_as_child_end(self):
1400 self.put_as_child(end = 1)
1402 def put_as_child(self, end = 0):
1403 node = self.get_current()
1404 if self.clipboard == None:
1405 raise Beep
1406 new = self.clipboard.cloneNode(1)
1407 if new.nodeType == Node.DOCUMENT_FRAGMENT_NODE:
1408 to = []
1409 for n in new.childNodes:
1410 to.append(n)
1411 else:
1412 to = new
1413 try:
1414 if end:
1415 self.model.insert_before(None, new, parent = node)
1416 else:
1417 self.model.insert(node, new, index = 0)
1418 except:
1419 raise Beep
1421 self.move_to(to)
1423 def yank_value(self):
1424 if not self.current_attrib:
1425 raise Beep
1426 value = self.current_attrib.value
1427 self.clipboard = self.model.doc.createTextNode(value)
1428 #print "Clip now", self.clipboard
1430 def yank_attribs(self, name = None):
1431 if name:
1432 print "yank_attribs: DEPRECATED -- use Yank instead!"
1433 self.clipboard = self.model.doc.createDocumentFragment()
1434 if name:
1435 if not self.get_current().hasAttributeNS(None, name):
1436 raise Beep
1437 attribs = [self.get_current().getAttributeNodeNS(None, name)]
1438 else:
1439 attribs = []
1440 dict = self.get_current().attributes
1441 for a in dict.keys():
1442 attribs.append(dict[a])
1444 # Make sure the attributes always come out in the same order
1445 # (helps with macros).
1446 def by_name(a, b):
1447 diff = cmp(a.name, b.name)
1448 if diff == 0:
1449 diff = cmp(a.namespaceURI, b.namespaceURI)
1450 return diff
1452 attribs.sort(by_name)
1453 for a in attribs:
1454 n = self.model.doc.createElementNS(a.namespaceURI, a.nodeName)
1455 n.appendChild(self.model.doc.createTextNode(a.value))
1456 self.clipboard.appendChild(n)
1457 #print "Clip now", self.clipboard
1459 def paste_attribs(self):
1460 if self.clipboard.nodeType == Node.DOCUMENT_FRAGMENT_NODE:
1461 attribs = self.clipboard.childNodes
1462 else:
1463 attribs = [self.clipboard]
1464 new = []
1465 for a in attribs:
1466 try:
1467 new.append((a.nodeName, a.childNodes[0].data))
1468 except:
1469 raise Beep
1470 for node in self.current_nodes:
1471 # XXX: Set NS attribs first...
1472 for (name, value) in new:
1473 self.model.set_attrib(node, name, value)
1475 def compare(self):
1476 "Ensure that all selected nodes have the same value."
1477 if len(self.current_nodes) < 2:
1478 raise Beep # Not enough nodes!
1479 base = self.current_nodes[0]
1480 for n in self.current_nodes[1:]:
1481 if not same(base, n):
1482 raise Beep(may_record = 1)
1484 def fail(self):
1485 raise Beep(may_record = 1)
1487 def do_pass(self):
1488 pass
1490 def fail_if(self, xpath):
1491 """Evaluate xpath as a boolean, and fail if true."""
1492 src = self.get_current()
1493 ns = self.model.namespaces.uri
1494 c = Context.Context(src.parentNode, [src.parentNode], processorNss = ns)
1496 rt = XPath.Evaluate(xpath, context = c)
1497 #print "Got", rt
1498 if src in rt:
1499 raise Beep(may_record = 1)
1501 def attribute(self, namespace = None, attrib = ''):
1502 node = self.get_current()
1504 if attrib == '':
1505 self.move_to(node)
1506 return
1508 if attrib == 'xmlns':
1509 attrib = None
1510 #print "(ns, attrib)", `namespace`, attrib
1512 a = node.attributes.get((namespace, attrib), None)
1514 if a:
1515 self.move_to(node, a)
1516 else:
1517 print "No such attribute"
1518 print "Looking for %s in %s" % ((namespace, attrib), node.attributes)
1519 raise Beep()
1521 def set_attrib(self, value):
1522 a = self.current_attrib
1523 if not a:
1524 raise Beep()
1525 node = self.get_current()
1526 a = self.model.set_attrib(node, a.name, value)
1527 self.move_to(node, a)
1529 def rename_attrib(self, new):
1530 a = self.current_attrib
1531 if not a:
1532 raise Beep()
1533 node = self.get_current()
1534 new_attr = self.model.set_attrib(node, new, a.value)
1535 self.model.set_attrib(node, a.name, None)
1536 self.move_to(node, new_attr)
1538 def add_attrib(self, UNUSED, name, value = ''):
1539 node = self.get_current()
1540 a = self.model.set_attrib(node, name, value)
1541 self.move_to(node, a)
1543 def set_root_from_doc(self, doc):
1544 new = self.model.import_with_ns(doc.documentElement)
1546 if self.root:
1547 self.model.unlock(self.root)
1548 self.move_to([])
1549 self.model.replace_node(self.root, new)
1550 self.model.lock(new)
1551 self.root = new
1552 self.move_to(self.root)
1554 def load_html(self, path):
1555 "Replace root with contents of this HTML file."
1556 print "Reading HTML..."
1557 doc = self.model.load_html(path)
1558 self.set_root_from_doc(doc)
1560 def load_xml(self, path):
1561 "Replace root with contents of this XML (or Dome) file."
1562 print "Reading XML..."
1563 data = file(path).read()
1564 doc = support.parse_data(data, path)
1565 self.set_root_from_doc(doc)
1567 def select_dups(self):
1568 node = self.get_current()
1569 select = []
1570 for n in node.parentNode.childNodes:
1571 if n is node:
1572 continue
1573 if same(node, n):
1574 select.append(n)
1575 self.move_to(select)
1577 def select_marked_region(self, attr = "unused"):
1578 select = []
1579 if len(self.marked) != 1:
1580 print "Must be exactly one marked node!"
1581 raise Beep()
1582 if len(self.current_nodes) != 1:
1583 print "Must be exactly one selected node!"
1584 raise Beep()
1585 import Path
1586 a = Path.path_to(self.get_current())
1587 b = Path.path_to(self.marked.keys()[0])
1589 while a and b and a[0] == b[0]:
1590 del a[0]
1591 del b[0]
1593 if a and b:
1594 select = []
1595 s = 0
1596 a = a[0]
1597 b = b[0]
1598 for x in a.parentNode.childNodes:
1599 if x == a:
1600 s = not s
1601 elif x == b:
1602 s = not s
1603 if s:
1604 select.append(x)
1605 self.move_to(select)
1606 else:
1607 print "One node is a parent of the other!"
1608 raise Beep()
1610 def show_html(self):
1611 from HTML import HTML
1612 HTML(self.model, self.get_current()).show()
1614 def show_canvas(self):
1615 from Canvas import Canvas
1616 Canvas(self, self.get_current()).show()
1618 def toggle_hidden(self):
1619 nodes = self.current_nodes[:]
1620 self.move_to([])
1621 for node in nodes:
1622 if node.nodeType != Node.ELEMENT_NODE:
1623 raise Beep
1624 if node.hasAttributeNS(None, 'hidden'):
1625 new = None
1626 else:
1627 new = 'yes'
1628 self.model.set_attrib(node, 'hidden', new, with_update = 0)
1629 self.model.update_all(self.root)
1630 self.move_to(nodes)
1632 def soap_send(self):
1633 copy = node_to_xml(self.get_current())
1634 env = copy.documentElement
1635 from Ft.Xml.Lib.Nss import GetAllNs
1636 nss = GetAllNs(env)
1637 for p, u in self.model.namespaces.uri.iteritems():
1638 if p in nss:
1639 assert nss[p] == u
1640 elif p not in ('xml', 'xmlns'):
1641 env.setAttributeNS(XMLNS_NAMESPACE, 'xmlns:%s' % p, u)
1643 if env.namespaceURI != SOAPENV_NS:
1644 alert("Not a SOAP-ENV:Envelope (bad namespace)")
1645 raise Done()
1646 if env.localName != 'Envelope':
1647 alert("Not a SOAP-ENV:Envelope (bad local name)")
1648 raise Done()
1650 if len(env.childNodes) != 2:
1651 alert("SOAP-ENV:Envelope must have one header and one body")
1652 raise Done()
1654 kids = elements(env)
1655 head = kids[0]
1656 body = kids[1]
1658 if head.namespaceURI != SOAPENV_NS or \
1659 head.localName != 'Head':
1660 alert("First child must be a SOAP-ENV:Head element")
1661 raise Done()
1663 if body.namespaceURI != SOAPENV_NS or \
1664 body.localName != 'Body':
1665 alert("Second child must be a SOAP-ENV:Body element")
1666 raise Done()
1668 sft = None
1669 for header in elements(head):
1670 if header.namespaceURI == DOME_NS and header.localName == 'soap-forward-to':
1671 sft = header
1672 break
1673 print header.namespaceURI
1674 print header.localName
1676 if not sft:
1677 alert("Head must contain a dome:soap-forward-to element")
1678 raise Done()
1680 dest = sft.childNodes[0].data
1681 parent = sft.parentNode
1682 if len(elements(parent)) == 1:
1683 sft = parent
1684 parent = sft.parentNode # Delete the whole header
1685 parent.removeChild(sft)
1687 import httplib, urlparse
1689 (scheme, addr, path, p, q, f) = urlparse.urlparse(dest, allow_fragments = 0)
1690 if scheme != 'http':
1691 alert("SOAP is only supported for 'http:' -- sorry!")
1692 raise Done()
1694 stream = StrGrab()
1695 PrettyPrint(copy, stream = stream)
1696 message = stream.data
1697 print message
1699 conn = httplib.HTTP(addr)
1700 conn.putrequest("POST", path)
1701 conn.putheader('Content-Type', 'text/xml; charset="utf-8"')
1702 conn.putheader('Content-Length', str(len(message)))
1703 conn.putheader('SOAPAction', '')
1704 conn.endheaders()
1705 conn.send(message)
1706 (code, r_mess, r_headers) = conn.getreply()
1708 reply = conn.getfile().read()
1709 print "Got:\n", reply
1711 new_doc = support.parse_data(reply, None)
1713 new = self.model.doc.importNode(new_doc.documentElement, 1)
1715 self.model.strip_space(new)
1717 old = self.get_current()
1718 self.move_to([])
1719 self.model.replace_node(old, new)
1720 self.move_to(new)
1722 def program_changed(self, changed_op):
1723 print "Check points..."
1724 if self.rec_point:
1725 (op, exit) = self.rec_point
1726 if not op.parent:
1727 print "Lost rec_point"
1728 self.rec_point = None
1729 if self.exec_point:
1730 (op, exit) = self.exec_point
1731 if not op.parent:
1732 print "Lost exec_point"
1733 self.exec_point = None
1734 for l in self.lists:
1735 l.update_points()
1736 self.status_changed()
1738 def prog_tree_changed(self):
1739 pass
1741 def export_all(self):
1742 doc = implementation.createDocument(DOME_NS, 'dome:dome', None)
1744 doc.documentElement.appendChild(self.model.namespaces.to_xml(doc))
1746 node = self.model.root_program.to_xml(doc)
1747 doc.documentElement.appendChild(node)
1748 node = doc.createElementNS(DOME_NS, 'dome:dome-data')
1749 doc.documentElement.appendChild(node)
1751 if self.chroots:
1752 print "*** WARNING: Saving from a chroot!"
1753 model = self.model
1754 data = doc.importNode(model.doc.documentElement, 1)
1755 node.appendChild(data)
1757 return doc
1759 def blank_all(self):
1760 doc = implementation.createDocument(None, 'root', None)
1761 self.move_home()
1762 self.clipboard = self.model.doc.createElementNS(None, 'root')
1763 self.put_replace()
1765 def mark_switch(self):
1766 new = self.marked.keys()
1767 self.set_marked(self.current_nodes)
1768 self.move_to(new)
1770 def set_marked(self, new):
1771 update = self.marked
1772 for x in self.marked.keys():
1773 self.model.unlock(x)
1774 self.marked = {}
1775 for x in new:
1776 self.model.lock(x)
1777 self.marked[x] = None
1778 update[x] = None
1779 update = update.keys()
1780 for display in self.displays:
1781 display.marked_changed(update)
1783 def mark_selection(self):
1784 self.set_marked(self.current_nodes)
1786 def clear_mark(self):
1787 self.set_marked([])
1789 def normalise(self):
1790 self.model.normalise(self.get_current())
1792 def remove_ns(self):
1793 print "remove_ns: Disabled"
1794 return
1795 nodes = self.current_nodes[:]
1796 self.move_to([])
1797 nodes = map(self.model.remove_ns, nodes)
1798 self.move_to(nodes)
1800 def convert_to(self, fn):
1801 nodes = self.current_nodes[:]
1802 self.move_to([])
1803 nodes = map(fn, nodes)
1804 self.move_to(nodes)
1805 def convert_to_element(self): self.convert_to(self.model.convert_to_element)
1806 def convert_to_text(self): self.convert_to(self.model.convert_to_text)
1807 def convert_to_comment(self): self.convert_to(self.model.convert_to_comment)
1809 class StrGrab:
1810 data = ''
1812 def write(self, str):
1813 self.data += str