Class: Oga::XPath::Evaluator Private

Inherits:
Object
  • Object
show all
Defined in:
lib/oga/xpath/evaluator.rb

Overview

This class is part of a private API. You should avoid using this class if possible, as it may be removed or be changed in the future.

The Evaluator class evaluates XPath expressions, either as a String or an AST of AST::Node instances.

Thread Safety

This class is not thread-safe, you can not share the same instance between multiple threads. This is due to the use of an internal stack (see below for more information). It is however perfectly fine to use multiple separated instances as this class does not use a thread global state.

Node Set Stack

This class uses an internal stack of XML node sets. This stack is used for functions that require access to the set of nodes a predicate belongs to. An example of such a function is position().

An alternative would be to pass the node sets a predicate belongs to as an extra argument to the various on_* methods. The problematic part of this approach is that it requires every method to take and pass along the argument. It's far too easy to make mistakes in such a setup and as such I've chosen to use an internal stack instead.

See #with_node_set and #current_node_set for more information.

Set Indices

XPath node sets start at index 1 instead of index 0. In other words, if you want to access the first node in a set you have to use index 1, not 0. Certain methods such as #on_call_last and #on_call_position take care of converting indices from Ruby to XPath.

Number Types

The XPath specification states that all numbers produced by an expression should be returned as double-precision 64bit IEEE 754 floating point numbers. For example, the return value of position() should be a float (e.g. "1.0", not "1").

Oga takes care internally of converting numbers to integers and/or floats where needed. The output types however will always be floats.

For more information on the specification, see http://www.w3.org/TR/xpath/#numbers.

Variables

The evaluator supports the binding of custom variables in the #initialize method. Variables can be bound by passing in a Hash with the keys set to the variable names (minus the $ sign) and their values to the variable values. The keys of the variables Hash must be Strings.

A basic example:

evaluator = Evaluator.new(document, 'number' => 10)

evaluator.evaluate('$number') # => 10

Constant Summary collapse

STAR =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

Wildcard for node names/namespace prefixes.

'*'

Instance Method Summary collapse

Constructor Details

#initialize(document, variables = {}) ⇒ Evaluator

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns a new instance of Evaluator.

Parameters:



72
73
74
75
76
# File 'lib/oga/xpath/evaluator.rb', line 72

def initialize(document, variables = {})
  @document  = document
  @variables = variables
  @node_sets = []
end

Instance Method Details

#child_nodes(nodes) ⇒ Oga::XML::NodeSet

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns a node set containing all the child nodes of the given set of nodes.

Parameters:

Returns:



1610
1611
1612
1613
1614
1615
1616
1617
1618
# File 'lib/oga/xpath/evaluator.rb', line 1610

def child_nodes(nodes)
  children = XML::NodeSet.new

  nodes.each do |xml_node|
    children.concat(xml_node.children)
  end

  children
end

#current_node_setOga::XML::NodeSet

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns:



1773
1774
1775
# File 'lib/oga/xpath/evaluator.rb', line 1773

def current_node_set
  @node_sets.last
end

#evaluate(string) ⇒ Mixed

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Evaluates an XPath expression as a String.

Examples:

evaluator = Oga::XPath::Evaluator.new(document)

evaluator.evaluate('//a')

Parameters:

  • string (String)

    An XPath expression as a String.

Returns:

  • (Mixed)


89
90
91
92
93
# File 'lib/oga/xpath/evaluator.rb', line 89

def evaluate(string)
  ast = Parser.parse_with_cache(string)

  evaluate_ast(ast)
end

#evaluate_ast(ast) ⇒ Mixed

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Evaluates a pre-parsed XPath expression.

Parameters:

  • ast (AST::Node)

Returns:

  • (Mixed)


101
102
103
104
105
# File 'lib/oga/xpath/evaluator.rb', line 101

def evaluate_ast(ast)
  context = XML::NodeSet.new([@document])

  process(ast, context)
end

#first_node_text(set) ⇒ String

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns the text of the first node in the node set, or an empty string if the node set is empty.

Parameters:

Returns:

  • (String)


1599
1600
1601
# File 'lib/oga/xpath/evaluator.rb', line 1599

def first_node_text(set)
  set[0].respond_to?(:text) ? set[0].text : ''
end

#function_node(context, expression = nil) ⇒ Oga::XML::Node

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns the node for a function call. This node is either the first node in the supplied node set, or the first node in the current context.

Parameters:

Returns:



1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
# File 'lib/oga/xpath/evaluator.rb', line 1576

def function_node(context, expression = nil)
  if expression
    node = process(expression, context)

    if node.is_a?(XML::NodeSet)
      node = node.first
    else
      raise TypeError, 'only node sets can be used as arguments'
    end
  else
    node = context.first
  end

  node
end

#has_parent?(ast_node) ⇒ TrueClass|FalseClass

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Parameters:

Returns:

  • (TrueClass|FalseClass)


1716
1717
1718
# File 'lib/oga/xpath/evaluator.rb', line 1716

def has_parent?(ast_node)
  ast_node.respond_to?(:parent) && !!ast_node.parent
end

#name_matches?(xml_node, name) ⇒ Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns true if the name of the XML node matches the given name or matches a wildcard.

Parameters:

Returns:

  • (Boolean)


1691
1692
1693
1694
1695
# File 'lib/oga/xpath/evaluator.rb', line 1691

def name_matches?(xml_node, name)
  return false unless xml_node.respond_to?(:name)

  name == STAR ? true : xml_node.name == name
end

#namespace_matches?(xml_node, ns) ⇒ Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns true if the namespace of the XML node matches the given namespace or matches a wildcard.

Parameters:

Returns:

  • (Boolean)


1704
1705
1706
1707
1708
1709
1710
# File 'lib/oga/xpath/evaluator.rb', line 1704

def namespace_matches?(xml_node, ns)
  return false unless xml_node.respond_to?(:namespace)

  return true if ns == STAR

  xml_node.namespace && xml_node.namespace.name == ns
end

#node_matches?(xml_node, ast_node) ⇒ Oga::XML::NodeSet

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Checks if a given Oga::XML::Node instance matches a AST::Node instance.

