vcdom 0.3.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.rdoc +33 -0
- data/lib/vcdom/attr.rb +3 -3
- data/lib/vcdom/character_data.rb +8 -1
- data/lib/vcdom/document.rb +27 -11
- data/lib/vcdom/element.rb +87 -1
- data/lib/vcdom/node.rb +29 -1
- data/lib/vcdom/parent.rb +9 -0
- data/lib/vcdom/{xml_parser.rb → xml_ls/xml_parser.rb} +123 -94
- data/lib/vcdom/{xml_serializer.rb → xml_ls/xml_serializer.rb} +1 -1
- data/lib/vcdom/xml_ls.rb +42 -0
- data/lib/vcdom/xpath/evaluative.rb.old +73 -0
- data/lib/vcdom/xpath/internal/command.rb +121 -0
- data/lib/vcdom/xpath/internal/evaluator.rb +307 -0
- data/lib/vcdom/xpath/internal/expr.rb +35 -0
- data/lib/vcdom/xpath/internal/parser.rb +333 -0
- data/lib/vcdom/xpath/internal/value.rb +342 -0
- data/lib/vcdom/xpath/xpath_evaluator_mod.rb +32 -0
- data/lib/vcdom/xpath/xpath_exception.rb +23 -0
- data/lib/vcdom/xpath/xpath_expression.rb +79 -0
- data/lib/vcdom/xpath/xpath_ns_resolver.rb +36 -0
- data/lib/vcdom/xpath/xpath_result.rb +104 -0
- data/lib/vcdom/xpath.rb +15 -0
- data/test/main_test_1.8.rb.old +1 -0
- data/test/main_test_1.9.rb.old +25 -0
- data/test/test_main.rb +6 -0
- data/test/test_parsing.rb +41 -0
- data/test/test_xpath.rb +266 -0
- metadata +34 -25
@@ -0,0 +1,333 @@
|
|
1
|
+
# coding : utf-8
|
2
|
+
|
3
|
+
require "strscan"
|
4
|
+
|
5
|
+
require "vcdom/xpath/xpath_expression"
|
6
|
+
require "vcdom/xpath/xpath_result"
|
7
|
+
require "vcdom/xpath/internal/value"
|
8
|
+
require "vcdom/xpath/internal/expr"
|
9
|
+
require "vcdom/xpath/internal/command"
|
10
|
+
|
11
|
+
module VCDOM::XPath::Internal # :nodoc:
|
12
|
+
|
13
|
+
class Parser # :nodoc: all
|
14
|
+
|
15
|
+
regexp_str_creating_proc = lambda { |*args|
|
16
|
+
args.length == 1 ? [ args[0] ].pack( "U1" ) : [ args[0], 0x2D, args[1] ].pack( "U3" )
|
17
|
+
}
|
18
|
+
|
19
|
+
xml_name_start_char = [
|
20
|
+
[ 0xC0, 0xD6 ],
|
21
|
+
[ 0xD8, 0xF6 ],
|
22
|
+
[ 0xF8, 0x2FF ],
|
23
|
+
[ 0x370, 0x37D ],
|
24
|
+
[ 0x37F, 0x1FFF ],
|
25
|
+
[ 0x200C, 0x200D ],
|
26
|
+
[ 0x2070, 0x218F ],
|
27
|
+
[ 0x2C00, 0x2FEF ],
|
28
|
+
[ 0x3001, 0xD7FF ],
|
29
|
+
[ 0xF900, 0xFDCF ],
|
30
|
+
[ 0xFDF0, 0xFFFD ],
|
31
|
+
[ 0x10000, 0xEFFFF ]
|
32
|
+
].inject( String.new( "_a-zA-Z" ) ) do |s,pair|
|
33
|
+
s << regexp_str_creating_proc.call( *pair )
|
34
|
+
end
|
35
|
+
|
36
|
+
xml_name_char = [
|
37
|
+
[ 0xB7 ],
|
38
|
+
[ 0x0300, 0x036F ],
|
39
|
+
[ 0x203F, 0x2040 ]
|
40
|
+
].inject( String.new( xml_name_start_char + "\\-\\.\\d" ) ) do |s,pair|
|
41
|
+
s << regexp_str_creating_proc.call( *pair )
|
42
|
+
end
|
43
|
+
|
44
|
+
xml_nc_name = "[#{xml_name_start_char}][#{xml_name_char}]*"
|
45
|
+
xml_q_name = "(?:#{xml_nc_name}:)?#{xml_nc_name}"
|
46
|
+
xpath_literal = "\"[^\"]*\"|'[^']*'"
|
47
|
+
xpath_operator = "\\/?\\/|\\||\\+|\\-|=|\\!=|<=?|>=?"
|
48
|
+
xpath_sign = "\\(|\\)|\\[|\\]|\\.\\.?|@|,|::"
|
49
|
+
xpath_number = "\\d+(?:\\.\\d*)?|\\.\\d+"
|
50
|
+
#xpath_varref = "\\$#{xml_q_name}" # varref は anyname と結合
|
51
|
+
xpath_anyname = "(?:#{xml_nc_name}:)?\\*|\\$?#{xml_q_name}"
|
52
|
+
#XML_NC_NAME_REGEXP = /#{xml_nc_name}/u
|
53
|
+
#XML_Q_NAME_REGEXP = /(?:#{xml_nc_name}:)?#{xml_nc_name}/u
|
54
|
+
|
55
|
+
xpath_token = xpath_literal + "|" + xpath_number + "|" + xpath_anyname + "|" + xpath_operator + "|" + xpath_sign
|
56
|
+
|
57
|
+
XML_Q_NAME = /#{xml_q_name}/u
|
58
|
+
XPATH_TOKEN_REGEXP = /#{xpath_token}/u
|
59
|
+
XPATH_WHITE_SPACE_REGEXP = /[\x20\x0A\x0D\x09]+/u
|
60
|
+
|
61
|
+
#def next_token()
|
62
|
+
# @scanner.skip XPATH_WHITE_SPACE_REGEXP
|
63
|
+
# if ( token = @scanner.scan XPATH_TOKEN_REGEXP ).nil? then
|
64
|
+
# raise "Invalid XPath expression" unless @scanner.eos?
|
65
|
+
# end
|
66
|
+
# token
|
67
|
+
#end
|
68
|
+
|
69
|
+
class XPathStringScanner < StringScanner
|
70
|
+
XPATH_WHITE_SPACE_REGEXP = /[\x20\x0A\x0D\x09]+/u
|
71
|
+
def scan( regexp )
|
72
|
+
skip XPATH_WHITE_SPACE_REGEXP
|
73
|
+
super( regexp )
|
74
|
+
end
|
75
|
+
def check( regexp )
|
76
|
+
skip XPATH_WHITE_SPACE_REGEXP
|
77
|
+
super( regexp )
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def initialize( xpath_str, ns_resolver )
|
82
|
+
@scanner = XPathStringScanner.new( xpath_str )
|
83
|
+
@ns_resolver = ns_resolver
|
84
|
+
end
|
85
|
+
|
86
|
+
def parse()
|
87
|
+
expr = parse_expr()
|
88
|
+
raise "invalid xpath [#{@scanner.rest}]" unless @scanner.eos?
|
89
|
+
expr
|
90
|
+
end
|
91
|
+
|
92
|
+
def parse_expr()
|
93
|
+
parse_or_expr()
|
94
|
+
end
|
95
|
+
|
96
|
+
def parse_or_expr()
|
97
|
+
expr = parse_and_expr()
|
98
|
+
if @scanner.check( /or/u ) then
|
99
|
+
tmp = expr
|
100
|
+
expr = OrExpr.new()
|
101
|
+
expr << tmp
|
102
|
+
while @scanner.scan( /or/u ) do
|
103
|
+
expr << parse_and_expr()
|
104
|
+
end
|
105
|
+
end
|
106
|
+
expr
|
107
|
+
end
|
108
|
+
|
109
|
+
def parse_and_expr()
|
110
|
+
expr = parse_equality_expr()
|
111
|
+
if token = @scanner.check( /and/u ) then
|
112
|
+
tmp = expr
|
113
|
+
expr = AndExpr.new()
|
114
|
+
expr << tmp
|
115
|
+
while @scanner.scan( /and/u ) do
|
116
|
+
expr << parse_equality_expr()
|
117
|
+
end
|
118
|
+
end
|
119
|
+
expr
|
120
|
+
end
|
121
|
+
|
122
|
+
def parse_equality_expr()
|
123
|
+
expr = VCDOM::XPath::Internal::EqualityExpr.new()
|
124
|
+
parse_relational_expr( expr )
|
125
|
+
while token = @scanner.scan( /=|\!=/u ) do
|
126
|
+
parse_relational_expr( expr )
|
127
|
+
expr << VCDOM::XPath::Internal.get_operation_command( token.intern )
|
128
|
+
end
|
129
|
+
expr
|
130
|
+
end
|
131
|
+
|
132
|
+
def parse_relational_expr( expr )
|
133
|
+
parse_additive_expr( expr )
|
134
|
+
while token = @scanner.scan( /\<=?|\>=?/u ) do
|
135
|
+
parse_additive_expr( expr )
|
136
|
+
expr << VCDOM::XPath::Internal.get_operation_command( token.intern )
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def parse_additive_expr( expr )
|
141
|
+
parse_multiplicative_expr( expr )
|
142
|
+
while token = @scanner.scan( /\+|\-/u ) do
|
143
|
+
parse_multiplicative_expr( expr )
|
144
|
+
expr << VCDOM::XPath::Internal.get_operation_command( token.intern )
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def parse_multiplicative_expr( expr )
|
149
|
+
parse_unary_expr( expr )
|
150
|
+
while token = @scanner.scan( /\*|div|mod/u ) do
|
151
|
+
parse_unary_expr( expr )
|
152
|
+
expr << VCDOM::XPath::Internal.get_operation_command( token.intern )
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def parse_unary_expr( expr )
|
157
|
+
num = 0
|
158
|
+
while @scanner.scan( /\-/u ) do
|
159
|
+
num += 1
|
160
|
+
end
|
161
|
+
parse_union_expr( expr )
|
162
|
+
num.times do
|
163
|
+
expr << VCDOM::XPath::Internal.get_operation_command( :"-@" )
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def parse_union_expr( expr )
|
168
|
+
parse_path_expr( expr )
|
169
|
+
while @scanner.scan( /\|/u ) do
|
170
|
+
parse_path_expr( expr )
|
171
|
+
expr << VCDOM::XPath::Internal.get_operation_command( :"|" )
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
XPATH_PATH_EXPR_STARTER_REGEXP = /#{xpath_number}|#{xpath_literal}|#{xpath_anyname}|\/\/?|\.\.?|@|\(/u
|
176
|
+
XPATH_VARIABLE_REFERENCE_REGEXP = /\$#{xml_q_name}/u
|
177
|
+
XPATH_NUMBER_REGEXP = /#{xpath_number}/u
|
178
|
+
XPATH_LITERAL_REGEXP = /#{xpath_literal}/u
|
179
|
+
XPATH_NAME_TEST = /(?:#{xml_nc_name}:)?\*|#{xml_q_name}/u
|
180
|
+
XPATH_STEP_STARTER_REGEXP = /\*|\.\.?|@|#{xml_q_name}/u
|
181
|
+
# VariableReference
|
182
|
+
# '(' Expr ')'
|
183
|
+
# Literal
|
184
|
+
# Number
|
185
|
+
# FunctionCall
|
186
|
+
def parse_path_expr( expr )
|
187
|
+
is_path = false
|
188
|
+
if token = @scanner.scan( XPATH_NUMBER_REGEXP ) then
|
189
|
+
# Number の場合
|
190
|
+
expr << VCDOM::XPath::Internal::NumberValue.new( token.to_f() )
|
191
|
+
elsif token = @scanner.scan( XPATH_LITERAL_REGEXP) then
|
192
|
+
# Literal の場合
|
193
|
+
token[0,1] = ""
|
194
|
+
token[-1,1] = ""
|
195
|
+
expr << VCDOM::XPath::Internal::StringValue.new( token )
|
196
|
+
elsif token = @scanner.scan( XPATH_VARIABLE_REFERENCE_REGEXP ) then
|
197
|
+
# VariableReference の場合
|
198
|
+
raise "VariableReference is not supported"
|
199
|
+
elsif @scanner.scan( /\(/u ) then
|
200
|
+
# "(" Expr ")" の場合
|
201
|
+
expr << VCDOM::XPath::Internal::ExprEvalCommand.new( parse_expr() )
|
202
|
+
raise "invalid xpath expression" unless @scanner.scan( /\)/u )
|
203
|
+
else
|
204
|
+
# Path or FunctionCall
|
205
|
+
pos = @scanner.pos
|
206
|
+
is_path = true
|
207
|
+
if token = @scanner.scan( XML_Q_NAME ) then
|
208
|
+
# FunctionName or AxisName or NameTest or NodeType
|
209
|
+
if @scanner.scan( /\(/u ) then
|
210
|
+
# FunctionName or NodeType
|
211
|
+
case token
|
212
|
+
when "comment", "text", "processing-instruction", "node"
|
213
|
+
# do nothing
|
214
|
+
else
|
215
|
+
# FunctionName
|
216
|
+
is_path = false
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
if is_path then
|
221
|
+
# Path
|
222
|
+
@scanner.pos = pos
|
223
|
+
if token = @scanner.scan( /\/\/?/u ) then
|
224
|
+
# "/" or "//"
|
225
|
+
expr << VCDOM::XPath::Internal::RootNodeCommand.get_instance()
|
226
|
+
if token == "/" then
|
227
|
+
# 続く要素がなければここで終了
|
228
|
+
return unless @scanner.check( XPATH_STEP_STARTER_REGEXP )
|
229
|
+
else # "//"
|
230
|
+
expr << VCDOM::XPath::Internal::NodeSelectionCommand.new( :"descendant-or-self", :node, nil, nil, nil )
|
231
|
+
end
|
232
|
+
else
|
233
|
+
expr << VCDOM::XPath::Internal::ContextNodeCommand.get_instance()
|
234
|
+
end
|
235
|
+
expr << parse_step()
|
236
|
+
else
|
237
|
+
# FunctionCall
|
238
|
+
function_name = token.intern
|
239
|
+
arg_exprs = @scanner.check( /\)/u ) ? nil : parse_expr_list()
|
240
|
+
raise "invalid xpath [#{@scanner.rest}]" unless @scanner.scan( /\)/u )
|
241
|
+
expr << FunctionCallCommand.new( function_name, arg_exprs )
|
242
|
+
end
|
243
|
+
end
|
244
|
+
if not is_path then
|
245
|
+
if @scanner.check( /\[/u ) then
|
246
|
+
pred_exprs = parse_predicates()
|
247
|
+
expr << PredsEvalCommand.new( pred_exprs )
|
248
|
+
end
|
249
|
+
end
|
250
|
+
while token = @scanner.scan( /\/\/?/ ) do
|
251
|
+
if token == "//" then
|
252
|
+
expr << VCDOM::XPath::Internal::NodeSelectionCommand.new( :"descendant-or-self", :node, nil, nil, nil )
|
253
|
+
end
|
254
|
+
expr << parse_step()
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
AXIS_NAME_LIST = [ :'ancestor', :'ancestor-or-self', :'attribute', :'child', :'descendant', :'descendant-or-self',
|
259
|
+
:'following', :'following-sibling', :'namespace', :'parent', :'preceding', :'preceding-sibling', :'self' ]
|
260
|
+
NODE_TYPE_LIST = [ :'comment', :'text', :'processing-instruction', :'node' ]
|
261
|
+
def parse_step()
|
262
|
+
# "." or ".."
|
263
|
+
if @scanner.scan( /\.\./u ) then
|
264
|
+
return VCDOM::XPath::Internal::NodeSelectionCommand.new( :parent, :node, nil, nil, nil )
|
265
|
+
elsif token = @scanner.scan( /\./u ) then
|
266
|
+
return VCDOM::XPath::Internal::NodeSelectionCommand.new( :self, :node, nil, nil, nil )
|
267
|
+
end
|
268
|
+
# AxisName
|
269
|
+
pos = @scanner.pos
|
270
|
+
if @scanner.scan( /@/u ) then
|
271
|
+
axis_name = :attribute
|
272
|
+
elsif token = @scanner.scan( XML_Q_NAME ) then
|
273
|
+
if @scanner.scan( /::/u ) then
|
274
|
+
axis_name = token.intern
|
275
|
+
raise "invalid AxisName [#{axis_name}]" unless AXIS_NAME_LIST.include? axis_name
|
276
|
+
else
|
277
|
+
axis_name = :child
|
278
|
+
@scanner.pos = pos
|
279
|
+
end
|
280
|
+
else
|
281
|
+
axis_name = :child
|
282
|
+
end
|
283
|
+
# NameTest or NodeType
|
284
|
+
if token = @scanner.scan( XPATH_NAME_TEST ) then
|
285
|
+
if @scanner.scan( /\(/u ) then
|
286
|
+
# NodeType
|
287
|
+
node_type = token.intern
|
288
|
+
raise "invalid NodeType [#{node_type}]" unless NODE_TYPE_LIST.include? node_type
|
289
|
+
node_name = ( node_type == :"processing-instruction" and token = @scanner.scan( XPATH_LITERAL ) ) ? token.intern : nil
|
290
|
+
node_ns_uri = nil
|
291
|
+
raise "invalid xpath expression" unless @scanner.scan( /\)/u )
|
292
|
+
else
|
293
|
+
# NameTest
|
294
|
+
node_type = :named_node
|
295
|
+
name_pair = token.split( /:/u )
|
296
|
+
if name_pair.length == 2 then
|
297
|
+
node_name = ( name_pair[1] == "*" ) ? nil : name_pair[1].intern
|
298
|
+
node_ns_uri = @ns_resolver.lookup_namespace_uri( name_pair[0] )
|
299
|
+
node_ns_uri.nil? or node_ns_uri = node_ns_uri.intern
|
300
|
+
else
|
301
|
+
# length == 1
|
302
|
+
node_name = ( name_pair[0] == "*" ) ? nil : name_pair[0].intern
|
303
|
+
node_ns_uri = nil
|
304
|
+
end
|
305
|
+
end
|
306
|
+
else
|
307
|
+
raise "invalid xpath expression [#{@scanner.rest}]"
|
308
|
+
end
|
309
|
+
# Predicate
|
310
|
+
pred_exprs = @scanner.check( /\[/u ) ? parse_predicates() : nil
|
311
|
+
VCDOM::XPath::Internal::NodeSelectionCommand.new( axis_name, node_type, node_ns_uri, node_name, pred_exprs )
|
312
|
+
end
|
313
|
+
|
314
|
+
def parse_expr_list()
|
315
|
+
exprs = Array.new()
|
316
|
+
exprs << parse_expr()
|
317
|
+
while @scanner.scan( /,/u ) do
|
318
|
+
exprs << parse_expr()
|
319
|
+
end
|
320
|
+
exprs
|
321
|
+
end
|
322
|
+
|
323
|
+
def parse_predicates()
|
324
|
+
exprs = Array.new()
|
325
|
+
while @scanner.scan( /\[/u ) do
|
326
|
+
exprs << parse_expr()
|
327
|
+
raise "invalid xpath expression" unless @scanner.scan( /\]/u )
|
328
|
+
end
|
329
|
+
exprs
|
330
|
+
end
|
331
|
+
|
332
|
+
end
|
333
|
+
end
|
@@ -0,0 +1,342 @@
|
|
1
|
+
# coding : utf-8
|
2
|
+
|
3
|
+
module VCDOM::XPath::Internal # :nodoc:
|
4
|
+
|
5
|
+
class AbstractValue # :nodoc:
|
6
|
+
|
7
|
+
def is_value?; true end
|
8
|
+
def is_expr?; false end
|
9
|
+
def is_command?; false end
|
10
|
+
|
11
|
+
def to_s()
|
12
|
+
"XPathValue(#{value})"
|
13
|
+
end
|
14
|
+
|
15
|
+
def -@(); - self.to_number_value end
|
16
|
+
def +( val ); self.to_number_value + val end
|
17
|
+
def -( val ); self.to_number_value - val end
|
18
|
+
def *( val ); self.to_number_value * val end
|
19
|
+
def /( val ); self.to_number_value / val end
|
20
|
+
def %( val ); self.to_number_value % val end
|
21
|
+
|
22
|
+
def |( val )
|
23
|
+
raise "User Error : xpath operator \"|\" must operate NodeSet"
|
24
|
+
end
|
25
|
+
|
26
|
+
def ==( val )
|
27
|
+
types = [ self.value_type, val.value_type ]
|
28
|
+
if types.include? :boolean then
|
29
|
+
return val.to_boolean_value.value == self.to_boolean_value.value ? BooleanValue.true : BooleanValue.false
|
30
|
+
elsif types.include? :node_set then
|
31
|
+
# TODO : node-set が含まれている場合
|
32
|
+
val == self
|
33
|
+
elsif types.include? :number
|
34
|
+
return val.to_number_value.value == self.to_number_value.value ? BooleanValue.true : BooleanValue.false
|
35
|
+
else
|
36
|
+
return val.to_string_value.value == self.to_string_value.value ? BooleanValue.true : BooleanValue.false
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def neq?( val )
|
41
|
+
if val.value_type == :node_set then
|
42
|
+
return val.neq? self
|
43
|
+
else
|
44
|
+
return ( self == val ? BooleanValue.false : BooleanValue.true )
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def <=>( val )
|
49
|
+
if val.value_type == :node_set then
|
50
|
+
return ( val <=> self ) * -1
|
51
|
+
else
|
52
|
+
return self.to_number_value.value <=> val.to_number_value.value
|
53
|
+
end
|
54
|
+
end
|
55
|
+
def <( val )
|
56
|
+
( self <=> val ) < 0 ? BooleanValue.true : BooleanValue.false
|
57
|
+
end
|
58
|
+
def <=( val )
|
59
|
+
( self <=> val ) <= 0 ? BooleanValue.true : BooleanValue.false
|
60
|
+
end
|
61
|
+
def >( val )
|
62
|
+
( self <=> val ) > 0 ? BooleanValue.true : BooleanValue.false
|
63
|
+
end
|
64
|
+
def >=( val )
|
65
|
+
( self <=> val ) >= 0 ? BooleanValue.true : BooleanValue.false
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
class NumberValue < AbstractValue # :nodoc:
|
71
|
+
|
72
|
+
def initialize( val )
|
73
|
+
@value = val.to_f
|
74
|
+
end
|
75
|
+
##
|
76
|
+
# NaN -> "NaN"
|
77
|
+
# 正ゼロ, 負ゼロ -> "0"
|
78
|
+
# 正の無限大 -> "Infinity"
|
79
|
+
# 負の無限大 -> "-Infinity"
|
80
|
+
# 数値が整数である場合, 数値は 10 進形式で, 小数点がなく先頭のゼロもない [0-9]+ として表現され,
|
81
|
+
# 数値が負である場合には負号 (-) が前につく
|
82
|
+
# それ以外 -> 数値は 10 進形式で [0-9]+ "." [0-9]+ として表現され, 数値が負である場合には負号 (-) が前につく
|
83
|
+
# 小数点の直前にある必須の数字 1 個を別として, 小数点の前に先頭のゼロがあってはならない.
|
84
|
+
# 小数点の後の必須の数字1個を越えてそれ以上の数字は, その数値をその他すべての IEEE 754 数値から一意的に区別するのに
|
85
|
+
# 必要な数字がなければならないが, 必要なだけしかあってはならない
|
86
|
+
def to_string_value()
|
87
|
+
if @value.nan? then
|
88
|
+
str = StringValue.new("NaN")
|
89
|
+
elsif @value.infinite? == 1 then
|
90
|
+
str = StringValue.new("Infinity")
|
91
|
+
elsif @value.infinite? == -1 then
|
92
|
+
str = StringValue.new("-Infinity")
|
93
|
+
elsif @value.zero? then
|
94
|
+
str = StringValue.new("0")
|
95
|
+
elsif @value.truncate == @value then
|
96
|
+
str = StringValue.new(@value.to_i.to_s)
|
97
|
+
else
|
98
|
+
str = StringValue.new(@value.to_s)
|
99
|
+
end
|
100
|
+
str
|
101
|
+
end
|
102
|
+
|
103
|
+
def to_number_value()
|
104
|
+
self
|
105
|
+
end
|
106
|
+
|
107
|
+
# 数値は、正または負のゼロでなくNaNでもない場合に、かつない場合にのみ、真である。
|
108
|
+
def to_boolean_value()
|
109
|
+
( @value.zero? || @value.nan? ) ? BooleanValue.false : BooleanValue.true
|
110
|
+
end
|
111
|
+
|
112
|
+
attr_reader :value
|
113
|
+
def value_type; :number end
|
114
|
+
|
115
|
+
# define operations
|
116
|
+
|
117
|
+
def -@(); @value = - @value; self end
|
118
|
+
def +( val ); NumberValue.new( @value + val.to_number_value.value ) end
|
119
|
+
def -( val ); NumberValue.new( @value - val.to_number_value.value ) end
|
120
|
+
def *( val ); NumberValue.new( @value * val.to_number_value.value ) end
|
121
|
+
def /( val ); NumberValue.new( @value / val.to_number_value.value ) end
|
122
|
+
def %( val )
|
123
|
+
value1 = val.to_number_value.value
|
124
|
+
value1 < 0 && value1 = -value1
|
125
|
+
value2 = @value
|
126
|
+
if value2 < 0 then
|
127
|
+
value2 = - value2
|
128
|
+
value2 %= value1
|
129
|
+
value2 = - value2
|
130
|
+
else
|
131
|
+
value2 %= value1
|
132
|
+
end
|
133
|
+
NumberValue.new( value2 )
|
134
|
+
end
|
135
|
+
|
136
|
+
end
|
137
|
+
|
138
|
+
class StringValue < AbstractValue # :nodoc:
|
139
|
+
|
140
|
+
def initialize( val )
|
141
|
+
@value = val.to_s
|
142
|
+
end
|
143
|
+
def to_string_value()
|
144
|
+
self
|
145
|
+
end
|
146
|
+
|
147
|
+
# StringValue を NumberValue に変換する
|
148
|
+
# オプションの空白にオプションの負号 1 個が続き, [0-9]("." [0-9]*)? | "." [0-9]+ が続いて空白が続いたものからなる文字列は,
|
149
|
+
# その文字列が表す数学的値に (IEEE 754 最近接値丸めルールに従い) 最も近い IEEE 754 数値に変換される.
|
150
|
+
# その他の文字列はどれも NaN に変換される.
|
151
|
+
def to_number_value()
|
152
|
+
str = @value.gsub( /[\x20\x09\x0A\x0D]/u, "" )
|
153
|
+
if str =~ /\A\-?(?:\d+(?:\.\d*)?|\.\d+)\Z/ then
|
154
|
+
# NaN
|
155
|
+
return NumberValue.new( str.to_f )
|
156
|
+
else
|
157
|
+
# NaN
|
158
|
+
return NumberValue.new( 0.0 / 0.0 )
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
# 文字列は、その長さが非ゼロである場合に、かつ非ゼロである場合にのみ、真である。
|
163
|
+
def to_boolean_value()
|
164
|
+
@value.length != 0 ? BooleanValue.true : BooleanValue.false
|
165
|
+
end
|
166
|
+
|
167
|
+
attr_reader :value
|
168
|
+
def value_type; :string end
|
169
|
+
|
170
|
+
end
|
171
|
+
|
172
|
+
class BooleanValue < AbstractValue # :nodoc:
|
173
|
+
|
174
|
+
def initialize( val )
|
175
|
+
@value = !! val
|
176
|
+
end
|
177
|
+
def to_string_value()
|
178
|
+
@value ? StringValue.new("true") : StringValue.new("false")
|
179
|
+
end
|
180
|
+
|
181
|
+
# convert a BooleanValue object to a NumberValue object.
|
182
|
+
# オプションの空白にオプションの負号 1 個が続き, [0-9]("." [0-9]*)? | "." [0-9]+ が続いて空白が続いたものからなる文字列は,
|
183
|
+
# その文字列が表す数学的値に (IEEE 754 最近接値丸めルールに従い) 最も近い IEEE 754 数値に変換される.
|
184
|
+
# その他の文字列はどれも NaN に変換される.
|
185
|
+
def to_number_value()
|
186
|
+
@value ? NumberValue.new( 1.0 ) : NumberValue.new( 0.0 )
|
187
|
+
end
|
188
|
+
|
189
|
+
def to_boolean_value()
|
190
|
+
self
|
191
|
+
end
|
192
|
+
|
193
|
+
attr_reader :value
|
194
|
+
def value_type; :boolean end
|
195
|
+
|
196
|
+
TRUE = self.new( true )
|
197
|
+
FALSE = self.new( false )
|
198
|
+
def self.true; TRUE end
|
199
|
+
def self.false; FALSE end
|
200
|
+
|
201
|
+
end
|
202
|
+
|
203
|
+
class NodeSetValue < AbstractValue # :nodoc:
|
204
|
+
|
205
|
+
def initialize( *nodes )
|
206
|
+
@value = nodes
|
207
|
+
end
|
208
|
+
def to_string_value()
|
209
|
+
# TODO
|
210
|
+
raise "NOT SUPPORT"
|
211
|
+
end
|
212
|
+
|
213
|
+
def to_number_value()
|
214
|
+
# TODO
|
215
|
+
raise "NOT SUPPORT"
|
216
|
+
end
|
217
|
+
|
218
|
+
def to_boolean_value()
|
219
|
+
@value.empty? ? BooleanValue.false : BooleanValue.true
|
220
|
+
end
|
221
|
+
|
222
|
+
def []( *args )
|
223
|
+
@value.[]( *args )
|
224
|
+
end
|
225
|
+
def <<( node )
|
226
|
+
@value << node
|
227
|
+
end
|
228
|
+
|
229
|
+
def sort( document_order = true )
|
230
|
+
# TODO
|
231
|
+
#@value.sort! { |a,b|
|
232
|
+
#
|
233
|
+
# case a. b
|
234
|
+
# when :document_position_preceding,
|
235
|
+
# when :document_position_following, # a が b より後ろ
|
236
|
+
#}
|
237
|
+
end
|
238
|
+
|
239
|
+
def each
|
240
|
+
# TODO : ブロックが与えられなかった場合の処理
|
241
|
+
@value.each do |v|
|
242
|
+
yield v
|
243
|
+
end
|
244
|
+
end
|
245
|
+
def each_with_index
|
246
|
+
# TODO : ブロックが与えられなかった場合の処理
|
247
|
+
@value.each_with_index do |v,i|
|
248
|
+
yield v,i
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
def length
|
253
|
+
@value.length
|
254
|
+
end
|
255
|
+
|
256
|
+
attr_reader :value
|
257
|
+
def value_type; :node_set end
|
258
|
+
|
259
|
+
def |( val )
|
260
|
+
val.|(self) if val.value_type != :node_set
|
261
|
+
@value = @value + val.value
|
262
|
+
@value.uniq!
|
263
|
+
self
|
264
|
+
end
|
265
|
+
|
266
|
+
# 比較されるべきオブジェクトの双方がノードセットである場合、比較は、2つのノードの文字列値について
|
267
|
+
# 比較を実行した結果が真であるようなノードが、1つ目のノードセットと2つ目のノードセットとにある場合に、
|
268
|
+
# かつある場合にのみ、真ということになる。比較されるべきオブジェクトの一方がノードセットであり、
|
269
|
+
# 他方が数値である場合、比較は、比較されるべき数値と、number 関数を用いてノードの文字列値を数値に変換
|
270
|
+
# した結果とについて比較を実行した結果が真であるようなノードが、ノードセットの中にある場合に、かつある
|
271
|
+
# 場合にのみ、真ということになる。比較されるべきオブジェクトの一方がノードセットであり、他方が文字列で
|
272
|
+
# ある場合、比較は、ノードの文字列値と他方の文字列とについて比較を実行した結果が真であるようなノードが、
|
273
|
+
# ノードセットの中にある場合に、かつある場合にのみ、真ということになる。比較されるべきオブジェクトの一方が
|
274
|
+
# ノードセットであり、他方がブール値である場合、比較は、ブール値と、boolean 関数を用いてノードセット
|
275
|
+
# をブール値に変換した結果とについて比較を実行した結果が真である場合に、かつ真である場合にのみ、真ということになる。
|
276
|
+
def ==( val )
|
277
|
+
case val.value_type
|
278
|
+
when :node_set
|
279
|
+
@value.each do |node1|
|
280
|
+
str1 = StringValue.new( node1.text_content )
|
281
|
+
val.each do |node2|
|
282
|
+
return BooleanValue.true if ( str1 == StringValue.new( node2.text_content ) ) == BooleanValue.true
|
283
|
+
end
|
284
|
+
end
|
285
|
+
return BooleanValue.false
|
286
|
+
when :number
|
287
|
+
# TODO
|
288
|
+
raise "NOT SUPPORT"
|
289
|
+
when :string
|
290
|
+
# TODO
|
291
|
+
raise "NOT SUPPORT"
|
292
|
+
when :boolean
|
293
|
+
return val == self
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
def neq?( val )
|
298
|
+
case val.value_type
|
299
|
+
when :node_set
|
300
|
+
@value.each do |node1|
|
301
|
+
str1 = StringValue.new( node1.text_content )
|
302
|
+
val.each do |node2|
|
303
|
+
return BooleanValue.true if ( str1.neq? StringValue.new( node2.text_content ) ) == BooleanValue.true
|
304
|
+
end
|
305
|
+
end
|
306
|
+
return BooleanValue.false
|
307
|
+
when :number
|
308
|
+
# TODO
|
309
|
+
raise "NOT SUPPORT"
|
310
|
+
when :string
|
311
|
+
# TODO
|
312
|
+
raise "NOT SUPPORT"
|
313
|
+
when :boolean
|
314
|
+
return val.neq? self
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
def <=>( val )
|
319
|
+
# TODO
|
320
|
+
case val.value_type
|
321
|
+
when :node_set
|
322
|
+
@value.each do |node1|
|
323
|
+
str1 = StringValue.new( node1.text_content )
|
324
|
+
val.each do |node2|
|
325
|
+
return BooleanValue.true if ( str1 <=> StringValue.new( node2.text_content ) ) == BooleanValue.true
|
326
|
+
end
|
327
|
+
end
|
328
|
+
return BooleanValue.false
|
329
|
+
when :number
|
330
|
+
# TODO
|
331
|
+
raise "NOT SUPPORT"
|
332
|
+
when :string
|
333
|
+
# TODO
|
334
|
+
raise "NOT SUPPORT"
|
335
|
+
when :boolean
|
336
|
+
return self.to_boolean_value <=> val
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
end
|
341
|
+
|
342
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# coding : utf-8
|
2
|
+
|
3
|
+
require "vcdom/xpath/xpath_expression"
|
4
|
+
require "vcdom/xpath/xpath_ns_resolver"
|
5
|
+
require "vcdom/xpath/xpath_result"
|
6
|
+
require "vcdom/xpath/internal/parser"
|
7
|
+
|
8
|
+
module VCDOM::XPath
|
9
|
+
|
10
|
+
module XPathEvaluatorMod
|
11
|
+
|
12
|
+
def create_expression( expression, resolver )
|
13
|
+
XPathExpression.new( Internal::Parser.new( expression, resolver ).parse() )
|
14
|
+
end
|
15
|
+
|
16
|
+
# Adapts any DOM node to resolve namespaces so that an XPath expression can be easily
|
17
|
+
# evaluated relative to the context of the node where it appeared within the document.
|
18
|
+
# This adapter works like the DOM Level 3 method lookupNamespaceURI on nodes in resolving
|
19
|
+
# the namespaceURI from a given prefix using the current information available in the
|
20
|
+
# node's hierarchy at the time lookupNamespaceURI is called. also correctly resolving
|
21
|
+
# the implicit xml prefix.
|
22
|
+
def create_ns_resolver( node_resolver )
|
23
|
+
XPathNSResolver.new( node_resolver )
|
24
|
+
end
|
25
|
+
|
26
|
+
def evaluate( expression, context_node, resolver, type, result = nil )
|
27
|
+
return self.create_expression( expression, resolver ).evaluate( context_node, type, result )
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|