Skip to content

Commit c90feec

Browse files
committed
Sort nodeset on demand
1 parent 5815baf commit c90feec

3 files changed

Lines changed: 26 additions & 21 deletions

File tree

lib/rexml/functions.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ def Functions::get_namespace( node_set = nil )
8484
else
8585
if node_set.respond_to? :each
8686
result = []
87-
node_set.each do |node|
87+
XPathParser.sort(node_set.to_a).each do |node|
8888
result << yield(node) if node.respond_to?(:namespace)
8989
end
9090
result
@@ -146,7 +146,7 @@ def Functions::string( object=@@context[:node] )
146146
else
147147
case object
148148
when Array
149-
string(object[0])
149+
string(XPathParser.sort(object)[0])
150150
when Float
151151
if object.nan?
152152
"NaN"

lib/rexml/xpath_parser.rb

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ def match(path_stack, node)
155155
result = expr(path_stack, nodeset)
156156
case result
157157
when Array # nodeset
158-
result.uniq
158+
XPathParser.sort(result)
159159
else
160160
[result]
161161
end
@@ -198,7 +198,7 @@ def expr( path_stack, nodeset, context=nil )
198198
nodeset = [nodeset.first.root_node]
199199
when :self
200200
nodeset = step(path_stack) do
201-
[nodeset]
201+
[XPathParser.sort(nodeset)]
202202
end
203203
when :child
204204
nodeset = step(path_stack) do
@@ -234,7 +234,7 @@ def expr( path_stack, nodeset, context=nil )
234234
nodesets
235235
end
236236
when :ancestor
237-
nodeset = step(path_stack, axis_order: :reverse) do
237+
nodeset = step(path_stack) do
238238
nodesets = []
239239
# new_nodes = {}
240240
nodeset.each do |node|
@@ -250,7 +250,7 @@ def expr( path_stack, nodeset, context=nil )
250250
nodesets
251251
end
252252
when :ancestor_or_self
253-
nodeset = step(path_stack, axis_order: :reverse) do
253+
nodeset = step(path_stack) do
254254
nodesets = []
255255
# new_nodes = {}
256256
nodeset.each do |node|
@@ -288,7 +288,7 @@ def expr( path_stack, nodeset, context=nil )
288288
end.compact
289289
end
290290
when :preceding_sibling
291-
nodeset = step(path_stack, axis_order: :reverse) do
291+
nodeset = step(path_stack) do
292292
nodeset.map do |node|
293293
next unless node.respond_to?(:parent)
294294
next if node.parent.nil?
@@ -300,7 +300,7 @@ def expr( path_stack, nodeset, context=nil )
300300
end.compact
301301
end
302302
when :preceding
303-
nodeset = step(path_stack, axis_order: :reverse) do
303+
nodeset = step(path_stack) do
304304
nodeset.map do |node|
305305
preceding(node)
306306
end
@@ -393,7 +393,7 @@ def expr( path_stack, nodeset, context=nil )
393393
# If result is a nodeset, apply following predicates
394394
path_stack.unshift(:node)
395395
nodeset = step(path_stack) do
396-
[result]
396+
[XPathParser.sort(result)]
397397
end
398398
else
399399
return result
@@ -407,7 +407,7 @@ def expr( path_stack, nodeset, context=nil )
407407
leave(:expr, path_stack, nodeset) if @debug
408408
end
409409

410-
def step(path_stack, any_type: :element, axis_order: :forward)
410+
def step(path_stack, any_type: :element)
411411
nodesets = yield
412412
begin
413413
enter(:step, path_stack, nodesets) if @debug
@@ -417,18 +417,14 @@ def step(path_stack, any_type: :element, axis_order: :forward)
417417
predicate_expression = path_stack.shift.dclone
418418
nodesets = evaluate_predicate(predicate_expression, nodesets)
419419
end
420-
if nodesets.size == 1
421-
new_nodeset = axis_order == :forward ? nodesets.first : nodesets.first.reverse
422-
else
423-
nodes = Set.new.compare_by_identity
424-
nodesets.each do |nodeset|
425-
nodeset.each do |node|
426-
nodes << node
427-
end
420+
421+
nodes = Set.new.compare_by_identity
422+
nodesets.each do |nodeset|
423+
nodeset.each do |node|
424+
nodes << node
428425
end
429-
new_nodeset = sort(nodes.to_a)
430426
end
431-
new_nodeset
427+
new_nodeset = nodes.to_a
432428
ensure
433429
leave(:step, path_stack, new_nodeset) if @debug
434430
end
@@ -579,7 +575,9 @@ def leave(tag, *args)
579575
# in and out of function calls. If I knew what the index of the nodes was,
580576
# I wouldn't have to do this. Maybe add a document IDX for each node?
581577
# Problems with mutable documents. Or, rewrite everything.
582-
def sort(array_of_nodes)
578+
def self.sort(array_of_nodes)
579+
return array_of_nodes if array_of_nodes.size <= 1
580+
583581
new_arry = []
584582
array_of_nodes.each { |node|
585583
node_idx = []

test/test_jaxen.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,13 @@ def process_value_of(context, variables, namespaces, value_of)
8787
xpath = value_of.attributes["select"]
8888
matched = XPath.match(context, xpath, namespaces, variables, strict: true)
8989

90+
# XPath.match can be a nodeset or a primitive value wrapped in an array.
91+
# We need to unwrap primitive value because Functions doesn't accept array which is not a nodeset.
92+
unless matched.all? { |node| node.is_a?(REXML::Node) }
93+
raise "[BUG] Primitive value should be a single value: #{matched.inspect}" if matched.size != 1
94+
matched = matched.first
95+
end
96+
9097
message = user_message(context, xpath, matched)
9198
assert_equal(expected || "",
9299
REXML::Functions.string(matched),

0 commit comments

Comments
 (0)