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

root/tags/RELEASE_1_0_1/lib/SOAPData.rb

Revision 10, 11.5 kB (checked in by nakahiro, 8 years ago)

Use 'def self.foo' instead of 'module_function'.
Removed parameter 'namespace' from 'encode'. Misunderstanding of namespace

specification...

Added SOAPInt.
Handles SOAP/1.1 array. Be carefull there still be many restrictions...

  • Property svn:eol-style set to native
  • Property svn:executable set to *
  • Property svn:keywords set to author date id revision
Line 
1 =begin
2 SOAP4R
3 Copyright (C) 2000 NAKAMURA Hiroshi.
4
5 This program is free software; you can redistribute it and/or modify it under
6 the terms of the GNU General Public License as published by the Free Software
7 Foundation; either version 2 of the License, or (at your option) any later
8 version.
9
10 This program is distributed in the hope that it will be useful, but WITHOUT ANY
11 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
12 PRATICULAR PURPOSE. See the GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License along with
15 this program; if not, write to the Free Software Foundation, Inc., 675 Mass
16 Ave, Cambridge, MA 02139, USA.
17 =end
18
19 require 'SOAP'
20 require 'XMLSchemaDatatypes'
21 require 'xmltreebuilder'
22
23
24 ###
25 ## SOAP utility module for classes( not instances! )
26 #
27 class SOAPNS
28   public
29
30   attr_reader :defaultNamespace
31   attr_reader :namespaceTag
32
33   def initialize( initNamespace = {} )
34     @namespaceTag = initNamespace
35     @defaultNamespace = nil
36   end
37
38   def assign( namespace, name = nil )
39     if ( @namespaceTag.has_key?( namespace ))
40       false
41     elsif ( name == '' )
42       @defaultNamespace = namespace
43       name
44     elsif ( @namespaceTag.has_value?( name ))
45       # Already assigned.  Should raise Error?
46       name = SOAPNS.assign( namespace )
47       @namespaceTag[ namespace ] = name
48       name
49     else
50       name ||= SOAPNS.assign( namespace )
51       @namespaceTag[ namespace ] = name
52       name
53     end
54   end
55
56   def []( namespace )
57     if ( @namespaceTag.has_key?( namespace ))
58       @namespaceTag[ namespace ]
59     else
60       nil
61     end
62   end
63
64   def clone()
65     SOAPNS.new( @namespaceTag.dup )
66   end
67
68   def name( namespace, name )
69     if ( namespace == @defaultNamespace )
70       name
71     elsif @namespaceTag.has_key?( namespace )
72       @namespaceTag[ namespace ] + ':' << name
73     else
74       raise FormatDecodeError.new( 'Namespace: ' << namespace << ' not defined yet.' )
75     end
76   end
77
78   def compare( namespace, name, rhs )
79     if ( namespace == @defaultNamespace )
80       return true if ( name == rhs )
81     end
82
83     if @namespaceTag.has_key?( namespace )
84       return (( @namespaceTag[ namespace ] + ':' << name ) == rhs )
85     end
86
87     return false
88   end
89
90   # $1 and $2 are necessary.
91   ParseRegexp = Regexp.new( '^([^:]+)(?::(.+))?$' )
92
93   def parse( elem )
94     namespace = nil
95     name = nil
96     ParseRegexp =~ elem
97     if $2
98       namespace = @namespaceTag.index( $1 )
99       name = $2
100       if !namespace
101         raise FormatDecodeError.new( 'Unknown namespace qualifier: ' << $1 )
102       end
103     elsif $1
104       namespace = @defaultNamespace
105       name = $1
106     end
107     if !name
108       raise FormatDecodeError.new( 'Illegal element format: ' << elem )
109     end
110     return namespace, name
111   end
112
113   private
114
115   AssigningName = [ 0 ]
116
117   def self.assign( namespace )
118     AssigningName[ 0 ] += 1
119     'n' << AssigningName[ 0 ].to_s
120   end
121
122   def self.reset()
123     AssigningName[ 0 ] = 0
124   end
125 end
126
127
128 ###
129 ## SOAP related datatypes.
130 #
131 module SOAPModuleUtils
132   include SOAP
133   include XML::SimpleTree
134
135   public
136
137   def decode( ns, elem )
138     elem.normalize
139     value = if elem.childNodes[0]
140         elem.childNodes[0].nodeValue
141       else
142         ''
143       end
144     d = self.new( value )
145     d.namespace, = ns.parse( elem.nodeName )
146     d
147   end
148
149   private
150
151   def decodeChild( ns, elem )
152     if isNull( ns, elem )
153       return SOAPNull.decode( ns, elem )
154     end
155
156     case getType( ns, elem )
157     when 'int'
158       SOAPInt.decode( ns, elem )
159     when 'integer'
160       SOAPInteger.decode( ns, elem )
161     when 'boolean'
162       SOAPBoolean.decode( ns, elem )
163     when 'string'
164       SOAPString.decode( ns, elem )
165     when 'timeInstant'
166       SOAPTimeInstant.decode( ns, elem )
167     when /\[\d*\]$/
168       SOAPArray.decode( ns, elem )
169     else
170       bOnlyText = true
171       elem.childNodes.each do | child |
172         next if ( isEmptyText( child ))
173         bOnlyText = false
174         break
175       end
176       if bOnlyText
177         # No type is set. Decode as SOAPString by default.
178         SOAPString.decode( ns, elem )
179       else
180         SOAPStruct.decode( ns, elem )
181       end
182     end
183   end
184
185   EmptyTextRegexp = Regexp.new( '\s*(?:\n\s*)*' )
186
187   def isEmptyText( node )
188     (( node.nodeName == '#text' ) and ( EmptyTextRegexp =~ node.nodeValue ))
189   end
190
191   def isNull( ns, elem )
192     elem.attributes.each do | attr |
193       if ( ns.compare( XSD::InstanceNamespace, 'null', '1' ))
194         return true
195       end
196     end
197     false
198   end
199
200   def getType( ns, elem )
201     elem.attributes.each do | attr |
202       if ( ns.compare( XSD::InstanceNamespace, 'type', attr.nodeName ))
203         return attr.nodeValue
204       end
205     end
206     nil
207   end
208
209   # $1 is necessary.
210   NSParseRegexp = Regexp.new( '^xmlns:?(.*)$' )
211
212   def parseNS( ns, elem )
213     return unless elem.attributes
214     elem.attributes.each do | attr |
215       next unless ( NSParseRegexp =~ attr.nodeName )
216       # '' means 'default namespace'.
217       tag = $1 || ''
218       ns.assign( attr.nodeValue, tag )
219     end
220   end
221 end
222
223 module SOAPBasetypeUtils
224   include SOAP
225   include XML::SimpleTree
226
227   public
228
229   def initialize( *vars )
230     super( *vars )
231
232 # SOAP Basetype is a XSD type.  No need to reset @namespace.
233 #    @namespace = EnvelopeNamespace
234
235   end
236
237   def encode( ns, name )
238     attrs = []
239     getExtraNSAttr( attrs, ns )
240     getDatatypeAttr( attrs, ns )
241
242     if ( self.to_s.empty? )
243       Element.new( name, attrs )
244     else
245       Element.new( name, attrs, Text.new( self.to_s ))
246     end
247   end
248
249   def ==( rhs )
250     self.data == rhs
251   end
252
253   private
254
255   def getDatatypeAttr( attrs, ns )
256     attrs.push( Attr.new( ns.name( XSD::InstanceNamespace, 'type' ), ns.name( @namespace, @typeName )))
257   end
258
259   def getExtraNSAttr( attrs, ns )
260     unless ns[ XSD::Namespace ]
261       tag = ns.assign( XSD::Namespace )
262       attrs.push( Attr.new( 'xmlns:' << tag, XSD::Namespace ))
263     end
264   end
265 end
266
267
268 ###
269 ## Basic datatypes.
270 #
271 class SOAPNull < XSDNull
272   extend SOAPModuleUtils
273   include SOAPBasetypeUtils
274
275   private
276
277   # Override the definition in SOAPBasetypeUtils.
278   def getDatatypeAttr( ns )
279     Attr.new( ns.name( XSD::Namespace, 'null' ), '1' )
280   end
281
282   # Override the definition in SOAPModuleUtils
283   def decode( ns, elem )
284     d = self.new()
285     d.namespace, = ns.parse( elem.nodeName )
286     d
287   end
288 end
289
290 class SOAPBoolean < XSDBoolean
291   extend SOAPModuleUtils
292   include SOAPBasetypeUtils
293 end
294
295 class SOAPString < XSDString
296   extend SOAPModuleUtils
297   include SOAPBasetypeUtils
298 end
299
300 class SOAPInteger < XSDInteger
301   extend SOAPModuleUtils
302   include SOAPBasetypeUtils
303 end
304
305 class SOAPInt < XSDInt
306   extend SOAPModuleUtils
307   include SOAPBasetypeUtils
308 end
309
310 class SOAPTimeInstant < XSDTimeInstant
311   extend SOAPModuleUtils
312   include SOAPBasetypeUtils
313 end
314
315
316 ###
317 ## Compound datatypes.
318 #
319 class SOAPCompoundBase < NSDBase
320   extend SOAPModuleUtils
321
322   public
323
324   def initialize( typeName )
325     super( typeName, EnvelopeNamespace )
326   end
327 end
328
329
330 class SOAPStruct < SOAPCompoundBase
331   include Enumerable
332
333   public
334
335   attr_reader :array
336   attr_reader :data
337
338   def initialize( typeName )
339     super( typeName )
340     @array = []
341     @data = {}
342   end
343
344   def to_s()
345     str = ''
346     self.each do | key, data |
347       str << "#{ key }: #{ data }\n"
348     end
349     str
350   end
351
352   def add( name, newMember )
353     addMember( name, newMember )
354   end
355
356   def []( idx )
357     if ( idx > array.size )
358       raise ArrayIndexOutOfBoundsError.new( 'In ' << @typeName )
359     end
360     @array[ idx ]
361   end
362
363   def has_key?( name )
364     @data.has_key?( name )
365   end
366
367   def each
368     @array.each do | key |
369       yield( key, @data[ key ] )
370     end
371   end
372
373   def encode( ns, name )
374     attrs = []
375     getExtraNSAttr( attrs, ns )
376     getDatatypeAttr( attrs, ns )
377
378     children = @array.collect { | child |
379       @data[ child ].encode( ns.clone, child )
380     }
381
382     Element.new( name, attrs, children )
383   end
384
385   def self.decode( ns, elem )
386     namespace, name = ns.parse( elem.nodeName )
387     s = SOAPStruct.new( name )
388     s.namespace = namespace
389
390     elem.childNodes.each do | child |
391       childNS = ns.clone
392       parseNS( childNS, child )
393       next if ( isEmptyText( child ))
394       childName = ns.parse( child.nodeName )[1]
395       s.add( childName, decodeChild( childNS, child ))
396     end
397     s
398   end
399
400   private
401
402   def getDatatypeAttr( attrs, ns )
403     attrs.push( Attr.new( ns.name( XSD::InstanceNamespace, 'type' ), ns.name( @namespace, @typeName )))
404   end
405
406   def getExtraNSAttr( attrs, ns )
407     unless ns[ @namespace ]
408       tag = ns.assign( @namespace )
409       attrs.push( Attr.new( 'xmlns:' << tag, @namespace ))
410     end
411   end
412
413   def addMember( name, initMember = nil )
414     initMember = SOAPNull.new() unless initMember
415
416     instance_eval <<-EOS
417       def #{ name }()
418         @data[ '#{ name }' ]
419       end
420
421       def #{ name }=( newMember )
422         @data[ '#{ name }' ] = newMember
423       end
424     EOS
425
426     @array.push( name )
427     @data[ name ] = initMember
428   end
429 end
430
431 class SOAPArray < SOAPCompoundBase
432   include Enumerable
433
434   public
435
436   attr_reader :data
437
438   def initialize( typeName = nil )
439     super( typeName )
440     @data = [ [] ]
441     @variant = false
442     @rank = 1
443   end
444
445   def set( newArray )
446     raise NotImplementError.new( 'Partially transmittion does not supported' )
447   end
448
449   def add( newMember )
450     if ( @rank != 1 )
451       raise NotImplementError.new( 'Rank must be 1' )
452     end
453     if ( @data[ 0 ].empty? and !@typeName )
454       @typeName = SOAPArray.getAtype( newMember.typeName, @rank )
455       @namespace = newMember.namespace # ??
456     end
457     if ( @typeName != newMember.typeName )
458       @variant = true
459     end
460     @data[ 0 ] << newMember
461   end
462
463   def []( idx )
464     if ( @rank != 1 )
465       raise NotImplementError.new( 'Rank must be 1' )
466     end
467     if ( idx > @data[ 0 ].size )
468       raise ArrayIndexOutOfBoundsError.new( 'In ' << @typeName )
469     end
470     @data[ 0 ][ idx ]
471   end
472
473   def each
474     if ( @rank != 1 )
475       raise NotImplementError.new( 'Rank must be 1' )
476     end
477     @data[ 0 ].each do | datum |
478       yield( datum )
479     end
480   end
481
482   def encode( ns, name )
483     attrs = []
484     getExtraNSAttr( attrs, ns )
485     getDatatypeAttr( attrs, ns )
486
487     children = @data[ 0 ].collect { | child |
488       childTypeName = contentsTypeName().gsub( /\[,*\]/, 'Array' )
489       child.encode( ns.clone, childTypeName )
490     }
491     Element.new( name, attrs, children )
492   end
493
494   def isVariant?
495     @variant
496   end
497
498   private
499
500   def getDatatypeAttr( attrs, ns )
501     attrs.push( Attr.new( ns.name( EncodingNamespace, 'arrayType' ), ns.name( @namespace, arrayTypeValue() )))
502   end
503
504   def getExtraNSAttr( attrs, ns )
505     unless ns[ @namespace ]
506       tag = ns.assign( @namespace )
507       attrs.push( Attr.new( 'xmlns:' << tag, @namespace ))
508     end
509     unless ns[ EncodingNamespace ]
510       tag = ns.assign( EncodingNamespace )
511       attrs.push( Attr.new( 'xmlns:' << tag, EncodingNamespace ))
512     end
513   end
514
515   def contentsTypeName()
516     @typeName.dup.sub( /\[,*\]$/, '' )
517   end
518
519   def arrayTypeValue()
520     contentsTypeName << '[' << @data.collect { |i| i.size }.join( ',' ) << ']'
521   end
522
523   # Module function
524
525   public
526
527   def self.decode( ns, elem )
528     typeNamespace, typeNameString = ns.parse( getType( ns, elem ))
529     typeName, nofArray = parseType( typeNameString )
530     s = SOAPArray.new( typeName )
531     s.namespace, = ns.parse( elem.nodeName )
532
533     i = 0
534     elem.childNodes.each do | child |
535       childNS = ns.clone
536       parseNS( childNS, child )
537       next if ( isEmptyText( child ))
538       s.add( decodeChild( childNS, child ))
539       i += 1
540       if ( nofArray and ( i > nofArray.to_i ))
541         raise ArrayIndexOutOfBoundsError.new( 'In ' << elem.nodeName )
542       end
543     end
544     s
545   end
546
547   private
548
549   def self.getAtype( typeName, rank )
550     "#{ typeName }[" << ',' * ( rank - 1 ) << ']'
551   end
552
553   TypeParseRegexp = Regexp.new( '^(.+)\[(\d*)\]$' )
554
555   def self.parseType( string )
556     TypeParseRegexp =~ string
557     return $1, $2
558   end
559 end
Note: See TracBrowser for help on using the browser.