Welcome to the "trac"-ing site of soap4r!
[soap4r] [httpclient] [openpgp4u] [pkcs1] [logger] [csv] [vtr]

root/branches/1_5/lib/soap/parser.rb

Revision 1978, 6.4 kB (checked in by nahi, 1 year ago)
  • unqualified element 'Envelope', 'Header', 'Body' and 'Fault' caused an error while parsing. closes #431.
  • Property svn:eol-style set to native
  • Property svn:keywords set to author date id revision
Line 
1 # SOAP4R - SOAP XML Instance Parser library.
2 # Copyright (C) 2000-2007  NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
3
4 # This program is copyrighted free software by NAKAMURA, Hiroshi.  You can
5 # redistribute it and/or modify it under the same terms of Ruby's license;
6 # either the dual license version in 2003, or any later version.
7
8
9 require 'xsd/xmlparser'
10 require 'soap/soap'
11 require 'soap/ns'
12 require 'soap/baseData'
13 require 'soap/encodingstyle/handler'
14
15
16 module SOAP
17
18
19 class Parser
20   include SOAP
21
22   class ParseError < Error; end
23   class FormatDecodeError < ParseError; end
24   class UnexpectedElementError < ParseError; end
25
26 private
27
28   class ParseFrame
29     attr_reader :node
30     attr_reader :name
31     attr_reader :ns
32     attr_reader :encodingstyle
33     attr_reader :handler
34
35     class NodeContainer
36       def initialize(node)
37         @node = node
38       end
39
40       def node
41         @node
42       end
43
44       def replace_node(node)
45         @node = node
46       end
47     end
48
49   public
50
51     def initialize(ns, name, node, encodingstyle, handler)
52       @ns = ns
53       @name = name
54       @node = NodeContainer.new(node)
55       @encodingstyle = encodingstyle
56       @handler = handler
57     end
58
59     # to avoid memory consumption
60     def update(ns, name, node, encodingstyle, handler)
61       @ns = ns
62       @name = name
63       @node.replace_node(node)
64       @encodingstyle = encodingstyle
65       @handler = handler
66       self
67     end
68   end
69
70 public
71
72   attr_accessor :envelopenamespace
73   attr_accessor :default_encodingstyle
74   attr_accessor :decode_typemap
75   attr_accessor :allow_unqualified_element
76
77   def initialize(opt = {})
78     @opt = opt
79     @parser = XSD::XMLParser.create_parser(self, opt)
80     @parsestack = nil
81     @recycleframe = nil
82     @lastnode = nil
83     @handlers = {}
84     @envelopenamespace = opt[:envelopenamespace] || EnvelopeNamespace
85     @default_encodingstyle = opt[:default_encodingstyle] || EncodingNamespace
86     @decode_typemap = opt[:decode_typemap] || nil
87     @allow_unqualified_element = opt[:allow_unqualified_element] || false
88   end
89
90   def charset
91     @parser.charset
92   end
93
94   def parse(string_or_readable)
95     @parsestack = []
96     @lastnode = nil
97
98     @handlers.each do |uri, handler|
99       handler.decode_prologue
100     end
101
102     @parser.do_parse(string_or_readable)
103
104     unless @parsestack.empty?
105       raise FormatDecodeError.new("Unbalanced tag in XML.")
106     end
107
108     @handlers.each do |uri, handler|
109       handler.decode_epilogue
110     end
111
112     @lastnode
113   end
114
115   def start_element(name, raw_attrs)
116     lastframe = @parsestack.last
117     ns = parent = parent_encodingstyle = nil
118     if lastframe
119       ns = lastframe.ns
120       parent = lastframe.node
121       parent_encodingstyle = lastframe.encodingstyle
122     else
123       ns = SOAP::NS.new
124       parent = ParseFrame::NodeContainer.new(nil)
125       parent_encodingstyle = nil
126     end
127     # ns might be the same
128     ns, raw_attrs = XSD::XMLParser.filter_ns(ns, raw_attrs)
129     attrs = decode_attrs(ns, raw_attrs)
130     encodingstyle = attrs[AttrEncodingStyleName]
131     # Children's encodingstyle is derived from its parent.
132     if encodingstyle.nil?
133       if parent.node.is_a?(SOAPHeader)
134         encodingstyle = LiteralNamespace
135       else
136         encodingstyle = parent_encodingstyle || @default_encodingstyle
137       end
138     end
139     handler = find_handler(encodingstyle)
140     unless handler
141       raise FormatDecodeError.new("Unknown encodingStyle: #{ encodingstyle }.")
142     end
143     node = decode_tag(ns, name, attrs, parent, handler)
144     if @recycleframe
145       @parsestack << @recycleframe.update(ns, name, node, encodingstyle, handler)
146       @recycleframe = nil
147     else
148       @parsestack << ParseFrame.new(ns, name, node, encodingstyle, handler)
149     end
150   end
151
152   def characters(text)
153     # Ignore Text outside of SOAP Envelope.
154     if lastframe = @parsestack.last
155       # Need not to be cloned because character does not have attr.
156       decode_text(lastframe.ns, text, lastframe.handler)
157     end
158   end
159
160   def end_element(name)
161     lastframe = @parsestack.pop
162     unless name == lastframe.name
163       raise UnexpectedElementError.new("Closing element name '#{ name }' does not match with opening element '#{ lastframe.name }'.")
164     end
165     decode_tag_end(lastframe.ns, lastframe.node, lastframe.handler)
166     @lastnode = lastframe.node.node
167     @recycleframe = lastframe
168   end
169
170 private
171
172   def decode_tag(ns, name, attrs, parent, handler)
173     ele = ns.parse(name)
174     # Envelope based parsing.
175     if ((ele.namespace == @envelopenamespace) ||
176         (@allow_unqualified_element && ele.namespace.nil?))
177       o = decode_soap_envelope(ns, ele, attrs, parent)
178       return o if o
179     end
180     # Encoding based parsing.
181     return handler.decode_tag(ns, ele, attrs, parent)
182   end
183
184   def decode_tag_end(ns, node, handler)
185     return handler.decode_tag_end(ns, node)
186   end
187
188   def decode_attrs(ns, attrs)
189     extraattr = {}
190     attrs.each do |key, value|
191       qname = ns.parse_local(key)
192       extraattr[qname] = value
193     end
194     extraattr
195   end
196
197   def decode_text(ns, text, handler)
198     handler.decode_text(ns, text)
199   end
200
201   def decode_soap_envelope(ns, ele, attrs, parent)
202     o = nil
203     if ele.name == EleEnvelope
204       o = SOAPEnvelope.new
205       if ext = @opt[:external_content]
206         ext.each do |k, v|
207           o.external_content[k] = v
208         end
209       end
210     elsif ele.name == EleHeader
211       return nil unless parent.node.is_a?(SOAPEnvelope)
212       o = SOAPHeader.new
213       parent.node.header = o
214     elsif ele.name == EleBody
215       return nil unless parent.node.is_a?(SOAPEnvelope)
216       o = SOAPBody.new
217       parent.node.body = o
218     elsif ele.name == EleFault
219       if parent.node.is_a?(SOAPBody)
220         o = SOAPFault.new
221         parent.node.fault = o
222       elsif parent.node.is_a?(SOAPEnvelope)
223         # live.com server returns SOAPFault as a direct child of SOAPEnvelope.
224         # support it even if it's not spec compliant.
225         warn("Fault must be a child of Body.")
226         body = SOAPBody.new
227         parent.node.body = body
228         o = SOAPFault.new
229         body.fault = o
230       else
231         return nil
232       end
233     end
234     o.extraattr.update(attrs) if o
235     o
236   end
237
238   def find_handler(encodingstyle)
239     unless @handlers.key?(encodingstyle)
240       handler_factory = SOAP::EncodingStyle::Handler.handler(encodingstyle) ||
241         SOAP::EncodingStyle::Handler.handler(EncodingNamespace)
242       handler = handler_factory.new(@parser.charset)
243       handler.decode_typemap = @decode_typemap
244       handler.decode_prologue
245       @handlers[encodingstyle] = handler
246     end
247     @handlers[encodingstyle]
248   end
249 end
250
251
252 end
Note: See TracBrowser for help on using the browser.