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

root/trunk/lib/soap/encodingstyle/soapHandler.rb

Revision 1975, 14.0 kB (checked in by nahi, 1 year ago)
  • XML attribute must not be affected by default namespace. closes #430.
  • Property svn:eol-style set to native
  • Property svn:keywords set to author date id revision
Line 
1 # SOAP4R - SOAP EncodingStyle handler 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 'soap/encodingstyle/handler'
10 require 'soap/mapping/registry'
11
12
13 module SOAP
14 module EncodingStyle
15
16
17 class SOAPHandler < Handler
18   Namespace = SOAP::EncodingNamespace
19   add_handler
20
21   def initialize(charset = nil)
22     super(charset)
23     @refpool = []
24     @idpool = []
25     @textbuf = []
26     @is_first_top_ele = true
27   end
28
29
30   ###
31   ## encode interface.
32   #
33   def encode_data(generator, ns, data, parent)
34     attrs = encode_attrs(generator, ns, data, parent)
35     if parent && parent.is_a?(SOAPArray) && parent.position
36       attrs[ns.name(AttrPositionName)] = "[#{parent.position.join(',')}]"
37     end
38     name = generator.encode_name(ns, data, attrs)
39     case data
40     when SOAPReference
41       attrs['href'] = data.refidstr
42       generator.encode_tag(name, attrs)
43     when SOAPExternalReference
44       data.referred
45       attrs['href'] = data.refidstr
46       generator.encode_tag(name, attrs)
47     when SOAPRawString
48       generator.encode_tag(name, attrs)
49       generator.encode_rawstring(data.to_s)
50     when XSD::XSDString
51       generator.encode_tag(name, attrs)
52       generator.encode_string(@charset ?
53         XSD::Charset.encoding_to_xml(data.to_s, @charset) : data.to_s)
54     when XSD::XSDAnySimpleType
55       generator.encode_tag(name, attrs)
56       generator.encode_string(data.to_s)
57     when SOAPStruct
58       generator.encode_tag(name, attrs)
59       data.each do |key, value|
60         generator.encode_child(ns, value, data)
61       end
62     when SOAPArray
63       generator.encode_tag(name, attrs)
64       data.traverse do |child, *rank|
65         data.position = data.sparse ? rank : nil
66         generator.encode_child(ns, child, data)
67       end
68     else
69       raise EncodingStyleError.new(
70         "unknown object:#{data} in this encodingStyle")
71     end
72   end
73
74   def encode_data_end(generator, ns, data, parent)
75     name = generator.encode_name_end(ns, data)
76     cr = (data.is_a?(SOAPCompoundtype) and data.have_member)
77     generator.encode_tag_end(name, cr)
78   end
79
80
81   ###
82   ## decode interface.
83   #
84   class SOAPTemporalObject
85     attr_accessor :parent
86     attr_accessor :position
87     attr_accessor :id
88     attr_accessor :root
89
90     def initialize
91       @parent = nil
92       @position = nil
93       @id = nil
94       @root = nil
95     end
96   end
97
98   class SOAPUnknown < SOAPTemporalObject
99     attr_reader :type
100     attr_accessor :definedtype
101     attr_reader :extraattr
102
103     def initialize(handler, elename, type, extraattr)
104       super()
105       @handler = handler
106       @elename = elename
107       @type = type
108       @extraattr = extraattr
109       @definedtype = nil
110     end
111
112     def as_struct
113       if @extraattr[XSD::AttrNilName] == 'true'
114         return as_nil
115       end
116       o = SOAPStruct.decode(@elename, @type)
117       o.id = @id
118       o.root = @root
119       o.parent = @parent
120       o.position = @position
121       o.extraattr.update(@extraattr)
122       @handler.decode_parent(@parent, o)
123       o
124     end
125
126     def as_string
127       if @extraattr[XSD::AttrNilName] == 'true'
128         return as_nil
129       end
130       o = SOAPString.decode(@elename)
131       o.id = @id
132       o.root = @root
133       o.parent = @parent
134       o.position = @position
135       o.extraattr.update(@extraattr)
136       @handler.decode_parent(@parent, o)
137       o
138     end
139
140     def as_nil
141       o = SOAPNil.decode(@elename)
142       o.id = @id
143       o.root = @root
144       o.parent = @parent
145       o.position = @position
146       o.extraattr.update(@extraattr)
147       @handler.decode_parent(@parent, o)
148       o
149     end
150   end
151
152   def decode_tag(ns, elename, attrs, parent)
153     @textbuf.clear
154     is_nil, type, arytype, root, offset, position, href, id =
155       extract_attrs(ns, attrs)
156     o = nil
157     if is_nil
158       o = SOAPNil.decode(elename)
159     elsif href
160       o = SOAPReference.decode(elename, href)
161       @refpool << o
162     elsif @decode_typemap
163       o = decode_tag_by_wsdl(ns, elename, type, parent.node, arytype, attrs)
164     else
165       o = decode_tag_by_type(ns, elename, type, parent.node, arytype, attrs)
166     end
167
168     if o.is_a?(SOAPArray)
169       if offset
170         o.offset = decode_arypos(offset)
171         o.sparse = true
172       else
173         o.sparse = false
174       end
175     end
176
177     o.parent = parent
178     o.id = id
179     o.root = root
180     o.position = position
181
182     unless o.is_a?(SOAPTemporalObject)
183       @idpool << o if o.id
184       decode_parent(parent, o)
185     end
186     o
187   end
188
189   def decode_tag_end(ns, node)
190     textbufstr = @textbuf.join
191     @textbuf.clear
192     o = node.node
193     if o.is_a?(SOAPUnknown)
194       newnode = if /\A\s*\z/ =~ textbufstr
195         o.as_struct
196       else
197         o.as_string
198       end
199       if newnode.id
200         @idpool << newnode
201       end
202       node.replace_node(newnode)
203       o = node.node
204     end
205     decode_textbuf(o, textbufstr)
206     # unlink definedtype
207     o.definedtype = nil
208   end
209
210   def decode_text(ns, text)
211     @textbuf << text
212   end
213
214   def decode_prologue
215     @refpool.clear
216     @idpool.clear
217     @is_first_top_ele = true
218   end
219
220   def decode_epilogue
221     decode_resolve_id
222   end
223
224   def decode_parent(parent, node)
225     return unless parent.node
226     case parent.node
227     when SOAPUnknown
228       newparent = parent.node.as_struct
229       node.parent = newparent
230       if newparent.id
231         @idpool << newparent
232       end
233       parent.replace_node(newparent)
234       decode_parent(parent, node)
235     when SOAPStruct
236       parent.node.add(node.elename.name, node)
237       node.parent = parent.node
238     when SOAPArray
239       if node.position
240         parent.node[*(decode_arypos(node.position))] = node
241         parent.node.sparse = true
242       else
243         parent.node.add(node)
244       end
245       node.parent = parent.node
246     else
247       raise EncodingStyleError.new("illegal parent: #{parent.node}")
248     end
249   end
250
251 private
252
253   def content_ranksize(typename)
254     typename.scan(/\[[\d,]*\]$/)[0]
255   end
256
257   def content_typename(typename)
258     typename.sub(/\[,*\]$/, '')
259   end
260
261   def create_arytype(ns, data)
262     XSD::QName.new(data.arytype.namespace,
263       content_typename(data.arytype.name) + "[#{data.size.join(',')}]")
264   end
265
266   def encode_attrs(generator, ns, data, parent)
267     attrs = {}
268     return attrs if data.is_a?(SOAPReference)
269
270     if !parent || parent.encodingstyle != EncodingNamespace
271       if @generate_explicit_type
272         Generator.assign_ns(attrs, ns, EnvelopeNamespace)
273         attrs[ns.name(AttrEncodingStyleName)] = EncodingNamespace
274       end
275       data.encodingstyle = EncodingNamespace
276     end
277
278     if data.is_a?(SOAPNil)
279       attrs[ns.name(XSD::AttrNilName)] = XSD::NilValue
280     elsif @generate_explicit_type
281       if data.type.namespace
282         Generator.assign_ns(attrs, ns, data.type.namespace)
283       end
284       if data.is_a?(SOAPArray)
285         if data.arytype.namespace
286           Generator.assign_ns(attrs, ns, data.arytype.namespace)
287         end
288         Generator.assign_ns(attrs, ns, EncodingNamespace)
289         attrs[ns.name(AttrArrayTypeName)] = ns.name(create_arytype(ns, data))
290         if data.type.name
291           attrs[ns.name(XSD::AttrTypeName)] = ns.name(data.type)
292         end
293       elsif parent && parent.is_a?(SOAPArray) && (parent.arytype == data.type)
294         # No need to add.
295       elsif !data.type.namespace
296         # No need to add.
297       else
298         attrs[ns.name(XSD::AttrTypeName)] = ns.name(data.type)
299       end
300     end
301     data.extraattr.each do |key, value|
302       keytag = key
303       if key.is_a?(XSD::QName)
304         keytag = encode_attr_key(attrs, ns, key)
305       end
306       if value.is_a?(XSD::QName)
307         value = encode_qname(attrs, ns, value)
308       else
309         value = encode_attr_value(generator, ns, key, value)
310       end
311       attrs[keytag] = value
312     end
313     if data.id
314       attrs['id'] = data.id
315     end
316     attrs
317   end
318
319   def encode_attr_value(generator, ns, qname, value)
320     case value
321     when SOAPType
322       ref = SOAPReference.new(value)
323       generator.add_reftarget(qname.name, value)
324       ref.refidstr
325     else
326       value.to_s
327     end
328   end
329
330   def decode_tag_by_wsdl(ns, elename, typestr, parent, arytypestr, attrs)
331     o = nil
332     if parent.class == SOAPBody
333       # root element: should branch by root attribute?
334       if @is_first_top_ele
335         # Unqualified name is allowed here.
336         @is_first_top_ele = false
337         type = @decode_typemap[elename] ||
338           @decode_typemap.find_name(elename.name)
339         if type
340           o = SOAPStruct.new(elename)
341           o.elename = elename
342           o.definedtype = type
343           return o
344         end
345       end
346       # multi-ref element.
347       if typestr
348         typename = ns.parse(typestr)
349         typedef = @decode_typemap[typename]
350         if typedef
351           return decode_definedtype(elename, typename, typedef, arytypestr)
352         end
353       end
354       return decode_tag_by_type(ns, elename, typestr, parent, arytypestr, attrs)
355     end
356
357     if parent.type == XSD::AnyTypeName
358       return decode_tag_by_type(ns, elename, typestr, parent, arytypestr, attrs)
359     end
360
361     # parent.definedtype == nil means the parent is SOAPUnknown.  SOAPUnknown
362     # is generated by decode_tag_by_type when its type is anyType.
363     parenttype = parent.definedtype || @decode_typemap[parent.type]
364     unless parenttype
365       return decode_tag_by_type(ns, elename, typestr, parent, arytypestr, attrs)
366     end
367
368     definedtype_name = parenttype.child_type(elename)
369     if definedtype_name and (klass = TypeMap[definedtype_name])
370       return decode_basetype(klass, elename)
371     elsif definedtype_name == XSD::AnyTypeName
372       return decode_tag_by_type(ns, elename, typestr, parent, arytypestr, attrs)
373     end
374
375     if definedtype_name
376       typedef = @decode_typemap[definedtype_name]
377     else
378       typedef = parenttype.child_defined_complextype(elename)
379     end
380     decode_definedtype(elename, definedtype_name, typedef, arytypestr)
381   end
382
383   def decode_definedtype(elename, typename, typedef, arytypestr)
384     unless typedef
385       raise EncodingStyleError.new("unknown type '#{typename}'")
386     end
387     if typedef.is_a?(::WSDL::XMLSchema::SimpleType)
388       decode_defined_simpletype(elename, typename, typedef, arytypestr)
389     else
390       decode_defined_complextype(elename, typename, typedef, arytypestr)
391     end
392   end
393
394   def decode_basetype(klass, elename)
395     klass.decode(elename)
396   end
397
398   def decode_defined_simpletype(elename, typename, typedef, arytypestr)
399     if typedef.base
400       o = decode_basetype(TypeMap[typedef.base], elename)
401       o.definedtype = typedef
402       o
403     else
404       raise RuntimeError.new("unsupported simpleType: #{typedef}")
405     end
406   end
407
408   def decode_defined_complextype(elename, typename, typedef, arytypestr)
409     case typedef.compoundtype
410     when :TYPE_STRUCT, :TYPE_MAP
411       o = SOAPStruct.decode(elename, typename)
412       o.definedtype = typedef
413       return o
414     when :TYPE_ARRAY
415       expected_arytype = typedef.find_arytype
416       if arytypestr
417         actual_arytype = XSD::QName.new(expected_arytype.namespace,
418           content_typename(expected_arytype.name) <<
419           content_ranksize(arytypestr))
420         o = SOAPArray.decode(elename, typename, actual_arytype)
421       else
422         o = SOAPArray.new(typename, 1, expected_arytype)
423         o.elename = elename
424       end
425       o.definedtype = typedef
426       return o
427     when :TYPE_EMPTY
428       o = SOAPNil.decode(elename)
429       o.definedtype = typedef
430       return o
431     else
432       raise RuntimeError.new(
433         "Unknown kind of complexType: #{typedef.compoundtype}")
434     end
435     nil
436   end
437
438   def decode_tag_by_type(ns, elename, typestr, parent, arytypestr, attrs)
439     if arytypestr
440       type = typestr ? ns.parse(typestr) : ValueArrayName
441       node = SOAPArray.decode(elename, type, ns.parse(arytypestr))
442       node.extraattr.update(attrs)
443       return node
444     end
445
446     type = nil
447     if typestr
448       type = ns.parse(typestr)
449     elsif parent.is_a?(SOAPArray)
450       type = parent.arytype
451     else
452       # Since it's in dynamic(without any type) encoding process,
453       # assumes entity as its type itself.
454       #   <SOAP-ENC:Array ...> => type Array in SOAP-ENC.
455       #   <Country xmlns="foo"> => type Country in foo.
456       type = elename
457     end
458
459     if klass = TypeMap[type]
460       node = decode_basetype(klass, elename)
461       node.extraattr.update(attrs)
462       return node
463     end
464
465     # Unknown type... Struct or String
466     SOAPUnknown.new(self, elename, type, attrs)
467   end
468
469   def decode_textbuf(node, textbufstr)
470     case node
471     when XSD::XSDHexBinary, XSD::XSDBase64Binary
472       node.set_encoded(textbufstr)
473     when XSD::XSDString
474       if @charset
475         textbufstr = XSD::Charset.encoding_from_xml(textbufstr, @charset)
476       end
477       if node.definedtype
478         node.definedtype.check_lexical_format(textbufstr)
479       end
480       node.set(textbufstr)
481     when SOAPNil
482       # Nothing to do.
483     when SOAPBasetype
484       node.set(textbufstr)
485     else
486       # Nothing to do...
487     end
488   end
489
490   NilLiteralMap = {
491     'true' => true,
492     '1' => true,
493     'false' => false,
494     '0' => false
495   }
496   RootLiteralMap = {
497     '1' => 1,
498     '0' => 0
499   }
500   def extract_attrs(ns, attrs)
501     is_nil = NilLiteralMap[attrs[XSD::AttrNilName]]
502     type = attrs[XSD::AttrTypeName]
503     arytype = attrs[AttrArrayTypeName]
504     root = attrs[AttrRootName]
505     offset = attrs[AttrOffsetName]
506     position = attrs[AttrPositionName]
507     href = attrs[AttrHrefName]
508     id = attrs[AttrIdName]
509     if attrs.key?(Mapping::RubyIVarName)
510       attrs[Mapping::RubyIVarName] =
511         decode_ref_value(ns, attrs[Mapping::RubyIVarName])
512     end
513     return is_nil, type, arytype, root, offset, position, href, id
514   end
515
516   def decode_ref_value(ns, value)
517     if /\A#/ =~ value
518       o = SOAPReference.decode(nil, value)
519       @refpool << o
520       o
521     else
522       value
523     end
524   end
525
526   def decode_arypos(position)
527     /^\[(.+)\]$/ =~ position
528     $1.split(',').collect { |s| s.to_i }
529   end
530
531   def decode_resolve_id
532     count = @refpool.length     # To avoid infinite loop
533     while !@refpool.empty? && count > 0
534       @refpool = @refpool.find_all { |ref|
535         o = @idpool.find { |item|
536           item.id == ref.refid
537         }
538         if o.is_a?(SOAPReference)
539           true  # link of link.
540         elsif o
541           ref.__setobj__(o)
542           false
543         elsif o = ref.rootnode.external_content[ref.refid]
544           ref.__setobj__(o)
545           false
546         else
547           raise EncodingStyleError.new("unresolved reference: #{ref.refid}")
548         end
549       }
550       count -= 1
551     end
552   end
553 end
554
555 SOAPHandler.new
556
557
558 end
559 end
Note: See TracBrowser for help on using the browser.