class REXML::XPathParser
You donโt want to use this class. Really. Use XPath, which is a wrapper for this class. Believe me. You donโt want to poke around in here. There is strange, dark magic at work in this code. Beware. Go back! Go back while you still can!
Constants
- DEBUG
- LITERAL
Public Class Methods
Source
# File lib/rexml/xpath_parser.rb, line 61 def initialize(strict: false) @debug = DEBUG @parser = REXML::Parsers::XPathParser.new @namespaces = nil @variables = {} @functions = FunctionsClass.new @attlist_mappings = nil @document = nil @element_namespaces_cache = {} @node_indexes = nil @nest = 0 @strict = strict end
# File lib/rexml/xpath_parser.rb, line 834 def self.sort(array_of_nodes, node_indexes = nil) return array_of_nodes if array_of_nodes.size <= 1 node_indexes ||= {}.compare_by_identity array_of_nodes.sort_by do |node| if node.node_type == :attribute # An attribute has no place of its own in the child tree, so its key # extends that of the element carrying it. ancestor_indexes(node.element, node_indexes) << ATTRIBUTE_POSITION << attribute_position(node, node_indexes) else ancestor_indexes(node, node_indexes) end end end
Reorders an array of nodes so that they are in document order.
Node sets are built up as unordered sets by the axis scanners, so they have to be put back into order here. A node is keyed on the index it holds under each of its ancestors, outermost first, so that comparing two keys compares them at their first differing ancestor.
node_indexes caches the index of every child and attribute that had to be looked up. Pass the same one to every sort of an evaluation: a sort can happen many times per evaluation (Functions#string sorts once per candidate node), and those sorts usually revisit the same parents. Passing nothing just means the caching lasts for this one call.
Public Instance Methods
Source
# File lib/rexml/xpath_parser.rb, line 105 def []=( variable_name, value ) @variables[variable_name] = coerce_variable(value) end
Source
# File lib/rexml/xpath_parser.rb, line 114 def first( path_stack, node ) return nil if path.size == 0 case path[0] when :document # do nothing first( path[1..-1], node ) when :child for c in node.children r = first( path[1..-1], c ) return r if r end when :qname name = path[2] if node.name == name return node if path.size == 3 first( path[3..-1], node ) else nil end when :descendant_or_self r = first( path[1..-1], node ) return r if r for c in node.children r = first( path, c ) return r if r end when :node first( path[1..-1], node ) when :any first( path[1..-1], node ) else nil end end
Performs a depth-first (document order) XPath search, and returns the first match. This is the fastest, lightest way to return a single result.
FIXME: This method is incomplete!
Source
# File lib/rexml/xpath_parser.rb, line 94 def get_first path, node path_stack = @parser.parse( path ) @document = node.document first( path_stack, node ) end
Source
# File lib/rexml/xpath_parser.rb, line 151 def match(path_stack, node) @document = node.document # A fresh cache per evaluation, so that a document modified between two # evaluations is not ordered by stale indexes. @node_indexes = {}.compare_by_identity @functions.node_indexes = @node_indexes nodeset = [node] result = expr(path_stack, nodeset) case result when Array # nodeset XPathParser.sort(result, @node_indexes) else [result] end ensure # Let go of the indexes: they are no use to the next evaluation, and the # cache holds one entry per node it had to look up. The document itself # stays reachable through @document and @element_namespaces_cache, which # outlive the evaluation. @node_indexes = nil @functions.node_indexes = nil end
Source
# File lib/rexml/xpath_parser.rb, line 75 def namespaces=( namespaces={} ) @namespaces = namespaces end
Source
# File lib/rexml/xpath_parser.rb, line 83 def parse path, node path_stack = @parser.parse( path ) if node.is_a?(Array) Kernel.warn("REXML::XPath.each, REXML::XPath.first, REXML::XPath.match dropped support for nodeset...", uplevel: 1) return [] if node.empty? node = node.first end match(path_stack, node) end
Source
# File lib/rexml/xpath_parser.rb, line 100 def predicate path, node path_stack = @parser.parse( path ) match( path_stack, node ) end
Source
# File lib/rexml/xpath_parser.rb, line 79 def variables=(vars) @variables = vars.transform_values { |v| coerce_variable(v) } end