method
expr
Related methods
- Class methods (1)
- new
- Instance methods (35)
- []=
- child
- compare
- descendant
- descendant_or_self (<= v2_5_5)
- descendant_recursive
- document_order (<= v2_5_5)
- d_o_s (<= v2_5_5)
- enter
- equality_relational_compare
- evaluate_predicate
- expr
- filter_nodeset
- first
- following
- following_node_of
- get_first
- get_namespace
- leave
- match
- namespaces=
- next_sibling_node
- node_test
- norm
- parse
- preceding
- preceding_node_of
- predicate
- recurse (<= v2_5_5)
- sort
- step
- strict?
- trace
- unnode
- variables=
= private
= protected
expr( path_stack, nodeset, context=nil )
private
Expr takes a stack of path elements and a set of nodes (either a Parent or an Array and returns an Array of matching nodes
Show source
# File lib/rexml/xpath_parser.rb, line 164 def expr( path_stack, nodeset, context=nil ) # enter(:expr, path_stack, nodeset) return nodeset if path_stack.length == 0 || nodeset.length == 0 while path_stack.length > 0 # trace(:while, path_stack, nodeset) if nodeset.length == 0 path_stack.clear return [] end op = path_stack.shift case op when :document first_raw_node = nodeset.first.raw_node nodeset = [XPathNode.new(first_raw_node.root_node, position: 1)] when :self nodeset = step(path_stack) do [nodeset] end when :child nodeset = step(path_stack) do child(nodeset) end when :literal # trace(:literal, path_stack, nodeset) return path_stack.shift when :attribute nodeset = step(path_stack, any_type: :attribute) do nodesets = [] nodeset.each do |node| raw_node = node.raw_node next unless raw_node.node_type == :element attributes = raw_node.attributes next if attributes.empty? nodesets << attributes.each_attribute.collect.with_index do |attribute, i| XPathNode.new(attribute, position: i + 1) end end nodesets end when :namespace pre_defined_namespaces = { "xml" => "http://www.w3.org/XML/1998/namespace", } nodeset = step(path_stack, any_type: :namespace) do nodesets = [] nodeset.each do |node| raw_node = node.raw_node case raw_node.node_type when :element if @namespaces nodesets << pre_defined_namespaces.merge(@namespaces) else nodesets << pre_defined_namespaces.merge(raw_node.namespaces) end when :attribute if @namespaces nodesets << pre_defined_namespaces.merge(@namespaces) else nodesets << pre_defined_namespaces.merge(raw_node.element.namespaces) end end end nodesets end when :parent nodeset = step(path_stack) do nodesets = [] nodeset.each do |node| raw_node = node.raw_node if raw_node.node_type == :attribute parent = raw_node.element else parent = raw_node.parent end nodesets << [XPathNode.new(parent, position: 1)] if parent end nodesets end when :ancestor nodeset = step(path_stack) do nodesets = [] # new_nodes = {} nodeset.each do |node| raw_node = node.raw_node new_nodeset = [] while raw_node.parent raw_node = raw_node.parent # next if new_nodes.key?(node) new_nodeset << XPathNode.new(raw_node, position: new_nodeset.size + 1) # new_nodes[node] = true end nodesets << new_nodeset unless new_nodeset.empty? end nodesets end when :ancestor_or_self nodeset = step(path_stack) do nodesets = [] # new_nodes = {} nodeset.each do |node| raw_node = node.raw_node next unless raw_node.node_type == :element new_nodeset = [XPathNode.new(raw_node, position: 1)] # new_nodes[node] = true while raw_node.parent raw_node = raw_node.parent # next if new_nodes.key?(node) new_nodeset << XPathNode.new(raw_node, position: new_nodeset.size + 1) # new_nodes[node] = true end nodesets << new_nodeset unless new_nodeset.empty? end nodesets end when :descendant_or_self nodeset = step(path_stack) do descendant(nodeset, true) end when :descendant nodeset = step(path_stack) do descendant(nodeset, false) end when :following_sibling nodeset = step(path_stack) do nodesets = [] nodeset.each do |node| raw_node = node.raw_node next unless raw_node.respond_to?(:parent) next if raw_node.parent.nil? all_siblings = raw_node.parent.children current_index = all_siblings.index(raw_node) following_siblings = all_siblings[(current_index + 1)..-1] next if following_siblings.empty? nodesets << following_siblings.collect.with_index do |sibling, i| XPathNode.new(sibling, position: i + 1) end end nodesets end when :preceding_sibling nodeset = step(path_stack, order: :reverse) do nodesets = [] nodeset.each do |node| raw_node = node.raw_node next unless raw_node.respond_to?(:parent) next if raw_node.parent.nil? all_siblings = raw_node.parent.children current_index = all_siblings.index(raw_node) preceding_siblings = all_siblings[0, current_index].reverse next if preceding_siblings.empty? nodesets << preceding_siblings.collect.with_index do |sibling, i| XPathNode.new(sibling, position: i + 1) end end nodesets end when :preceding nodeset = step(path_stack, order: :reverse) do unnode(nodeset) do |node| preceding(node) end end when :following nodeset = step(path_stack) do unnode(nodeset) do |node| following(node) end end when :variable var_name = path_stack.shift return [@variables[var_name]] # :and, :or, :eq, :neq, :lt, :lteq, :gt, :gteq # TODO: Special case for :or and :and -- not evaluate the right # operand if the left alone determines result (i.e. is true for # :or and false for :and). when :eq, :neq, :lt, :lteq, :gt, :gteq, :or left = expr( path_stack.shift, nodeset.dup, context ) right = expr( path_stack.shift, nodeset.dup, context ) res = equality_relational_compare( left, op, right ) # trace(op, left, right, res) return res when :and left = expr( path_stack.shift, nodeset.dup, context ) return [] unless left if left.respond_to?(:inject) and !left.inject(false) {|a,b| a | b} return [] end right = expr( path_stack.shift, nodeset.dup, context ) res = equality_relational_compare( left, op, right ) return res when :div, :mod, :mult, :plus, :minus left = expr(path_stack.shift, nodeset, context) right = expr(path_stack.shift, nodeset, context) left = unnode(left) if left.is_a?(Array) right = unnode(right) if right.is_a?(Array) left = Functions::number(left) right = Functions::number(right) case op when :div return left / right when :mod return left % right when :mult return left * right when :plus return left + right when :minus return left - right else raise "[BUG] Unexpected operator: <#{op.inspect}>" end when :union left = expr( path_stack.shift, nodeset, context ) right = expr( path_stack.shift, nodeset, context ) left = unnode(left) if left.is_a?(Array) right = unnode(right) if right.is_a?(Array) return (left | right) when :neg res = expr( path_stack, nodeset, context ) res = unnode(res) if res.is_a?(Array) return -Functions.number(res) when :not when :function func_name = path_stack.shift.tr('-','_') arguments = path_stack.shift subcontext = context ? nil : { :size => nodeset.size } res = [] cont = context nodeset.each_with_index do |node, i| if subcontext if node.is_a?(XPathNode) subcontext[:node] = node.raw_node subcontext[:index] = node.position else subcontext[:node] = node subcontext[:index] = i end cont = subcontext end arg_clone = arguments.dclone args = arg_clone.collect do |arg| result = expr( arg, [node], cont ) result = unnode(result) if result.is_a?(Array) result end Functions.context = cont res << Functions.send( func_name, *args ) end return res else raise "[BUG] Unexpected path: <#{op.inspect}>: <#{path_stack.inspect}>" end end # while return nodeset # ensure # leave(:expr, path_stack, nodeset) end