This method can use both "test" and "type-test" nodes. In case of "type-test" nodes the procedure is as following:

  1. Evaluate the expression
  2. If the return value is non empty return true, otherwise return false

For "test" nodes the procedure is as following instead:

  1. Match the name
  2. Match the namespace

For both the name and namespace a wildcard (*) can be used.

Parameters:

Returns:



1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
# File 'lib/oga/xpath/evaluator.rb', line 1642

def node_matches?(xml_node, ast_node)
  ns, name = *ast_node.children

  if ast_node.type.equal?(:type_test)
    return type_matches?(xml_node, ast_node)
  end

  # If only the name is given and is a wildcard then we'll also want to
  # match the namespace as a wildcard.
  if !ns and name == STAR
    ns = STAR
  end

  name_matches = name_matches?(xml_node, name)
  ns_matches   = false

  if ns
    ns_matches = namespace_matches?(xml_node, ns)

  elsif name_matches and !xml_node.namespace
    ns_matches = true
  end

  if !ns and !ns_matches
    ns_matches = xml_node.respond_to?(:default_namespace?) &&
      xml_node.default_namespace?
  end

  name_matches && ns_matches
end

#on_absolute_path(ast_node, context) ⇒ Oga::XML::NodeSet

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Processes an absolute XPath expression such as /foo.

Parameters:

Returns:



132
133
134
135
136
137
138
139
140
141
# File 'lib/oga/xpath/evaluator.rb', line 132

def on_absolute_path(ast_node, context)
  if @document.respond_to?(:root_node)
    context = XML::NodeSet.new([@document.root_node])
  else
    context = XML::NodeSet.new([@document])
  end

  # If the expression is just "/" we'll just return the current context.
  ast_node.children.empty? ? context : on_path(ast_node, context)
end

#on_add(ast_node, context) ⇒ Float

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Processes the + operator.

This operator converts the left and right expressions to numbers and adds them together.

Parameters:

Returns:

  • (Float)


695
696
697
698
699
# File 'lib/oga/xpath/evaluator.rb', line 695

def on_add(ast_node, context)
  left, right = *ast_node.children

  on_call_number(context, left) + on_call_number(context, right)
end

#on_and(ast_node, context) ⇒ TrueClass|FalseClass

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Processes the and operator.

This operator returns true if both the left and right expression evaluate to true. If the first expression evaluates to false the right expression is ignored.

Parameters:

Returns:

  • (TrueClass|FalseClass)


662
663
664
665
666
# File 'lib/oga/xpath/evaluator.rb', line 662

def on_and(ast_node, context)
  left, right = *ast_node.children

  on_call_boolean(context, left) && on_call_boolean(context, right)
end

#on_axis(ast_node, context) ⇒ Oga::XML::NodeSet

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Dispatches the processing of axes to dedicated methods. This works similar to #process except the handler names are "on_axis_X" with "X" being the axis name.

Parameters:

Returns:



236
237
238
239
240
241
242
# File 'lib/oga/xpath/evaluator.rb', line 236

def on_axis(ast_node, context)
  name, test = *ast_node.children

  handler = name.gsub('-', '_')

  send("on_axis_#{handler}", test, context)
end

#on_axis_ancestor(ast_node, context) ⇒ Oga::XML::NodeSet

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Processes the ancestor axis. This axis walks through the entire ancestor chain until a matching node is found.

Evaluation happens using a "short-circuit" mechanism. The moment a matching node is found it is returned immediately.

Parameters:

Returns:



255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
# File 'lib/oga/xpath/evaluator.rb', line 255

def on_axis_ancestor(ast_node, context)
  nodes = XML::NodeSet.new

  context.each do |xml_node|
    while has_parent?(xml_node)
      xml_node = xml_node.parent

      if node_matches?(xml_node, ast_node)
        nodes << xml_node
        break
      end
    end
  end

  nodes
end

#on_axis_ancestor_or_self(ast_node, context) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Processes the ancestor-or-self axis.

