class REXML::QuickPath
Constants
- EMPTY_HASH
A base Hash object to be used when initializing a default empty namespaces set.
- OPERAND_
Public Class Methods
# File lib/rexml/quickpath.rb, line 205 def QuickPath::attribute( name ) return Functions.node.attributes[name] if Functions.node.kind_of? Element end
# File lib/rexml/quickpath.rb, line 105 def QuickPath::axe( elements, axe_name, rest ) matches = [] matches = filter( elements.dup, rest ) if axe_name =~ /-or-self$/u case axe_name when /^descendant/u elements.each do |element| matches |= filter( element.to_a, "descendant-or-self::#{rest}" ) if element.kind_of? Element end when /^ancestor/u elements.each do |element| while element.parent matches << element.parent element = element.parent end end matches = filter( matches, rest ) when "self" matches = filter( elements, rest ) when "child" elements.each do |element| matches |= filter( element.to_a, rest ) if element.kind_of? Element end when "attribute" elements.each do |element| matches << element.attributes[ rest ] if element.kind_of? Element end when "parent" matches = filter(elements.collect{|element| element.parent}.uniq, rest) when "following-sibling" matches = filter(elements.collect{|element| element.next_sibling}.uniq, rest) when "previous-sibling" matches = filter(elements.collect{|element| element.previous_sibling}.uniq, rest ) end return matches.uniq end
# File lib/rexml/quickpath.rb, line 17 def QuickPath::each element, path, namespaces=EMPTY_HASH, &block path = "*" unless path match(element, path, namespaces).each( &block ) 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 49 def QuickPath::filter elements, path return elements if path.nil? or path == '' or elements.size == 0 case path when /^\/\//u # Descendant return axe( elements, "descendant-or-self", $' ) when /^\/?\b(\w[-\w]*)\b::/u # Axe return axe( elements, $1, $' ) when /^\/(?=\b([:!\w][-\.\w]*:)?[-!\*\.\w]*\b([^:(]|$)|\*)/u # Child rest = $' results = [] elements.each do |element| results |= filter( element.to_a, rest ) end return results when /^\/?(\w[-\w]*)\(/u # / Function return function( elements, $1, $' ) when Namespace::NAMESPLIT # Element name name = $2 ns = $1 rest = $' elements.delete_if do |element| !(element.kind_of? Element and (element.expanded_name == name or (element.name == name and element.namespace == Functions.namespace_context[ns]))) end return filter( elements, rest ) when /^\/\[/u matches = [] elements.each do |element| matches |= predicate( element.to_a, path[1..-1] ) if element.kind_of? Element end return matches when /^\[/u # Predicate return predicate( elements, path ) when /^\/?\.\.\./u # Ancestor return axe( elements, "ancestor", $' ) when /^\/?\.\./u # Parent return filter( elements.collect{|e|e.parent}, $' ) when /^\/?\./u # Self return filter( elements, $' ) when /^\*/u # Any results = [] elements.each do |element| results |= filter( [element], $' ) if element.kind_of? Element #if element.kind_of? Element # children = element.to_a # children.delete_if { |child| !child.kind_of?(Element) } # results |= filter( children, $' ) #end end return results end return [] end
# File lib/rexml/quickpath.rb, line 13 def QuickPath::first element, path, namespaces=EMPTY_HASH match(element, path, namespaces)[0] end
# File lib/rexml/quickpath.rb, line 221 def QuickPath::function( elements, fname, rest ) args = parse_args( elements, rest ) Functions.pair = [0, elements.size] results = [] elements.each do |element| Functions.pair[0] += 1 Functions.node = element res = Functions.send( fname, *args ) case res when true results << element when Integer results << element if Functions.pair[0] == res end end return results end
# File lib/rexml/quickpath.rb, line 22 def QuickPath::match element, path, namespaces=EMPTY_HASH raise "nil is not a valid xpath" unless path results = nil Functions::namespace_context = namespaces case path when /^\/([^\/]|$)/u # match on root path = path[1..-1] return [element.root.parent] if path == '' results = filter([element.root], path) when /^[-\w]*::/u results = filter([element], path) when /^\*/u results = filter(element.to_a, path) when /^[\[!\w:]/u # match on child children = element.to_a results = filter(children, path) else results = filter([element], path) end return results end
# File lib/rexml/quickpath.rb, line 213 def QuickPath::method_missing( id, *args ) begin Functions.send( id.id2name, *args ) rescue Exception raise "METHOD: #{id.id2name}(#{args.join ', '})\n#{$!.message}" end end
# File lib/rexml/quickpath.rb, line 209 def QuickPath::name() return Functions.node.name if Functions.node.kind_of? Element end
# File lib/rexml/quickpath.rb, line 239 def QuickPath::parse_args( element, string ) # /.*?(?:\)|,)/ arguments = [] buffer = "" while string and string != "" c = string[0] string.sub!(/^./u, "") case c when ?, # if depth = 1, then we start a new argument arguments << evaluate( buffer ) #arguments << evaluate( string[0..count] ) when ?( # start a new method call function( element, buffer, string ) buffer = "" when ?) # close the method call and return arguments return arguments else buffer << c end end "" 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 is equivalent to para.
# File lib/rexml/quickpath.rb, line 160 def QuickPath::predicate( elements, path ) ind = 1 bcount = 1 while bcount > 0 bcount += 1 if path[ind] == ?[ bcount -= 1 if path[ind] == ?] ind += 1 end ind -= 1 predicate = path[1..ind-1] rest = path[ind+1..-1] # have to change 'a [=<>] b [=<>] c' into 'a [=<>] b and b [=<>] c' # predicate.gsub!( /#{OPERAND_}\s*([<>=])\s*#{OPERAND_}\s*([<>=])\s*#{OPERAND_}/u, '\1 \2 \3 and \3 \4 \5' ) # Let's do some Ruby trickery to avoid some work: predicate.gsub!( /&/u, "&&" ) predicate.gsub!( /=/u, "==" ) predicate.gsub!( /@(\w[-\w.]*)/u, 'attribute("\1")' ) predicate.gsub!( /\bmod\b/u, "%" ) predicate.gsub!( /\b(\w[-\w.]*\()/u ) { fname = $1 fname.gsub( /-/u, "_" ) } Functions.pair = [ 0, elements.size ] results = [] elements.each do |element| Functions.pair[0] += 1 Functions.node = element res = eval( predicate ) case res when true results << element when Integer results << element if Functions.pair[0] == res when String results << element end end return filter( results, rest ) end