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

root/branches/1_5/lib/soap/mapping/encodedregistry.rb

Revision 2015, 17.4 kB (checked in by nahi, 14 hours ago)
  • ruby-1.8.7 warning cleanups. closes #497.
  • Property svn:eol-style set to native
  • Property svn:keywords set to author date id revision
Line 
1 # SOAP4R - encoded mapping registry.
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/baseData'
10 require 'soap/mapping/mapping'
11 require 'soap/mapping/typeMap'
12 require 'soap/mapping/factory'
13 require 'soap/mapping/rubytypeFactory'
14
15
16 module SOAP
17 module Mapping
18
19
20 # Inner class to pass an exception.
21 class SOAPException
22   attr_reader :excn_type_name, :cause
23
24   def initialize(e)
25     @excn_type_name = Mapping.name2elename(e.class.to_s)
26     @cause = e
27   end
28
29   def to_e
30     if @cause.is_a?(::Exception)
31       @cause.extend(::SOAP::Mapping::MappedException)
32       return @cause
33     elsif @cause.respond_to?(:message) and @cause.respond_to?(:backtrace)
34       e = RuntimeError.new(@cause.message)
35       e.set_backtrace(@cause.backtrace)
36       return e
37     end
38     klass = Mapping.class_from_name(Mapping.elename2name(@excn_type_name.to_s))
39     if klass.nil? or not klass <= ::Exception
40       return RuntimeError.new(@cause.inspect)
41     end
42     obj = klass.new(@cause.message)
43     obj.extend(::SOAP::Mapping::MappedException)
44     obj
45   end
46 end
47
48
49 class EncodedRegistry
50   include TraverseSupport
51   include RegistrySupport
52
53   class Map
54     def initialize(registry)
55       @obj2soap = {}
56       @soap2obj = {}
57       @registry = registry
58     end
59
60     def obj2soap(obj)
61       klass = obj.class
62       if map = @obj2soap[klass]
63         map.each do |soap_class, factory, info|
64           ret = factory.obj2soap(soap_class, obj, info, @registry)
65           return ret if ret
66         end
67       end
68       klass.ancestors.each do |baseclass|
69         next if baseclass == klass
70         if map = @obj2soap[baseclass]
71           map.each do |soap_class, factory, info|
72             if info[:derived_class]
73               ret = factory.obj2soap(soap_class, obj, info, @registry)
74               return ret if ret
75             end
76           end
77         end
78       end
79       nil
80     end
81
82     def soap2obj(node, klass = nil)
83       if map = @soap2obj[node.class]
84         map.each do |obj_class, factory, info|
85           next if klass and obj_class != klass
86           conv, obj = factory.soap2obj(obj_class, node, info, @registry)
87           return true, obj if conv
88         end
89       end
90       return false, nil
91     end
92
93     # Give priority to former entry.
94     def init(init_map = [])
95       clear
96       init_map.reverse_each do |obj_class, soap_class, factory, info|
97         add(obj_class, soap_class, factory, info)
98       end
99     end
100
101     # Give priority to latter entry.
102     def add(obj_class, soap_class, factory, info)
103       info ||= {}
104       (@obj2soap[obj_class] ||= []).unshift([soap_class, factory, info])
105       (@soap2obj[soap_class] ||= []).unshift([obj_class, factory, info])
106     end
107
108     def clear
109       @obj2soap.clear
110       @soap2obj.clear
111     end
112
113     def find_mapped_soap_class(target_obj_class)
114       map = @obj2soap[target_obj_class]
115       map.empty? ? nil : map[0][1]
116     end
117
118     def find_mapped_obj_class(target_soap_class)
119       map = @soap2obj[target_soap_class]
120       map.empty? ? nil : map[0][0]
121     end
122   end
123
124   StringFactory = StringFactory_.new
125   BasetypeFactory = BasetypeFactory_.new
126   FixnumFactory = FixnumFactory_.new
127   DateTimeFactory = DateTimeFactory_.new
128   ArrayFactory = ArrayFactory_.new
129   Base64Factory = Base64Factory_.new
130   URIFactory = URIFactory_.new
131   TypedArrayFactory = TypedArrayFactory_.new
132   TypedStructFactory = TypedStructFactory_.new
133
134   HashFactory = HashFactory_.new
135
136   SOAPBaseMap = [
137     [::NilClass,     ::SOAP::SOAPNil,        BasetypeFactory],
138     [::TrueClass,    ::SOAP::SOAPBoolean,    BasetypeFactory],
139     [::FalseClass,   ::SOAP::SOAPBoolean,    BasetypeFactory],
140     [::String,       ::SOAP::SOAPString,     StringFactory,
141       {:derived_class => true}],
142     [::DateTime,     ::SOAP::SOAPDateTime,   DateTimeFactory],
143     [::Date,         ::SOAP::SOAPDate,       DateTimeFactory],
144     [::Time,         ::SOAP::SOAPDateTime,   DateTimeFactory],
145     [::Time,         ::SOAP::SOAPTime,       DateTimeFactory],
146     [::Float,        ::SOAP::SOAPDouble,     BasetypeFactory,
147       {:derived_class => true}],
148     [::Float,        ::SOAP::SOAPFloat,      BasetypeFactory,
149       {:derived_class => true}],
150     [::Fixnum,       ::SOAP::SOAPInt,        FixnumFactory],
151     [::Integer,      ::SOAP::SOAPInt,        BasetypeFactory,
152       {:derived_class => true}],
153     [::Integer,      ::SOAP::SOAPLong,       BasetypeFactory,
154       {:derived_class => true}],
155     [::Integer,      ::SOAP::SOAPInteger,    BasetypeFactory,
156       {:derived_class => true}],
157     [::Integer,      ::SOAP::SOAPShort,      BasetypeFactory,
158       {:derived_class => true}],
159     [::Integer,      ::SOAP::SOAPByte,       BasetypeFactory,
160       {:derived_class => true}],
161     [::Integer,      ::SOAP::SOAPNonPositiveInteger, BasetypeFactory,
162       {:derived_class => true}],
163     [::Integer,      ::SOAP::SOAPNegativeInteger, BasetypeFactory,
164       {:derived_class => true}],
165     [::Integer,      ::SOAP::SOAPNonNegativeInteger, BasetypeFactory,
166       {:derived_class => true}],
167     [::Integer,      ::SOAP::SOAPPositiveInteger, BasetypeFactory,
168       {:derived_class => true}],
169     [::Integer,      ::SOAP::SOAPUnsignedLong, BasetypeFactory,
170       {:derived_class => true}],
171     [::Integer,      ::SOAP::SOAPUnsignedInt, BasetypeFactory,
172       {:derived_class => true}],
173     [::Integer,      ::SOAP::SOAPUnsignedShort, BasetypeFactory,
174       {:derived_class => true}],
175     [::Integer,      ::SOAP::SOAPUnsignedByte, BasetypeFactory,
176       {:derived_class => true}],
177     [::URI::Generic, ::SOAP::SOAPAnyURI,     URIFactory,
178       {:derived_class => true}],
179     [::String,       ::SOAP::SOAPBase64,     Base64Factory],
180     [::String,       ::SOAP::SOAPHexBinary,  Base64Factory],
181     [::String,       ::SOAP::SOAPDecimal,    BasetypeFactory],
182     [::String,       ::SOAP::SOAPDuration,   BasetypeFactory],
183     [::String,       ::SOAP::SOAPGYearMonth, BasetypeFactory],
184     [::String,       ::SOAP::SOAPGYear,      BasetypeFactory],
185     [::String,       ::SOAP::SOAPGMonthDay,  BasetypeFactory],
186     [::String,       ::SOAP::SOAPGDay,       BasetypeFactory],
187     [::String,       ::SOAP::SOAPGMonth,     BasetypeFactory],
188     [::String,       ::SOAP::SOAPQName,      BasetypeFactory],
189
190     [::Hash,         ::SOAP::SOAPArray,      HashFactory,
191       {:derived_class => true}],
192     [::Hash,         ::SOAP::SOAPStruct,     HashFactory,
193       {:derived_class => true}],
194
195     [::Array,        ::SOAP::SOAPArray,      ArrayFactory,
196       {:derived_class => true}],
197
198     [::SOAP::Mapping::SOAPException,
199                      ::SOAP::SOAPStruct,     TypedStructFactory,
200       {:type => XSD::QName.new(RubyCustomTypeNamespace, "SOAPException")}],
201  ]
202
203   RubyOriginalMap = [
204     [::NilClass,     ::SOAP::SOAPNil,        BasetypeFactory],
205     [::TrueClass,    ::SOAP::SOAPBoolean,    BasetypeFactory],
206     [::FalseClass,   ::SOAP::SOAPBoolean,    BasetypeFactory],
207     [::String,       ::SOAP::SOAPString,     StringFactory],
208     [::DateTime,     ::SOAP::SOAPDateTime,   DateTimeFactory],
209     [::Date,         ::SOAP::SOAPDate,       DateTimeFactory],
210     [::Time,         ::SOAP::SOAPDateTime,   DateTimeFactory],
211     [::Time,         ::SOAP::SOAPTime,       DateTimeFactory],
212     [::Float,        ::SOAP::SOAPDouble,     BasetypeFactory,
213       {:derived_class => true}],
214     [::Float,        ::SOAP::SOAPFloat,      BasetypeFactory,
215       {:derived_class => true}],
216     [::Fixnum,       ::SOAP::SOAPInt,        FixnumFactory],
217     [::Integer,      ::SOAP::SOAPInt,        BasetypeFactory,
218       {:derived_class => true}],
219     [::Integer,      ::SOAP::SOAPLong,       BasetypeFactory,
220       {:derived_class => true}],
221     [::Integer,      ::SOAP::SOAPInteger,    BasetypeFactory,
222       {:derived_class => true}],
223     [::Integer,      ::SOAP::SOAPShort,      BasetypeFactory,
224       {:derived_class => true}],
225     [::Integer,      ::SOAP::SOAPByte,       BasetypeFactory,
226       {:derived_class => true}],
227     [::Integer,      ::SOAP::SOAPNonPositiveInteger, BasetypeFactory,
228       {:derived_class => true}],
229     [::Integer,      ::SOAP::SOAPNegativeInteger, BasetypeFactory,
230       {:derived_class => true}],
231     [::Integer,      ::SOAP::SOAPNonNegativeInteger, BasetypeFactory,
232       {:derived_class => true}],
233     [::Integer,      ::SOAP::SOAPPositiveInteger, BasetypeFactory,
234       {:derived_class => true}],
235     [::Integer,      ::SOAP::SOAPUnsignedLong, BasetypeFactory,
236       {:derived_class => true}],
237     [::Integer,      ::SOAP::SOAPUnsignedInt, BasetypeFactory,
238       {:derived_class => true}],
239     [::Integer,      ::SOAP::SOAPUnsignedShort, BasetypeFactory,
240       {:derived_class => true}],
241     [::Integer,      ::SOAP::SOAPUnsignedByte, BasetypeFactory,
242       {:derived_class => true}],
243     [::URI::Generic, ::SOAP::SOAPAnyURI,     URIFactory,
244       {:derived_class => true}],
245     [::String,       ::SOAP::SOAPBase64,     Base64Factory],
246     [::String,       ::SOAP::SOAPHexBinary,  Base64Factory],
247     [::String,       ::SOAP::SOAPDecimal,    BasetypeFactory],
248     [::String,       ::SOAP::SOAPDuration,   BasetypeFactory],
249     [::String,       ::SOAP::SOAPGYearMonth, BasetypeFactory],
250     [::String,       ::SOAP::SOAPGYear,      BasetypeFactory],
251     [::String,       ::SOAP::SOAPGMonthDay,  BasetypeFactory],
252     [::String,       ::SOAP::SOAPGDay,       BasetypeFactory],
253     [::String,       ::SOAP::SOAPGMonth,     BasetypeFactory],
254     [::String,       ::SOAP::SOAPQName,      BasetypeFactory],
255
256     [::Hash,         ::SOAP::SOAPArray,      HashFactory],
257     [::Hash,         ::SOAP::SOAPStruct,     HashFactory],
258
259     # Does not allow Array's subclass here.
260     [::Array,        ::SOAP::SOAPArray,      ArrayFactory],
261
262     [::SOAP::Mapping::SOAPException,
263                      ::SOAP::SOAPStruct,     TypedStructFactory,
264       {:type => XSD::QName.new(RubyCustomTypeNamespace, "SOAPException")}],
265   ]
266
267   attr_accessor :default_factory
268   attr_accessor :excn_handler_obj2soap
269   attr_accessor :excn_handler_soap2obj
270
271   def initialize(config = {})
272     super()
273     @config = config
274     @map = Map.new(self)
275     if @config[:allow_original_mapping]
276       @allow_original_mapping = true
277       @map.init(RubyOriginalMap)
278     else
279       @allow_original_mapping = false
280       @map.init(SOAPBaseMap)
281     end
282     @allow_untyped_struct = @config.key?(:allow_untyped_struct) ?
283       @config[:allow_untyped_struct] : true
284     @rubytype_factory = RubytypeFactory.new(
285       :allow_untyped_struct => @allow_untyped_struct,
286       :allow_original_mapping => @allow_original_mapping
287     )
288     @default_factory = @rubytype_factory
289     @excn_handler_obj2soap = nil
290     @excn_handler_soap2obj = nil
291   end
292
293   # initial mapping interface
294   # new interface Registry#register is defined in RegisterSupport
295   def add(obj_class, soap_class, factory, info = nil)
296     @map.add(obj_class, soap_class, factory, info)
297   end
298   alias set add
299
300   def obj2soap(obj, type_qname = nil)
301     soap = _obj2soap(obj, type_qname)
302     if @allow_original_mapping
303       addextend2soap(soap, obj)
304     end
305     soap
306   end
307
308   def soap2obj(node, klass = nil)
309     obj = _soap2obj(node, klass)
310     if @allow_original_mapping
311       addextend2obj(obj, node.extraattr[RubyExtendName])
312       addiv2obj(obj, node.extraattr[RubyIVarName])
313     end
314     obj
315   end
316
317   def find_mapped_soap_class(obj_class)
318     @map.find_mapped_soap_class(obj_class)
319   end
320
321   def find_mapped_obj_class(soap_class)
322     @map.find_mapped_obj_class(soap_class)
323   end
324
325 private
326
327   def _obj2soap(obj, type_qname = nil)
328     ret = nil
329     if obj.is_a?(SOAPCompoundtype)
330       obj.replace do |ele|
331         Mapping._obj2soap(ele, self)
332       end
333       return obj
334     elsif obj.is_a?(SOAPBasetype)
335       return obj
336     elsif type_qname && type = TypeMap[type_qname]
337       return base2soap(obj, type)
338     end
339     cause = nil
340     begin
341       if definition = schema_definition_from_class(obj.class)
342         return stubobj2soap(obj, definition)
343       end
344       ret = @map.obj2soap(obj) ||
345         @default_factory.obj2soap(nil, obj, nil, self)
346       return ret if ret
347     rescue MappingError
348       cause = $!
349     end
350     if @excn_handler_obj2soap
351       ret = @excn_handler_obj2soap.call(obj) { |yield_obj|
352         Mapping._obj2soap(yield_obj, self)
353       }
354       return ret if ret
355     end
356     raise MappingError.new("Cannot map #{ obj.class.name } to SOAP/OM.", cause)
357   end
358
359   # Might return nil as a mapping result.
360   def _soap2obj(node, klass = nil)
361     definition = find_node_definition(node)
362     if klass
363       klass_definition = schema_definition_from_class(klass)
364       if definition and (definition.class_for < klass)
365         klass = definition.class_for
366       else
367         definition = klass_definition
368       end
369     else
370       klass = definition.class_for if definition
371     end
372     if definition and node.is_a?(::SOAP::SOAPNameAccessible)
373       return elesoap2stubobj(node, klass, definition)
374     end
375     if node.extraattr.key?(RubyTypeName)
376       conv, obj = @rubytype_factory.soap2obj(nil, node, nil, self)
377       return obj if conv
378     end
379     conv, obj = @map.soap2obj(node)
380     return obj if conv
381     conv, obj = @default_factory.soap2obj(nil, node, nil, self)
382     return obj if conv
383     cause = nil
384     if @excn_handler_soap2obj
385       begin
386         return @excn_handler_soap2obj.call(node) { |yield_node|
387             Mapping._soap2obj(yield_node, self)
388           }
389       rescue Exception
390         cause = $!
391       end
392     end
393     raise MappingError.new("Cannot map #{ node.type.name } to Ruby object.", cause)
394   end
395
396   def addiv2obj(obj, attr)
397     return unless attr
398     vars = {}
399     attr.__getobj__.each do |name, value|
400       vars[name] = Mapping._soap2obj(value, self)
401     end
402     Mapping.set_attributes(obj, vars)
403   end
404
405   if RUBY_VERSION >= '1.8.0'
406     def addextend2obj(obj, attr)
407       return unless attr
408       attr.split(/ /).reverse_each do |mstr|
409         obj.extend(Mapping.module_from_name(mstr))
410       end
411     end
412   else
413     # (class < false; self; end).ancestors includes "TrueClass" under 1.6...
414     def addextend2obj(obj, attr)
415       return unless attr
416       attr.split(/ /).reverse_each do |mstr|
417         m = Mapping.module_from_name(mstr)
418         obj.extend(m)
419       end
420     end
421   end
422
423   def addextend2soap(node, obj)
424     return if obj.is_a?(Symbol) or obj.is_a?(Fixnum)
425     list = (class << obj; self; end).ancestors - obj.class.ancestors
426     unless list.empty?
427       node.extraattr[RubyExtendName] = list.collect { |c|
428         name = c.name
429         if name.nil? or name.empty?
430           raise TypeError.new("singleton can't be dumped #{ obj }")
431         end
432         name
433       }.join(" ")
434     end
435   end
436
437   def stubobj2soap(obj, definition)
438     case obj
439     when ::Array
440       array2soap(obj, definition)
441     else
442       unknownstubobj2soap(obj, definition)
443     end
444   end
445
446   def array2soap(obj, definition)
447     return SOAPNil.new if obj.nil?      # ToDo: check nillable.
448     eledef = definition.elements[0]
449     soap_obj = SOAPArray.new(ValueArrayName, 1, eledef.elename)
450     mark_marshalled_obj(obj, soap_obj)
451     obj.each do |item|
452       soap_obj.add(typedobj2soap(item, eledef.mapped_class))
453     end
454     soap_obj
455   end
456
457   def unknownstubobj2soap(obj, definition)
458     return SOAPNil.new if obj.nil?
459     if definition.elements.size == 0
460       ele = Mapping.obj2soap(obj)
461       ele.elename = definition.elename if definition.elename
462       ele.extraattr[XSD::AttrTypeName] = definition.type if definition.type
463       return ele
464     else
465       ele = SOAPStruct.new(definition.type)
466       mark_marshalled_obj(obj, ele)
467     end
468     definition.elements.each do |eledef|
469       name = eledef.elename.name
470       if obj.is_a?(::Array) and eledef.as_array?
471         obj.each do |item|
472           ele.add(name, typedobj2soap(item, eledef.mapped_class))
473         end
474       else
475         child = Mapping.get_attribute(obj, eledef.varname)
476         if child.is_a?(::Array) and eledef.as_array?
477           child.each do |item|
478             ele.add(name, typedobj2soap(item, eledef.mapped_class))
479           end
480         else
481           ele.add(name, typedobj2soap(child, eledef.mapped_class))
482         end
483       end
484     end
485     ele
486   end
487
488   def typedobj2soap(value, klass)
489     if klass and klass.include?(::SOAP::SOAPBasetype)
490       base2soap(value, klass)
491     else
492       Mapping._obj2soap(value, self)
493     end
494   end
495
496   def elesoap2stubobj(node, obj_class, definition)
497     obj = Mapping.create_empty_object(obj_class)
498     add_elesoap2stubobj(node, obj, definition)
499     obj
500   end
501
502   # XXX consider to merge with the method in LiteralRegistry
503   def add_elesoap2stubobj(node, obj, definition)
504     vars = {}
505     node.each do |name, value|
506       item = definition.elements.find_element(value.elename)
507       if item
508         child = soap2typedobj(value, item.mapped_class)
509       else
510         # unknown element is treated as anyType.
511         child = Mapping._soap2obj(value, self)
512       end
513       if item and item.as_array?
514         (vars[name] ||= []) << child
515       elsif vars.key?(name)
516         vars[name] = [vars[name], child].flatten
517       else
518         vars[name] = child
519       end
520     end
521     if obj.is_a?(::Array) and is_stubobj_elements_for_array(vars)
522       Array.instance_method(:replace).bind(obj).call(vars.values[0])
523     else
524       Mapping.set_attributes(obj, vars)
525     end
526   end
527
528   def soap2typedobj(value, klass)
529     unless klass
530       raise MappingError.new("unknown class: #{klass}")
531     end
532     if klass.include?(::SOAP::SOAPBasetype)
533       obj = base2obj(value, klass)
534     else
535       obj = Mapping._soap2obj(value, self, klass)
536     end
537     obj
538   end
539 end
540
541
542 Registry = EncodedRegistry
543 DefaultRegistry = EncodedRegistry.new
544 RubyOriginalRegistry = EncodedRegistry.new(:allow_original_mapping => true)
545
546
547 end
548 end
Note: See TracBrowser for help on using the browser.