xmlrpc 0.1.0
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.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.travis.yml +4 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +56 -0
- data/README.md +58 -0
- data/Rakefile +10 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/xmlrpc.rb +293 -0
- data/lib/xmlrpc/base64.rb +63 -0
- data/lib/xmlrpc/client.rb +629 -0
- data/lib/xmlrpc/config.rb +39 -0
- data/lib/xmlrpc/create.rb +287 -0
- data/lib/xmlrpc/datetime.rb +130 -0
- data/lib/xmlrpc/marshal.rb +67 -0
- data/lib/xmlrpc/parser.rb +642 -0
- data/lib/xmlrpc/server.rb +708 -0
- data/lib/xmlrpc/utils.rb +172 -0
- data/xmlrpc.gemspec +26 -0
- metadata +107 -0
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: false
|
2
|
+
#
|
3
|
+
# Copyright (C) 2001, 2002, 2003 by Michael Neumann (mneumann@ntecs.de)
|
4
|
+
#
|
5
|
+
# $Id$
|
6
|
+
#
|
7
|
+
|
8
|
+
require "xmlrpc/parser"
|
9
|
+
require "xmlrpc/create"
|
10
|
+
require "xmlrpc/config"
|
11
|
+
require "xmlrpc/utils"
|
12
|
+
|
13
|
+
module XMLRPC # :nodoc:
|
14
|
+
|
15
|
+
# Marshalling of XMLRPC::Create#methodCall and XMLRPC::Create#methodResponse
|
16
|
+
class Marshal
|
17
|
+
include ParserWriterChooseMixin
|
18
|
+
|
19
|
+
class << self
|
20
|
+
|
21
|
+
def dump_call( methodName, *params )
|
22
|
+
new.dump_call( methodName, *params )
|
23
|
+
end
|
24
|
+
|
25
|
+
def dump_response( param )
|
26
|
+
new.dump_response( param )
|
27
|
+
end
|
28
|
+
|
29
|
+
def load_call( stringOrReadable )
|
30
|
+
new.load_call( stringOrReadable )
|
31
|
+
end
|
32
|
+
|
33
|
+
def load_response( stringOrReadable )
|
34
|
+
new.load_response( stringOrReadable )
|
35
|
+
end
|
36
|
+
|
37
|
+
alias dump dump_response
|
38
|
+
alias load load_response
|
39
|
+
|
40
|
+
end # class self
|
41
|
+
|
42
|
+
def initialize( parser = nil, writer = nil )
|
43
|
+
set_parser( parser )
|
44
|
+
set_writer( writer )
|
45
|
+
end
|
46
|
+
|
47
|
+
def dump_call( methodName, *params )
|
48
|
+
create.methodCall( methodName, *params )
|
49
|
+
end
|
50
|
+
|
51
|
+
def dump_response( param )
|
52
|
+
create.methodResponse( ! param.kind_of?( XMLRPC::FaultException ) , param )
|
53
|
+
end
|
54
|
+
|
55
|
+
# Returns <code>[ methodname, params ]</code>
|
56
|
+
def load_call( stringOrReadable )
|
57
|
+
parser.parseMethodCall( stringOrReadable )
|
58
|
+
end
|
59
|
+
|
60
|
+
# Returns +paramOrFault+
|
61
|
+
def load_response( stringOrReadable )
|
62
|
+
parser.parseMethodResponse( stringOrReadable )[1]
|
63
|
+
end
|
64
|
+
|
65
|
+
end # class Marshal
|
66
|
+
|
67
|
+
end
|
@@ -0,0 +1,642 @@
|
|
1
|
+
# frozen_string_literal: false
|
2
|
+
# Copyright (C) 2001, 2002, 2003 by Michael Neumann (mneumann@ntecs.de)
|
3
|
+
#
|
4
|
+
# $Id$
|
5
|
+
#
|
6
|
+
|
7
|
+
|
8
|
+
require "date"
|
9
|
+
require "xmlrpc/base64"
|
10
|
+
require "xmlrpc/datetime"
|
11
|
+
|
12
|
+
|
13
|
+
module XMLRPC # :nodoc:
|
14
|
+
|
15
|
+
# Raised when the remote procedure returns a fault-structure, which has two
|
16
|
+
# accessor-methods +faultCode+ an Integer, and +faultString+ a String.
|
17
|
+
class FaultException < StandardError
|
18
|
+
attr_reader :faultCode, :faultString
|
19
|
+
|
20
|
+
# Creates a new XMLRPC::FaultException instance.
|
21
|
+
#
|
22
|
+
# +faultString+ is passed to StandardError as the +msg+ of the Exception.
|
23
|
+
def initialize(faultCode, faultString)
|
24
|
+
@faultCode = faultCode
|
25
|
+
@faultString = faultString
|
26
|
+
super(@faultString)
|
27
|
+
end
|
28
|
+
|
29
|
+
# The +faultCode+ and +faultString+ of the exception in a Hash.
|
30
|
+
def to_h
|
31
|
+
{"faultCode" => @faultCode, "faultString" => @faultString}
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Helper class used to convert types.
|
36
|
+
module Convert
|
37
|
+
|
38
|
+
# Converts a String to an Integer
|
39
|
+
#
|
40
|
+
# See also String.to_i
|
41
|
+
def self.int(str)
|
42
|
+
str.to_i
|
43
|
+
end
|
44
|
+
|
45
|
+
# Converts a String to +true+ or +false+
|
46
|
+
#
|
47
|
+
# Raises an exception if +str+ is not +0+ or +1+
|
48
|
+
def self.boolean(str)
|
49
|
+
case str
|
50
|
+
when "0" then false
|
51
|
+
when "1" then true
|
52
|
+
else
|
53
|
+
raise "RPC-value of type boolean is wrong"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Converts a String to a Float
|
58
|
+
#
|
59
|
+
# See also String.to_f
|
60
|
+
def self.double(str)
|
61
|
+
str.to_f
|
62
|
+
end
|
63
|
+
|
64
|
+
# Converts a the given +str+ to a +dateTime.iso8601+ formatted date.
|
65
|
+
#
|
66
|
+
# Raises an exception if the String isn't in +dateTime.iso8601+ format.
|
67
|
+
#
|
68
|
+
# See also, XMLRPC::DateTime
|
69
|
+
def self.dateTime(str)
|
70
|
+
case str
|
71
|
+
when /^(-?\d\d\d\d)-?(\d\d)-?(\d\d)T(\d\d):(\d\d):(\d\d)(?:Z|([+-])(\d\d):?(\d\d))?$/
|
72
|
+
a = [$1, $2, $3, $4, $5, $6].collect{|i| i.to_i}
|
73
|
+
if $7
|
74
|
+
ofs = $8.to_i*3600 + $9.to_i*60
|
75
|
+
ofs = -ofs if $7=='+'
|
76
|
+
utc = Time.utc(*a) + ofs
|
77
|
+
a = [ utc.year, utc.month, utc.day, utc.hour, utc.min, utc.sec ]
|
78
|
+
end
|
79
|
+
XMLRPC::DateTime.new(*a)
|
80
|
+
when /^(-?\d\d)-?(\d\d)-?(\d\d)T(\d\d):(\d\d):(\d\d)(Z|([+-]\d\d):(\d\d))?$/
|
81
|
+
a = [$1, $2, $3, $4, $5, $6].collect{|i| i.to_i}
|
82
|
+
if a[0] < 70
|
83
|
+
a[0] += 2000
|
84
|
+
else
|
85
|
+
a[0] += 1900
|
86
|
+
end
|
87
|
+
if $7
|
88
|
+
ofs = $8.to_i*3600 + $9.to_i*60
|
89
|
+
ofs = -ofs if $7=='+'
|
90
|
+
utc = Time.utc(*a) + ofs
|
91
|
+
a = [ utc.year, utc.month, utc.day, utc.hour, utc.min, utc.sec ]
|
92
|
+
end
|
93
|
+
XMLRPC::DateTime.new(*a)
|
94
|
+
else
|
95
|
+
raise "wrong dateTime.iso8601 format " + str
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# Decodes the given +str+ using XMLRPC::Base64.decode
|
100
|
+
def self.base64(str)
|
101
|
+
XMLRPC::Base64.decode(str)
|
102
|
+
end
|
103
|
+
|
104
|
+
# Converts the given +hash+ to a marshalled object.
|
105
|
+
#
|
106
|
+
# Returns the given +hash+ if an exception occurs.
|
107
|
+
def self.struct(hash)
|
108
|
+
# convert to marshalled object
|
109
|
+
klass = hash["___class___"]
|
110
|
+
if klass.nil? or Config::ENABLE_MARSHALLING == false
|
111
|
+
hash
|
112
|
+
else
|
113
|
+
begin
|
114
|
+
mod = Module
|
115
|
+
klass.split("::").each {|const| mod = mod.const_get(const.strip)}
|
116
|
+
|
117
|
+
obj = mod.allocate
|
118
|
+
|
119
|
+
hash.delete "___class___"
|
120
|
+
hash.each {|key, value|
|
121
|
+
obj.instance_variable_set("@#{ key }", value) if key =~ /^([a-zA-Z_]\w*)$/
|
122
|
+
}
|
123
|
+
obj
|
124
|
+
rescue
|
125
|
+
hash
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
# Converts the given +hash+ to an XMLRPC::FaultException object by passing
|
131
|
+
# the +faultCode+ and +faultString+ attributes of the Hash to
|
132
|
+
# XMLRPC::FaultException.new
|
133
|
+
#
|
134
|
+
# Raises an Exception if the given +hash+ doesn't meet the requirements.
|
135
|
+
# Those requirements being:
|
136
|
+
# * 2 keys
|
137
|
+
# * <code>'faultCode'</code> key is an Integer
|
138
|
+
# * <code>'faultString'</code> key is a String
|
139
|
+
def self.fault(hash)
|
140
|
+
if hash.kind_of? Hash and hash.size == 2 and
|
141
|
+
hash.has_key? "faultCode" and hash.has_key? "faultString" and
|
142
|
+
hash["faultCode"].kind_of? Integer and hash["faultString"].kind_of? String
|
143
|
+
|
144
|
+
XMLRPC::FaultException.new(hash["faultCode"], hash["faultString"])
|
145
|
+
else
|
146
|
+
raise "wrong fault-structure: #{hash.inspect}"
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
end # module Convert
|
151
|
+
|
152
|
+
# Parser for XML-RPC call and response
|
153
|
+
module XMLParser
|
154
|
+
|
155
|
+
class AbstractTreeParser
|
156
|
+
|
157
|
+
def parseMethodResponse(str)
|
158
|
+
methodResponse_document(createCleanedTree(str))
|
159
|
+
end
|
160
|
+
|
161
|
+
def parseMethodCall(str)
|
162
|
+
methodCall_document(createCleanedTree(str))
|
163
|
+
end
|
164
|
+
|
165
|
+
private
|
166
|
+
|
167
|
+
# Removes all whitespaces but in the tags i4, i8, int, boolean....
|
168
|
+
# and all comments
|
169
|
+
def removeWhitespacesAndComments(node)
|
170
|
+
remove = []
|
171
|
+
childs = node.childNodes.to_a
|
172
|
+
childs.each do |nd|
|
173
|
+
case _nodeType(nd)
|
174
|
+
when :TEXT
|
175
|
+
# TODO: add nil?
|
176
|
+
unless %w(i4 i8 int boolean string double dateTime.iso8601 base64).include? node.nodeName
|
177
|
+
|
178
|
+
if node.nodeName == "value"
|
179
|
+
if not node.childNodes.to_a.detect {|n| _nodeType(n) == :ELEMENT}.nil?
|
180
|
+
remove << nd if nd.nodeValue.strip == ""
|
181
|
+
end
|
182
|
+
else
|
183
|
+
remove << nd if nd.nodeValue.strip == ""
|
184
|
+
end
|
185
|
+
end
|
186
|
+
when :COMMENT
|
187
|
+
remove << nd
|
188
|
+
else
|
189
|
+
removeWhitespacesAndComments(nd)
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
remove.each { |i| node.removeChild(i) }
|
194
|
+
end
|
195
|
+
|
196
|
+
|
197
|
+
def nodeMustBe(node, name)
|
198
|
+
cmp = case name
|
199
|
+
when Array
|
200
|
+
name.include?(node.nodeName)
|
201
|
+
when String
|
202
|
+
name == node.nodeName
|
203
|
+
else
|
204
|
+
raise "error"
|
205
|
+
end
|
206
|
+
|
207
|
+
if not cmp then
|
208
|
+
raise "wrong xml-rpc (name)"
|
209
|
+
end
|
210
|
+
|
211
|
+
node
|
212
|
+
end
|
213
|
+
|
214
|
+
# Returns, when successfully the only child-node
|
215
|
+
def hasOnlyOneChild(node, name=nil)
|
216
|
+
if node.childNodes.to_a.size != 1
|
217
|
+
raise "wrong xml-rpc (size)"
|
218
|
+
end
|
219
|
+
if name != nil then
|
220
|
+
nodeMustBe(node.firstChild, name)
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
|
225
|
+
def assert(b)
|
226
|
+
if not b then
|
227
|
+
raise "assert-fail"
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
# The node `node` has empty string or string
|
232
|
+
def text_zero_one(node)
|
233
|
+
nodes = node.childNodes.to_a.size
|
234
|
+
|
235
|
+
if nodes == 1
|
236
|
+
text(node.firstChild)
|
237
|
+
elsif nodes == 0
|
238
|
+
""
|
239
|
+
else
|
240
|
+
raise "wrong xml-rpc (size)"
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
|
245
|
+
def integer(node)
|
246
|
+
#TODO: check string for float because to_i returnsa
|
247
|
+
# 0 when wrong string
|
248
|
+
nodeMustBe(node, %w(i4 i8 int))
|
249
|
+
hasOnlyOneChild(node)
|
250
|
+
|
251
|
+
Convert.int(text(node.firstChild))
|
252
|
+
end
|
253
|
+
|
254
|
+
def boolean(node)
|
255
|
+
nodeMustBe(node, "boolean")
|
256
|
+
hasOnlyOneChild(node)
|
257
|
+
|
258
|
+
Convert.boolean(text(node.firstChild))
|
259
|
+
end
|
260
|
+
|
261
|
+
def v_nil(node)
|
262
|
+
nodeMustBe(node, "nil")
|
263
|
+
assert( node.childNodes.to_a.size == 0 )
|
264
|
+
nil
|
265
|
+
end
|
266
|
+
|
267
|
+
def string(node)
|
268
|
+
nodeMustBe(node, "string")
|
269
|
+
text_zero_one(node)
|
270
|
+
end
|
271
|
+
|
272
|
+
def double(node)
|
273
|
+
#TODO: check string for float because to_f returnsa
|
274
|
+
# 0.0 when wrong string
|
275
|
+
nodeMustBe(node, "double")
|
276
|
+
hasOnlyOneChild(node)
|
277
|
+
|
278
|
+
Convert.double(text(node.firstChild))
|
279
|
+
end
|
280
|
+
|
281
|
+
def dateTime(node)
|
282
|
+
nodeMustBe(node, "dateTime.iso8601")
|
283
|
+
hasOnlyOneChild(node)
|
284
|
+
|
285
|
+
Convert.dateTime( text(node.firstChild) )
|
286
|
+
end
|
287
|
+
|
288
|
+
def base64(node)
|
289
|
+
nodeMustBe(node, "base64")
|
290
|
+
#hasOnlyOneChild(node)
|
291
|
+
|
292
|
+
Convert.base64(text_zero_one(node))
|
293
|
+
end
|
294
|
+
|
295
|
+
def member(node)
|
296
|
+
nodeMustBe(node, "member")
|
297
|
+
assert( node.childNodes.to_a.size == 2 )
|
298
|
+
|
299
|
+
[ name(node[0]), value(node[1]) ]
|
300
|
+
end
|
301
|
+
|
302
|
+
def name(node)
|
303
|
+
nodeMustBe(node, "name")
|
304
|
+
#hasOnlyOneChild(node)
|
305
|
+
text_zero_one(node)
|
306
|
+
end
|
307
|
+
|
308
|
+
def array(node)
|
309
|
+
nodeMustBe(node, "array")
|
310
|
+
hasOnlyOneChild(node, "data")
|
311
|
+
data(node.firstChild)
|
312
|
+
end
|
313
|
+
|
314
|
+
def data(node)
|
315
|
+
nodeMustBe(node, "data")
|
316
|
+
|
317
|
+
node.childNodes.to_a.collect do |val|
|
318
|
+
value(val)
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
def param(node)
|
323
|
+
nodeMustBe(node, "param")
|
324
|
+
hasOnlyOneChild(node, "value")
|
325
|
+
value(node.firstChild)
|
326
|
+
end
|
327
|
+
|
328
|
+
def methodResponse(node)
|
329
|
+
nodeMustBe(node, "methodResponse")
|
330
|
+
hasOnlyOneChild(node, %w(params fault))
|
331
|
+
child = node.firstChild
|
332
|
+
|
333
|
+
case child.nodeName
|
334
|
+
when "params"
|
335
|
+
[ true, params(child,false) ]
|
336
|
+
when "fault"
|
337
|
+
[ false, fault(child) ]
|
338
|
+
else
|
339
|
+
raise "unexpected error"
|
340
|
+
end
|
341
|
+
|
342
|
+
end
|
343
|
+
|
344
|
+
def methodName(node)
|
345
|
+
nodeMustBe(node, "methodName")
|
346
|
+
hasOnlyOneChild(node)
|
347
|
+
text(node.firstChild)
|
348
|
+
end
|
349
|
+
|
350
|
+
def params(node, call=true)
|
351
|
+
nodeMustBe(node, "params")
|
352
|
+
|
353
|
+
if call
|
354
|
+
node.childNodes.to_a.collect do |n|
|
355
|
+
param(n)
|
356
|
+
end
|
357
|
+
else # response (only one param)
|
358
|
+
hasOnlyOneChild(node)
|
359
|
+
param(node.firstChild)
|
360
|
+
end
|
361
|
+
end
|
362
|
+
|
363
|
+
def fault(node)
|
364
|
+
nodeMustBe(node, "fault")
|
365
|
+
hasOnlyOneChild(node, "value")
|
366
|
+
f = value(node.firstChild)
|
367
|
+
Convert.fault(f)
|
368
|
+
end
|
369
|
+
|
370
|
+
|
371
|
+
|
372
|
+
# _nodeType is defined in the subclass
|
373
|
+
def text(node)
|
374
|
+
assert( _nodeType(node) == :TEXT )
|
375
|
+
assert( node.hasChildNodes == false )
|
376
|
+
assert( node.nodeValue != nil )
|
377
|
+
|
378
|
+
node.nodeValue.to_s
|
379
|
+
end
|
380
|
+
|
381
|
+
def struct(node)
|
382
|
+
nodeMustBe(node, "struct")
|
383
|
+
|
384
|
+
hash = {}
|
385
|
+
node.childNodes.to_a.each do |me|
|
386
|
+
n, v = member(me)
|
387
|
+
hash[n] = v
|
388
|
+
end
|
389
|
+
|
390
|
+
Convert.struct(hash)
|
391
|
+
end
|
392
|
+
|
393
|
+
|
394
|
+
def value(node)
|
395
|
+
nodeMustBe(node, "value")
|
396
|
+
nodes = node.childNodes.to_a.size
|
397
|
+
if nodes == 0
|
398
|
+
return ""
|
399
|
+
elsif nodes > 1
|
400
|
+
raise "wrong xml-rpc (size)"
|
401
|
+
end
|
402
|
+
|
403
|
+
child = node.firstChild
|
404
|
+
|
405
|
+
case _nodeType(child)
|
406
|
+
when :TEXT
|
407
|
+
text_zero_one(node)
|
408
|
+
when :ELEMENT
|
409
|
+
case child.nodeName
|
410
|
+
when "i4", "i8", "int" then integer(child)
|
411
|
+
when "boolean" then boolean(child)
|
412
|
+
when "string" then string(child)
|
413
|
+
when "double" then double(child)
|
414
|
+
when "dateTime.iso8601" then dateTime(child)
|
415
|
+
when "base64" then base64(child)
|
416
|
+
when "struct" then struct(child)
|
417
|
+
when "array" then array(child)
|
418
|
+
when "nil"
|
419
|
+
if Config::ENABLE_NIL_PARSER
|
420
|
+
v_nil(child)
|
421
|
+
else
|
422
|
+
raise "wrong/unknown XML-RPC type 'nil'"
|
423
|
+
end
|
424
|
+
else
|
425
|
+
raise "wrong/unknown XML-RPC type"
|
426
|
+
end
|
427
|
+
else
|
428
|
+
raise "wrong type of node"
|
429
|
+
end
|
430
|
+
|
431
|
+
end
|
432
|
+
|
433
|
+
def methodCall(node)
|
434
|
+
nodeMustBe(node, "methodCall")
|
435
|
+
assert( (1..2).include?( node.childNodes.to_a.size ) )
|
436
|
+
name = methodName(node[0])
|
437
|
+
|
438
|
+
if node.childNodes.to_a.size == 2 then
|
439
|
+
pa = params(node[1])
|
440
|
+
else # no parameters given
|
441
|
+
pa = []
|
442
|
+
end
|
443
|
+
[name, pa]
|
444
|
+
end
|
445
|
+
|
446
|
+
end # module TreeParserMixin
|
447
|
+
|
448
|
+
class AbstractStreamParser
|
449
|
+
def parseMethodResponse(str)
|
450
|
+
parser = @parser_class.new
|
451
|
+
parser.parse(str)
|
452
|
+
raise "No valid method response!" if parser.method_name != nil
|
453
|
+
if parser.fault != nil
|
454
|
+
# is a fault structure
|
455
|
+
[false, parser.fault]
|
456
|
+
else
|
457
|
+
# is a normal return value
|
458
|
+
raise "Missing return value!" if parser.params.size == 0
|
459
|
+
raise "Too many return values. Only one allowed!" if parser.params.size > 1
|
460
|
+
[true, parser.params[0]]
|
461
|
+
end
|
462
|
+
end
|
463
|
+
|
464
|
+
def parseMethodCall(str)
|
465
|
+
parser = @parser_class.new
|
466
|
+
parser.parse(str)
|
467
|
+
raise "No valid method call - missing method name!" if parser.method_name.nil?
|
468
|
+
[parser.method_name, parser.params]
|
469
|
+
end
|
470
|
+
end
|
471
|
+
|
472
|
+
module StreamParserMixin
|
473
|
+
attr_reader :params
|
474
|
+
attr_reader :method_name
|
475
|
+
attr_reader :fault
|
476
|
+
|
477
|
+
def initialize(*a)
|
478
|
+
super(*a)
|
479
|
+
@params = []
|
480
|
+
@values = []
|
481
|
+
@val_stack = []
|
482
|
+
|
483
|
+
@names = []
|
484
|
+
@name = []
|
485
|
+
|
486
|
+
@structs = []
|
487
|
+
@struct = {}
|
488
|
+
|
489
|
+
@method_name = nil
|
490
|
+
@fault = nil
|
491
|
+
|
492
|
+
@data = nil
|
493
|
+
end
|
494
|
+
|
495
|
+
def startElement(name, attrs=[])
|
496
|
+
@data = nil
|
497
|
+
case name
|
498
|
+
when "value"
|
499
|
+
@value = nil
|
500
|
+
when "nil"
|
501
|
+
raise "wrong/unknown XML-RPC type 'nil'" unless Config::ENABLE_NIL_PARSER
|
502
|
+
@value = :nil
|
503
|
+
when "array"
|
504
|
+
@val_stack << @values
|
505
|
+
@values = []
|
506
|
+
when "struct"
|
507
|
+
@names << @name
|
508
|
+
@name = []
|
509
|
+
|
510
|
+
@structs << @struct
|
511
|
+
@struct = {}
|
512
|
+
end
|
513
|
+
end
|
514
|
+
|
515
|
+
def endElement(name)
|
516
|
+
@data ||= ""
|
517
|
+
case name
|
518
|
+
when "string"
|
519
|
+
@value = @data
|
520
|
+
when "i4", "i8", "int"
|
521
|
+
@value = Convert.int(@data)
|
522
|
+
when "boolean"
|
523
|
+
@value = Convert.boolean(@data)
|
524
|
+
when "double"
|
525
|
+
@value = Convert.double(@data)
|
526
|
+
when "dateTime.iso8601"
|
527
|
+
@value = Convert.dateTime(@data)
|
528
|
+
when "base64"
|
529
|
+
@value = Convert.base64(@data)
|
530
|
+
when "value"
|
531
|
+
@value = @data if @value.nil?
|
532
|
+
@values << (@value == :nil ? nil : @value)
|
533
|
+
when "array"
|
534
|
+
@value = @values
|
535
|
+
@values = @val_stack.pop
|
536
|
+
when "struct"
|
537
|
+
@value = Convert.struct(@struct)
|
538
|
+
|
539
|
+
@name = @names.pop
|
540
|
+
@struct = @structs.pop
|
541
|
+
when "name"
|
542
|
+
@name[0] = @data
|
543
|
+
when "member"
|
544
|
+
@struct[@name[0]] = @values.pop
|
545
|
+
|
546
|
+
when "param"
|
547
|
+
@params << @values[0]
|
548
|
+
@values = []
|
549
|
+
|
550
|
+
when "fault"
|
551
|
+
@fault = Convert.fault(@values[0])
|
552
|
+
|
553
|
+
when "methodName"
|
554
|
+
@method_name = @data
|
555
|
+
end
|
556
|
+
|
557
|
+
@data = nil
|
558
|
+
end
|
559
|
+
|
560
|
+
def character(data)
|
561
|
+
if @data
|
562
|
+
@data << data
|
563
|
+
else
|
564
|
+
@data = data
|
565
|
+
end
|
566
|
+
end
|
567
|
+
|
568
|
+
end # module StreamParserMixin
|
569
|
+
|
570
|
+
class REXMLStreamParser < AbstractStreamParser
|
571
|
+
def initialize
|
572
|
+
require "rexml/document"
|
573
|
+
@parser_class = StreamListener
|
574
|
+
end
|
575
|
+
|
576
|
+
class StreamListener
|
577
|
+
include StreamParserMixin
|
578
|
+
|
579
|
+
alias :tag_start :startElement
|
580
|
+
alias :tag_end :endElement
|
581
|
+
alias :text :character
|
582
|
+
alias :cdata :character
|
583
|
+
|
584
|
+
def method_missing(*a)
|
585
|
+
# ignore
|
586
|
+
end
|
587
|
+
|
588
|
+
def parse(str)
|
589
|
+
REXML::Document.parse_stream(str, self)
|
590
|
+
end
|
591
|
+
end
|
592
|
+
|
593
|
+
end
|
594
|
+
|
595
|
+
class LibXMLStreamParser < AbstractStreamParser
|
596
|
+
def initialize
|
597
|
+
require 'libxml'
|
598
|
+
@parser_class = LibXMLStreamListener
|
599
|
+
end
|
600
|
+
|
601
|
+
class LibXMLStreamListener
|
602
|
+
include StreamParserMixin
|
603
|
+
|
604
|
+
def on_start_element_ns(name, attributes, prefix, uri, namespaces)
|
605
|
+
startElement(name)
|
606
|
+
end
|
607
|
+
|
608
|
+
def on_end_element_ns(name, prefix, uri)
|
609
|
+
endElement(name)
|
610
|
+
end
|
611
|
+
|
612
|
+
alias :on_characters :character
|
613
|
+
alias :on_cdata_block :character
|
614
|
+
|
615
|
+
def method_missing(*a)
|
616
|
+
end
|
617
|
+
|
618
|
+
def parse(str)
|
619
|
+
parser = LibXML::XML::SaxParser.string(str)
|
620
|
+
parser.callbacks = self
|
621
|
+
parser.parse()
|
622
|
+
end
|
623
|
+
end
|
624
|
+
end
|
625
|
+
|
626
|
+
Classes = [REXMLStreamParser, LibXMLStreamParser]
|
627
|
+
|
628
|
+
# yields an instance of each installed parser
|
629
|
+
def self.each_installed_parser
|
630
|
+
XMLRPC::XMLParser::Classes.each do |klass|
|
631
|
+
begin
|
632
|
+
yield klass.new
|
633
|
+
rescue LoadError
|
634
|
+
end
|
635
|
+
end
|
636
|
+
end
|
637
|
+
|
638
|
+
end # module XMLParser
|
639
|
+
|
640
|
+
|
641
|
+
end # module XMLRPC
|
642
|
+
|