1 from xml
.dom
import Node
3 def literal_match(node
):
4 return "[ext:match('%s')]" % node
.nodeValue
6 # Return a string that will match this node in an XPath.
7 # ns is updated with any new namespace required.
8 def match_name(node
, ns
):
9 if node
.nodeType
== Node
.TEXT_NODE
:
11 elif node
.nodeType
== Node
.COMMENT_NODE
:
13 elif node
.nodeType
== Node
.ELEMENT_NODE
:
16 if ns
[x
] == node
.namespaceURI
:
17 return '%s:%s' % (x
, node
.localName
)
21 if not ns
.has_key(key
):
24 ns
[key
] = node
.namespaceURI
25 return '%s:%s' % (key
, node
.localName
)
30 def jump_to_sibling(src
, dst
, ns
):
31 "Return an XPath which, given a context 'src' will move to sibling 'dst'."
32 "Namespace 'ns' may be updated if new names are required"
34 if dst
.nodeType
== Node
.ATTRIBUTE_NODE
:
35 return 'attribute::%s/' % dst
.nodeName
37 # Search forwards for 'dst', counting how many matching nodes we pass.
41 check
= check
.nextSibling
44 if check
.nodeName
== dst
.nodeName
:
47 return 'following-sibling::%s[%d]/' % (match_name(dst
, ns
), count
)
49 # Not found - search backwards for 'dst', counting how many matching nodes we pass.
53 check
= check
.previousSibling
55 raise Exception("Can't get from %s to %s!" % (src
, dst
))
56 if check
.nodeName
== dst
.nodeName
:
58 return 'preceding-sibling::%s[%d]/' % (match_name(dst
, ns
), count
)
61 "Returns a path to the node in the form [root, ... , node]"
63 while node
.parentNode
:
64 node
= node
.parentNode
68 def make_relative_path(src_node
, dst_node
, lit
, ns
):
69 "Return an XPath string which will move us from src to dst."
70 "If 'lit' then the text of the (data) node must match too."
71 "Namespace 'ns' is updated with any required namespaces."
76 if src_node
== dst_node
:
79 src_parents
= path_to(src_node
)
80 dst_parents
= path_to(dst_node
)
82 # Trim off all the common path elements...
83 # Note that this may leave either path empty, if one node is an ancestor of the other.
84 while src_parents
and dst_parents
and src_parents
[0] == dst_parents
[0]:
88 # Now, the initial context node is 'src_node'.
89 # Build the path from here...
92 # We need to go up one level for each element left in src_parents, less one
93 # (so we end up as a child of the lowest common parent, on the src side).
94 # If src is an ancestor of dst then this does nothing.
95 # If dst is an ancestor of src then go up an extra level, because we don't jump
96 # across in the next step.
97 for p
in range(0, len(src_parents
) - 1):
102 # We then jump across to the correct sibling and head back down the tree...
103 # If src is an ancestor of dst or the other way round we do nothing.
104 if src_parents
and dst_parents
:
105 path
+= jump_to_sibling(src_parents
[0], dst_parents
[0], ns
)
108 # dst_parents is now a list of nodes to visit to get to dst.
109 for node
in dst_parents
:
113 while p
.previousSibling
:
114 p
= p
.previousSibling
115 if p
.nodeName
== node
.nodeName
:
118 path
+= 'child::%s[%d]/' % (match_name(node
, ns
), prev
)
122 path
+= literal_match(dst_node
)
123 #print "%s [%s]" % (path, ns)