1 from __future__
import nested_scopes
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
19 from StringIO
import StringIO
21 from Program
import Op
, Block
25 import urllib
, urllib2
28 from constants
import *
32 #http://www.w3.org/2001/12/soap-envelope'
33 SOAPENV_NS
= 'http://schemas.xmlsoap.org/soap/envelope/'
37 for x
in node
.childNodes
:
38 if x
.nodeType
== Node
.ELEMENT_NODE
:
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]*$')
47 # - A ref to a DOM document
48 # - A set of current nodes
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 '.'
65 "delete_node_no_clipboard",
96 "Recursivly compare two nodes."
97 if a
.nodeType
!= b
.nodeType
or a
.nodeName
!= b
.nodeName
:
99 if a
.nodeValue
!= b
.nodeValue
:
103 if len(aks
) != len(bks
):
105 for (ak
, bk
) in map(None, aks
, bks
):
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"
116 def __init__(self
, model
, callback_handlers
= None):
117 """callback_handlers is an (idle_add, idle_remove) tuple"""
121 self
.single_step
= 1 # 0 = Play 1 = Step-into 2 = Step-over
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
130 if not callback_handlers
:
132 self
.idle_add
, self
.idle_remove
= g
.idle_add
, g
.idle_remove
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
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
159 self
.model
.unlock(self
.root
)
162 self
.model
.remove_view(self
)
163 self
.model
.root_program
.watchers
.remove(self
)
165 self
.model
.root_program
.watchers
.append(self
)
167 self
.set_display_root(self
.model
.get_root())
168 self
.move_to(self
.root
)
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."
177 self
.idle_remove(self
.idle_cb
)
180 self
.innermost_failure
= None
181 self
.call_on_done
= callback
182 self
.callback_on_return
= None
183 while self
.exec_stack
:
185 self
.reset_foreach_stack()
186 self
.status_changed()
188 self
.set_status(None)
190 def reset_foreach_stack(self
):
191 for block
, nodes
, restore
, mark
in self
.foreach_stack
:
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
)
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."
212 def set_exec(self
, pos
):
213 if self
.op_in_progress
:
214 raise Exception("Operation in progress...")
216 assert isinstance(pos
[0], Op
)
217 assert pos
[1] in ['next', 'fail']
218 self
.exec_point
= pos
220 #print "set_exec: %s:%s" % pos
224 def set_rec(self
, pos
):
228 self
.status_changed()
230 def record_at_point(self
):
231 if not self
.exec_point
:
232 alert("No current point!")
234 self
.set_rec(self
.exec_point
)
237 def stop_recording(self
):
239 self
.set_exec(self
.rec_point
)
242 alert("Not recording!")
244 def may_record(self
, action
):
245 "Perform and, possibly, record this action"
249 print "RECORD:", rec
, action
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()
258 op
.link_to(new_op
, old_exit
)
263 if isinstance(new_op
, Block
):
264 self
.set_rec((new_op
.start
, 'next'))
266 self
.set_rec((new_op
, 'next'))
268 play_op
, exit
= self
.exec_point
269 # (do_one_step may have stopped recording)
271 self
.set_rec((new_op
, exit
))
277 self
.do_action(action
)
283 (type, val
, tb
) = sys
.exc_info()
284 #if not val.may_record:
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
:
303 def update_replace(self
, old
, new
):
306 if old
in self
.current_nodes
:
308 self
.model
.unlock(old
)
309 self
.current_nodes
.remove(old
)
310 self
.current_nodes
.append(new
)
311 self
.update_all(new
.parentNode
)
313 self
.update_all(new
.parentNode
)
315 def has_ancestor(self
, node
, ancestor
):
316 while node
!= ancestor
:
317 node
= node
.parentNode
322 def update_all(self
, node
):
323 for display
in self
.displays
:
324 display
.update_all(node
)
327 #print "View deleted"
328 self
.model
.root_program
.watchers
.remove(self
)
332 self
.model
.unlock(self
.root
)
334 self
.model
.remove_view(self
)
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
:
344 if attrib
and attrib
.nodeType
!= Node
.ATTRIBUTE_NODE
:
345 raise Exception('attrib not of type ATTRIBUTE_NODE!')
347 if type(nodes
) != list:
351 for n
in nodes
: assert n
.nodeType
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
):
380 new
= [n
.previousSibling
or 1/0 for n
in self
.current_nodes
]
385 def move_next_sib(self
):
387 new
= [n
.nextSibling
or 1/0 for n
in self
.current_nodes
]
394 for n
in self
.current_nodes
:
402 def move_right(self
):
404 for n
in self
.current_nodes
:
413 self
.move_to(self
.root
)
416 if not self
.get_current().childNodes
:
418 node
= self
.get_current().childNodes
[0]
419 while node
.nextSibling
:
420 node
= node
.nextSibling
423 def set_display_root(self
, root
):
424 self
.model
.lock(root
)
426 self
.model
.unlock(self
.root
)
428 self
.update_all(root
)
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!')
441 new_model
= self
.model
.lock_and_copy(node
)
442 self
.chroots
.append((self
.model
, node
, self
.marked
))
443 self
.set_model(new_model
)
447 """Undo the effect of the last chroot()."""
455 (old_model
, old_node
, old_marked
) = self
.chroots
.pop()
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
)
463 self
.set_marked(old_marked
.keys())
466 model
.undo_stack
= None
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])
482 #print "DO:", action[0]
485 new
= apply(fn
, action
[1:])
489 if not self
.op_in_progress
:
494 if not self
.op_in_progress
:
496 traceback
.print_exc()
500 if self
.op_in_progress
:
501 op
= self
.op_in_progress
503 self
.set_exec((op
, exit
))
507 def breakpoint(self
):
508 if self
.breakpoints
.has_key(self
.exec_point
):
510 op
= self
.exec_point
[0]
511 if op
.parent
.start
== op
and op
.next
== None:
512 return 1 # Empty program
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.")
524 if not self
.exec_point
:
525 alert("No current playback point.")
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())
535 l
.show_prog(op
.get_program())
538 next
= getattr(op
, exit
)
542 self
.do_action(next
.action
) # May raise InProgress
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
):
553 if not op
.parent
.is_toplevel():
554 self
.set_exec((op
.parent
, exit
))
557 print "(skipped a whole program!)"
558 if self
.callback_on_return
:
559 cb
= self
.callback_on_return
560 self
.callback_on_return
= None
565 def set_oip(self
, op
):
566 #print "set_oip:", self.exec_point
569 self
.op_in_progress
= op
573 def fast_global(self
, name
):
574 "Search for nodes with this name anywhere under the root (//name)"
575 #print "Fast global", name
577 (prefix
, localName
) = string
.split(name
, ':', 1)
579 (prefix
, localName
) = (None, name
)
580 if self
.current_nodes
:
581 src
= self
.current_nodes
[-1]
584 namespaceURI
= self
.model
.prefix_to_namespace(src
, prefix
)
587 if node
.nodeType
!= Node
.ELEMENT_NODE
:
589 if node
.localName
== localName
and node
.namespaceURI
== namespaceURI
:
591 map(add
, node
.childNodes
)
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:])
605 assert not self
.op_in_progress
or (self
.op_in_progress
.action
[1] == pattern
)
607 code
= self
.op_in_progress
.cached_code
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
)
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
628 def select_children(self
):
630 for n
in self
.current_nodes
:
631 new
.extend(n
.childNodes
)
634 def select_region(self
, path
, ns
= None):
635 if len(self
.current_nodes
) == 0:
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
)
644 if not self
.has_ancestor(x
, self
.root
):
645 print "[ skipping search result above root ]"
650 print "*** Search for '%s' in select_region failed" % path
651 print " (namespaces were '%s')" % ns
653 if node
.parentNode
!= src
.parentNode
:
654 print "Nodes must have same parent!"
658 for n
in src
.parentNode
.childNodes
:
660 if n
is src
or n
is node
:
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:
670 node
= self
.get_current()
671 if node
.nodeType
== Node
.TEXT_NODE
:
674 if self
.current_attrib
:
675 current
= self
.current_attrib
.value
677 current
= node
.nodeName
678 pattern
= pattern
.replace('@CURRENT@', current
)
679 #print "Searching for", pattern
682 def do_search(self
, pattern
, ns
= None, toggle
= FALSE
):
683 if len(self
.current_nodes
) == 0:
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)
691 code
= self
.op_in_progress
.cached_code
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
)
704 if not self
.has_ancestor(x
, self
.root
):
705 print "[ skipping search result above root ]"
709 #if self.node_to_line[x] > self.current_line:
713 #print "*** Search for '%s' failed" % pattern
714 #print " (namespaces were '%s')" % ns
717 new
= self
.current_nodes
[:]
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
736 new
, num
= re
.subn(replace
, with
, a
.value
)
739 a
= self
.model
.set_attrib(nodes
[0], a
.name
, new
)
740 self
.move_to(nodes
[0], a
)
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
:
751 self
.model
.set_data(n
, new
)
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
:
759 new_ns
, x
= self
.model
.split_qname(n
, new
)
760 final
.append(self
.model
.set_name(n
, new_ns
, new
))
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:
772 src
= self
.current_nodes
[-1]
775 code
= self
.op_in_progress
.cached_code
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()
798 self
.model
.replace_node(node
, new
)
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
808 self
.set_exec((op
, exit
))
809 if not self
.single_step
:
811 self
.status_changed()
813 print "(nothing to resume)"
816 def ask_cb(result
, self
= self
):
820 self
.clipboard
= self
.model
.doc
.createTextNode(result
)
823 from GetArg
import GetArg
824 box
= GetArg('Input:', ask_cb
, [q
], destroy_return
= 1)
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
)
833 nlist
.appendChild(self
.python_to_node(x
))
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
))
844 self
.clipboard
= self
.model
.doc
.createDocumentFragment()
845 for n
in self
.current_nodes
:
846 c
= n
.cloneNode(deep
)
848 self
.clipboard
.appendChild(c
)
850 #print "Clip now", self.clipboard
852 def shallow_yank(self
):
855 def delete_shallow(self
):
856 nodes
= self
.current_nodes
[:]
859 if self
.root
in nodes
:
864 self
.model
.delete_shallow(n
)
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
[:]
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)
881 if self
.root
in nodes
:
884 new
= [x
.parentNode
for x
in nodes
]
886 self
.model
.delete_nodes(nodes
)
889 nodes
= self
.current_nodes
[:]
891 self
.model
.unlock(self
.root
)
895 self
.model
.lock(self
.root
)
896 self
.move_to(filter(lambda x
: self
.has_ancestor(x
, self
.root
), nodes
))
899 nodes
= self
.current_nodes
[:]
901 self
.model
.unlock(self
.root
)
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
915 self
.set_exec((op
, exit
))
917 print "No operation to return to!"
918 c
= self
.call_on_done
920 self
.call_on_done
= None
923 self
.jump_to_innermost_failure()
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'))
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
944 done
= self
.default_done
946 def cbor(self
= self
, op
= self
.op_in_progress
, done
= done
,
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
960 #print "Resume op '%s' (%s)" % (op.program.name, op)
965 self
.callback_on_return
= cbor
967 if self
.single_step
== 2:
970 if self
.op_in_progress
:
971 self
.push_stack(self
.op_in_progress
)
973 self
.play_block(prog
.code
)
975 self
.status_changed()
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
:
984 stack_block
, nodes_list
, restore
, old_mark
= self
.foreach_stack
[-1]
985 if stack_block
!= block
:
986 self
.reset_foreach_stack()
988 raise Exception("Reached the end of a block we never entered")
994 restore
.extend(self
.current_nodes
)
995 if continuing
== 'fail':
996 print "Error in block; exiting early in program", block
.get_program()
998 [self
.model
.unlock(x
) for x
in old_mark
]
999 self
.foreach_stack
.pop()
1002 while nodes_list
and nodes_list
[0].parentNode
== None:
1003 print "Skipping deleted node", nodes_list
[0]
1007 self
.foreach_stack
.pop()
1010 nodes
= filter(lambda x
: self
.has_ancestor(x
, self
.root
), restore
)
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]
1021 print "[ %d after this ]" % len(nodes_list
),
1026 self
.set_exec((block
.start
, 'next'))
1029 def play_block(self
, block
):
1030 assert isinstance(block
, Block
)
1031 #print "Enter Block!"
1033 list = self
.current_nodes
[:]
1035 list = [self
.current_nodes
[:]] # List of one item, containing everything
1038 marks
= self
.marked
.copy()
1039 [self
.model
.lock(x
) for x
in marks
]
1042 self
.foreach_stack
.append((block
, list, [], marks
))
1045 if not self
.start_block_iteration(block
):
1046 # No nodes selected...
1047 if not block
.is_toplevel():
1048 self
.set_exec((block
, 'next'))
1051 self
.set_exec((block
.start
, 'next'))
1055 assert self
.op_in_progress
1056 oip
= self
.op_in_progress
1058 self
.play_block(oip
)
1059 if not self
.single_step
:
1064 if self
.op_in_progress
:
1065 raise Exception("Operation in progress")
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
)
1074 self
.in_callback
= 1
1078 self
.in_callback
= 0
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())
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
1099 self
.set_exec((node
, 'fail'))
1100 self
.status_changed()
1102 if self
.op_in_progress
or self
.single_step
:
1103 self
.status_changed()
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
):
1121 nodes
= self
.current_nodes
[:]
1123 print "map of nothing: skipping..."
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."
1129 print "[ %d to go ]" % len(nodes
),
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]
1138 return self
.default_done(exit
)
1139 self
.move_to(nodes
[0])
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!")
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
)
1157 prog
= prog
.subprograms
[comps
[0]]
1161 def change_node(self
, new_data
):
1162 nodes
= self
.current_nodes
1166 if nodes
[0].nodeType
== Node
.ELEMENT_NODE
:
1167 # Slow, so do this here, even if vaguely incorrect...
1168 assert ' ' not in new_data
1170 (prefix
, localName
) = string
.split(new_data
, ':', 1)
1172 (prefix
, localName
) = (None, new_data
)
1173 namespaceURI
= self
.model
.prefix_to_namespace(nodes
[0], prefix
)
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
)
1182 new
= self
.model
.set_name(node
, namespaceURI
, new_data
)
1187 self
.model
.set_data(node
, new_data
)
1190 def add_node(self
, where
, data
):
1191 cur
= self
.get_current()
1194 (prefix
, localName
) = string
.split(data
, ':', 1)
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
)
1203 new
= self
.model
.doc
.createTextNode(data
)
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
)
1213 self
.model
.insert(cur
, 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."""
1223 if node
.nodeType
== Node
.TEXT_NODE
:
1224 uri
= node
.nodeValue
1228 elif node
.hasAttributeNS(None, 'uri'):
1229 uri
= node
.getAttributeNS(None, 'uri')
1231 for attr
in node
.attributes
.keys():
1232 a_node
= node
.attributes
[attr
]
1233 if a_node
.namespaceURI
== XMLNS_NAMESPACE
:
1236 if uri
.find('//') != -1 or uri
.find('.htm') != -1:
1239 print "Can't suck", node
, "(no uri attribute found)"
1241 if uri
.find('//') == -1:
1242 base
= self
.model
.get_base_uri(node
)
1244 base
= os
.path
.dirname(base
)
1245 print "Relative URI..."
1247 print "Base URI is:", base
, "add", uri
1248 if uri
.startswith('/'):
1249 uri
= urlparse
.urljoin(base
, uri
)
1251 uri
= base
+ '/' + uri
1252 print "Final URI is:", uri
1255 #print "Warning: Can't find 'uri' attribute!"
1257 uri
= 'file://' + uri
1258 request
= urllib2
.Request(uri
)
1262 def http_post(self
):
1263 node
= self
.get_current()
1264 attrs
= node
.attributes
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
)
1273 post
.append((str(name
), value
))
1275 request
.add_data(urllib
.urlencode(post
))
1276 node
= self
.suck_node(node
, request
)
1280 def suck(self
, md5_only
= 0):
1281 nodes
= self
.current_nodes
[:]
1282 attrib
= self
.current_attrib
1286 request
= self
.request_from_node(x
, attrib
)
1288 new
= self
.suck_node(x
, request
, md5_only
= md5_only
)
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
1300 uri
= request
.get_full_url()
1301 self
.set_status("Fetching %s (connecting)..." % uri
)
1303 if uri
.startswith('file:///'):
1304 assert not request
.has_data()
1305 stream
= open(uri
[7:])
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
)
1318 root
= support
.parse_data(data
, uri
)
1322 new
= self
.model
.import_with_ns(root
.documentElement
)
1323 new
.setAttributeNS(None, 'uri', uri
)
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
)
1333 self
.model
.replace_node(node
, new
)
1334 self
.model
.strip_space(new
)
1340 def put_before(self
):
1341 node
= self
.get_current()
1342 if self
.clipboard
== None:
1344 new
= self
.clipboard
.cloneNode(1)
1346 self
.model
.insert_before(node
, new
)
1350 def put_after(self
):
1351 node
= self
.get_current()
1352 if self
.clipboard
== None:
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!"
1362 if self
.current_attrib
:
1363 if self
.clipboard
.nodeType
== Node
.DOCUMENT_FRAGMENT_NODE
:
1364 value
= self
.clipboard
.childNodes
[0].data
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
)
1372 if self
.clipboard
.nodeType
== Node
.DOCUMENT_FRAGMENT_NODE
:
1373 if len(self
.clipboard
.childNodes
) != 1:
1374 print "Multiple nodes in clipboard!"
1376 new
= self
.clipboard
.childNodes
[0].cloneNode(1)
1378 new
= self
.clipboard
.cloneNode(1)
1379 if new
.nodeType
!= Node
.ELEMENT_NODE
:
1383 if node
== self
.root
:
1384 self
.model
.unlock(self
.root
)
1386 self
.model
.replace_node(self
.root
, new
)
1389 self
.model
.lock(self
.root
)
1391 self
.model
.replace_node(node
, new
)
1394 type, val
, tb
= sys
.exc_info()
1395 traceback
.print_exception(type, val
, tb
)
1396 print "Replace failed!"
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:
1406 new
= self
.clipboard
.cloneNode(1)
1407 if new
.nodeType
== Node
.DOCUMENT_FRAGMENT_NODE
:
1409 for n
in new
.childNodes
:
1415 self
.model
.insert_before(None, new
, parent
= node
)
1417 self
.model
.insert(node
, new
, index
= 0)
1423 def yank_value(self
):
1424 if not self
.current_attrib
:
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):
1432 print "yank_attribs: DEPRECATED -- use Yank instead!"
1433 self
.clipboard
= self
.model
.doc
.createDocumentFragment()
1435 if not self
.get_current().hasAttributeNS(None, name
):
1437 attribs
= [self
.get_current().getAttributeNodeNS(None, name
)]
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).
1447 diff
= cmp(a
.name
, b
.name
)
1449 diff
= cmp(a
.namespaceURI
, b
.namespaceURI
)
1452 attribs
.sort(by_name
)
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
1463 attribs
= [self
.clipboard
]
1467 new
.append((a
.nodeName
, a
.childNodes
[0].data
))
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
)
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)
1485 raise Beep(may_record
= 1)
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
)
1499 raise Beep(may_record
= 1)
1501 def attribute(self
, namespace
= None, attrib
= ''):
1502 node
= self
.get_current()
1508 if attrib
== 'xmlns':
1510 #print "(ns, attrib)", `namespace`, attrib
1512 a
= node
.attributes
.get((namespace
, attrib
), None)
1515 self
.move_to(node
, a
)
1517 print "No such attribute"
1518 print "Looking for %s in %s" % ((namespace
, attrib
), node
.attributes
)
1521 def set_attrib(self
, value
):
1522 a
= self
.current_attrib
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
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
)
1547 self
.model
.unlock(self
.root
)
1549 self
.model
.replace_node(self
.root
, new
)
1550 self
.model
.lock(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()
1570 for n
in node
.parentNode
.childNodes
:
1575 self
.move_to(select
)
1577 def select_marked_region(self
, attr
= "unused"):
1579 if len(self
.marked
) != 1:
1580 print "Must be exactly one marked node!"
1582 if len(self
.current_nodes
) != 1:
1583 print "Must be exactly one selected node!"
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]:
1598 for x
in a
.parentNode
.childNodes
:
1605 self
.move_to(select
)
1607 print "One node is a parent of the other!"
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
[:]
1622 if node
.nodeType
!= Node
.ELEMENT_NODE
:
1624 if node
.hasAttributeNS(None, 'hidden'):
1628 self
.model
.set_attrib(node
, 'hidden', new
, with_update
= 0)
1629 self
.model
.update_all(self
.root
)
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
1637 for p
, u
in self
.model
.namespaces
.uri
.iteritems():
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)")
1646 if env
.localName
!= 'Envelope':
1647 alert("Not a SOAP-ENV:Envelope (bad local name)")
1650 if len(env
.childNodes
) != 2:
1651 alert("SOAP-ENV:Envelope must have one header and one body")
1654 kids
= elements(env
)
1658 if head
.namespaceURI
!= SOAPENV_NS
or \
1659 head
.localName
!= 'Head':
1660 alert("First child must be a SOAP-ENV:Head element")
1663 if body
.namespaceURI
!= SOAPENV_NS
or \
1664 body
.localName
!= 'Body':
1665 alert("Second child must be a SOAP-ENV:Body element")
1669 for header
in elements(head
):
1670 if header
.namespaceURI
== DOME_NS
and header
.localName
== 'soap-forward-to':
1673 print header
.namespaceURI
1674 print header
.localName
1677 alert("Head must contain a dome:soap-forward-to element")
1680 dest
= sft
.childNodes
[0].data
1681 parent
= sft
.parentNode
1682 if len(elements(parent
)) == 1:
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!")
1695 PrettyPrint(copy
, stream
= stream
)
1696 message
= stream
.data
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', '')
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()
1719 self
.model
.replace_node(old
, new
)
1722 def program_changed(self
, changed_op
):
1723 print "Check points..."
1725 (op
, exit
) = self
.rec_point
1727 print "Lost rec_point"
1728 self
.rec_point
= None
1730 (op
, exit
) = self
.exec_point
1732 print "Lost exec_point"
1733 self
.exec_point
= None
1734 for l
in self
.lists
:
1736 self
.status_changed()
1738 def prog_tree_changed(self
):
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
)
1752 print "*** WARNING: Saving from a chroot!"
1754 data
= doc
.importNode(model
.doc
.documentElement
, 1)
1755 node
.appendChild(data
)
1759 def blank_all(self
):
1760 doc
= implementation
.createDocument(None, 'root', None)
1762 self
.clipboard
= self
.model
.doc
.createElementNS(None, 'root')
1765 def mark_switch(self
):
1766 new
= self
.marked
.keys()
1767 self
.set_marked(self
.current_nodes
)
1770 def set_marked(self
, new
):
1771 update
= self
.marked
1772 for x
in self
.marked
.keys():
1773 self
.model
.unlock(x
)
1777 self
.marked
[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
):
1789 def normalise(self
):
1790 self
.model
.normalise(self
.get_current())
1792 def remove_ns(self
):
1793 print "remove_ns: Disabled"
1795 nodes
= self
.current_nodes
[:]
1797 nodes
= map(self
.model
.remove_ns
, nodes
)
1800 def convert_to(self
, fn
):
1801 nodes
= self
.current_nodes
[:]
1803 nodes
= map(fn
, 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
)
1812 def write(self
, str):