diff --git a/lib/rexml/parsers/xpathparser.rb b/lib/rexml/parsers/xpathparser.rb index a6d76fdc..c4b8531f 100644 --- a/lib/rexml/parsers/xpathparser.rb +++ b/lib/rexml/parsers/xpathparser.rb @@ -654,7 +654,7 @@ def PrimaryExpr path, parsed contents = contents[1..-2] n = [] OrExpr( contents, n ) - parsed.concat(n) + parsed.push(:group, n) end path end diff --git a/lib/rexml/xpath_parser.rb b/lib/rexml/xpath_parser.rb index c5d420ce..02cd5056 100644 --- a/lib/rexml/xpath_parser.rb +++ b/lib/rexml/xpath_parser.rb @@ -439,7 +439,18 @@ def expr( path_stack, nodeset, context=nil ) end Functions.context = target_context return Functions.send(func_name, *args) - + when :group + sub_expression = path_stack.shift + result = expr(sub_expression, nodeset, context) + if result.is_a?(Array) + # If result is a nodeset, apply following predicates + path_stack.unshift(:node) + nodeset = step(path_stack) do + [result] + end + else + return result + end else raise "[BUG] Unexpected path: <#{op.inspect}>: <#{path_stack.inspect}>" end @@ -594,7 +605,6 @@ def filter_nodeset(nodeset) def evaluate_predicate(expression, nodesets) enter(:predicate, expression, nodesets) if @debug - new_nodeset_count = 0 new_nodesets = nodesets.collect do |nodeset| new_nodeset = [] subcontext = { :size => nodeset.size } @@ -611,20 +621,17 @@ def evaluate_predicate(expression, nodesets) result = result[0] if result.kind_of? Array and result.length == 1 if result.kind_of? Numeric if result == node.position - new_nodeset_count += 1 - new_nodeset << XPathNode.new(node, position: new_nodeset_count) + new_nodeset << XPathNode.new(node, position: new_nodeset.size + 1) end elsif result.instance_of? Array if result.size > 0 and result.inject(false) {|k,s| s or k} if result.size > 0 - new_nodeset_count += 1 - new_nodeset << XPathNode.new(node, position: new_nodeset_count) + new_nodeset << XPathNode.new(node, position: new_nodeset.size + 1) end end else if result - new_nodeset_count += 1 - new_nodeset << XPathNode.new(node, position: new_nodeset_count) + new_nodeset << XPathNode.new(node, position: new_nodeset.size + 1) end end end diff --git a/test/xpath/test_base.rb b/test/xpath/test_base.rb index 1c6eb624..1abba546 100644 --- a/test/xpath/test_base.rb +++ b/test/xpath/test_base.rb @@ -575,18 +575,45 @@ def test_nested_predicates matches = XPath.match(doc, '(/div/div/test[3])').map(&:text) assert_equal [], matches + matches = XPath.match(doc, '/div/div/test[1][1]').map(&:text) + assert_equal ["ab", "ef", "hi"], matches matches = XPath.match(doc, '(/div/div/test[1])[1]').map(&:text) assert_equal ["ab"], matches + matches = XPath.match(doc, '/div/div/test[1][2]').map(&:text) + assert_equal [], matches matches = XPath.match(doc, '(/div/div/test[1])[2]').map(&:text) assert_equal ["ef"], matches matches = XPath.match(doc, '(/div/div/test[1])[3]').map(&:text) assert_equal ["hi"], matches + matches = XPath.match(doc, '/div/div/test[2][1]').map(&:text) + assert_equal ["cd", "gh"], matches matches = XPath.match(doc, '(/div/div/test[2])[1]').map(&:text) assert_equal ["cd"], matches + matches = XPath.match(doc, '/div/div/test[2][2]').map(&:text) + assert_equal [], matches matches = XPath.match(doc, '(/div/div/test[2])[2]').map(&:text) assert_equal ["gh"], matches matches = XPath.match(doc, '(/div/div/test[2])[3]').map(&:text) assert_equal [], matches + matches = XPath.match(doc, '//div[1]/test|//div[2]/test[2]').map(&:text) + assert_equal ["ab", "cd", "gh"], matches + matches = XPath.match(doc, '(//div[1]/test|//div[2]/test)[2]').map(&:text) + assert_equal ["cd"], matches + + xpath = '/div/div/test/preceding::*' + without_parentheses = XPath.match(doc, xpath).map(&:text) + with_parentheses = XPath.match(doc, "(#{xpath})").map(&:text) + assert_equal without_parentheses, with_parentheses + + xpath = '/div/div/test/preceding-sibling::*' + without_parentheses = XPath.match(doc, xpath).map(&:text) + with_parentheses = XPath.match(doc, "(#{xpath})").map(&:text) + assert_equal without_parentheses, with_parentheses + + xpath = '/div/div/test/ancestor::*' + without_parentheses = XPath.match(doc, xpath).map(&:text) + with_parentheses = XPath.match(doc, "(#{xpath})").map(&:text) + assert_equal without_parentheses, with_parentheses end # Contributed by Mike Stok