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

root/trunk/lib/soap/mapping/rubytypeFactory.rb

Revision 2000, 14.5 kB (checked in by nahi, 1 year ago)
  • removed backward compatibility definitions for soap4r-1.4.X. closes #445.
    • removed SOAPlet#{app_scope_router,add_servant} -> use methods in HTTPServer instead.
    • removed SOAP::WSDLDriver#generateEncodeType -> use SOAP::WSDLDriver#generate_explicit_type
    • removed SOAP::SOAPGenerator -> use SOAP::Generator instead.
    • removed compatibility method definitions for ruby-1.6.X.
  • rpc/encoded service + detail element without xsi:type attribute caused NameError? since 1.5.6. (#435)
  • extract attr_proxy definition. added soap/attrproxy.rb and wsdl/xmlSchema/ref.rb.
  • Property svn:eol-style set to native
  • Property svn:keywords set to author date id revision
Line 
1 # SOAP4R - Ruby type mapping factory.
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 module SOAP
10 module Mapping
11
12
13 class RubytypeFactory < Factory
14   TYPE_STRING = XSD::QName.new(RubyTypeNamespace, 'String')
15   TYPE_TIME = XSD::QName.new(RubyTypeNamespace, 'Time')
16   TYPE_ARRAY = XSD::QName.new(RubyTypeNamespace, 'Array')
17   TYPE_REGEXP = XSD::QName.new(RubyTypeNamespace, 'Regexp')
18   TYPE_RANGE = XSD::QName.new(RubyTypeNamespace, 'Range')
19   TYPE_CLASS = XSD::QName.new(RubyTypeNamespace, 'Class')
20   TYPE_MODULE = XSD::QName.new(RubyTypeNamespace, 'Module')
21   TYPE_SYMBOL = XSD::QName.new(RubyTypeNamespace, 'Symbol')
22   TYPE_STRUCT = XSD::QName.new(RubyTypeNamespace, 'Struct')
23   TYPE_HASH = XSD::QName.new(RubyTypeNamespace, 'Map')
24
25   def initialize(config = {})
26     @config = config
27     @allow_untyped_struct = @config.key?(:allow_untyped_struct) ?
28       @config[:allow_untyped_struct] : true
29     @allow_original_mapping = @config.key?(:allow_original_mapping) ?
30       @config[:allow_original_mapping] : false
31     @string_factory = StringFactory_.new(true)
32     @basetype_factory = BasetypeFactory_.new(true)
33     @datetime_factory = DateTimeFactory_.new(true)
34     @array_factory = ArrayFactory_.new(true)
35     @hash_factory = HashFactory_.new(true)
36   end
37
38   def obj2soap(soap_class, obj, info, map)
39     param = nil
40     case obj
41     when ::String
42       unless @allow_original_mapping
43         return nil
44       end
45       param = @string_factory.obj2soap(SOAPString, obj, info, map)
46       if obj.class != String
47         param.extraattr[RubyTypeName] = obj.class.name
48       end
49       addiv2soapattr(param, obj, map)
50     when ::Time
51       unless @allow_original_mapping
52         return nil
53       end
54       param = @datetime_factory.obj2soap(SOAPDateTime, obj, info, map)
55       if obj.class != Time
56         param.extraattr[RubyTypeName] = obj.class.name
57       end
58       addiv2soapattr(param, obj, map)
59     when ::Array
60       unless @allow_original_mapping
61         return nil
62       end
63       param = @array_factory.obj2soap(nil, obj, info, map)
64       if obj.class != Array
65         param.extraattr[RubyTypeName] = obj.class.name
66       end
67       addiv2soapattr(param, obj, map)
68     when ::NilClass
69       unless @allow_original_mapping
70         return nil
71       end
72       param = @basetype_factory.obj2soap(SOAPNil, obj, info, map)
73       addiv2soapattr(param, obj, map)
74     when ::FalseClass, ::TrueClass
75       unless @allow_original_mapping
76         return nil
77       end
78       param = @basetype_factory.obj2soap(SOAPBoolean, obj, info, map)
79       addiv2soapattr(param, obj, map)
80     when ::Integer
81       unless @allow_original_mapping
82         return nil
83       end
84       param = @basetype_factory.obj2soap(SOAPInt, obj, info, map)
85       param ||= @basetype_factory.obj2soap(SOAPInteger, obj, info, map)
86       param ||= @basetype_factory.obj2soap(SOAPDecimal, obj, info, map)
87       addiv2soapattr(param, obj, map)
88     when ::Float
89       unless @allow_original_mapping
90         return nil
91       end
92       param = @basetype_factory.obj2soap(SOAPDouble, obj, info, map)
93       if obj.class != Float
94         param.extraattr[RubyTypeName] = obj.class.name
95       end
96       addiv2soapattr(param, obj, map)
97     when ::Hash
98       unless @allow_original_mapping
99         return nil
100       end
101       if obj.respond_to?(:default_proc) && obj.default_proc
102         raise TypeError.new("cannot dump hash with default proc")
103       end
104       param = SOAPStruct.new(TYPE_HASH)
105       mark_marshalled_obj(obj, param)
106       if obj.class != Hash
107         param.extraattr[RubyTypeName] = obj.class.name
108       end
109       obj.each do |key, value|
110         elem = SOAPStruct.new # Undefined type.
111         elem.add("key", Mapping._obj2soap(key, map))
112         elem.add("value", Mapping._obj2soap(value, map))
113         param.add("item", elem)
114       end
115       param.add('default', Mapping._obj2soap(obj.default, map))
116       addiv2soapattr(param, obj, map)
117     when ::Regexp
118       unless @allow_original_mapping
119         return nil
120       end
121       param = SOAPStruct.new(TYPE_REGEXP)
122       mark_marshalled_obj(obj, param)
123       if obj.class != Regexp
124         param.extraattr[RubyTypeName] = obj.class.name
125       end
126       param.add('source', SOAPBase64.new(obj.source))
127       options = obj.options
128       param.add('options', SOAPInt.new(options))
129       addiv2soapattr(param, obj, map)
130     when ::Range
131       unless @allow_original_mapping
132         return nil
133       end
134       param = SOAPStruct.new(TYPE_RANGE)
135       mark_marshalled_obj(obj, param)
136       if obj.class != Range
137         param.extraattr[RubyTypeName] = obj.class.name
138       end
139       param.add('begin', Mapping._obj2soap(obj.begin, map))
140       param.add('end', Mapping._obj2soap(obj.end, map))
141       param.add('exclude_end', SOAP::SOAPBoolean.new(obj.exclude_end?))
142       addiv2soapattr(param, obj, map)
143     when ::Class
144       unless @allow_original_mapping
145         return nil
146       end
147       if obj.to_s[0] == ?#
148         raise TypeError.new("can't dump anonymous class #{obj}")
149       end
150       param = SOAPStruct.new(TYPE_CLASS)
151       mark_marshalled_obj(obj, param)
152       param.add('name', SOAPString.new(obj.name))
153       addiv2soapattr(param, obj, map)
154     when ::Module
155       unless @allow_original_mapping
156         return nil
157       end
158       if obj.to_s[0] == ?#
159         raise TypeError.new("can't dump anonymous module #{obj}")
160       end
161       param = SOAPStruct.new(TYPE_MODULE)
162       mark_marshalled_obj(obj, param)
163       param.add('name', SOAPString.new(obj.name))
164       addiv2soapattr(param, obj, map)
165     when ::Symbol
166       unless @allow_original_mapping
167         return nil
168       end
169       param = SOAPStruct.new(TYPE_SYMBOL)
170       mark_marshalled_obj(obj, param)
171       param.add('id', SOAPString.new(obj.id2name))
172       addiv2soapattr(param, obj, map)
173     when ::Struct
174       unless @allow_original_mapping
175         # treat it as an user defined class. [ruby-talk:104980]
176         #param = unknownobj2soap(soap_class, obj, info, map)
177         param = SOAPStruct.new(XSD::AnyTypeName)
178         mark_marshalled_obj(obj, param)
179         obj.members.each do |member|
180           param.add(Mapping.name2elename(member),
181             Mapping._obj2soap(obj[member], map))
182         end
183       else
184         param = SOAPStruct.new(TYPE_STRUCT)
185         mark_marshalled_obj(obj, param)
186         param.add('type', ele_type = SOAPString.new(obj.class.to_s))
187         ele_member = SOAPStruct.new
188         obj.members.each do |member|
189           ele_member.add(Mapping.name2elename(member),
190             Mapping._obj2soap(obj[member], map))
191         end
192         param.add('member', ele_member)
193         addiv2soapattr(param, obj, map)
194       end
195     when ::IO, ::Binding, ::Continuation, ::Data, ::Dir, ::File::Stat,
196         ::MatchData, Method, ::Proc, ::Process::Status, ::Thread,
197         ::ThreadGroup, ::UnboundMethod
198       return nil
199     when ::SOAP::Mapping::Object
200       param = SOAPStruct.new(XSD::AnyTypeName)
201       mark_marshalled_obj(obj, param)
202       obj.__xmlele.each do |key, value|
203         param.add(key.name, Mapping._obj2soap(value, map))
204       end
205       obj.__xmlattr.each do |key, value|
206         param.extraattr[key] = value
207       end
208     when ::Exception
209       typestr = Mapping.name2elename(obj.class.to_s)
210       param = SOAPStruct.new(XSD::QName.new(RubyTypeNamespace, typestr))
211       mark_marshalled_obj(obj, param)
212       param.add('message', Mapping._obj2soap(obj.message, map))
213       param.add('backtrace', Mapping._obj2soap(obj.backtrace, map))
214       addiv2soapattr(param, obj, map)
215     else
216       param = unknownobj2soap(soap_class, obj, info, map)
217     end
218     param
219   end
220
221   def soap2obj(obj_class, node, info, map)
222     rubytype = node.extraattr[RubyTypeName]
223     if rubytype or node.type.namespace == RubyTypeNamespace
224       rubytype2obj(node, info, map, rubytype)
225     elsif node.type == XSD::AnyTypeName or node.type == XSD::AnySimpleTypeName
226       anytype2obj(node, info, map)
227     else
228       unknowntype2obj(node, info, map)
229     end
230   end
231
232 private
233
234   def addiv2soapattr(node, obj, map)
235     return if obj.instance_variables.empty?
236     ivars = SOAPStruct.new    # Undefined type.
237     setiv2soap(ivars, obj, map)
238     node.extraattr[RubyIVarName] = ivars
239   end
240
241   def unknownobj2soap(soap_class, obj, info, map)
242     if anonymous_class?(obj)
243       raise TypeError.new("can't dump anonymous class #{obj}")
244     end
245     singleton_class = class << obj; self; end
246     if !obj.singleton_methods(true).empty? or
247         !singleton_class.instance_variables.empty?
248       raise TypeError.new("singleton can't be dumped #{obj}")
249     end
250     if !(singleton_class.ancestors - obj.class.ancestors).empty?
251       typestr = Mapping.name2elename(obj.class.to_s)
252       type = XSD::QName.new(RubyTypeNamespace, typestr)
253     else
254       type = Mapping.class2element(obj.class)
255     end
256     param = SOAPStruct.new(type)
257     mark_marshalled_obj(obj, param)
258     setiv2soap(param, obj, map)
259     param
260   end
261
262   def rubytype2obj(node, info, map, rubytype)
263     klass = rubytype ? Mapping.class_from_name(rubytype) : nil
264     obj = nil
265     case node
266     when SOAPString
267       return @string_factory.soap2obj(klass || String, node, info, map)
268     when SOAPDateTime
269       #return @datetime_factory.soap2obj(klass || Time, node, info, map)
270       klass ||= Time
271       t = node.to_time
272       arg = [t.year, t.month, t.mday, t.hour, t.min, t.sec, t.usec]
273       obj = t.gmt? ? klass.gm(*arg) : klass.local(*arg)
274       mark_unmarshalled_obj(node, obj)
275       return true, obj
276     when SOAPArray
277       return @array_factory.soap2obj(klass || Array, node, info, map)
278     when SOAPNil, SOAPBoolean, SOAPInt, SOAPInteger, SOAPDecimal, SOAPDouble
279       return @basetype_factory.soap2obj(nil, node, info, map)
280     when SOAPStruct
281       return rubytypestruct2obj(node, info, map, rubytype)
282     else
283       raise
284     end
285   end
286
287   def rubytypestruct2obj(node, info, map, rubytype)
288     klass = rubytype ? Mapping.class_from_name(rubytype) : nil
289     obj = nil
290     case node.type
291     when TYPE_HASH
292       klass = rubytype ? Mapping.class_from_name(rubytype) : Hash
293       obj = Mapping.create_empty_object(klass)
294       mark_unmarshalled_obj(node, obj)
295       node.each do |key, value|
296         next unless key == 'item'
297         obj[Mapping._soap2obj(value['key'], map)] =
298           Mapping._soap2obj(value['value'], map)
299       end
300       if node.key?('default')
301         obj.default = Mapping._soap2obj(node['default'], map)
302       end
303     when TYPE_REGEXP
304       klass = rubytype ? Mapping.class_from_name(rubytype) : Regexp
305       obj = Mapping.create_empty_object(klass)
306       mark_unmarshalled_obj(node, obj)
307       source = node['source'].string
308       options = node['options'].data || 0
309       Regexp.instance_method(:initialize).bind(obj).call(source, options)
310     when TYPE_RANGE
311       klass = rubytype ? Mapping.class_from_name(rubytype) : Range
312       obj = Mapping.create_empty_object(klass)
313       mark_unmarshalled_obj(node, obj)
314       first = Mapping._soap2obj(node['begin'], map)
315       last = Mapping._soap2obj(node['end'], map)
316       exclude_end = node['exclude_end'].data
317       Range.instance_method(:initialize).bind(obj).call(first, last, exclude_end)
318     when TYPE_CLASS
319       obj = Mapping.class_from_name(node['name'].data)
320     when TYPE_MODULE
321       obj = Mapping.class_from_name(node['name'].data)
322     when TYPE_SYMBOL
323       obj = node['id'].data.intern
324     when TYPE_STRUCT
325       typestr = Mapping.elename2name(node['type'].data)
326       klass = Mapping.class_from_name(typestr)
327       if klass.nil?
328         return false
329       end
330       unless klass <= ::Struct
331         return false
332       end
333       obj = Mapping.create_empty_object(klass)
334       mark_unmarshalled_obj(node, obj)
335       node['member'].each do |name, value|
336         obj[Mapping.elename2name(name)] = Mapping._soap2obj(value, map)
337       end
338     else
339       return unknowntype2obj(node, info, map)
340     end
341     return true, obj
342   end
343
344   def anytype2obj(node, info, map)
345     case node
346     when SOAPBasetype
347       return true, node.data
348     when SOAPStruct
349       klass = ::SOAP::Mapping::Object
350       obj = klass.new
351       mark_unmarshalled_obj(node, obj)
352       node.each do |name, value|
353         obj.__add_xmlele_value(XSD::QName.new(nil, name),
354           Mapping._soap2obj(value, map))
355       end
356       unless node.extraattr.empty?
357         obj.instance_variable_set('@__xmlattr', node.extraattr)
358       end
359       return true, obj
360     else
361       return false
362     end
363   end
364
365   def unknowntype2obj(node, info, map)
366     case node
367     when SOAPBasetype
368       return true, node.data
369     when SOAPArray
370       return @array_factory.soap2obj(Array, node, info, map)
371     when SOAPStruct
372       obj = unknownstruct2obj(node, info, map)
373       return true, obj if obj
374       if !@allow_untyped_struct
375         return false
376       end
377       return anytype2obj(node, info, map)
378     else
379       # Basetype which is not defined...
380       return false
381     end
382   end
383
384   def unknownstruct2obj(node, info, map)
385     unless node.type.name
386       return nil
387     end
388     typestr = Mapping.elename2name(node.type.name)
389     klass = Mapping.class_from_name(typestr)
390     if klass.respond_to?(:soap_marshallable) and !klass.soap_marshallable
391       return nil
392     end
393     if klass.nil? and @allow_untyped_struct
394       klass = Mapping.class_from_name(typestr, true)    # lenient
395     end
396     if klass.nil?
397       return nil
398     end
399     if klass <= ::Exception
400       return exception2obj(klass, node, map)
401     end
402     klass_type = Mapping.class2qname(klass)
403     return nil unless node.type.match(klass_type)
404     obj = nil
405     begin
406       obj = Mapping.create_empty_object(klass)
407     rescue
408       # type name "data" tries Data.new which raises TypeError
409       nil
410     end
411     mark_unmarshalled_obj(node, obj)
412     setiv2obj(obj, node, map)
413     obj
414   end
415
416   def exception2obj(klass, node, map)
417     message = Mapping._soap2obj(node['message'], map)
418     backtrace = Mapping._soap2obj(node['backtrace'], map)
419     obj = Mapping.create_empty_object(klass)
420     obj = obj.exception(message)
421     mark_unmarshalled_obj(node, obj)
422     obj.set_backtrace(backtrace)
423     obj
424   end
425
426   # Only creates empty array.  Do String#replace it with real string.
427   def array2obj(node, map, rubytype)
428     klass = rubytype ? Mapping.class_from_name(rubytype) : Array
429     obj = Mapping.create_empty_object(klass)
430     mark_unmarshalled_obj(node, obj)
431     obj
432   end
433
434   # Only creates empty string.  Do String#replace it with real string.
435   def string2obj(node, map, rubytype)
436     klass = rubytype ? Mapping.class_from_name(rubytype) : String
437     obj = Mapping.create_empty_object(klass)
438     mark_unmarshalled_obj(node, obj)
439     obj
440   end
441 end
442
443
444 end
445 end
Note: See TracBrowser for help on using the browser.