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 206
def QuickPath::attribute( name )
  return 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 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
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
    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
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 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 Fixnum
      results << element if Functions.pair[0] == res
    end
  end
  return 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
  return results
end
method_missing( id, *args ) click to toggle source
# File 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
name() click to toggle source
# File lib/rexml/quickpath.rb, line 210
def QuickPath::name()
  return Functions.node.name if Functions.node.kind_of? Element
end
parse_args( element, string ) click to toggle source
# File 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
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 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 Fixnum
      results << element if Functions.pair[0] == res
    when String
      results << element
    end
  end
  return filter( results, rest )
end