OSDN Git Service

ruby-1.9.1-rc1
[splhack/AndroidRuby.git] / lib / ruby-1.9.1-rc1 / lib / xmlrpc / parser.rb
1 #
2 # Parser for XML-RPC call and response
3
4 # Copyright (C) 2001, 2002, 2003 by Michael Neumann (mneumann@ntecs.de)
5 #
6 # $Id: parser.rb 19657 2008-10-01 13:46:53Z mame $
7 #
8
9
10 require "date"
11 require "xmlrpc/base64"
12 require "xmlrpc/datetime"
13
14
15 # add some methods to NQXML::Node
16 module NQXML
17   class Node
18
19     def removeChild(node)
20       @children.delete(node)
21     end
22     def childNodes
23       @children
24     end
25     def hasChildNodes
26       not @children.empty?
27     end
28     def [] (index)
29       @children[index]
30     end
31
32     def nodeType
33       if @entity.instance_of? NQXML::Text then :TEXT
34       elsif @entity.instance_of? NQXML::Comment then :COMMENT
35       #elsif @entity.instance_of? NQXML::Element then :ELEMENT
36       elsif @entity.instance_of? NQXML::Tag then :ELEMENT
37       else :ELSE
38       end
39     end
40
41     def nodeValue
42       #TODO: error when wrong Entity-type
43       @entity.text
44     end
45     def nodeName
46       #TODO: error when wrong Entity-type
47       @entity.name
48     end
49   end # class Node
50 end # module NQXML
51
52 module XMLRPC
53
54   class FaultException < StandardError
55     attr_reader :faultCode, :faultString
56
57     alias message faultString
58
59     def initialize(faultCode, faultString)
60       @faultCode   = faultCode
61       @faultString = faultString
62     end
63     
64     # returns a hash
65     def to_h
66       {"faultCode" => @faultCode, "faultString" => @faultString}
67     end
68   end
69
70   module Convert
71     def self.int(str)
72       str.to_i
73     end
74
75     def self.boolean(str)
76       case str
77       when "0" then false
78       when "1" then true
79       else
80         raise "RPC-value of type boolean is wrong" 
81       end
82     end
83
84     def self.double(str)
85       str.to_f
86     end
87
88     def self.dateTime(str)
89       case str
90       when /^(-?\d\d\d\d)-?(\d\d)-?(\d\d)T(\d\d):(\d\d):(\d\d)(?:Z|([+-])(\d\d):?(\d\d))?$/
91         a = [$1, $2, $3, $4, $5, $6].collect{|i| i.to_i}
92         if $7
93           ofs = $8.to_i*3600 + $9.to_i*60
94           ofs = -ofs if $7=='+'
95           utc = Time.utc(*a) + ofs
96           a = [ utc.year, utc.month, utc.day, utc.hour, utc.min, utc.sec ]
97         end
98         XMLRPC::DateTime.new(*a)
99       when /^(-?\d\d)-?(\d\d)-?(\d\d)T(\d\d):(\d\d):(\d\d)(Z|([+-]\d\d):(\d\d))?$/
100         a = [$1, $2, $3, $4, $5, $6].collect{|i| i.to_i}
101         if a[0] < 70
102           a[0] += 2000
103         else
104           a[0] += 1900
105         end
106         if $7
107           ofs = $8.to_i*3600 + $9.to_i*60
108           ofs = -ofs if $7=='+'
109           utc = Time.utc(*a) + ofs
110           a = [ utc.year, utc.month, utc.day, utc.hour, utc.min, utc.sec ]
111         end
112         XMLRPC::DateTime.new(*a)
113       else
114         raise "wrong dateTime.iso8601 format " + str
115       end
116     end
117
118     def self.base64(str)
119       XMLRPC::Base64.decode(str)
120     end
121
122     def self.struct(hash)
123       # convert to marhalled object
124       klass = hash["___class___"]
125       if klass.nil? or Config::ENABLE_MARSHALLING == false 
126         hash
127       else
128         begin
129           mod = Module
130           klass.split("::").each {|const| mod = mod.const_get(const.strip)}
131
132           obj = mod.allocate
133           
134           hash.delete "___class___"
135           hash.each {|key, value| 
136             obj.instance_variable_set("@#{ key }", value) if key =~ /^([\w_][\w_0-9]*)$/
137           }
138           obj
139         rescue
140           hash
141         end
142       end
143     end
144
145     def self.fault(hash)
146       if hash.kind_of? Hash and hash.size == 2 and 
147         hash.has_key? "faultCode" and hash.has_key? "faultString" and 
148         hash["faultCode"].kind_of? Integer and hash["faultString"].kind_of? String
149
150         XMLRPC::FaultException.new(hash["faultCode"], hash["faultString"]) 
151       else
152         raise "wrong fault-structure: #{hash.inspect}"
153       end
154     end
155
156   end # module Convert
157
158   module XMLParser
159
160     class AbstractTreeParser
161
162       def parseMethodResponse(str)
163         methodResponse_document(createCleanedTree(str))
164       end
165
166       def parseMethodCall(str)
167         methodCall_document(createCleanedTree(str))
168       end
169
170       private
171
172       #
173       # remove all whitespaces but in the tags i4, int, boolean....
174       # and all comments
175       #
176       def removeWhitespacesAndComments(node)
177         remove = []
178         childs = node.childNodes.to_a
179         childs.each do |nd|
180           case _nodeType(nd)
181           when :TEXT
182             # TODO: add nil?
183             unless %w(i4 int boolean string double dateTime.iso8601 base64).include? node.nodeName
184
185                if node.nodeName == "value" 
186                  if not node.childNodes.to_a.detect {|n| _nodeType(n) == :ELEMENT}.nil?
187                    remove << nd if nd.nodeValue.strip == "" 
188                  end
189                else
190                  remove << nd if nd.nodeValue.strip == ""
191                end
192             end
193           when :COMMENT
194             remove << nd
195           else
196             removeWhitespacesAndComments(nd)
197           end 
198         end
199
200         remove.each { |i| node.removeChild(i) }
201       end
202
203
204       def nodeMustBe(node, name)
205         cmp = case name
206         when Array 
207           name.include?(node.nodeName)
208         when String
209           name == node.nodeName
210         else
211           raise "error"
212         end  
213
214         if not cmp then
215           raise "wrong xml-rpc (name)"
216         end
217
218         node
219       end
220
221       #
222       # returns, when successfully the only child-node
223       #
224       def hasOnlyOneChild(node, name=nil)
225         if node.childNodes.to_a.size != 1
226           raise "wrong xml-rpc (size)"
227         end
228         if name != nil then
229           nodeMustBe(node.firstChild, name)
230         end
231       end
232
233
234       def assert(b)
235         if not b then
236           raise "assert-fail" 
237         end
238       end
239
240       # the node `node` has empty string or string
241       def text_zero_one(node)
242         nodes = node.childNodes.to_a.size
243
244         if nodes == 1
245           text(node.firstChild)
246         elsif nodes == 0
247           ""
248         else
249           raise "wrong xml-rpc (size)"
250         end
251       end
252      
253
254       def integer(node)
255         #TODO: check string for float because to_i returnsa
256         #      0 when wrong string
257          nodeMustBe(node, %w(i4 int))    
258         hasOnlyOneChild(node)
259         
260         Convert.int(text(node.firstChild))
261       end
262
263       def boolean(node)
264         nodeMustBe(node, "boolean")    
265         hasOnlyOneChild(node)
266         
267         Convert.boolean(text(node.firstChild))
268       end
269
270       def v_nil(node)
271         nodeMustBe(node, "nil")
272         assert( node.childNodes.to_a.size == 0 )
273         nil
274       end
275
276       def string(node)
277         nodeMustBe(node, "string")    
278         text_zero_one(node)
279       end
280
281       def double(node)
282         #TODO: check string for float because to_f returnsa
283         #      0.0 when wrong string
284         nodeMustBe(node, "double")    
285         hasOnlyOneChild(node)
286         
287         Convert.double(text(node.firstChild))
288       end
289
290       def dateTime(node)
291         nodeMustBe(node, "dateTime.iso8601")
292         hasOnlyOneChild(node)
293         
294         Convert.dateTime( text(node.firstChild) )
295       end
296
297       def base64(node)
298         nodeMustBe(node, "base64")
299         #hasOnlyOneChild(node)
300          
301         Convert.base64(text_zero_one(node))
302       end
303
304       def member(node)
305         nodeMustBe(node, "member")
306         assert( node.childNodes.to_a.size == 2 ) 
307
308         [ name(node[0]), value(node[1]) ]
309       end
310
311       def name(node)
312         nodeMustBe(node, "name")
313         #hasOnlyOneChild(node)
314         text_zero_one(node) 
315       end
316
317       def array(node)
318         nodeMustBe(node, "array")
319         hasOnlyOneChild(node, "data") 
320         data(node.firstChild)  
321       end
322
323       def data(node)
324         nodeMustBe(node, "data")
325
326         node.childNodes.to_a.collect do |val|
327           value(val)
328         end 
329       end
330
331       def param(node)
332         nodeMustBe(node, "param")
333         hasOnlyOneChild(node, "value")
334         value(node.firstChild) 
335       end
336  
337       def methodResponse(node)
338         nodeMustBe(node, "methodResponse")
339         hasOnlyOneChild(node, %w(params fault))
340         child = node.firstChild
341
342         case child.nodeName
343         when "params"
344           [ true, params(child,false) ] 
345         when "fault"
346           [ false, fault(child) ]
347         else
348           raise "unexpected error"
349         end
350
351       end
352
353       def methodName(node)
354         nodeMustBe(node, "methodName")
355         hasOnlyOneChild(node)
356         text(node.firstChild) 
357       end
358
359       def params(node, call=true)
360         nodeMustBe(node, "params")
361
362         if call 
363           node.childNodes.to_a.collect do |n|
364             param(n)
365           end
366         else # response (only one param)
367           hasOnlyOneChild(node)
368           param(node.firstChild)
369         end
370       end
371
372       def fault(node)
373         nodeMustBe(node, "fault")
374         hasOnlyOneChild(node, "value")
375         f = value(node.firstChild) 
376         Convert.fault(f)
377       end
378
379
380
381       # _nodeType is defined in the subclass
382       def text(node)
383         assert( _nodeType(node) == :TEXT )
384         assert( node.hasChildNodes == false )
385         assert( node.nodeValue != nil )
386
387         node.nodeValue.to_s
388       end
389
390       def struct(node)
391         nodeMustBe(node, "struct")    
392
393         hash = {}
394         node.childNodes.to_a.each do |me|
395           n, v = member(me)  
396           hash[n] = v
397         end 
398
399         Convert.struct(hash)
400       end
401
402
403       def value(node)
404         nodeMustBe(node, "value")
405         nodes = node.childNodes.to_a.size
406         if nodes == 0 
407           return ""
408         elsif nodes > 1 
409           raise "wrong xml-rpc (size)"
410         end
411
412         child = node.firstChild
413
414         case _nodeType(child)
415         when :TEXT
416           text_zero_one(node)
417         when :ELEMENT
418           case child.nodeName
419           when "i4", "int"        then integer(child)
420           when "boolean"          then boolean(child)
421           when "string"           then string(child)
422           when "double"           then double(child)
423           when "dateTime.iso8601" then dateTime(child)
424           when "base64"           then base64(child)
425           when "struct"           then struct(child)
426           when "array"            then array(child) 
427           when "nil"              
428             if Config::ENABLE_NIL_PARSER
429               v_nil(child)
430             else
431               raise "wrong/unknown XML-RPC type 'nil'"
432             end
433           else 
434             raise "wrong/unknown XML-RPC type"
435           end
436         else
437           raise "wrong type of node"
438         end
439
440       end
441
442       def methodCall(node)
443         nodeMustBe(node, "methodCall")
444         assert( (1..2).include?( node.childNodes.to_a.size ) ) 
445         name = methodName(node[0])
446
447         if node.childNodes.to_a.size == 2 then
448           pa = params(node[1])
449         else # no parameters given
450           pa = []
451         end
452         [name, pa]
453       end
454
455     end # module TreeParserMixin
456
457     class AbstractStreamParser
458       def parseMethodResponse(str)
459         parser = @parser_class.new
460         parser.parse(str)
461         raise "No valid method response!" if parser.method_name != nil
462         if parser.fault != nil
463           # is a fault structure
464           [false, parser.fault] 
465         else
466           # is a normal return value
467           raise "Missing return value!" if parser.params.size == 0
468           raise "Too many return values. Only one allowed!" if parser.params.size > 1
469           [true, parser.params[0]]
470         end
471       end
472
473       def parseMethodCall(str)
474         parser = @parser_class.new
475         parser.parse(str)
476         raise "No valid method call - missing method name!" if parser.method_name.nil?
477         [parser.method_name, parser.params]
478       end
479     end
480
481     module StreamParserMixin
482       attr_reader :params
483       attr_reader :method_name
484       attr_reader :fault
485
486       def initialize(*a)
487         super(*a)
488         @params = []
489         @values = []
490         @val_stack = []
491
492         @names = []
493         @name = []
494
495         @structs = []
496         @struct = {}
497
498         @method_name = nil
499         @fault = nil
500
501         @data = nil
502       end
503
504       def startElement(name, attrs=[])
505         @data = nil
506         case name
507         when "value"
508           @value = nil
509         when "nil"
510           raise "wrong/unknown XML-RPC type 'nil'" unless Config::ENABLE_NIL_PARSER
511           @value = :nil 
512         when "array"
513           @val_stack << @values
514           @values = []
515         when "struct"
516           @names << @name
517           @name = []
518
519           @structs << @struct
520           @struct = {} 
521         end
522       end
523
524       def endElement(name)
525         @data ||= ""
526         case name
527         when "string"
528           @value = @data
529         when "i4", "int"
530           @value = Convert.int(@data)
531         when "boolean"
532           @value = Convert.boolean(@data)
533         when "double"
534           @value = Convert.double(@data)
535         when "dateTime.iso8601"
536           @value = Convert.dateTime(@data)
537         when "base64"
538           @value = Convert.base64(@data)
539         when "value"
540           @value = @data if @value.nil?
541           @values << (@value == :nil ? nil : @value) 
542         when "array"
543           @value = @values
544           @values = @val_stack.pop
545         when "struct"
546           @value = Convert.struct(@struct)
547
548           @name = @names.pop
549           @struct = @structs.pop
550         when "name"
551           @name[0] = @data 
552         when "member"
553           @struct[@name[0]] = @values.pop 
554
555         when "param"
556           @params << @values[0]
557           @values = []
558
559         when "fault"
560           @fault = Convert.fault(@values[0])
561
562         when "methodName"
563           @method_name = @data 
564         end
565
566         @data = nil
567       end
568
569       def character(data)
570         if @data
571           @data << data
572         else
573           @data = data
574         end
575       end
576
577     end # module StreamParserMixin
578
579     # ---------------------------------------------------------------------------
580     class XMLStreamParser < AbstractStreamParser
581       def initialize
582         require "xmlparser"
583         @parser_class = Class.new(::XMLParser) {
584           include StreamParserMixin
585         }
586       end
587     end # class XMLStreamParser
588     # ---------------------------------------------------------------------------
589     class NQXMLStreamParser < AbstractStreamParser
590       def initialize
591         require "nqxml/streamingparser"
592         @parser_class = XMLRPCParser
593       end
594
595       class XMLRPCParser 
596         include StreamParserMixin
597
598         def parse(str)
599           parser = NQXML::StreamingParser.new(str)
600           parser.each do |ele|
601             case ele
602             when NQXML::Text
603               @data = ele.text
604               #character(ele.text)
605             when NQXML::Tag
606               if ele.isTagEnd
607                 endElement(ele.name)
608               else
609                 startElement(ele.name, ele.attrs)
610               end
611             end
612           end # do
613         end # method parse
614       end # class XMLRPCParser
615
616     end # class NQXMLStreamParser
617     # ---------------------------------------------------------------------------
618     class XMLTreeParser < AbstractTreeParser
619
620       def initialize
621         require "xmltreebuilder"
622
623         # The new XMLParser library (0.6.2+) uses a slightly different DOM implementation. 
624         # The following code removes the differences between both versions.
625         if defined? XML::DOM::Builder 
626           return if defined? XML::DOM::Node::DOCUMENT # code below has been already executed
627           klass = XML::DOM::Node
628           klass.const_set("DOCUMENT", klass::DOCUMENT_NODE)
629           klass.const_set("TEXT", klass::TEXT_NODE)
630           klass.const_set("COMMENT", klass::COMMENT_NODE)
631           klass.const_set("ELEMENT", klass::ELEMENT_NODE)
632         end
633       end
634
635       private
636
637       def _nodeType(node)
638         tp = node.nodeType
639         if tp == XML::SimpleTree::Node::TEXT then :TEXT
640         elsif tp == XML::SimpleTree::Node::COMMENT then :COMMENT 
641         elsif tp == XML::SimpleTree::Node::ELEMENT then :ELEMENT 
642         else :ELSE
643         end
644       end
645
646
647       def methodResponse_document(node)
648         assert( node.nodeType == XML::SimpleTree::Node::DOCUMENT )
649         hasOnlyOneChild(node, "methodResponse")
650         
651         methodResponse(node.firstChild)
652       end
653
654       def methodCall_document(node)
655         assert( node.nodeType == XML::SimpleTree::Node::DOCUMENT )
656         hasOnlyOneChild(node, "methodCall")
657         
658         methodCall(node.firstChild)
659       end
660
661       def createCleanedTree(str)
662         doc = XML::SimpleTreeBuilder.new.parse(str)
663         doc.documentElement.normalize
664         removeWhitespacesAndComments(doc)
665         doc
666       end
667
668     end # class XMLParser
669     # ---------------------------------------------------------------------------
670     class NQXMLTreeParser < AbstractTreeParser
671
672       def initialize
673         require "nqxml/treeparser"
674       end
675
676       private
677
678       def _nodeType(node)
679         node.nodeType
680       end
681
682       def methodResponse_document(node)
683         methodResponse(node)
684       end
685
686       def methodCall_document(node)
687         methodCall(node)
688       end
689
690       def createCleanedTree(str)
691         doc = ::NQXML::TreeParser.new(str).document.rootNode 
692         removeWhitespacesAndComments(doc)
693         doc
694       end
695
696     end # class NQXMLTreeParser
697     # ---------------------------------------------------------------------------
698     class REXMLStreamParser < AbstractStreamParser
699       def initialize
700         require "rexml/document"
701         @parser_class = StreamListener
702       end
703
704       class StreamListener 
705         include StreamParserMixin
706
707         alias :tag_start :startElement
708         alias :tag_end :endElement
709         alias :text :character
710         alias :cdata :character
711
712         def method_missing(*a)
713           # ignore
714         end
715
716         def parse(str)
717           parser = REXML::Document.parse_stream(str, self)
718         end
719       end 
720
721     end
722     # ---------------------------------------------------------------------------
723     class XMLScanStreamParser < AbstractStreamParser
724       def initialize
725         require "xmlscan/parser"
726         @parser_class = XMLScanParser
727       end
728
729       class XMLScanParser
730         include StreamParserMixin
731
732         Entities = {
733           "lt"   => "<",
734           "gt"   => ">",
735           "amp"  => "&",
736           "quot" => '"',
737           "apos" => "'"
738         }
739
740         def parse(str)
741           parser  = XMLScan::XMLParser.new(self)
742           parser.parse(str)
743         end
744
745         alias :on_stag :startElement
746         alias :on_etag :endElement
747
748         def on_stag_end(name); end
749
750         def on_stag_end_empty(name)
751           startElement(name)
752           endElement(name)
753         end
754        
755         def on_chardata(str)
756           character(str)
757         end
758
759         def on_cdata(str)
760           character(str)
761         end
762
763         def on_entityref(ent)
764           str = Entities[ent]
765           if str
766             character(str)
767           else
768             raise "unknown entity"
769           end
770         end
771
772         def on_charref(code)
773           character(code.chr)
774         end
775
776         def on_charref_hex(code)
777           character(code.chr)
778         end
779
780         def method_missing(*a)
781         end
782
783         # TODO: call/implement?
784         # valid_name?
785         # valid_chardata?
786         # valid_char?
787         # parse_error  
788
789       end
790     end
791     # ---------------------------------------------------------------------------
792     XMLParser   = XMLTreeParser
793     NQXMLParser = NQXMLTreeParser
794
795     Classes = [XMLStreamParser, XMLTreeParser, 
796                NQXMLStreamParser, NQXMLTreeParser, 
797                REXMLStreamParser, XMLScanStreamParser]
798
799     # yields an instance of each installed parser
800     def self.each_installed_parser
801       XMLRPC::XMLParser::Classes.each do |klass|
802         begin
803           yield klass.new
804         rescue LoadError
805         end
806       end
807     end
808
809   end # module XMLParser
810
811
812 end # module XMLRPC
813