| Class | REXML::QuickPath |
| In: |
lib/rexml/quickpath.rb
|
| Parent: | Object |
| EMPTY_HASH | = | {} |
# File lib/rexml/quickpath.rb, line 206
206: def QuickPath::attribute( name )
207: return Functions.node.attributes[name] if Functions.node.kind_of? Element
208: end
# File lib/rexml/quickpath.rb, line 106
106: def QuickPath::axe( elements, axe_name, rest )
107: matches = []
108: matches = filter( elements.dup, rest ) if axe_name =~ /-or-self$/u
109: case axe_name
110: when /^descendant/u
111: elements.each do |element|
112: matches |= filter( element.to_a, "descendant-or-self::#{rest}" ) if element.kind_of? Element
113: end
114: when /^ancestor/u
115: elements.each do |element|
116: while element.parent
117: matches << element.parent
118: element = element.parent
119: end
120: end
121: matches = filter( matches, rest )
122: when "self"
123: matches = filter( elements, rest )
124: when "child"
125: elements.each do |element|
126: matches |= filter( element.to_a, rest ) if element.kind_of? Element
127: end
128: when "attribute"
129: elements.each do |element|
130: matches << element.attributes[ rest ] if element.kind_of? Element
131: end
132: when "parent"
133: matches = filter(elements.collect{|element| element.parent}.uniq, rest)
134: when "following-sibling"
135: matches = filter(elements.collect{|element| element.next_sibling}.uniq,
136: rest)
137: when "previous-sibling"
138: matches = filter(elements.collect{|element|
139: element.previous_sibling}.uniq, rest )
140: end
141: return matches.uniq
142: end
# File lib/rexml/quickpath.rb, line 15
15: def QuickPath::each element, path, namespaces=EMPTY_HASH, &block
16: path = "*" unless path
17: match(element, path, namespaces).each( &block )
18: end
Given an array of nodes it filters the array based on the path. The result is that when this method returns, the array will contain elements which match the path
# File lib/rexml/quickpath.rb, line 48
48: def QuickPath::filter elements, path
49: return elements if path.nil? or path == '' or elements.size == 0
50: case path
51: when /^\/\//u # Descendant
52: return axe( elements, "descendant-or-self", $' )
53: when /^\/?\b(\w[-\w]*)\b::/u # Axe
54: axe_name = $1
55: rest = $'
56: return axe( elements, $1, $' )
57: when /^\/(?=\b([:!\w][-\.\w]*:)?[-!\*\.\w]*\b([^:(]|$)|\*)/u # Child
58: rest = $'
59: results = []
60: elements.each do |element|
61: results |= filter( element.to_a, rest )
62: end
63: return results
64: when /^\/?(\w[-\w]*)\(/u # / Function
65: return function( elements, $1, $' )
66: when Namespace::NAMESPLIT # Element name
67: name = $2
68: ns = $1
69: rest = $'
70: elements.delete_if do |element|
71: !(element.kind_of? Element and
72: (element.expanded_name == name or
73: (element.name == name and
74: element.namespace == Functions.namespace_context[ns])))
75: end
76: return filter( elements, rest )
77: when /^\/\[/u
78: matches = []
79: elements.each do |element|
80: matches |= predicate( element.to_a, path[1..-1] ) if element.kind_of? Element
81: end
82: return matches
83: when /^\[/u # Predicate
84: return predicate( elements, path )
85: when /^\/?\.\.\./u # Ancestor
86: return axe( elements, "ancestor", $' )
87: when /^\/?\.\./u # Parent
88: return filter( elements.collect{|e|e.parent}, $' )
89: when /^\/?\./u # Self
90: return filter( elements, $' )
91: when /^\*/u # Any
92: results = []
93: elements.each do |element|
94: results |= filter( [element], $' ) if element.kind_of? Element
95: #if element.kind_of? Element
96: # children = element.to_a
97: # children.delete_if { |child| !child.kind_of?(Element) }
98: # results |= filter( children, $' )
99: #end
100: end
101: return results
102: end
103: return []
104: end
# File lib/rexml/quickpath.rb, line 11
11: def QuickPath::first element, path, namespaces=EMPTY_HASH
12: match(element, path, namespaces)[0]
13: end
# File lib/rexml/quickpath.rb, line 222
222: def QuickPath::function( elements, fname, rest )
223: args = parse_args( elements, rest )
224: Functions.pair = [0, elements.size]
225: results = []
226: elements.each do |element|
227: Functions.pair[0] += 1
228: Functions.node = element
229: res = Functions.send( fname, *args )
230: case res
231: when true
232: results << element
233: when Fixnum
234: results << element if Functions.pair[0] == res
235: end
236: end
237: return results
238: end
# File lib/rexml/quickpath.rb, line 20
20: def QuickPath::match element, path, namespaces=EMPTY_HASH
21: raise "nil is not a valid xpath" unless path
22: results = nil
23: Functions::namespace_context = namespaces
24: case path
25: when /^\/([^\/]|$)/u
26: # match on root
27: path = path[1..-1]
28: return [element.root.parent] if path == ''
29: results = filter([element.root], path)
30: when /^[-\w]*::/u
31: results = filter([element], path)
32: when /^\*/u
33: results = filter(element.to_a, path)
34: when /^[\[!\w:]/u
35: # match on child
36: matches = []
37: children = element.to_a
38: results = filter(children, path)
39: else
40: results = filter([element], path)
41: end
42: return results
43: end
# File lib/rexml/quickpath.rb, line 214
214: def QuickPath::method_missing( id, *args )
215: begin
216: Functions.send( id.id2name, *args )
217: rescue Exception
218: raise "METHOD: #{id.id2name}(#{args.join ', '})\n#{$!.message}"
219: end
220: end
# File lib/rexml/quickpath.rb, line 210
210: def QuickPath::name()
211: return Functions.node.name if Functions.node.kind_of? Element
212: end
# File lib/rexml/quickpath.rb, line 240
240: def QuickPath::parse_args( element, string )
241: # /.*?(?:\)|,)/
242: arguments = []
243: buffer = ""
244: while string and string != ""
245: c = string[0]
246: string.sub!(/^./u, "")
247: case c
248: when ?,
249: # if depth = 1, then we start a new argument
250: arguments << evaluate( buffer )
251: #arguments << evaluate( string[0..count] )
252: when ?(
253: # start a new method call
254: function( element, buffer, string )
255: buffer = ""
256: when ?)
257: # close the method call and return arguments
258: return arguments
259: else
260: buffer << c
261: end
262: end
263: ""
264: end
A predicate filters a node-set with respect to an axis to produce a new node-set. For each node in the node-set to be filtered, the PredicateExpr is evaluated with that node as the context node, with the number of nodes in the node-set as the context size, and with the proximity position of the node in the node-set with respect to the axis as the context position; if PredicateExpr evaluates to true for that node, the node is included in the new node-set; otherwise, it is not included.
A PredicateExpr is evaluated by evaluating the Expr and converting the result to a boolean. If the result is a number, the result will be converted to true if the number is equal to the context position and will be converted to false otherwise; if the result is not a number, then the result will be converted as if by a call to the boolean function. Thus a location path para[3] is equivalent to para[position()=3].
# File lib/rexml/quickpath.rb, line 160
160: def QuickPath::predicate( elements, path )
161: ind = 1
162: bcount = 1
163: while bcount > 0
164: bcount += 1 if path[ind] == ?[
165: bcount -= 1 if path[ind] == ?]
166: ind += 1
167: end
168: ind -= 1
169: predicate = path[1..ind-1]
170: rest = path[ind+1..-1]
171:
172: # have to change 'a [=<>] b [=<>] c' into 'a [=<>] b and b [=<>] c'
173: predicate.gsub!( /([^\s(and)(or)<>=]+)\s*([<>=])\s*([^\s(and)(or)<>=]+)\s*([<>=])\s*([^\s(and)(or)<>=]+)/u ) {
174: "#$1 #$2 #$3 and #$3 #$4 #$5"
175: }
176: # Let's do some Ruby trickery to avoid some work:
177: predicate.gsub!( /&/u, "&&" )
178: predicate.gsub!( /=/u, "==" )
179: predicate.gsub!( /@(\w[-\w.]*)/u ) {
180: "attribute(\"#$1\")"
181: }
182: predicate.gsub!( /\bmod\b/u, "%" )
183: predicate.gsub!( /\b(\w[-\w.]*\()/u ) {
184: fname = $1
185: fname.gsub( /-/u, "_" )
186: }
187:
188: Functions.pair = [ 0, elements.size ]
189: results = []
190: elements.each do |element|
191: Functions.pair[0] += 1
192: Functions.node = element
193: res = eval( predicate )
194: case res
195: when true
196: results << element
197: when Fixnum
198: results << element if Functions.pair[0] == res
199: when String
200: results << element
201: end
202: end
203: return filter( results, rest )
204: end