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 rexml-3.2.8/lib/rexml/quickpath.rb, line 206 def QuickPath::attribute( name ) return Functions.node.attributes[name] if Functions.node.kind_of? Element end
# File rexml-3.2.8/lib/rexml/quickpath.rb, line 106 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 rexml-3.2.8/lib/rexml/quickpath.rb, line 18 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 rexml-3.2.8/lib/rexml/quickpath.rb, line 50 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 rexml-3.2.8/lib/rexml/quickpath.rb, line 14 def QuickPath::first element, path, namespaces=EMPTY_HASH match(element, path, namespaces)[0] end
# File rexml-3.2.8/lib/rexml/quickpath.rb, line 222 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 rexml-3.2.8/lib/rexml/quickpath.rb, line 23 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 rexml-3.2.8/lib/rexml/quickpath.rb, line 214 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 rexml-3.2.8/lib/rexml/quickpath.rb, line 210 def QuickPath::name() return Functions.node.name if Functions.node.kind_of? Element end
# File rexml-3.2.8/lib/rexml/quickpath.rb, line 240 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 rexml-3.2.8/lib/rexml/quickpath.rb, line 161 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