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

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

Revision 2008, 21.5 kB (checked in by nahi, 10 months ago)
  • enabled Sriver#generate_explicit_type for literal services. closes #449.
  • Property svn:eol-style set to native
  • Property svn:keywords set to author date id revision
Line 
1 # soap/baseData.rb: SOAP4R - Base type 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/datatypes'
10 require 'soap/soap'
11 require 'xsd/codegen/gensupport'
12 require 'soap/mapping/mapping'
13
14
15 module SOAP
16
17
18 ###
19 ## Mix-in module for SOAP base type classes.
20 #
21 module SOAPModuleUtils
22   include SOAP
23
24 public
25
26   def decode(elename)
27     d = self.new
28     d.elename = elename
29     d
30   end
31
32   def to_data(str)
33     new(str).data
34   end
35 end
36
37
38 ###
39 ## for SOAP type(base and compound)
40 #
41 module SOAPType
42   attr_accessor :encodingstyle
43   attr_accessor :elename
44   attr_accessor :id
45   attr_reader :precedents
46   attr_accessor :root
47   attr_accessor :parent
48   attr_accessor :position
49   attr_reader :extraattr
50   attr_accessor :definedtype
51   attr_accessor :force_typed
52
53   def initialize(*arg)
54     super
55     @encodingstyle = nil
56     @elename = XSD::QName::EMPTY
57     @id = nil
58     @precedents = []
59     @root = false
60     @parent = nil
61     @position = nil
62     @definedtype = nil
63     @extraattr = {}
64     @force_typed = false
65   end
66
67   def inspect
68     if self.is_a?(XSD::NSDBase)
69       sprintf("#<%s:0x%x %s %s>", self.class.name, __id__, self.elename, self.type)
70     else
71       sprintf("#<%s:0x%x %s>", self.class.name, __id__, self.elename)
72     end
73   end
74
75   def rootnode
76     node = self
77     while node = node.parent
78       break if SOAPEnvelope === node
79     end
80     node
81   end
82 end
83
84
85 ###
86 ## for SOAP base type
87 #
88 module SOAPBasetype
89   include SOAPType
90   include SOAP
91
92   attr_accessor :qualified
93
94   def initialize(*arg)
95     super
96     @qualified = nil
97   end
98 end
99
100
101 ###
102 ## for SOAP compound type
103 #
104 module SOAPCompoundtype
105   include SOAPType
106   include SOAP
107
108   attr_accessor :qualified
109
110   def initialize(*arg)
111     super
112     @qualified = nil
113   end
114 end
115
116 # marker for compound types which have named accessor
117 module SOAPNameAccessible
118 end
119
120
121 ###
122 ## Convenience datatypes.
123 #
124 class SOAPReference < XSD::NSDBase
125   include SOAPBasetype
126   extend SOAPModuleUtils
127
128 public
129
130   attr_accessor :refid
131
132   # Override the definition in SOAPBasetype.
133   def initialize(obj = nil)
134     super()
135     @type = XSD::QName::EMPTY
136     @refid = nil
137     @obj = nil
138     __setobj__(obj) if obj
139   end
140
141   def __getobj__
142     @obj
143   end
144
145   def __setobj__(obj)
146     @obj = obj
147     @refid = @obj.id || SOAPReference.create_refid(@obj)
148     @obj.id = @refid unless @obj.id
149     @obj.precedents << self
150     # Copies NSDBase information
151     @obj.type = @type unless @obj.type
152   end
153
154   # Why don't I use delegate.rb?
155   # -> delegate requires target object type at initialize time.
156   # Why don't I use forwardable.rb?
157   # -> forwardable requires a list of forwarding methods.
158   #
159   # ToDo: Maybe I should use forwardable.rb and give it a methods list like
160   # delegate.rb...
161   #
162   def method_missing(msg_id, *params)
163     if @obj
164       @obj.send(msg_id, *params)
165     else
166       nil
167     end
168   end
169
170   # for referenced base type such as a long value from Axis.
171   # base2obj requires a node to respond to :data
172   def data
173     if @obj.respond_to?(:data)
174       @obj.data
175     end
176   end
177
178   def refidstr
179     '#' + @refid
180   end
181
182   def self.create_refid(obj)
183     'id' + obj.__id__.to_s
184   end
185
186   def self.decode(elename, refidstr)
187     if /\A#(.*)\z/ =~ refidstr
188       refid = $1
189     elsif /\Acid:(.*)\z/ =~ refidstr
190       refid = $1
191     else
192       raise ArgumentError.new("illegal refid #{refidstr}")
193     end
194     d = super(elename)
195     d.refid = refid
196     d
197   end
198 end
199
200
201 class SOAPExternalReference < XSD::NSDBase
202   include SOAPBasetype
203   extend SOAPModuleUtils
204
205   def initialize
206     super()
207     @type = XSD::QName::EMPTY
208   end
209
210   def referred
211     rootnode.external_content[external_contentid] = self
212   end
213
214   def refidstr
215     'cid:' + external_contentid
216   end
217
218 private
219
220   def external_contentid
221     raise NotImplementedError.new
222   end
223 end
224
225
226 class SOAPNil < XSD::XSDNil
227   include SOAPBasetype
228   extend SOAPModuleUtils
229
230 public
231
232   def initialize(value = nil)
233     super(value)
234     @extraattr[XSD::AttrNilName] = 'true'
235   end
236 end
237
238 # SOAPRawString is for sending raw string.  In contrast to SOAPString,
239 # SOAP4R does not do XML encoding and does not convert its CES.  The string it
240 # holds is embedded to XML instance directly as a 'xsd:string'.
241 class SOAPRawString < XSD::XSDString
242   include SOAPBasetype
243   extend SOAPModuleUtils
244 end
245
246
247 ###
248 ## Basic datatypes.
249 #
250 class SOAPAnySimpleType < XSD::XSDAnySimpleType
251   include SOAPBasetype
252   extend SOAPModuleUtils
253 end
254
255 class SOAPString < XSD::XSDString
256   include SOAPBasetype
257   extend SOAPModuleUtils
258   SOAPENCType = QName.new(EncodingNamespace, StringLiteral)
259 end
260
261 class SOAPNormalizedString < XSD::XSDNormalizedString
262   include SOAPBasetype
263   extend SOAPModuleUtils
264   SOAPENCType = QName.new(EncodingNamespace, NormalizedStringLiteral)
265 end
266
267 class SOAPToken < XSD::XSDToken
268   include SOAPBasetype
269   extend SOAPModuleUtils
270   SOAPENCType = QName.new(EncodingNamespace, TokenLiteral)
271 end
272
273 class SOAPLanguage < XSD::XSDLanguage
274   include SOAPBasetype
275   extend SOAPModuleUtils
276   SOAPENCType = QName.new(EncodingNamespace, LanguageLiteral)
277 end
278
279 class SOAPNMTOKEN < XSD::XSDNMTOKEN
280   include SOAPBasetype
281   extend SOAPModuleUtils
282   SOAPENCType = QName.new(EncodingNamespace, NMTOKENLiteral)
283 end
284
285 class SOAPNMTOKENS < XSD::XSDNMTOKENS
286   include SOAPBasetype
287   extend SOAPModuleUtils
288   SOAPENCType = QName.new(EncodingNamespace, NMTOKENSLiteral)
289 end
290
291 class SOAPName < XSD::XSDName
292   include SOAPBasetype
293   extend SOAPModuleUtils
294   SOAPENCType = QName.new(EncodingNamespace, NameLiteral)
295 end
296
297 class SOAPNCName < XSD::XSDNCName
298   include SOAPBasetype
299   extend SOAPModuleUtils
300   SOAPENCType = QName.new(EncodingNamespace, NCNameLiteral)
301 end
302
303 class SOAPID < XSD::XSDID
304   include SOAPBasetype
305   extend SOAPModuleUtils
306   SOAPENCType = QName.new(EncodingNamespace, IDLiteral)
307 end
308
309 class SOAPIDREF < XSD::XSDIDREF
310   include SOAPBasetype
311   extend SOAPModuleUtils
312   SOAPENCType = QName.new(EncodingNamespace, IDREFLiteral)
313 end
314
315 class SOAPIDREFS < XSD::XSDIDREFS
316   include SOAPBasetype
317   extend SOAPModuleUtils
318   SOAPENCType = QName.new(EncodingNamespace, IDREFSLiteral)
319 end
320
321 class SOAPENTITY < XSD::XSDENTITY
322   include SOAPBasetype
323   extend SOAPModuleUtils
324   SOAPENCType = QName.new(EncodingNamespace, ENTITYLiteral)
325 end
326
327 class SOAPENTITIES < XSD::XSDENTITIES
328   include SOAPBasetype
329   extend SOAPModuleUtils
330   SOAPENCType = QName.new(EncodingNamespace, ENTITIESLiteral)
331 end
332
333 class SOAPBoolean < XSD::XSDBoolean
334   include SOAPBasetype
335   extend SOAPModuleUtils
336   SOAPENCType = QName.new(EncodingNamespace, BooleanLiteral)
337 end
338
339 class SOAPDecimal < XSD::XSDDecimal
340   include SOAPBasetype
341   extend SOAPModuleUtils
342   SOAPENCType = QName.new(EncodingNamespace, DecimalLiteral)
343 end
344
345 class SOAPFloat < XSD::XSDFloat
346   include SOAPBasetype
347   extend SOAPModuleUtils
348   SOAPENCType = QName.new(EncodingNamespace, FloatLiteral)
349 end
350
351 class SOAPDouble < XSD::XSDDouble
352   include SOAPBasetype
353   extend SOAPModuleUtils
354   SOAPENCType = QName.new(EncodingNamespace, DoubleLiteral)
355 end
356
357 class SOAPDuration < XSD::XSDDuration
358   include SOAPBasetype
359   extend SOAPModuleUtils
360   SOAPENCType = QName.new(EncodingNamespace, DurationLiteral)
361 end
362
363 class SOAPDateTime < XSD::XSDDateTime
364   include SOAPBasetype
365   extend SOAPModuleUtils
366   SOAPENCType = QName.new(EncodingNamespace, DateTimeLiteral)
367 end
368
369 class SOAPTime < XSD::XSDTime
370   include SOAPBasetype
371   extend SOAPModuleUtils
372   SOAPENCType = QName.new(EncodingNamespace, TimeLiteral)
373 end
374
375 class SOAPDate < XSD::XSDDate
376   include SOAPBasetype
377   extend SOAPModuleUtils
378   SOAPENCType = QName.new(EncodingNamespace, DateLiteral)
379 end
380
381 class SOAPGYearMonth < XSD::XSDGYearMonth
382   include SOAPBasetype
383   extend SOAPModuleUtils
384   SOAPENCType = QName.new(EncodingNamespace, GYearMonthLiteral)
385 end
386
387 class SOAPGYear < XSD::XSDGYear
388   include SOAPBasetype
389   extend SOAPModuleUtils
390   SOAPENCType = QName.new(EncodingNamespace, GYearLiteral)
391 end
392
393 class SOAPGMonthDay < XSD::XSDGMonthDay
394   include SOAPBasetype
395   extend SOAPModuleUtils
396   SOAPENCType = QName.new(EncodingNamespace, GMonthDayLiteral)
397 end
398
399 class SOAPGDay < XSD::XSDGDay
400   include SOAPBasetype
401   extend SOAPModuleUtils
402   SOAPENCType = QName.new(EncodingNamespace, GDayLiteral)
403 end
404
405 class SOAPGMonth < XSD::XSDGMonth
406   include SOAPBasetype
407   extend SOAPModuleUtils
408   SOAPENCType = QName.new(EncodingNamespace, GMonthLiteral)
409 end
410
411 class SOAPHexBinary < XSD::XSDHexBinary
412   include SOAPBasetype
413   extend SOAPModuleUtils
414   SOAPENCType = QName.new(EncodingNamespace, HexBinaryLiteral)
415 end
416
417 class SOAPBase64 < XSD::XSDBase64Binary
418   include SOAPBasetype
419   extend SOAPModuleUtils
420   Type = SOAPENCType = QName.new(EncodingNamespace, Base64Literal)
421
422 public
423
424   def initialize(value = nil)
425     super(value)
426     @type = Type
427   end
428
429   def as_xsd
430     @type = XSD::XSDBase64Binary::Type
431   end
432 end
433
434 class SOAPAnyURI < XSD::XSDAnyURI
435   include SOAPBasetype
436   extend SOAPModuleUtils
437   SOAPENCType = QName.new(EncodingNamespace, AnyURILiteral)
438 end
439
440 class SOAPQName < XSD::XSDQName
441   include SOAPBasetype
442   extend SOAPModuleUtils
443   SOAPENCType = QName.new(EncodingNamespace, QNameLiteral)
444 end
445
446
447 class SOAPInteger < XSD::XSDInteger
448   include SOAPBasetype
449   extend SOAPModuleUtils
450   SOAPENCType = QName.new(EncodingNamespace, IntegerLiteral)
451 end
452
453 class SOAPNonPositiveInteger < XSD::XSDNonPositiveInteger
454   include SOAPBasetype
455   extend SOAPModuleUtils
456   SOAPENCType = QName.new(EncodingNamespace, NonPositiveIntegerLiteral)
457 end
458
459 class SOAPNegativeInteger < XSD::XSDNegativeInteger
460   include SOAPBasetype
461   extend SOAPModuleUtils
462   SOAPENCType = QName.new(EncodingNamespace, NegativeIntegerLiteral)
463 end
464
465 class SOAPLong < XSD::XSDLong
466   include SOAPBasetype
467   extend SOAPModuleUtils
468   SOAPENCType = QName.new(EncodingNamespace, LongLiteral)
469 end
470
471 class SOAPInt < XSD::XSDInt
472   include SOAPBasetype
473   extend SOAPModuleUtils
474   SOAPENCType = QName.new(EncodingNamespace, IntLiteral)
475 end
476
477 class SOAPShort < XSD::XSDShort
478   include SOAPBasetype
479   extend SOAPModuleUtils
480   SOAPENCType = QName.new(EncodingNamespace, ShortLiteral)
481 end
482
483 class SOAPByte < XSD::XSDByte
484   include SOAPBasetype
485   extend SOAPModuleUtils
486   SOAPENCType = QName.new(EncodingNamespace, ByteLiteral)
487 end
488
489 class SOAPNonNegativeInteger < XSD::XSDNonNegativeInteger
490   include SOAPBasetype
491   extend SOAPModuleUtils
492   SOAPENCType = QName.new(EncodingNamespace, NonNegativeIntegerLiteral)
493 end
494
495 class SOAPUnsignedLong < XSD::XSDUnsignedLong
496   include SOAPBasetype
497   extend SOAPModuleUtils
498   SOAPENCType = QName.new(EncodingNamespace, UnsignedLongLiteral)
499 end
500
501 class SOAPUnsignedInt < XSD::XSDUnsignedInt
502   include SOAPBasetype
503   extend SOAPModuleUtils
504   SOAPENCType = QName.new(EncodingNamespace, UnsignedIntLiteral)
505 end
506
507 class SOAPUnsignedShort < XSD::XSDUnsignedShort
508   include SOAPBasetype
509   extend SOAPModuleUtils
510   SOAPENCType = QName.new(EncodingNamespace, UnsignedShortLiteral)
511 end
512
513 class SOAPUnsignedByte < XSD::XSDUnsignedByte
514   include SOAPBasetype
515   extend SOAPModuleUtils
516   SOAPENCType = QName.new(EncodingNamespace, UnsignedByteLiteral)
517 end
518
519 class SOAPPositiveInteger < XSD::XSDPositiveInteger
520   include SOAPBasetype
521   extend SOAPModuleUtils
522   SOAPENCType = QName.new(EncodingNamespace, PositiveIntegerLiteral)
523 end
524
525
526 ###
527 ## Compound datatypes.
528 #
529 class SOAPStruct < XSD::NSDBase
530   include Enumerable
531   include SOAPCompoundtype
532   include SOAPNameAccessible
533
534 public
535
536   def initialize(type = nil)
537     super()
538     @type = type || XSD::QName::EMPTY
539     @array = []
540     @data = []
541   end
542
543   def to_s
544     str = ''
545     self.each do |key, data|
546       str << "#{key}: #{data}\n"
547     end
548     str
549   end
550
551   def add(name, value)
552     value = SOAPNil.new if value.nil?
553     @array.push(name)
554     value.elename = value.elename.dup_name(name)
555     @data.push(value)
556     value.parent = self if value.respond_to?(:parent=)
557     value
558   end
559
560   def [](idx)
561     if idx.is_a?(Range)
562       @data[idx]
563     elsif idx.is_a?(Integer)
564       if (idx > @array.size)
565         raise ArrayIndexOutOfBoundsError.new('In ' << @type.name)
566       end
567       @data[idx]
568     else
569       if @array.include?(idx)
570         @data[@array.index(idx)]
571       else
572         nil
573       end
574     end
575   end
576
577   def []=(idx, data)
578     if @array.include?(idx)
579       data.parent = self if data.respond_to?(:parent=)
580       @data[@array.index(idx)] = data
581     else
582       add(idx, data)
583     end
584   end
585
586   def key?(name)
587     @array.include?(name)
588   end
589
590   def members
591     @array
592   end
593
594   def have_member
595     !@array.empty?
596   end
597
598   def to_obj
599     hash = {}
600     proptype = {}
601     each do |k, v|
602       value = v.respond_to?(:to_obj) ? v.to_obj : v.to_s
603       case proptype[k]
604       when :single
605         hash[k] = [hash[k], value]
606         proptype[k] = :multi
607       when :multi
608         hash[k] << value
609       else
610         hash[k] = value
611         proptype[k] = :single
612       end
613     end
614     hash
615   end
616
617   def each
618     idx = 0
619     while idx < @array.length
620       yield(@array[idx], @data[idx])
621       idx += 1
622     end
623   end
624
625   def replace
626     members.each do |member|
627       self[member] = yield(self[member])
628     end
629   end
630
631   def self.decode(elename, type)
632     s = SOAPStruct.new(type)
633     s.elename = elename
634     s
635   end
636 end
637
638
639 # SOAPElement is not typed so it is not derived from NSDBase.
640 class SOAPElement
641   include Enumerable
642   include SOAPCompoundtype
643   include SOAPNameAccessible
644
645   attr_accessor :type
646   # Text interface.
647   attr_accessor :text
648   alias data text
649
650   def initialize(elename, text = nil)
651     super()
652     if elename.nil?
653       elename = XSD::QName::EMPTY
654     else
655       elename = Mapping.to_qname(elename)
656     end
657     @encodingstyle = LiteralNamespace
658     @elename = elename
659     @type = nil
660
661     @array = []
662     @data = []
663     @text = text
664   end
665
666   def inspect
667     sprintf("#<%s:0x%x %s>", self.class.name, __id__, self.elename) +
668       (@text ? " #{@text.inspect}" : '') +
669       @data.collect { |ele| "\n#{ele.inspect}" }.join.gsub(/^/, '  ')
670   end
671
672   def set(value)
673     @text = value
674   end
675
676   # Element interfaces.
677   def add(value)
678     name = value.elename.name
679     @array.push(name)
680     @data.push(value)
681     value.parent = self if value.respond_to?(:parent=)
682     value
683   end
684
685   def [](idx)
686     if @array.include?(idx)
687       @data[@array.index(idx)]
688     else
689       nil
690     end
691   end
692
693   def []=(idx, data)
694     if @array.include?(idx)
695       data.parent = self if data.respond_to?(:parent=)
696       @data[@array.index(idx)] = data
697     else
698       add(data)
699     end
700   end
701
702   def key?(name)
703     @array.include?(name)
704   end
705
706   def members
707     @array
708   end
709
710   def have_member
711     !@array.empty?
712   end
713
714   def to_obj
715     if !have_member
716       @text
717     else
718       hash = {}
719       proptype = {}
720       each do |k, v|
721         value = v.respond_to?(:to_obj) ? v.to_obj : v.to_s
722         case proptype[k]
723         when :single
724           hash[k] = [hash[k], value]
725           proptype[k] = :multi
726         when :multi
727           hash[k] << value
728         else
729           hash[k] = value
730           proptype[k] = :single
731         end
732       end
733       hash
734     end
735   end
736
737   def each
738     idx = 0
739     while idx < @array.length
740       yield(@array[idx], @data[idx])
741       idx += 1
742     end
743   end
744
745   def self.decode(elename)
746     o = SOAPElement.new(elename)
747     o
748   end
749
750   def self.from_objs(objs)
751     objs.collect { |value|
752       if value.is_a?(SOAPElement)
753         value
754       else
755         k, v = value
756         ele = from_obj(v)
757         ele.elename = XSD::QName.new(nil, k)
758         ele
759       end
760     }
761   end
762
763   # when obj is a Hash or an Array:
764   #   when key starts with "xmlattr_":
765   #     value is added as an XML attribute with the key name however the
766   #     "xmlattr_" is dropped from the name.
767   #   when key starts with "xmlele_":
768   #     value is added as an XML element with the key name however the
769   #     "xmlele_" is dropped from the name.
770   #   if else:
771   #     value is added as an XML element with the key name.
772   def self.from_obj(obj, namespace = nil)
773     return obj if obj.is_a?(SOAPElement)
774     o = SOAPElement.new(nil)
775     case obj
776     when nil
777       o.text = nil
778     when Hash, Array
779       obj.each do |name, value|
780         addname, is_attr = parse_name(name, namespace)
781         if value.is_a?(Array)
782           value.each do |subvalue|
783             if is_attr
784               o.extraattr[addname] = subvalue
785             else
786               child = from_obj(subvalue, namespace)
787               child.elename = addname
788               o.add(child)
789             end
790           end
791         else
792           if is_attr
793             o.extraattr[addname] = value
794           else
795             child = from_obj(value, namespace)
796             child.elename = addname
797             o.add(child)
798           end
799         end
800       end
801     else
802       o.text = obj.to_s
803     end
804     o
805   end
806
807   def self.parse_name(obj, namespace = nil)
808     qname = to_qname(obj, namespace)
809     if /\Axmlele_/ =~ qname.name
810       qname = XSD::QName.new(qname.namespace, qname.name.sub(/\Axmlele_/, ''))
811       return qname, false
812     elsif /\Axmlattr_/ =~ qname.name
813       qname = XSD::QName.new(qname.namespace, qname.name.sub(/\Axmlattr_/, ''))
814       return qname, true
815     else
816       return qname, false
817     end
818   end
819
820   def self.to_qname(obj, namespace = nil)
821     if obj.is_a?(XSD::QName)
822       obj
823     elsif /\A(.+):([^:]+)\z/ =~ obj.to_s
824       XSD::QName.new($1, $2)
825     else
826       XSD::QName.new(namespace, obj.to_s)
827     end
828   end
829 end
830
831
832 class SOAPRawData < SOAPElement
833   def initialize(obj)
834     super(XSD::QName::EMPTY)
835     @obj = obj
836   end
837
838   def to_xmlpart
839     @obj.to_xmlpart
840   end
841 end
842
843
844 class SOAPREXMLElementWrap
845   def initialize(ele)
846     @ele = ele
847   end
848
849   def to_xmlpart
850     @ele.to_s
851   end
852 end
853
854
855 class SOAPArray < XSD::NSDBase
856   include SOAPCompoundtype
857   include Enumerable
858
859 public
860
861   attr_accessor :sparse
862
863   attr_reader :offset, :rank
864   attr_accessor :size, :size_fixed
865   attr_reader :arytype
866
867   def initialize(type = nil, rank = 1, arytype = nil)
868     super()
869     @type = type || ValueArrayName
870     @rank = rank
871     @data = Array.new
872     @sparse = false
873     @offset = Array.new(rank, 0)
874     @size = Array.new(rank, 0)
875     @size_fixed = false
876     @position = nil
877     @arytype = arytype
878   end
879
880   def offset=(var)
881     @offset = var
882     @sparse = true
883   end
884
885   def add(value)
886     self[*(@offset)] = value
887   end
888
889   def have_member
890     !@data.empty?
891   end
892
893   def [](*idxary)
894     if idxary.size != @rank
895       raise ArgumentError.new("given #{idxary.size} params does not match rank: #{@rank}")
896     end
897     retrieve(idxary)
898   end
899
900   def []=(*idxary)
901     value = idxary.slice!(-1)
902     if idxary.size != @rank
903       raise ArgumentError.new("given #{idxary.size} params(#{idxary}) does not match rank: #{@rank}")
904     end
905     idx = 0
906     while idx < idxary.size
907       if idxary[idx] + 1 > @size[idx]
908         @size[idx] = idxary[idx] + 1
909       end
910       idx += 1
911     end
912     data = retrieve(idxary[0, idxary.size - 1])
913     data[idxary.last] = value
914     if value.is_a?(SOAPType)
915       value.elename = ITEM_NAME
916       # Sync type
917       unless @type.name
918         @type = XSD::QName.new(value.type.namespace,
919           SOAPArray.create_arytype(value.type.name, @rank))
920       end
921       value.type ||= @type
922     end
923     @offset = idxary
924     value.parent = self if value.respond_to?(:parent=)
925     offsetnext
926   end
927
928   def each
929     @data.each do |data|
930       yield(data)
931     end
932   end
933
934   def to_a
935     @data.dup
936   end
937
938   def replace
939     @data = deep_map(@data) do |ele|
940       yield(ele)
941     end
942   end
943
944   def deep_map(ary, &block)
945     ary.collect do |ele|
946       if ele.is_a?(Array)
947         deep_map(ele, &block)
948       else
949         new_obj = block.call(ele)
950         new_obj.elename = ITEM_NAME
951         new_obj
952       end
953     end
954   end
955
956   def include?(var)
957     traverse_data(@data) do |v, *rank|
958       if v.is_a?(SOAPBasetype) && v.data == var
959         return true
960       end
961     end
962     false
963   end
964
965   def traverse
966     traverse_data(@data) do |v, *rank|
967       unless @sparse
968        yield(v)
969       else
970        yield(v, *rank) if v && !v.is_a?(SOAPNil)
971       end
972     end
973   end
974
975   def soap2array(ary)
976     traverse_data(@data) do |v, *position|
977       iteary = ary
978       rank = 1
979       while rank < position.size
980         idx = position[rank - 1]
981         if iteary[idx].nil?
982           iteary = iteary[idx] = Array.new
983         else
984           iteary = iteary[idx]
985         end
986         rank += 1
987       end
988       if block_given?
989         iteary[position.last] = yield(v)
990       else
991         iteary[position.last] = v
992       end
993     end
994   end
995
996   def position
997     @position
998   end
999
1000 private
1001
1002   ITEM_NAME = XSD::QName.new(nil, 'item')
1003
1004   def retrieve(idxary)
1005     data = @data
1006     rank = 1
1007     while rank <= idxary.size
1008       idx = idxary[rank - 1]
1009       if data[idx].nil?
1010         data = data[idx] = Array.new
1011       else
1012         data = data[idx]
1013       end
1014       rank += 1
1015     end
1016     data
1017   end
1018
1019   def traverse_data(data, rank = 1)
1020     idx = 0
1021     while idx < ranksize(rank)
1022       if rank < @rank and data[idx]
1023         traverse_data(data[idx], rank + 1) do |*v|
1024           v[1, 0] = idx
1025           yield(*v)
1026         end
1027       else
1028         yield(data[idx], idx)
1029       end
1030       idx += 1
1031     end
1032   end
1033
1034   def ranksize(rank)
1035     @size[rank - 1]
1036   end
1037
1038   def offsetnext
1039     move = false
1040     idx = @offset.size - 1
1041     while !move && idx >= 0
1042       @offset[idx] += 1
1043       if @size_fixed
1044         if @offset[idx] < @size[idx]
1045           move = true
1046         else
1047           @offset[idx] = 0
1048           idx -= 1
1049         end
1050       else
1051         move = true
1052       end
1053     end
1054   end
1055
1056   def self.decode(elename, type, arytype)
1057     typestr, nofary = parse_type(arytype.name)
1058     rank = nofary.count(',') + 1
1059     plain_arytype = XSD::QName.new(arytype.namespace, typestr)
1060     o = SOAPArray.new(type, rank, plain_arytype)
1061     size = []
1062     nofary.split(',').each do |s|
1063       if s.empty?
1064         size.clear
1065         break
1066       else
1067         size << s.to_i
1068       end
1069     end
1070     unless size.empty?
1071       o.size = size
1072       o.size_fixed = true
1073     end
1074     o.elename = elename
1075     o
1076   end
1077
1078   def self.create_arytype(typename, rank)
1079     "#{typename}[" << ',' * (rank - 1) << ']'
1080   end
1081
1082   TypeParseRegexp = Regexp.new('^(.+)\[([\d,]*)\]$', nil, 'NONE')
1083
1084   def self.parse_type(string)
1085     TypeParseRegexp =~ string
1086     return $1, $2
1087   end
1088 end
1089
1090
1091 require 'soap/mapping/typeMap'
1092
1093
1094 end
Note: See TracBrowser for help on using the browser.