class REXML::QuickPath

Constants

EMPTY_HASH

A base Hash object to be used when initializing a default empty namespaces set.

OPERAND_

Public Class Methods

attribute( name ) click to toggle source
# File lib/rexml/quickpath.rb, line 207
def QuickPath::attribute( name )
  Functions.node.attributes[name] if Functions.node.kind_of? Element
end
axe( elements, axe_name, rest ) click to toggle source
# File lib/rexml/quickpath.rb, line 107
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
  matches.uniq
end
each(element, path, namespaces=EMPTY_HASH, &block) click to toggle source
# File 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
filter(elements, path) click to toggle source

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 50
def QuickPath::filter elements, path
  return elements if path.nil? or path == '' or elements.size == 0
  case path
  when /^\/\//u                                                                                     # Descendant
    axe( elements, "descendant-or-self", $' )
  when /^\/?\b(\w[-\w]*)\b::/u                                                      # Axe
    axe( elements, $1, $' )
  when /^\/(?=\b([:!\w][-\.\w]*:)?[-!\*\.\w]*\b([^:(]|$)|\*)/u      # Child
    rest = $'
    results = []
    elements.each do |element|
      results |= filter( element.to_a, rest )
    end
    results
  when /^\/?(\w[-\w]*)\(/u                                                  # / Function
    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
    filter( elements, rest )
  when /^\/\[/u
    matches = []
    elements.each do |element|
      matches |= predicate( element.to_a, path[1..-1] ) if element.kind_of? Element
    end
    matches
  when /^\[/u                                                                                               # Predicate
    predicate( elements, path )
  when /^\/?\.\.\./u                                                                                # Ancestor
    axe( elements, "ancestor", $' )
  when /^\/?\.\./u                                                                                  # Parent
    filter( elements.collect{|e|e.parent}, $' )
  when /^\/?\./u                                                                                            # Self
    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
    results
  else
    []
  end
end
first(element, path, namespaces=EMPTY_HASH) click to toggle source
# File lib/rexml/quickpath.rb, line 14
def QuickPath::first element, path, namespaces=EMPTY_HASH
  match(element, path, namespaces)[0]
end
function( elements, fname, rest ) click to toggle source
# File lib/rexml/quickpath.rb, line 223
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
  results
end
match(element, path, namespaces=EMPTY_HASH) click to toggle source
# File 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
  results
end
method_missing( id, *args ) click to toggle source
# File lib/rexml/quickpath.rb, line 215
def QuickPath::method_missing( id, *args )
  begin
    Functions.send( id.id2name, *args )
  rescue Exception
    raise "METHOD: #{id.id2name}(#{args.join ', '})\n#{$!.message}"
  end
end
name() click to toggle source
# File lib/rexml/quickpath.rb, line 211
def QuickPath::name()
  Functions.node.name if Functions.node.kind_of? Element
end
parse_args( element, string ) click to toggle source
# File lib/rexml/quickpath.rb, line 241
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
predicate( elements, path ) click to toggle source

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 162
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
  filter( results, rest )
end