method
expr
expr( path_stack, nodeset, context=nil )
private
Hide source
# File lib/rexml/xpath_parser.rb, line 152 def expr( path_stack, nodeset, context=nil ) node_types = ELEMENTS return nodeset if path_stack.length == 0 || nodeset.length == 0 while path_stack.length > 0 if nodeset.length == 0 path_stack.clear return [] end case (op = path_stack.shift) when :document nodeset = [ nodeset[0].root_node ] when :qname prefix = path_stack.shift name = path_stack.shift nodeset.delete_if do |node| # FIXME: This DOUBLES the time XPath searches take ns = get_namespace( node, prefix ) if node.node_type == :element if node.name == name end end !(node.node_type == :element and node.name == name and node.namespace == ns ) end node_types = ELEMENTS when :any nodeset.delete_if { |node| !node_types.include?(node.node_type) } when :self # This space left intentionally blank when :processing_instruction target = path_stack.shift nodeset.delete_if do |node| (node.node_type != :processing_instruction) or ( target!='' and ( node.target != target ) ) end when :text nodeset.delete_if { |node| node.node_type != :text } when :comment nodeset.delete_if { |node| node.node_type != :comment } when :node # This space left intentionally blank node_types = ALL when :child new_nodeset = [] nt = nil nodeset.each do |node| nt = node.node_type new_nodeset += node.children if nt == :element or nt == :document end nodeset = new_nodeset node_types = ELEMENTS when :literal return path_stack.shift when :attribute new_nodeset = [] case path_stack.shift when :qname prefix = path_stack.shift name = path_stack.shift for element in nodeset if element.node_type == :element attrib = element.attribute( name, get_namespace(element, prefix) ) new_nodeset << attrib if attrib end end when :any for element in nodeset if element.node_type == :element new_nodeset += element.attributes.to_a end end end nodeset = new_nodeset when :parent nodeset = nodeset.collect{|n| n.parent}.compact #nodeset = expr(path_stack.dclone, nodeset.collect{|n| n.parent}.compact) node_types = ELEMENTS when :ancestor new_nodeset = [] nodeset.each do |node| while node.parent node = node.parent new_nodeset << node unless new_nodeset.include? node end end nodeset = new_nodeset node_types = ELEMENTS when :ancestor_or_self new_nodeset = [] nodeset.each do |node| if node.node_type == :element new_nodeset << node while ( node.parent ) node = node.parent new_nodeset << node unless new_nodeset.include? node end end end nodeset = new_nodeset node_types = ELEMENTS when :predicate new_nodeset = [] subcontext = { :size => nodeset.size } pred = path_stack.shift nodeset.each_with_index { |node, index| subcontext[ :node ] = node subcontext[ :index ] = index+1 pc = pred.dclone result = expr( pc, [node], subcontext ) result = result[0] if result.kind_of? Array and result.length == 1 if result.kind_of? Numeric new_nodeset << node if result == (index+1) elsif result.instance_of? Array if result.size > 0 and result.inject(false) {|k,s| s or k} new_nodeset << node if result.size > 0 end else new_nodeset << node if result end } nodeset = new_nodeset predicate = path_stack.shift ns = nodeset.clone result = expr( predicate, ns ) if result.kind_of? Array nodeset = result.zip(ns).collect{|m,n| n if m}.compact else nodeset = result ? nodeset : [] end when :descendant_or_self rv = descendant_or_self( path_stack, nodeset ) path_stack.clear nodeset = rv node_types = ELEMENTS when :descendant results = [] nt = nil nodeset.each do |node| nt = node.node_type results += expr( path_stack.dclone.unshift( :descendant_or_self ), node.children ) if nt == :element or nt == :document end nodeset = results node_types = ELEMENTS when :following_sibling results = [] nodeset.each do |node| next if node.parent.nil? all_siblings = node.parent.children current_index = all_siblings.index( node ) following_siblings = all_siblings[ current_index+1 .. -1 ] results += expr( path_stack.dclone, following_siblings ) end nodeset = results when :preceding_sibling results = [] nodeset.each do |node| next if node.parent.nil? all_siblings = node.parent.children current_index = all_siblings.index( node ) preceding_siblings = all_siblings[ 0, current_index ].reverse results += preceding_siblings end nodeset = results node_types = ELEMENTS when :preceding new_nodeset = [] nodeset.each do |node| new_nodeset += preceding( node ) end nodeset = new_nodeset node_types = ELEMENTS when :following new_nodeset = [] nodeset.each do |node| new_nodeset += following( node ) end nodeset = new_nodeset node_types = ELEMENTS when :namespace new_nodeset = [] prefix = path_stack.shift nodeset.each do |node| if (node.node_type == :element or node.node_type == :attribute) if @namespaces namespaces = @namespaces elsif (node.node_type == :element) namespaces = node.namespaces else namespaces = node.element.namesapces end if (node.namespace == namespaces[prefix]) new_nodeset << node end end end nodeset = new_nodeset 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 ) 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 left = Functions::number(expr(path_stack.shift, nodeset, context)).to_f right = Functions::number(expr(path_stack.shift, nodeset, context)).to_f return (left / right) when :mod left = Functions::number(expr(path_stack.shift, nodeset, context )).to_f right = Functions::number(expr(path_stack.shift, nodeset, context )).to_f return (left % right) when :mult left = Functions::number(expr(path_stack.shift, nodeset, context )).to_f right = Functions::number(expr(path_stack.shift, nodeset, context )).to_f return (left * right) when :plus left = Functions::number(expr(path_stack.shift, nodeset, context )).to_f right = Functions::number(expr(path_stack.shift, nodeset, context )).to_f return (left + right) when :minus left = Functions::number(expr(path_stack.shift, nodeset, context )).to_f right = Functions::number(expr(path_stack.shift, nodeset, context )).to_f return (left - right) when :union left = expr( path_stack.shift, nodeset, context ) right = expr( path_stack.shift, nodeset, context ) return (left | right) when :neg res = expr( path_stack, nodeset, context ) return -(res.to_f) 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 { |n, i| if subcontext subcontext[:node] = n subcontext[:index] = i cont = subcontext end arg_clone = arguments.dclone args = arg_clone.collect { |arg| expr( arg, [n], cont ) } Functions.context = cont res << Functions.send( func_name, *args ) } return res end end # while return nodeset end