See Also:

  • Oga::XPath::Evaluator.[[#on_axis_ancestor]


277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
# File 'lib/oga/xpath/evaluator.rb', line 277

def on_axis_ancestor_or_self(ast_node, context)
  nodes = XML::NodeSet.new

  context.each do |xml_node|
    while has_parent?(xml_node)
      if node_matches?(xml_node, ast_node)
        nodes << xml_node
        break
      end

      xml_node = xml_node.parent
    end
  end

  nodes
end

#on_axis_attribute(ast_node, context) ⇒ Oga::XML::NodeSet

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Processes the attribute axis. The node test is performed against all the attributes of the nodes in the current context.

Evaluation of the nodes continues until the node set has been exhausted (unlike some other methods which return the moment they find a matching node).

Parameters:

Returns:



306
307
308
309
310
311
312
313
314
315
316
# File 'lib/oga/xpath/evaluator.rb', line 306

def on_axis_attribute(ast_node, context)
  nodes = XML::NodeSet.new

  context.each do |xml_node|
    next unless xml_node.is_a?(XML::Element)

    nodes += on_test(ast_node, xml_node.attributes)
  end

  nodes
end

#on_axis_child(ast_node, context) ⇒ Oga::XML::NodeSet

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Evaluates the child axis. This axis simply takes all the child nodes of the current context nodes.

Parameters:

Returns:



326
327
328
# File 'lib/oga/xpath/evaluator.rb', line 326

def on_axis_child(ast_node, context)
  process(ast_node, child_nodes(context))
end

#on_axis_descendant(ast_node, context) ⇒ Oga::XML::NodeSet

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Evaluates the descendant axis. This method processes child nodes until the very end of the tree, no "short-circuiting" mechanism is used.

Parameters:

Returns:



338
339
340
341
342
343
344
345
346
347
348
# File 'lib/oga/xpath/evaluator.rb', line 338

def on_axis_descendant(ast_node, context)
  nodes = XML::NodeSet.new

  context.each do |context_node|
    context_node.each_node do |node|
      nodes.concat(process(ast_node, XML::NodeSet.new([node])))
    end
  end

  nodes
end

#on_axis_descendant_or_self(ast_node, context) ⇒ Oga::XML::NodeSet

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Evaluates the descendant-or-self axis.

Parameters:

Returns:



357
358
359
360
361
362
363
# File 'lib/oga/xpath/evaluator.rb', line 357

def on_axis_descendant_or_self(ast_node, context)
  nodes = on_test(ast_node, context)

  nodes.concat(on_axis_descendant(ast_node, context))

  nodes
end

#on_axis_following(ast_node, context) ⇒ Oga::XML::NodeSet

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Evaluates the following axis.

Parameters:

Returns:



372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
# File 'lib/oga/xpath/evaluator.rb', line 372

def on_axis_following(ast_node, context)
  nodes = XML::NodeSet.new
  root  = root_node(@document)

  context.each do |context_node|
    check = false

    root.each_node do |doc_node|
      # Skip child nodes of the current context node, compare all
      # following nodes.
      if doc_node == context_node
        check = true
        throw :skip_children
      end

      next unless check

      nodes << doc_node if node_matches?(doc_node, ast_node)
    end
  end

  nodes
end

#on_axis_following_sibling(ast_node, context) ⇒ Oga::XML::NodeSet

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Evaluates the following-sibling axis.

Parameters:

Returns:



403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
# File 'lib/oga/xpath/evaluator.rb', line 403

def on_axis_following_sibling(ast_node, context)
  nodes = XML::NodeSet.new
  root  = parent_node(@document)

  context.each do |context_node|
    check  = false
    parent = has_parent?(context_node) ? context_node.parent : nil

    root.each_node do |doc_node|
      # Skip child nodes of the current context node, compare all
      # following nodes.
      if doc_node == context_node
        check = true
        throw :skip_children
      end

      if !check or parent != doc_node.parent
        next
      end

      if node_matches?(doc_node, ast_node)
        nodes << doc_node

        throw :skip_children
      end
    end
  end

  nodes
end

#on_axis_namespace(ast_node, context) ⇒ Oga::XML::NodeSet

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Evaluates the namespace axis.

Parameters:

Returns:



534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
# File 'lib/oga/xpath/evaluator.rb', line 534

def on_axis_namespace(ast_node, context)
  nodes = XML::NodeSet.new
  name  = ast_node.children[1]

  context.each do |context_node|
    next unless context_node.respond_to?(:available_namespaces)

    context_node.available_namespaces.each do |_, namespace|
      if namespace.name == name or name == STAR
        nodes << namespace
      end
    end
  end

  nodes
end

#on_axis_parent(ast_node, context) ⇒ Oga::XML::NodeSet

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Evaluates the parent axis.

Parameters:

Returns:



441
442
443
444
445
446
447
448
449
450
451
452
453
# File 'lib/oga/xpath/evaluator.rb', line 441

def on_axis_parent(ast_node, context)
  nodes = XML::NodeSet.new

  context.each do |context_node|
    next unless has_parent?(context_node)

    parent = context_node.parent

    nodes << parent if node_matches?(parent, ast_node)
  end

  nodes
end

#on_axis_preceding(ast_node, context) ⇒ Oga::XML::NodeSet

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Evaluates the preceding axis.

Parameters:

Returns:



462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
# File 'lib/oga/xpath/evaluator.rb', line 462

def on_axis_preceding(ast_node, context)
  nodes = XML::NodeSet.new
  root  = root_node(@document)

  context.each do |context_node|
    check = true

    root.each_node do |doc_node|
      # Test everything *until* we hit the current context node.
      if doc_node == context_node
        break
      elsif node_matches?(doc_node, ast_node)
        nodes << doc_node
      end
    end
  end

  nodes
end

#on_axis_preceding_sibling(ast_node, context) ⇒ Oga::XML::NodeSet

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Evaluates the preceding-sibling axis.

Parameters:

Returns:



489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
# File 'lib/oga/xpath/evaluator.rb', line 489

def on_axis_preceding_sibling(ast_node, context)
  nodes = XML::NodeSet.new
  root  = parent_node(@document)

  context.each do |context_node|
    check  = true
    parent = has_parent?(context_node) ? context_node.parent : nil

    root.each_node do |doc_node|
      # Test everything *until* we hit the current context node.
      if doc_node == context_node
        break
      elsif doc_node.parent == parent and node_matches?(doc_node, ast_node)
        nodes << doc_node
      end
    end
  end

  nodes
end

#on_axis_self(ast_node, context) ⇒ Oga::XML::NodeSet

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Evaluates the self axis.

Parameters:

Returns:



517
518
519
520
521
522
523
524
525
# File 'lib/oga/xpath/evaluator.rb', line 517

def on_axis_self(ast_node, context)
  nodes = XML::NodeSet.new

  context.each do |context_node|
    nodes << context_node if node_matches?(context_node, ast_node)
  end

  nodes
end

#on_call(ast_node, context) ⇒ Oga::XML::NodeSet

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Delegates function calls to specific handlers.

Handler functions take two arguments:

  1. The context node set
  2. A variable list of XPath function arguments, passed as individual Ruby method arguments.

Parameters:

Returns:



892
893
894
895
896
897
898
# File 'lib/oga/xpath/evaluator.rb', line 892

def on_call(ast_node, context)
  name, *args = *ast_node.children

  handler = name.gsub('-', '_')

  send("on_call_#{handler}", context, *args)
end

#on_call_boolean(context, expression) ⇒ TrueClass|FalseClass

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Processes the boolean() function call.

This function converts the 1st argument to a boolean.

The boolean true is returned for the following:

  • A non empty string
  • A non empty node set
  • A non zero number, either positive or negative

The boolean false is returned for all other cases.

Parameters:

Returns:

  • (TrueClass|FalseClass)


1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
# File 'lib/oga/xpath/evaluator.rb', line 1353

def on_call_boolean(context, expression)
  retval = process(expression, context)
  bool   = false

  if retval.is_a?(Numeric)
    bool = !retval.nan? && !retval.zero?
  elsif retval
    bool = !retval.respond_to?(:empty?) || !retval.empty?
  end

  bool
end

#on_call_ceiling(context, expression) ⇒ Float

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Processes the ceiling() function call.

This function call rounds the 1st argument up to the closest integer, and then returns that number as a float.

Parameters:

Returns:

  • (Float)


1494
1495
1496
1497
1498
# File 'lib/oga/xpath/evaluator.rb', line 1494

def on_call_ceiling(context, expression)
  number = on_call_number(context, expression)

  number.nan? ? number : number.ceil.to_f
end

#on_call_concat(context, first, second, *rest) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Processes the concat() function call.

This function call converts its arguments to strings and concatenates them. In case of node sets the text of the set is used.

Parameters:

  • context (Oga::XML::NodeSet)
  • first (AST::Node)
  • second (AST::Node)
  • rest (Array<AST::Node>)


1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
# File 'lib/oga/xpath/evaluator.rb', line 1130

def on_call_concat(context, first, second, *rest)
  args   = [first, second] + rest
  retval = ''

  args.each do |arg|
    retval << on_call_string(context, arg)
  end

  retval
end

#on_call_contains(context, haystack, needle) ⇒ String

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Processes the contains() function call.

This function call returns true if the string in the 1st argument contains the string in the 2nd argument. Node sets can also be used.

Examples:

contains("hello world", "o w") # => true

Parameters:

  • context (Oga::XML::NodeSet)
  • haystack (AST::Node)

    The string to search.

  • needle (AST::Node)

    The string to search for.

Returns:

  • (String)


1177
1178
1179
1180
1181
1182
# File 'lib/oga/xpath/evaluator.rb', line 1177

def on_call_contains(context, haystack, needle)
  haystack_str = on_call_string(context, haystack)
  needle_str   = on_call_string(context, needle)

  haystack_str.include?(needle_str)
end

#on_call_count(context, expression) ⇒ Float

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Processes the count() function call. This function counts the amount of nodes in expression and returns the result as a float.

Parameters:

Returns:

  • (Float)


933
934
935
936
937
938
939
940
941
# File 'lib/oga/xpath/evaluator.rb', line 933

def on_call_count(context, expression)
  retval = process(expression, context)

  unless retval.is_a?(XML::NodeSet)
    raise TypeError, 'count() can only operate on NodeSet instances'
  end

  retval.length.to_f
end

#on_call_false(context) ⇒ FalseClass

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Processes the false() function call.

This function simply returns the boolean false.

Parameters:

  • context (AST::NodeSet)

Returns:

  • (FalseClass)


1401
1402
1403
# File 'lib/oga/xpath/evaluator.rb', line 1401

def on_call_false(context)
  false
end

#on_call_floor(context, expression) ⇒ Float

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Processes the floor() function call.

This function call rounds the 1st argument down to the closest integer, and then returns that number as a float.

Parameters:

Returns:

  • (Float)


1478
1479
1480
1481
1482
# File 'lib/oga/xpath/evaluator.rb', line 1478

def on_call_floor(context, expression)
  number = on_call_number(context, expression)

  number.nan? ? number : number.floor.to_f
end

#on_call_id(context, expression) ⇒ Oga::XML::NodeSet

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Processes the id() function call.

The XPath specification states that this function's behaviour should be controlled by a DTD. If a DTD were to specify that the ID attribute for a certain element would be "foo" then this function should use said attribute.

Oga does not support DTD parsing/evaluation and as such always uses the "id" attribute.

This function searches the entire document for a matching node, regardless of the current position.

Parameters:

Returns:



961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
# File 'lib/oga/xpath/evaluator.rb', line 961

def on_call_id(context, expression)
  id    = process(expression, context)
  nodes = XML::NodeSet.new

  # Based on Nokogiri's/libxml behaviour it appears that when using a node
  # set the text of the set is used as the ID.
  id  = id.is_a?(XML::NodeSet) ? id.text : id.to_s
  ids = id.split(' ')

  @document.each_node do |node|
    next unless node.is_a?(XML::Element)

    attr = node.attribute('id')

    if attr and ids.include?(attr.value)
      nodes << node
    end
  end

  nodes
end

#on_call_lang(context, language) ⇒ TrueClass|FalseClass

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Processes the lang() function call.

This function returns true if the current context node is in the given language, false otherwise.

The language is based on the value of the "xml:lang" attribute of either the context node or an ancestor node (in case the context node has no such attribute).

Parameters:

Returns:

  • (TrueClass|FalseClass)


1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
# File 'lib/oga/xpath/evaluator.rb', line 1419

def on_call_lang(context, language)
  lang_str = on_call_string(context, language)
  node     = context.first

  while node.respond_to?(:attribute)
    found = node.attribute('xml:lang')

    return found.value == lang_str if found

    node = node.parent
  end

  false
end

#on_call_last(context) ⇒ Float

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Processes the last() function call. This function call returns the index of the last node in the current set.

Parameters:

Returns:

  • (Float)


907
908
909
910
# File 'lib/oga/xpath/evaluator.rb', line 907

def on_call_last(context)
  # XPath uses indexes 1 to N instead of 0 to N.
  current_node_set.length.to_f
end

#on_call_local_name(context, expression = nil) ⇒ Oga::XML::NodeSet

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Processes the local-name() function call.

This function call returns the name of one of the following:

  • The current context node (if any)
  • The first node in the supplied node set

Parameters:

Returns:



995
996
997
998
999
# File 'lib/oga/xpath/evaluator.rb', line 995

def on_call_local_name(context, expression = nil)
  node = function_node(context, expression)

  node.respond_to?(:name) ? node.name : ''
end

#on_call_name(context, expression = nil) ⇒ Oga::XML::NodeSet

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Processes the name() function call.

This function call is similar to local-name() (see #on_call_local_name) except that it includes the namespace name if present.

Parameters:

Returns:



1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
# File 'lib/oga/xpath/evaluator.rb', line 1012

def on_call_name(context, expression = nil)
  node = function_node(context, expression)

  if node.respond_to?(:name) and node.respond_to?(:namespace)
    if node.namespace
      return "#{node.namespace.name}:#{node.name}"
    else
      return node.name
    end
  else
    return ''
  end
end

#on_call_namespace_uri(context, expression = nil) ⇒ Oga::XML::NodeSet

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Processes the namespace-uri() function call.

This function call returns the namespace URI of one of the following:

  • The current context node (if any)
  • The first node in the supplied node set

Parameters:

Returns:



1038
1039
1040
1041
1042
1043
1044
1045
1046
# File 'lib/oga/xpath/evaluator.rb', line 1038

def on_call_namespace_uri(context, expression = nil)
  node = function_node(context, expression)

  if node.respond_to?(:namespace) and node.namespace
    return node.namespace.uri
  else
    return ''
  end
end

#on_call_normalize_space(context, expression = nil) ⇒ String

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Processes the normalize-space() function call.

This function strips the 1st argument string or the current context node of leading/trailing whitespace as well as replacing multiple whitespace sequences with single spaces.

Examples:

normalize-space(" fo  o    ") # => "fo o"

Parameters:

Returns:

  • (String)


1301
1302
1303
1304
1305
# File 'lib/oga/xpath/evaluator.rb', line 1301

def on_call_normalize_space(context, expression = nil)
  str = on_call_string(context, expression)

  str.strip.gsub(/\s+/, ' ')
end

#on_call_not(context, expression) ⇒ TrueClass|FalseClass

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Processes the not() function call.

This function converts the 1st argument to a boolean and returns the opposite boolean value. For example, if the first argument results in true then this function returns false instead.

Parameters:

Returns:

  • (TrueClass|FalseClass)


1377
1378
1379
# File 'lib/oga/xpath/evaluator.rb', line 1377

def on_call_not(context, expression)
  !on_call_boolean(context, expression)
end

#on_call_number(context, expression = nil) ⇒ Float

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Evaluates the number() function call.

This function call converts its first argument or the current context node to a number, similar to the string() function.

Examples:

number("10") # => 10.0

Parameters:

Returns:

  • (Float)

See Also:

  • Oga::XPath::Evaluator.[[#on_call_string]


1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
# File 'lib/oga/xpath/evaluator.rb', line 1094

def on_call_number(context, expression = nil)
  convert = nil

  if expression
    exp_retval = process(expression, context)

    if exp_retval.is_a?(XML::NodeSet)
      convert = first_node_text(exp_retval)

    elsif exp_retval == true
      convert = 1.0

    elsif exp_retval == false
      convert = 0.0

    elsif exp_retval
      convert = exp_retval
    end
  else
    convert = context.first.text
  end

  to_float(convert)
end

#on_call_position(context) ⇒ Float

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Processes the position() function call. This function returns the position of the current node in the current node set.

Parameters:

Returns:

  • (Float)


919
920
921
922
923
# File 'lib/oga/xpath/evaluator.rb', line 919

def on_call_position(context)
  index = current_node_set.index(context.first) + 1

  index.to_f
end

#on_call_round(context, expression) ⇒ Float

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Processes the round() function call.

This function call rounds the 1st argument to the closest integer, and then returns that number as a float.

Parameters:

Returns:

  • (Float)


1510
1511
1512
1513
1514
# File 'lib/oga/xpath/evaluator.rb', line 1510

def on_call_round(context, expression)
  number = on_call_number(context, expression)

  number.nan? ? number : number.round.to_f
end

#on_call_starts_with(context, haystack, needle) ⇒ TrueClass|FalseClass

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Processes the starts-with() function call.

This function call returns true if the string in the 1st argument starts with the string in the 2nd argument. Node sets can also be used.

Examples:

starts-with("hello world", "hello") # => true

Parameters:

  • context (Oga::XML::NodeSet)
  • haystack (AST::Node)

    The string to search.

  • needle (AST::Node)

    The string to search for.

Returns:

  • (TrueClass|FalseClass)


1155
1156
1157
1158
1159
1160
1161
# File 'lib/oga/xpath/evaluator.rb', line 1155

def on_call_starts_with(context, haystack, needle)
  haystack_str = on_call_string(context, haystack)
  needle_str   = on_call_string(context, needle)

  # https://github.com/jruby/jruby/issues/1923
  needle_str.empty? || haystack_str.start_with?(needle_str)
end

#on_call_string(context, expression = nil) ⇒ String

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Evaluates the string() function call.

This function call converts the given argument or the current context node to a string. If a node set is given then only the first node is converted to a string.

Examples:

string(10) # => "10"

Parameters:

Returns:

  • (String)


1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
# File 'lib/oga/xpath/evaluator.rb', line 1062

def on_call_string(context, expression = nil)
  if expression
    convert = process(expression, context)

    if convert.is_a?(XML::NodeSet)
      convert = convert[0]
    end
  else
    convert = context.first
  end

  if convert.respond_to?(:text)
    return convert.text
  else
    return to_string(convert)
  end
end

#on_call_string_length(context, expression = nil) ⇒ Float

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Processes the string-length() function.

This function returns the length of the string given in the 1st argument or the current context node. If the expression is not a string it's converted to a string using the string() function.

Parameters:

Returns:

  • (Float)

See Also:

  • Oga::XPath::Evaluator.[[#on_call_string]


1283
1284
1285
# File 'lib/oga/xpath/evaluator.rb', line 1283

def on_call_string_length(context, expression = nil)
  on_call_string(context, expression).length.to_f
end

#on_call_substring(context, haystack, start, length = nil) ⇒ String

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Processes the substring() function call.

This function call returns the substring of the 1st argument, starting at the position given in the 2nd argument. If the third argument is given it is used as the length for the substring, otherwise the string is consumed until the end.

XPath string indexes start from position 1, not position 0.

Examples:

Using a literal string

substring("foo", 2) # => "oo"

Using a literal string with a custom length

substring("foo", 1, 2) # => "fo"

Using a node set

substring(users/user/username, 5)

Parameters:

  • context (Oga::XML::NodeSet)
  • haystack (AST::Node)
  • start (AST::Node)
  • length (AST::Node) (defaults to: nil)

Returns:

  • (String)


1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
# File 'lib/oga/xpath/evaluator.rb', line 1257

def on_call_substring(context, haystack, start, length = nil)
  haystack_str = on_call_string(context, haystack)
  start_index  = on_call_number(context, start).to_i - 1

  if length
    length_int = on_call_number(context, length).to_i - 1
    stop_index = start_index + length_int
  else
    stop_index = -1
  end

  haystack_str[start_index..stop_index]
end

#on_call_substring_after(context, haystack, needle) ⇒ String

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Processes the substring-after() function call.

This function call returns the substring of the 1st argument that occurs after the string given in the 2nd argument. For example:

substring-after("2014-08-25", "-")

This would return "08-25" as it occurs after the first "-".

Parameters:

  • context (Oga::XML::NodeSet)
  • haystack (AST::Node)

    The string to search.

  • needle (AST::Node)

    The string to search for.

Returns:

  • (String)


1223
1224
1225
1226
1227
1228
1229
1230
# File 'lib/oga/xpath/evaluator.rb', line 1223

def on_call_substring_after(context, haystack, needle)
  haystack_str = on_call_string(context, haystack)
  needle_str   = on_call_string(context, needle)

  before, sep, after = haystack_str.partition(needle_str)

  sep.empty? ? sep : after
end

#on_call_substring_before(context, haystack, needle) ⇒ String

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Processes the substring-before() function call.

This function call returns the substring of the 1st argument that occurs before the string given in the 2nd argument. For example:

substring-before("2014-08-25", "-")

This would return "2014" as it occurs before the first "-".

Parameters:

  • context (Oga::XML::NodeSet)
  • haystack (AST::Node)

    The string to search.

  • needle (AST::Node)

    The string to search for.

Returns:

  • (String)


1199
1200
1201
1202
1203
1204
1205
1206
# File 'lib/oga/xpath/evaluator.rb', line 1199

def on_call_substring_before(context, haystack, needle)
  haystack_str = on_call_string(context, haystack)
  needle_str   = on_call_string(context, needle)

  before, sep, after = haystack_str.partition(needle_str)

  sep.empty? ? sep : before
end

#on_call_sum(context, expression) ⇒ Float

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Processes the sum() function call.

This function call takes a node set, converts each node to a number and then sums the values.

As an example, take the following XML:

<root>
  <a>1</a>
  <b>2</b>
</root>

Using the expression sum(root/*) the return value would be 3.0.

Parameters:

Returns:

  • (Float)


1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
# File 'lib/oga/xpath/evaluator.rb', line 1453

def on_call_sum(context, expression)
  nodes = process(expression, context)
  sum   = 0.0

  unless nodes.is_a?(XML::NodeSet)
    raise TypeError, 'sum() can only operate on NodeSet instances'
  end

  nodes.each do |node|
    sum += node.text.to_f
  end

  sum
end

#on_call_translate(context, input, find, replace) ⇒ String

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Processes the translate() function call.

This function takes the string of the 1st argument and replaces all characters of the 2nd argument with those specified in the 3rd argument.

Examples:

translate("bar", "abc", "ABC") # => "BAr"

Parameters:

Returns:

  • (String)


1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
# File 'lib/oga/xpath/evaluator.rb', line 1322

def on_call_translate(context, input, find, replace)
  input_str     = on_call_string(context, input)
  find_chars    = on_call_string(context, find).chars.to_a
  replace_chars = on_call_string(context, replace).chars.to_a
  replaced      = input_str

  find_chars.each_with_index do |char, index|
    replace_with = replace_chars[index] ? replace_chars[index] : ''
    replaced     = replaced.gsub(char, replace_with)
  end

  replaced
end

#on_call_true(context) ⇒ TrueClass

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Processes the true() function call.

This function simply returns the boolean true.

Parameters:

  • context (AST::NodeSet)

Returns:

  • (TrueClass)


1389
1390
1391
# File 'lib/oga/xpath/evaluator.rb', line 1389

def on_call_true(context)
  true
end

#on_div(ast_node, context) ⇒ Float

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Processes the div operator.

This operator converts the left and right expressions to numbers and divides the left number with the right number.

Parameters:

Returns:

  • (Float)


711
712
713
714
715
# File 'lib/oga/xpath/evaluator.rb', line 711

def on_div(ast_node, context)
  left, right = *ast_node.children

  on_call_number(context, left) / on_call_number(context, right)
end

#on_eq(ast_node, context) ⇒ TrueClass|FalseClass

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Processes the = operator.

This operator evaluates the expression on the left and right and returns true if they are equal. This operator can be used to compare strings, numbers and node sets. When using node sets the text of the set is compared instead of the nodes themselves. That is, nodes with different names but the same text are considered to be equal.

Parameters:

Returns:

  • (TrueClass|FalseClass)


778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
# File 'lib/oga/xpath/evaluator.rb', line 778

def on_eq(ast_node, context)
  left  = process(ast_node.children[0], context)
  right = process(ast_node.children[1], context)

  if left.is_a?(XML::NodeSet)
    left = first_node_text(left)
  end

  if right.is_a?(XML::NodeSet)
    right = first_node_text(right)
  end

  if left.is_a?(Numeric) and !right.is_a?(Numeric)
    right = to_float(right)
  end

  if left.is_a?(String) and !right.is_a?(String)
    right = to_string(right)
  end

  left == right
end

#on_float(ast_node, context) ⇒ Float

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Processes an (float) node.

Parameters:

Returns:

  • (Float)


1534
1535
1536
# File 'lib/oga/xpath/evaluator.rb', line 1534

def on_float(ast_node, context)
  ast_node.children[0]
end

#on_gt(ast_node, context) ⇒ TrueClass|FalseClass

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Processes the > operator.

This operator converts the left and right expression to a number and returns true if the first number is greater than the second number.

Parameters:

Returns:

  • (TrueClass|FalseClass)


839
840
841
842
843
# File 'lib/oga/xpath/evaluator.rb', line 839

def on_gt(ast_node, context)
  left, right = *ast_node.children

  on_call_number(context, left) > on_call_number(context, right)
end

#on_gte(ast_node, context) ⇒ TrueClass|FalseClass

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Processes the >= operator.

This operator converts the left and right expression to a number and returns true if the first number is greater-than or equal to the second number.

Parameters:

Returns:

  • (TrueClass|FalseClass)


873
874
875
876
877
# File 'lib/oga/xpath/evaluator.rb', line 873

def on_gte(ast_node, context)
  left, right = *ast_node.children

  on_call_number(context, left) >= on_call_number(context, right)
end

#on_int(ast_node, context) ⇒ Float

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Processes an (int) node.

Parameters:

Returns:

  • (Float)


1523
1524
1525
# File 'lib/oga/xpath/evaluator.rb', line 1523

def on_int(ast_node, context)
  ast_node.children[0].to_f
end

#on_lt(ast_node, context) ⇒ TrueClass|FalseClass

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Processes the < operator.

This operator converts the left and right expression to a number and returns true if the first number is lower than the second number.

Parameters:

Returns:

  • (TrueClass|FalseClass)


823
824
825
826
827
# File 'lib/oga/xpath/evaluator.rb', line 823

def on_lt(ast_node, context)
  left, right = *ast_node.children

  on_call_number(context, left) < on_call_number(context, right)
end

#on_lte(ast_node, context) ⇒ TrueClass|FalseClass

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Processes the <= operator.

This operator converts the left and right expression to a number and returns true if the first number is lower-than or equal to the second number.

Parameters:

Returns:

  • (TrueClass|FalseClass)


856
857
858
859
860
# File 'lib/oga/xpath/evaluator.rb', line 856

def on_lte(ast_node, context)
  left, right = *ast_node.children

  on_call_number(context, left) <= on_call_number(context, right)
end

#on_mod(ast_node, context) ⇒ Float

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Processes the mod operator.

This operator converts the left and right expressions to numbers and returns the modulo of the two numbers.

Parameters:

Returns:

  • (Float)


727
728
729
730
731
# File 'lib/oga/xpath/evaluator.rb', line 727

def on_mod(ast_node, context)
  left, right = *ast_node.children

  on_call_number(context, left) % on_call_number(context, right)
end

#on_mul(ast_node, context) ⇒ Float

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Processes the * operator.

This operator converts the left and right expressions to numbers and multiplies the left number with the right number.

Parameters:

Returns:

  • (Float)


743
744
745
746
747
# File 'lib/oga/xpath/evaluator.rb', line 743

def on_mul(ast_node, context)
  left, right = *ast_node.children

  on_call_number(context, left) * on_call_number(context, right)
end

#on_neq(ast_node, context) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Processes the != operator.

This operator does the exact opposite of the = operator. See #on_eq for more information.

See Also:

  • Oga::XPath::Evaluator.[[#on_eq]


809
810
811
# File 'lib/oga/xpath/evaluator.rb', line 809

def on_neq(ast_node, context)
  !on_eq(ast_node, context)
end

#on_or(ast_node, context) ⇒ TrueClass|FalseClass

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Processes the or operator.

This operator returns true if one of the expressions evaluates to true, otherwise false is returned. If the first expression evaluates to true the second expression is ignored.

Parameters:

Returns:

  • (TrueClass|FalseClass)


679
680
681
682
683
# File 'lib/oga/xpath/evaluator.rb', line 679

def on_or(ast_node, context)
  left, right = *ast_node.children

  on_call_boolean(context, left) || on_call_boolean(context, right)
end

#on_path(ast_node, context) ⇒ Oga::XML::NodeSet

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Processes a relative XPath expression such as foo.

Paths are evaluated using a "short-circuit" mechanism similar to Ruby's && / and operator. Whenever a path results in an empty node set the evaluation is aborted immediately.

Parameters:

Returns:



154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/oga/xpath/evaluator.rb', line 154

def on_path(ast_node, context)
  nodes = XML::NodeSet.new

  ast_node.children.each do |test|
    nodes = process(test, context)

    if nodes.empty?
      break
    else
      context = nodes
    end
  end

  nodes
end

#on_pipe(ast_node, context) ⇒ Oga::XML::NodeSet

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Processes the pipe (|) operator. This operator creates a union of two sets.

Parameters:

Returns:



645
646
647
648
649
# File 'lib/oga/xpath/evaluator.rb', line 645

def on_pipe(ast_node, context)
  left, right = *ast_node.children

  process(left, context) + process(right, context)
end

#on_predicate(ast_node, context) ⇒ Oga::XML::NodeSet

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Processes a predicate.

Parameters:

Returns:



194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
# File 'lib/oga/xpath/evaluator.rb', line 194

def on_predicate(ast_node, context)
  test, predicate = *ast_node.children
  final_nodes     = XML::NodeSet.new

  context.each do |context_node|
    initial_nodes = process(test, XML::NodeSet.new([context_node]))
    xpath_index   = 1

    initial_nodes.each do |xml_node|
      retval = with_node_set(initial_nodes) do
        process(predicate, XML::NodeSet.new([xml_node]))
      end

      # Numeric values are used as node set indexes.
      if retval.is_a?(Numeric)
        final_nodes << xml_node if retval.to_i == xpath_index

      # Node sets, strings, booleans, etc
      elsif retval
        if retval.respond_to?(:empty?) and retval.empty?
          next
        end

        final_nodes << xml_node
      end

      xpath_index += 1
    end
  end

  final_nodes
end

#on_string(ast_node, context) ⇒ String

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Processes a (string) node.

Parameters:

Returns:

  • (String)


1545
1546
1547
# File 'lib/oga/xpath/evaluator.rb', line 1545

def on_string(ast_node, context)
  ast_node.children[0]
end

#on_sub(ast_node, context) ⇒ Float

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Processes the - operator.

This operator converts the left and right expressions to numbers and subtracts the right number of the left number.

Parameters:

Returns:

  • (Float)


759
760
761
762
763
# File 'lib/oga/xpath/evaluator.rb', line 759

def on_sub(ast_node, context)
  left, right = *ast_node.children

  on_call_number(context, left) - on_call_number(context, right)
end

#on_test(ast_node, context) ⇒ Oga::XML::NodeSet

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Processes a node test.

Parameters:

Returns:



177
178
179
180
181
182
183
184
185
# File 'lib/oga/xpath/evaluator.rb', line 177

def on_test(ast_node, context)
  nodes = XML::NodeSet.new

  context.each do |xml_node|
    nodes << xml_node if node_matches?(xml_node, ast_node)
  end

  nodes
end

#on_type_test(ast_node, context) ⇒ Oga::XML::NodeSet

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Dispatches node type matching to dedicated handlers.

Parameters:

Returns:



558
559
560
561
562
563
564
# File 'lib/oga/xpath/evaluator.rb', line 558

def on_type_test(ast_node, context)
  name, test = *ast_node.children

  handler = name.gsub('-', '_')

  send("on_type_test_#{handler}", test, context)
end

#on_type_test_comment(ast_node, context) ⇒ Oga::XML::NodeSet

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Processes the comment() type test. This matches only comment nodes.

Parameters:

Returns:



609
610
611
612
613
614
615
616
617
# File 'lib/oga/xpath/evaluator.rb', line 609

def on_type_test_comment(ast_node, context)
  nodes = XML::NodeSet.new

  context.each do |node|
    nodes << node if node.is_a?(XML::Comment)
  end

  nodes
end

#on_type_test_node(ast_node, context) ⇒ Oga::XML::NodeSet

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Processes the node type matcher. This matcher matches all node types.

Parameters:

Returns:



573
574
575
576
577
578
579
580
581
582
583
# File 'lib/oga/xpath/evaluator.rb', line 573

def on_type_test_node(ast_node, context)
  nodes = XML::NodeSet.new

  context.each do |node|
    if node.is_a?(XML::Node) or node.is_a?(XML::Document)
      nodes << node
    end
  end

  nodes
end

#on_type_test_processing_instruction(ast_node, context) ⇒ Oga::XML::NodeSet

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Processes the processing-instruction() type test. This matches only processing-instruction nodes.

Parameters:

Returns:



627
628
629
630
631
632
633
634
635
# File 'lib/oga/xpath/evaluator.rb', line 627

def on_type_test_processing_instruction(ast_node, context)
  nodes = XML::NodeSet.new

  context.each do |node|
    nodes << node if node.is_a?(XML::ProcessingInstruction)
  end

  nodes
end

#on_type_test_text(ast_node, context) ⇒ Oga::XML::NodeSet

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Processes the text() type test. This matches only text nodes.

Parameters:

Returns:



592
593
594
595
596
597
598
599
600
# File 'lib/oga/xpath/evaluator.rb', line 592

def on_type_test_text(ast_node, context)
  nodes = XML::NodeSet.new

  context.each do |node|
    nodes << node if node.is_a?(XML::Text)
  end

  nodes
end

#on_var(ast_node, context) ⇒ Mixed

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Processes a variable reference. If the variable is not defined an error is raised.

Parameters:

Returns:

  • (Mixed)

Raises:

  • (RuntimeError)


1558
1559
1560
1561
1562
1563
1564
1565
1566
# File 'lib/oga/xpath/evaluator.rb', line 1558

def on_var(ast_node, context)
  name = ast_node.children[0]

  if @variables.key?(name)
    return @variables[name]
  else
    raise "Undefined XPath variable: #{name}"
  end
end

#parent_node(node) ⇒ Oga::XML::Node|Oga::XML::Document

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns the parent node of node, or node itself if its a Document.



1793
1794
1795
# File 'lib/oga/xpath/evaluator.rb', line 1793

def parent_node(node)
  node.respond_to?(:parent) ? node.parent : node
end

#process(ast_node, context) ⇒ Oga::XML::NodeSet

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Processes an XPath node by dispatching it and the given context to a dedicated handler method. Handler methods are called "on_X" where "X" is the node type.

Parameters:

  • ast_node (AST::Node)

    The XPath AST node to process.

  • context (Oga::XML::NodeSet)

    The context (a set of nodes) to evaluate an expression in.

Returns:



119
120
121
122
123
# File 'lib/oga/xpath/evaluator.rb', line 119

def process(ast_node, context)
  handler = "on_#{ast_node.type}"

  send(handler, ast_node, context)
end

#root_node(node) ⇒ Oga::XML::Node|Oga::XML::Document

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns the root node of node, or node itself if its a Document.



1783
1784
1785
# File 'lib/oga/xpath/evaluator.rb', line 1783

def root_node(node)
  node.respond_to?(:root_node) ? node.root_node : node
end

#to_float(value) ⇒ Float

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Converts the given value to a float. If the value can't be converted to a float NaN is returned instead.

Parameters:

  • value (Mixed)

Returns:

  • (Float)


1727
1728
1729
# File 'lib/oga/xpath/evaluator.rb', line 1727

def to_float(value)
  return Float(value) rescue Float::NAN
end

#to_string(value) ⇒ String

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Converts the given value to a string according to the XPath string conversion rules.

Parameters:

  • value (Mixed)

Returns:

  • (String)


1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
# File 'lib/oga/xpath/evaluator.rb', line 1738

def to_string(value)
  # If we have a number that has a zero decimal (e.g. 10.0) we want to
  # get rid of that decimal. For this we'll first convert the number to
  # an integer.
  if value.is_a?(Float) and value.modulo(1).zero?
    value = value.to_i
  end

  value.to_s
end

#type_matches?(xml_node, ast_node) ⇒ TrueClass|FalseClass

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Parameters:

Returns:

  • (TrueClass|FalseClass)


1678
1679
1680
1681
1682
# File 'lib/oga/xpath/evaluator.rb', line 1678

def type_matches?(xml_node, ast_node)
  context = XML::NodeSet.new([xml_node])

  process(ast_node, context).length > 0
end

#with_node_set(nodes) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Stores the specified node set and yields the supplied block. The return value of this method is whatever the block returned.

Examples:

retval = with_node_set(context) do
  process(....)
end

Parameters:



1760
1761
1762
1763
1764
1765
1766
1767
1768
# File 'lib/oga/xpath/evaluator.rb', line 1760

def with_node_set(nodes)
  @node_sets << nodes

  retval = yield

  @node_sets.pop

  retval
end