rexml 3.2.3 → 3.3.8
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of rexml might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/NEWS.md +502 -0
- data/README.md +11 -14
- data/doc/rexml/context.rdoc +143 -0
- data/doc/rexml/tasks/rdoc/child.rdoc +87 -0
- data/doc/rexml/tasks/rdoc/document.rdoc +276 -0
- data/doc/rexml/tasks/rdoc/element.rdoc +602 -0
- data/doc/rexml/tasks/rdoc/node.rdoc +97 -0
- data/doc/rexml/tasks/rdoc/parent.rdoc +267 -0
- data/doc/rexml/tasks/tocs/child_toc.rdoc +12 -0
- data/doc/rexml/tasks/tocs/document_toc.rdoc +30 -0
- data/doc/rexml/tasks/tocs/element_toc.rdoc +55 -0
- data/doc/rexml/tasks/tocs/master_toc.rdoc +135 -0
- data/doc/rexml/tasks/tocs/node_toc.rdoc +16 -0
- data/doc/rexml/tasks/tocs/parent_toc.rdoc +25 -0
- data/doc/rexml/tutorial.rdoc +1358 -0
- data/lib/rexml/attribute.rb +17 -11
- data/lib/rexml/doctype.rb +55 -31
- data/lib/rexml/document.rb +199 -35
- data/lib/rexml/element.rb +1802 -487
- data/lib/rexml/entity.rb +10 -39
- data/lib/rexml/formatters/pretty.rb +3 -3
- data/lib/rexml/functions.rb +1 -2
- data/lib/rexml/light/node.rb +0 -8
- data/lib/rexml/namespace.rb +8 -4
- data/lib/rexml/node.rb +8 -4
- data/lib/rexml/parseexception.rb +1 -0
- data/lib/rexml/parsers/baseparser.rb +513 -250
- data/lib/rexml/parsers/pullparser.rb +12 -0
- data/lib/rexml/parsers/sax2parser.rb +16 -19
- data/lib/rexml/parsers/streamparser.rb +16 -10
- data/lib/rexml/parsers/treeparser.rb +9 -21
- data/lib/rexml/parsers/xpathparser.rb +161 -97
- data/lib/rexml/rexml.rb +29 -22
- data/lib/rexml/source.rb +128 -98
- data/lib/rexml/text.rb +46 -22
- data/lib/rexml/xpath_parser.rb +43 -33
- data/lib/rexml.rb +3 -0
- metadata +42 -46
- data/.gitignore +0 -9
- data/.travis.yml +0 -24
- data/Gemfile +0 -6
- data/Rakefile +0 -8
- data/rexml.gemspec +0 -84
data/lib/rexml/source.rb
CHANGED
@@ -1,8 +1,28 @@
|
|
1
1
|
# coding: US-ASCII
|
2
2
|
# frozen_string_literal: false
|
3
|
+
|
4
|
+
require "strscan"
|
5
|
+
|
3
6
|
require_relative 'encoding'
|
4
7
|
|
5
8
|
module REXML
|
9
|
+
if StringScanner::Version < "1.0.0"
|
10
|
+
module StringScannerCheckScanString
|
11
|
+
refine StringScanner do
|
12
|
+
def check(pattern)
|
13
|
+
pattern = /#{Regexp.escape(pattern)}/ if pattern.is_a?(String)
|
14
|
+
super(pattern)
|
15
|
+
end
|
16
|
+
|
17
|
+
def scan(pattern)
|
18
|
+
pattern = /#{Regexp.escape(pattern)}/ if pattern.is_a?(String)
|
19
|
+
super(pattern)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
using StringScannerCheckScanString
|
24
|
+
end
|
25
|
+
|
6
26
|
# Generates Source-s. USE THIS CLASS.
|
7
27
|
class SourceFactory
|
8
28
|
# Generates a Source object
|
@@ -30,18 +50,27 @@ module REXML
|
|
30
50
|
# objects and provides consumption of text
|
31
51
|
class Source
|
32
52
|
include Encoding
|
33
|
-
# The current buffer (what we're going to read next)
|
34
|
-
attr_reader :buffer
|
35
53
|
# The line number of the last consumed text
|
36
54
|
attr_reader :line
|
37
55
|
attr_reader :encoding
|
38
56
|
|
57
|
+
module Private
|
58
|
+
SCANNER_RESET_SIZE = 100000
|
59
|
+
PRE_DEFINED_TERM_PATTERNS = {}
|
60
|
+
pre_defined_terms = ["'", '"', "<"]
|
61
|
+
pre_defined_terms.each do |term|
|
62
|
+
PRE_DEFINED_TERM_PATTERNS[term] = /#{Regexp.escape(term)}/
|
63
|
+
end
|
64
|
+
end
|
65
|
+
private_constant :Private
|
66
|
+
|
39
67
|
# Constructor
|
40
68
|
# @param arg must be a String, and should be a valid XML document
|
41
69
|
# @param encoding if non-null, sets the encoding of the source to this
|
42
70
|
# value, overriding all encoding detection
|
43
71
|
def initialize(arg, encoding=nil)
|
44
|
-
@orig =
|
72
|
+
@orig = arg
|
73
|
+
@scanner = StringScanner.new(@orig)
|
45
74
|
if encoding
|
46
75
|
self.encoding = encoding
|
47
76
|
else
|
@@ -50,6 +79,20 @@ module REXML
|
|
50
79
|
@line = 0
|
51
80
|
end
|
52
81
|
|
82
|
+
# The current buffer (what we're going to read next)
|
83
|
+
def buffer
|
84
|
+
@scanner.rest
|
85
|
+
end
|
86
|
+
|
87
|
+
def drop_parsed_content
|
88
|
+
if @scanner.pos > Private::SCANNER_RESET_SIZE
|
89
|
+
@scanner.string = @scanner.rest
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def buffer_encoding=(encoding)
|
94
|
+
@scanner.string.force_encoding(encoding)
|
95
|
+
end
|
53
96
|
|
54
97
|
# Inherited from Encoding
|
55
98
|
# Overridden to support optimized en/decoding
|
@@ -58,98 +101,78 @@ module REXML
|
|
58
101
|
encoding_updated
|
59
102
|
end
|
60
103
|
|
61
|
-
|
62
|
-
# usual scan() method. For one thing, the pattern argument has some
|
63
|
-
# requirements; for another, the source can be consumed. You can easily
|
64
|
-
# confuse this method. Originally, the patterns were easier
|
65
|
-
# to construct and this method more robust, because this method
|
66
|
-
# generated search regexps on the fly; however, this was
|
67
|
-
# computationally expensive and slowed down the entire REXML package
|
68
|
-
# considerably, since this is by far the most commonly called method.
|
69
|
-
# @param pattern must be a Regexp, and must be in the form of
|
70
|
-
# /^\s*(#{your pattern, with no groups})(.*)/. The first group
|
71
|
-
# will be returned; the second group is used if the consume flag is
|
72
|
-
# set.
|
73
|
-
# @param consume if true, the pattern returned will be consumed, leaving
|
74
|
-
# everything after it in the Source.
|
75
|
-
# @return the pattern, if found, or nil if the Source is empty or the
|
76
|
-
# pattern is not found.
|
77
|
-
def scan(pattern, cons=false)
|
78
|
-
return nil if @buffer.nil?
|
79
|
-
rv = @buffer.scan(pattern)
|
80
|
-
@buffer = $' if cons and rv.size>0
|
81
|
-
rv
|
104
|
+
def read(term = nil)
|
82
105
|
end
|
83
106
|
|
84
|
-
def
|
107
|
+
def read_until(term)
|
108
|
+
pattern = Private::PRE_DEFINED_TERM_PATTERNS[term] || /#{Regexp.escape(term)}/
|
109
|
+
data = @scanner.scan_until(pattern)
|
110
|
+
unless data
|
111
|
+
data = @scanner.rest
|
112
|
+
@scanner.pos = @scanner.string.bytesize
|
113
|
+
end
|
114
|
+
data
|
85
115
|
end
|
86
116
|
|
87
|
-
def
|
88
|
-
@buffer = $' if pattern.match( @buffer )
|
117
|
+
def ensure_buffer
|
89
118
|
end
|
90
119
|
|
91
|
-
def
|
92
|
-
|
120
|
+
def match(pattern, cons=false)
|
121
|
+
if cons
|
122
|
+
@scanner.scan(pattern).nil? ? nil : @scanner
|
123
|
+
else
|
124
|
+
@scanner.check(pattern).nil? ? nil : @scanner
|
125
|
+
end
|
93
126
|
end
|
94
127
|
|
95
|
-
def
|
96
|
-
|
97
|
-
@buffer = $'
|
98
|
-
return md
|
128
|
+
def position
|
129
|
+
@scanner.pos
|
99
130
|
end
|
100
131
|
|
101
|
-
def
|
102
|
-
|
103
|
-
@buffer = $' if cons and md
|
104
|
-
return md
|
132
|
+
def position=(pos)
|
133
|
+
@scanner.pos = pos
|
105
134
|
end
|
106
135
|
|
107
136
|
# @return true if the Source is exhausted
|
108
137
|
def empty?
|
109
|
-
@
|
110
|
-
end
|
111
|
-
|
112
|
-
def position
|
113
|
-
@orig.index( @buffer )
|
138
|
+
@scanner.eos?
|
114
139
|
end
|
115
140
|
|
116
141
|
# @return the current line in the source
|
117
142
|
def current_line
|
118
143
|
lines = @orig.split
|
119
|
-
res = lines.grep @
|
144
|
+
res = lines.grep @scanner.rest[0..30]
|
120
145
|
res = res[-1] if res.kind_of? Array
|
121
146
|
lines.index( res ) if res
|
122
147
|
end
|
123
148
|
|
124
149
|
private
|
150
|
+
|
125
151
|
def detect_encoding
|
126
|
-
|
152
|
+
scanner_encoding = @scanner.rest.encoding
|
127
153
|
detected_encoding = "UTF-8"
|
128
154
|
begin
|
129
|
-
@
|
130
|
-
if @
|
131
|
-
@buffer[0, 2] = ""
|
155
|
+
@scanner.string.force_encoding("ASCII-8BIT")
|
156
|
+
if @scanner.scan(/\xfe\xff/n)
|
132
157
|
detected_encoding = "UTF-16BE"
|
133
|
-
elsif @
|
134
|
-
@buffer[0, 2] = ""
|
158
|
+
elsif @scanner.scan(/\xff\xfe/n)
|
135
159
|
detected_encoding = "UTF-16LE"
|
136
|
-
elsif @
|
137
|
-
@buffer[0, 3] = ""
|
160
|
+
elsif @scanner.scan(/\xef\xbb\xbf/n)
|
138
161
|
detected_encoding = "UTF-8"
|
139
162
|
end
|
140
163
|
ensure
|
141
|
-
@
|
164
|
+
@scanner.string.force_encoding(scanner_encoding)
|
142
165
|
end
|
143
166
|
self.encoding = detected_encoding
|
144
167
|
end
|
145
168
|
|
146
169
|
def encoding_updated
|
147
170
|
if @encoding != 'UTF-8'
|
148
|
-
@
|
171
|
+
@scanner.string = decode(@scanner.rest)
|
149
172
|
@to_utf = true
|
150
173
|
else
|
151
174
|
@to_utf = false
|
152
|
-
@
|
175
|
+
@scanner.string.force_encoding(::Encoding::UTF_8)
|
153
176
|
end
|
154
177
|
end
|
155
178
|
end
|
@@ -172,7 +195,7 @@ module REXML
|
|
172
195
|
end
|
173
196
|
|
174
197
|
if !@to_utf and
|
175
|
-
@
|
198
|
+
@orig.respond_to?(:force_encoding) and
|
176
199
|
@source.respond_to?(:external_encoding) and
|
177
200
|
@source.external_encoding != ::Encoding::UTF_8
|
178
201
|
@force_utf8 = true
|
@@ -181,65 +204,72 @@ module REXML
|
|
181
204
|
end
|
182
205
|
end
|
183
206
|
|
184
|
-
def
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
rescue Iconv::IllegalSequence
|
196
|
-
raise
|
197
|
-
rescue
|
198
|
-
@source = nil
|
207
|
+
def read(term = nil, min_bytes = 1)
|
208
|
+
term = encode(term) if term
|
209
|
+
begin
|
210
|
+
str = readline(term)
|
211
|
+
@scanner << str
|
212
|
+
read_bytes = str.bytesize
|
213
|
+
begin
|
214
|
+
while read_bytes < min_bytes
|
215
|
+
str = readline(term)
|
216
|
+
@scanner << str
|
217
|
+
read_bytes += str.bytesize
|
199
218
|
end
|
219
|
+
rescue IOError
|
200
220
|
end
|
201
|
-
|
221
|
+
true
|
222
|
+
rescue Exception, NameError
|
223
|
+
@source = nil
|
224
|
+
false
|
202
225
|
end
|
203
|
-
rv.taint
|
204
|
-
rv
|
205
226
|
end
|
206
227
|
|
207
|
-
def
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
@source
|
228
|
+
def read_until(term)
|
229
|
+
pattern = Private::PRE_DEFINED_TERM_PATTERNS[term] || /#{Regexp.escape(term)}/
|
230
|
+
term = encode(term)
|
231
|
+
until str = @scanner.scan_until(pattern)
|
232
|
+
break if @source.nil?
|
233
|
+
break if @source.eof?
|
234
|
+
@scanner << readline(term)
|
235
|
+
end
|
236
|
+
if str
|
237
|
+
read if @scanner.eos? and !@source.eof?
|
238
|
+
str
|
239
|
+
else
|
240
|
+
rest = @scanner.rest
|
241
|
+
@scanner.pos = @scanner.string.bytesize
|
242
|
+
rest
|
212
243
|
end
|
213
244
|
end
|
214
245
|
|
215
|
-
def
|
216
|
-
|
246
|
+
def ensure_buffer
|
247
|
+
read if @scanner.eos? && @source
|
217
248
|
end
|
218
249
|
|
219
250
|
def match( pattern, cons=false )
|
220
|
-
|
221
|
-
|
222
|
-
while
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
rescue
|
228
|
-
@source = nil
|
251
|
+
# To avoid performance issue, we need to increase bytes to read per scan
|
252
|
+
min_bytes = 1
|
253
|
+
while true
|
254
|
+
if cons
|
255
|
+
md = @scanner.scan(pattern)
|
256
|
+
else
|
257
|
+
md = @scanner.check(pattern)
|
229
258
|
end
|
259
|
+
break if md
|
260
|
+
return nil if pattern.is_a?(String)
|
261
|
+
return nil if @source.nil?
|
262
|
+
return nil unless read(nil, min_bytes)
|
263
|
+
min_bytes *= 2
|
230
264
|
end
|
231
|
-
|
232
|
-
|
265
|
+
|
266
|
+
md.nil? ? nil : @scanner
|
233
267
|
end
|
234
268
|
|
235
269
|
def empty?
|
236
270
|
super and ( @source.nil? || @source.eof? )
|
237
271
|
end
|
238
272
|
|
239
|
-
def position
|
240
|
-
@er_source.pos rescue 0
|
241
|
-
end
|
242
|
-
|
243
273
|
# @return the current line in the source
|
244
274
|
def current_line
|
245
275
|
begin
|
@@ -263,8 +293,8 @@ module REXML
|
|
263
293
|
end
|
264
294
|
|
265
295
|
private
|
266
|
-
def readline
|
267
|
-
str = @source.readline(@line_break)
|
296
|
+
def readline(term = nil)
|
297
|
+
str = @source.readline(term || @line_break)
|
268
298
|
if @pending_buffer
|
269
299
|
if str.nil?
|
270
300
|
str = @pending_buffer
|
@@ -290,7 +320,7 @@ module REXML
|
|
290
320
|
@source.set_encoding(@encoding, @encoding)
|
291
321
|
end
|
292
322
|
@line_break = encode(">")
|
293
|
-
@pending_buffer, @
|
323
|
+
@pending_buffer, @scanner.string = @scanner.rest, ""
|
294
324
|
@pending_buffer.force_encoding(@encoding)
|
295
325
|
super
|
296
326
|
end
|
data/lib/rexml/text.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# frozen_string_literal:
|
1
|
+
# frozen_string_literal: true
|
2
2
|
require_relative 'security'
|
3
3
|
require_relative 'entity'
|
4
4
|
require_relative 'doctype'
|
@@ -109,7 +109,7 @@ module REXML
|
|
109
109
|
@string = arg.instance_variable_get(:@string).dup
|
110
110
|
@raw = arg.raw
|
111
111
|
@entity_filter = arg.instance_variable_get(:@entity_filter)
|
112
|
-
|
112
|
+
else
|
113
113
|
raise "Illegal argument of type #{arg.type} for Text constructor (#{arg})"
|
114
114
|
end
|
115
115
|
|
@@ -131,7 +131,7 @@ module REXML
|
|
131
131
|
def Text.check string, pattern, doctype
|
132
132
|
|
133
133
|
# illegal anywhere
|
134
|
-
if string
|
134
|
+
if !string.match?(VALID_XML_CHARS)
|
135
135
|
if String.method_defined? :encode
|
136
136
|
string.chars.each do |c|
|
137
137
|
case c.ord
|
@@ -151,25 +151,45 @@ module REXML
|
|
151
151
|
end
|
152
152
|
end
|
153
153
|
|
154
|
-
|
155
|
-
string.
|
156
|
-
if
|
157
|
-
raise "Illegal character #{
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
154
|
+
pos = 0
|
155
|
+
while (index = string.index(/<|&/, pos))
|
156
|
+
if string[index] == "<"
|
157
|
+
raise "Illegal character \"#{string[index]}\" in raw string #{string.inspect}"
|
158
|
+
end
|
159
|
+
|
160
|
+
unless (end_index = string.index(/[^\s];/, index + 1))
|
161
|
+
raise "Illegal character \"#{string[index]}\" in raw string #{string.inspect}"
|
162
|
+
end
|
163
|
+
|
164
|
+
value = string[(index + 1)..end_index]
|
165
|
+
if /\s/.match?(value)
|
166
|
+
raise "Illegal character \"#{string[index]}\" in raw string #{string.inspect}"
|
167
|
+
end
|
168
|
+
|
169
|
+
if value[0] == "#"
|
170
|
+
character_reference = value[1..-1]
|
171
|
+
|
172
|
+
unless (/\A(\d+|x[0-9a-fA-F]+)\z/.match?(character_reference))
|
173
|
+
if character_reference[0] == "x" || character_reference[-1] == "x"
|
174
|
+
raise "Illegal character \"#{string[index]}\" in raw string #{string.inspect}"
|
162
175
|
else
|
163
|
-
raise "Illegal character #{
|
176
|
+
raise "Illegal character #{string.inspect} in raw string #{string.inspect}"
|
164
177
|
end
|
165
|
-
# FIXME: below can't work but this needs API change.
|
166
|
-
# elsif @parent and $3 and !SUBSTITUTES.include?($1)
|
167
|
-
# if !doctype or !doctype.entities.has_key?($3)
|
168
|
-
# raise "Undeclared entity '#{$1}' in raw string \"#{string}\""
|
169
|
-
# end
|
170
178
|
end
|
179
|
+
|
180
|
+
case (character_reference[0] == "x" ? character_reference[1..-1].to_i(16) : character_reference[0..-1].to_i)
|
181
|
+
when *VALID_CHAR
|
182
|
+
else
|
183
|
+
raise "Illegal character #{string.inspect} in raw string #{string.inspect}"
|
184
|
+
end
|
185
|
+
elsif !(/\A#{Entity::NAME}\z/um.match?(value))
|
186
|
+
raise "Illegal character \"#{string[index]}\" in raw string #{string.inspect}"
|
171
187
|
end
|
188
|
+
|
189
|
+
pos = end_index + 1
|
172
190
|
end
|
191
|
+
|
192
|
+
string
|
173
193
|
end
|
174
194
|
|
175
195
|
def node_type
|
@@ -248,7 +268,8 @@ module REXML
|
|
248
268
|
# u = Text.new( "sean russell", false, nil, true )
|
249
269
|
# u.value #-> "sean russell"
|
250
270
|
def value
|
251
|
-
@unnormalized ||= Text::unnormalize(
|
271
|
+
@unnormalized ||= Text::unnormalize(@string, doctype,
|
272
|
+
entity_expansion_text_limit: document&.entity_expansion_text_limit)
|
252
273
|
end
|
253
274
|
|
254
275
|
# Sets the contents of this text node. This expects the text to be
|
@@ -371,7 +392,7 @@ module REXML
|
|
371
392
|
copy = input.to_s
|
372
393
|
# Doing it like this rather than in a loop improves the speed
|
373
394
|
#copy = copy.gsub( EREFERENCE, '&' )
|
374
|
-
copy = copy.gsub( "&", "&" )
|
395
|
+
copy = copy.gsub( "&", "&" ) if copy.include?("&")
|
375
396
|
if doctype
|
376
397
|
# Replace all ampersands that aren't part of an entity
|
377
398
|
doctype.entities.each_value do |entity|
|
@@ -382,18 +403,21 @@ module REXML
|
|
382
403
|
else
|
383
404
|
# Replace all ampersands that aren't part of an entity
|
384
405
|
DocType::DEFAULT_ENTITIES.each_value do |entity|
|
385
|
-
|
406
|
+
if copy.include?(entity.value)
|
407
|
+
copy = copy.gsub(entity.value, "&#{entity.name};" )
|
408
|
+
end
|
386
409
|
end
|
387
410
|
end
|
388
411
|
copy
|
389
412
|
end
|
390
413
|
|
391
414
|
# Unescapes all possible entities
|
392
|
-
def Text::unnormalize( string, doctype=nil, filter=nil, illegal=nil )
|
415
|
+
def Text::unnormalize( string, doctype=nil, filter=nil, illegal=nil, entity_expansion_text_limit: nil )
|
416
|
+
entity_expansion_text_limit ||= Security.entity_expansion_text_limit
|
393
417
|
sum = 0
|
394
418
|
string.gsub( /\r\n?/, "\n" ).gsub( REFERENCE ) {
|
395
419
|
s = Text.expand($&, doctype, filter)
|
396
|
-
if sum + s.bytesize >
|
420
|
+
if sum + s.bytesize > entity_expansion_text_limit
|
397
421
|
raise "entity expansion has grown too large"
|
398
422
|
else
|
399
423
|
sum += s.bytesize
|
data/lib/rexml/xpath_parser.rb
CHANGED
@@ -7,39 +7,45 @@ require_relative 'xmltokens'
|
|
7
7
|
require_relative 'attribute'
|
8
8
|
require_relative 'parsers/xpathparser'
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
10
|
+
module REXML
|
11
|
+
module DClonable
|
12
|
+
refine Object do
|
13
|
+
# provides a unified +clone+ operation, for REXML::XPathParser
|
14
|
+
# to use across multiple Object types
|
15
|
+
def dclone
|
16
|
+
clone
|
17
|
+
end
|
18
|
+
end
|
19
|
+
refine Symbol do
|
20
|
+
# provides a unified +clone+ operation, for REXML::XPathParser
|
21
|
+
# to use across multiple Object types
|
22
|
+
def dclone ; self ; end
|
23
|
+
end
|
24
|
+
refine Integer do
|
25
|
+
# provides a unified +clone+ operation, for REXML::XPathParser
|
26
|
+
# to use across multiple Object types
|
27
|
+
def dclone ; self ; end
|
28
|
+
end
|
29
|
+
refine Float do
|
30
|
+
# provides a unified +clone+ operation, for REXML::XPathParser
|
31
|
+
# to use across multiple Object types
|
32
|
+
def dclone ; self ; end
|
33
|
+
end
|
34
|
+
refine Array do
|
35
|
+
# provides a unified +clone+ operation, for REXML::XPathParser
|
36
|
+
# to use across multiple Object+ types
|
37
|
+
def dclone
|
38
|
+
klone = self.clone
|
39
|
+
klone.clear
|
40
|
+
self.each{|v| klone << v.dclone}
|
41
|
+
klone
|
42
|
+
end
|
43
|
+
end
|
40
44
|
end
|
41
45
|
end
|
42
46
|
|
47
|
+
using REXML::DClonable
|
48
|
+
|
43
49
|
module REXML
|
44
50
|
# You don't want to use this class. Really. Use XPath, which is a wrapper
|
45
51
|
# for this class. Believe me. You don't want to poke around in here.
|
@@ -584,6 +590,7 @@ module REXML
|
|
584
590
|
|
585
591
|
def evaluate_predicate(expression, nodesets)
|
586
592
|
enter(:predicate, expression, nodesets) if @debug
|
593
|
+
new_nodeset_count = 0
|
587
594
|
new_nodesets = nodesets.collect do |nodeset|
|
588
595
|
new_nodeset = []
|
589
596
|
subcontext = { :size => nodeset.size }
|
@@ -600,17 +607,20 @@ module REXML
|
|
600
607
|
result = result[0] if result.kind_of? Array and result.length == 1
|
601
608
|
if result.kind_of? Numeric
|
602
609
|
if result == node.position
|
603
|
-
|
610
|
+
new_nodeset_count += 1
|
611
|
+
new_nodeset << XPathNode.new(node, position: new_nodeset_count)
|
604
612
|
end
|
605
613
|
elsif result.instance_of? Array
|
606
614
|
if result.size > 0 and result.inject(false) {|k,s| s or k}
|
607
615
|
if result.size > 0
|
608
|
-
|
616
|
+
new_nodeset_count += 1
|
617
|
+
new_nodeset << XPathNode.new(node, position: new_nodeset_count)
|
609
618
|
end
|
610
619
|
end
|
611
620
|
else
|
612
621
|
if result
|
613
|
-
|
622
|
+
new_nodeset_count += 1
|
623
|
+
new_nodeset << XPathNode.new(node, position: new_nodeset_count)
|
614
624
|
end
|
615
625
|
end
|
616
626
|
end
|
data/lib/rexml.rb
ADDED