Skip to content

Commit 2732516

Browse files
authored
Partially support parenthesized xpath (#317)
Fix #25, and also reverts #122. Adds a new path_stack operator `:group`
1 parent 7ac7ef4 commit 2732516

3 files changed

Lines changed: 43 additions & 9 deletions

File tree

lib/rexml/parsers/xpathparser.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -654,7 +654,7 @@ def PrimaryExpr path, parsed
654654
contents = contents[1..-2]
655655
n = []
656656
OrExpr( contents, n )
657-
parsed.concat(n)
657+
parsed.push(:group, n)
658658
end
659659
path
660660
end

lib/rexml/xpath_parser.rb

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -439,7 +439,18 @@ def expr( path_stack, nodeset, context=nil )
439439
end
440440
Functions.context = target_context
441441
return Functions.send(func_name, *args)
442-
442+
when :group
443+
sub_expression = path_stack.shift
444+
result = expr(sub_expression, nodeset, context)
445+
if result.is_a?(Array)
446+
# If result is a nodeset, apply following predicates
447+
path_stack.unshift(:node)
448+
nodeset = step(path_stack) do
449+
[result]
450+
end
451+
else
452+
return result
453+
end
443454
else
444455
raise "[BUG] Unexpected path: <#{op.inspect}>: <#{path_stack.inspect}>"
445456
end
@@ -594,7 +605,6 @@ def filter_nodeset(nodeset)
594605

595606
def evaluate_predicate(expression, nodesets)
596607
enter(:predicate, expression, nodesets) if @debug
597-
new_nodeset_count = 0
598608
new_nodesets = nodesets.collect do |nodeset|
599609
new_nodeset = []
600610
subcontext = { :size => nodeset.size }
@@ -611,20 +621,17 @@ def evaluate_predicate(expression, nodesets)
611621
result = result[0] if result.kind_of? Array and result.length == 1
612622
if result.kind_of? Numeric
613623
if result == node.position
614-
new_nodeset_count += 1
615-
new_nodeset << XPathNode.new(node, position: new_nodeset_count)
624+
new_nodeset << XPathNode.new(node, position: new_nodeset.size + 1)
616625
end
617626
elsif result.instance_of? Array
618627
if result.size > 0 and result.inject(false) {|k,s| s or k}
619628
if result.size > 0
620-
new_nodeset_count += 1
621-
new_nodeset << XPathNode.new(node, position: new_nodeset_count)
629+
new_nodeset << XPathNode.new(node, position: new_nodeset.size + 1)
622630
end
623631
end
624632
else
625633
if result
626-
new_nodeset_count += 1
627-
new_nodeset << XPathNode.new(node, position: new_nodeset_count)
634+
new_nodeset << XPathNode.new(node, position: new_nodeset.size + 1)
628635
end
629636
end
630637
end

test/xpath/test_base.rb

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -575,18 +575,45 @@ def test_nested_predicates
575575
matches = XPath.match(doc, '(/div/div/test[3])').map(&:text)
576576
assert_equal [], matches
577577

578+
matches = XPath.match(doc, '/div/div/test[1][1]').map(&:text)
579+
assert_equal ["ab", "ef", "hi"], matches
578580
matches = XPath.match(doc, '(/div/div/test[1])[1]').map(&:text)
579581
assert_equal ["ab"], matches
582+
matches = XPath.match(doc, '/div/div/test[1][2]').map(&:text)
583+
assert_equal [], matches
580584
matches = XPath.match(doc, '(/div/div/test[1])[2]').map(&:text)
581585
assert_equal ["ef"], matches
582586
matches = XPath.match(doc, '(/div/div/test[1])[3]').map(&:text)
583587
assert_equal ["hi"], matches
588+
matches = XPath.match(doc, '/div/div/test[2][1]').map(&:text)
589+
assert_equal ["cd", "gh"], matches
584590
matches = XPath.match(doc, '(/div/div/test[2])[1]').map(&:text)
585591
assert_equal ["cd"], matches
592+
matches = XPath.match(doc, '/div/div/test[2][2]').map(&:text)
593+
assert_equal [], matches
586594
matches = XPath.match(doc, '(/div/div/test[2])[2]').map(&:text)
587595
assert_equal ["gh"], matches
588596
matches = XPath.match(doc, '(/div/div/test[2])[3]').map(&:text)
589597
assert_equal [], matches
598+
matches = XPath.match(doc, '//div[1]/test|//div[2]/test[2]').map(&:text)
599+
assert_equal ["ab", "cd", "gh"], matches
600+
matches = XPath.match(doc, '(//div[1]/test|//div[2]/test)[2]').map(&:text)
601+
assert_equal ["cd"], matches
602+
603+
xpath = '/div/div/test/preceding::*'
604+
without_parentheses = XPath.match(doc, xpath).map(&:text)
605+
with_parentheses = XPath.match(doc, "(#{xpath})").map(&:text)
606+
assert_equal without_parentheses, with_parentheses
607+
608+
xpath = '/div/div/test/preceding-sibling::*'
609+
without_parentheses = XPath.match(doc, xpath).map(&:text)
610+
with_parentheses = XPath.match(doc, "(#{xpath})").map(&:text)
611+
assert_equal without_parentheses, with_parentheses
612+
613+
xpath = '/div/div/test/ancestor::*'
614+
without_parentheses = XPath.match(doc, xpath).map(&:text)
615+
with_parentheses = XPath.match(doc, "(#{xpath})").map(&:text)
616+
assert_equal without_parentheses, with_parentheses
590617
end
591618

592619
# Contributed by Mike Stok

0 commit comments

Comments
 (0)