1 | # SOAP4R - SOAP XML Instance Parser library.
|
---|
2 | # Copyright (C) 2001, 2003 NAKAMURA, Hiroshi <[email protected]>.
|
---|
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/ns'
|
---|
10 | require 'xsd/xmlparser'
|
---|
11 | require 'soap/soap'
|
---|
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, :encodingstyle
|
---|
32 |
|
---|
33 | class NodeContainer
|
---|
34 | def initialize(node)
|
---|
35 | @node = node
|
---|
36 | end
|
---|
37 |
|
---|
38 | def node
|
---|
39 | @node
|
---|
40 | end
|
---|
41 |
|
---|
42 | def replace_node(node)
|
---|
43 | @node = node
|
---|
44 | end
|
---|
45 | end
|
---|
46 |
|
---|
47 | public
|
---|
48 |
|
---|
49 | def initialize(ns, name, node, encodingstyle)
|
---|
50 | @ns = ns
|
---|
51 | @name = name
|
---|
52 | self.node = node
|
---|
53 | @encodingstyle = encodingstyle
|
---|
54 | end
|
---|
55 |
|
---|
56 | def node=(node)
|
---|
57 | @node = NodeContainer.new(node)
|
---|
58 | end
|
---|
59 | end
|
---|
60 |
|
---|
61 | public
|
---|
62 |
|
---|
63 | attr_accessor :envelopenamespace
|
---|
64 | attr_accessor :default_encodingstyle
|
---|
65 | attr_accessor :decode_typemap
|
---|
66 | attr_accessor :allow_unqualified_element
|
---|
67 |
|
---|
68 | def initialize(opt = {})
|
---|
69 | @opt = opt
|
---|
70 | @parser = XSD::XMLParser.create_parser(self, opt)
|
---|
71 | @parsestack = nil
|
---|
72 | @lastnode = nil
|
---|
73 | @handlers = {}
|
---|
74 | @envelopenamespace = opt[:envelopenamespace] || EnvelopeNamespace
|
---|
75 | @default_encodingstyle = opt[:default_encodingstyle] || EncodingNamespace
|
---|
76 | @decode_typemap = opt[:decode_typemap] || nil
|
---|
77 | @allow_unqualified_element = opt[:allow_unqualified_element] || false
|
---|
78 | end
|
---|
79 |
|
---|
80 | def charset
|
---|
81 | @parser.charset
|
---|
82 | end
|
---|
83 |
|
---|
84 | def parse(string_or_readable)
|
---|
85 | @parsestack = []
|
---|
86 | @lastnode = nil
|
---|
87 |
|
---|
88 | @handlers.each do |uri, handler|
|
---|
89 | handler.decode_prologue
|
---|
90 | end
|
---|
91 |
|
---|
92 | @parser.do_parse(string_or_readable)
|
---|
93 |
|
---|
94 | unless @parsestack.empty?
|
---|
95 | raise FormatDecodeError.new("Unbalanced tag in XML.")
|
---|
96 | end
|
---|
97 |
|
---|
98 | @handlers.each do |uri, handler|
|
---|
99 | handler.decode_epilogue
|
---|
100 | end
|
---|
101 |
|
---|
102 | @lastnode
|
---|
103 | end
|
---|
104 |
|
---|
105 | def start_element(name, attrs)
|
---|
106 | lastframe = @parsestack.last
|
---|
107 | ns = parent = parent_encodingstyle = nil
|
---|
108 | if lastframe
|
---|
109 | ns = lastframe.ns.clone_ns
|
---|
110 | parent = lastframe.node
|
---|
111 | parent_encodingstyle = lastframe.encodingstyle
|
---|
112 | else
|
---|
113 | ns = XSD::NS.new
|
---|
114 | parent = ParseFrame::NodeContainer.new(nil)
|
---|
115 | parent_encodingstyle = nil
|
---|
116 | end
|
---|
117 |
|
---|
118 | attrs = XSD::XMLParser.filter_ns(ns, attrs)
|
---|
119 | encodingstyle = find_encodingstyle(ns, attrs)
|
---|
120 |
|
---|
121 | # Children's encodingstyle is derived from its parent.
|
---|
122 | if encodingstyle.nil?
|
---|
123 | if parent.node.is_a?(SOAPHeader)
|
---|
124 | encodingstyle = LiteralNamespace
|
---|
125 | else
|
---|
126 | encodingstyle = parent_encodingstyle || @default_encodingstyle
|
---|
127 | end
|
---|
128 | end
|
---|
129 |
|
---|
130 | node = decode_tag(ns, name, attrs, parent, encodingstyle)
|
---|
131 |
|
---|
132 | @parsestack << ParseFrame.new(ns, name, node, encodingstyle)
|
---|
133 | end
|
---|
134 |
|
---|
135 | def characters(text)
|
---|
136 | lastframe = @parsestack.last
|
---|
137 | if lastframe
|
---|
138 | # Need not to be cloned because character does not have attr.
|
---|
139 | decode_text(lastframe.ns, text, lastframe.encodingstyle)
|
---|
140 | else
|
---|
141 | # Ignore Text outside of SOAP Envelope.
|
---|
142 | p text if $DEBUG
|
---|
143 | end
|
---|
144 | end
|
---|
145 |
|
---|
146 | def end_element(name)
|
---|
147 | lastframe = @parsestack.pop
|
---|
148 | unless name == lastframe.name
|
---|
149 | raise UnexpectedElementError.new("Closing element name '#{ name }' does not match with opening element '#{ lastframe.name }'.")
|
---|
150 | end
|
---|
151 | decode_tag_end(lastframe.ns, lastframe.node, lastframe.encodingstyle)
|
---|
152 | @lastnode = lastframe.node.node
|
---|
153 | end
|
---|
154 |
|
---|
155 | private
|
---|
156 |
|
---|
157 | def find_encodingstyle(ns, attrs)
|
---|
158 | attrs.each do |key, value|
|
---|
159 | if (ns.compare(@envelopenamespace, AttrEncodingStyle, key))
|
---|
160 | return value
|
---|
161 | end
|
---|
162 | end
|
---|
163 | nil
|
---|
164 | end
|
---|
165 |
|
---|
166 | def decode_tag(ns, name, attrs, parent, encodingstyle)
|
---|
167 | ele = ns.parse(name)
|
---|
168 |
|
---|
169 | # Envelope based parsing.
|
---|
170 | if ((ele.namespace == @envelopenamespace) ||
|
---|
171 | (@allow_unqualified_element && ele.namespace.nil?))
|
---|
172 | o = decode_soap_envelope(ns, ele, attrs, parent)
|
---|
173 | return o if o
|
---|
174 | end
|
---|
175 |
|
---|
176 | # Encoding based parsing.
|
---|
177 | handler = find_handler(encodingstyle)
|
---|
178 | if handler
|
---|
179 | return handler.decode_tag(ns, ele, attrs, parent)
|
---|
180 | else
|
---|
181 | raise FormatDecodeError.new("Unknown encodingStyle: #{ encodingstyle }.")
|
---|
182 | end
|
---|
183 | end
|
---|
184 |
|
---|
185 | def decode_tag_end(ns, node, encodingstyle)
|
---|
186 | return unless encodingstyle
|
---|
187 |
|
---|
188 | handler = find_handler(encodingstyle)
|
---|
189 | if handler
|
---|
190 | return handler.decode_tag_end(ns, node)
|
---|
191 | else
|
---|
192 | raise FormatDecodeError.new("Unknown encodingStyle: #{ encodingstyle }.")
|
---|
193 | end
|
---|
194 | end
|
---|
195 |
|
---|
196 | def decode_text(ns, text, encodingstyle)
|
---|
197 | handler = find_handler(encodingstyle)
|
---|
198 |
|
---|
199 | if handler
|
---|
200 | handler.decode_text(ns, text)
|
---|
201 | else
|
---|
202 | # How should I do?
|
---|
203 | end
|
---|
204 | end
|
---|
205 |
|
---|
206 | def decode_soap_envelope(ns, ele, attrs, parent)
|
---|
207 | o = nil
|
---|
208 | if ele.name == EleEnvelope
|
---|
209 | o = SOAPEnvelope.new
|
---|
210 | if ext = @opt[:external_content]
|
---|
211 | ext.each do |k, v|
|
---|
212 | o.external_content[k] = v
|
---|
213 | end
|
---|
214 | end
|
---|
215 | elsif ele.name == EleHeader
|
---|
216 | unless parent.node.is_a?(SOAPEnvelope)
|
---|
217 | raise FormatDecodeError.new("Header should be a child of Envelope.")
|
---|
218 | end
|
---|
219 | o = SOAPHeader.new
|
---|
220 | parent.node.header = o
|
---|
221 | elsif ele.name == EleBody
|
---|
222 | unless parent.node.is_a?(SOAPEnvelope)
|
---|
223 | raise FormatDecodeError.new("Body should be a child of Envelope.")
|
---|
224 | end
|
---|
225 | o = SOAPBody.new
|
---|
226 | parent.node.body = o
|
---|
227 | elsif ele.name == EleFault
|
---|
228 | unless parent.node.is_a?(SOAPBody)
|
---|
229 | raise FormatDecodeError.new("Fault should be a child of Body.")
|
---|
230 | end
|
---|
231 | o = SOAPFault.new
|
---|
232 | parent.node.fault = o
|
---|
233 | end
|
---|
234 | o
|
---|
235 | end
|
---|
236 |
|
---|
237 | def find_handler(encodingstyle)
|
---|
238 | unless @handlers.key?(encodingstyle)
|
---|
239 | handler_factory = SOAP::EncodingStyle::Handler.handler(encodingstyle) ||
|
---|
240 | SOAP::EncodingStyle::Handler.handler(EncodingNamespace)
|
---|
241 | handler = handler_factory.new(@parser.charset)
|
---|
242 | handler.decode_typemap = @decode_typemap
|
---|
243 | handler.decode_prologue
|
---|
244 | @handlers[encodingstyle] = handler
|
---|
245 | end
|
---|
246 | @handlers[encodingstyle]
|
---|
247 | end
|
---|
248 | end
|
---|
249 |
|
---|
250 |
|
---|
251 | end
|
---|