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

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

Revision 2015, 10.6 kB (checked in by nahi, 13 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 - literal 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 'xsd/codegen/gensupport'
13 require 'xsd/namedelements'
14
15
16 module SOAP
17 module Mapping
18
19
20 class LiteralRegistry
21   include RegistrySupport
22
23   attr_accessor :excn_handler_obj2soap
24   attr_accessor :excn_handler_soap2obj
25
26   def initialize
27     super()
28     @excn_handler_obj2soap = nil
29     @excn_handler_soap2obj = nil
30   end
31
32   def obj2soap(obj, qname, obj_class = nil)
33     soap_obj = nil
34     if obj.is_a?(SOAPElement)
35       soap_obj = obj
36     else
37       soap_obj = any2soap(obj, qname, obj_class)
38     end
39     return soap_obj if soap_obj
40     if @excn_handler_obj2soap
41       soap_obj = @excn_handler_obj2soap.call(obj) { |yield_obj|
42         Mapping.obj2soap(yield_obj, nil, nil, MAPPING_OPT)
43       }
44       return soap_obj if soap_obj
45     end
46     raise MappingError.new("cannot map #{obj.class.name} as #{qname}")
47   end
48
49   # node should be a SOAPElement
50   def soap2obj(node, obj_class = nil)
51     cause = nil
52     begin
53       return any2obj(node, obj_class)
54     rescue MappingError
55       cause = $!
56     end
57     if @excn_handler_soap2obj
58       begin
59         return @excn_handler_soap2obj.call(node) { |yield_node|
60             Mapping.soap2obj(yield_node, nil, nil, MAPPING_OPT)
61           }
62       rescue Exception
63       end
64     end
65     raise MappingError.new("cannot map #{node.elename.name}/#{node.type.name} to Ruby object", cause)
66   end
67
68 private
69
70   MAPPING_OPT = { :no_reference => true }
71
72   def definedobj2soap(obj, definition)
73     obj2soap(obj, definition.elename, definition.mapped_class)
74   end
75
76   def any2soap(obj, qname, obj_class)
77     ele = nil
78     if obj.is_a?(SOAP::Mapping::Object)
79       return mappingobj2soap(obj, qname)
80     end
81     class_definition = schema_definition_from_class(obj.class)
82     if class_definition.nil? and obj_class
83       class_definition = schema_definition_from_class(obj_class)
84     end
85     elename_definition = schema_definition_from_elename(qname)
86     if !class_definition and !elename_definition
87       # no definition found
88       return anyobj2soap(obj, qname)
89     end
90     if !class_definition or !elename_definition
91       # use found one
92       return stubobj2soap(obj, qname, class_definition || elename_definition)
93     end
94     # found both:
95     if class_definition.class_for == elename_definition.class_for
96       # if two definitions are for the same class, give qname a priority.
97       return stubobj2soap(obj, qname, elename_definition)
98     end
99     # it should be a derived class
100     return stubobj2soap(obj, qname, class_definition)
101   end
102
103   def anyobj2soap(obj, qname)
104     ele = nil
105     case obj
106     when Hash
107       ele = SOAPElement.from_obj(obj, nil)
108       ele.elename = qname
109     when Array
110       # treat as a list of simpletype
111       ele = SOAPElement.new(qname, obj.join(" "))
112     when XSD::QName
113       ele = SOAPElement.new(qname)
114       ele.text = obj
115     else
116       # expected to be a basetype or an anyType.
117       # SOAPStruct, etc. is used instead of SOAPElement.
118       begin
119         ele = Mapping.obj2soap(obj, nil, nil, MAPPING_OPT)
120         ele.elename = qname
121       rescue MappingError
122         ele = SOAPElement.new(qname, obj.to_s)
123       end
124     end
125     add_attributes2soap(obj, ele)
126     ele
127   end
128
129   def stubobj2soap(obj, qname, definition)
130     if obj.nil?
131       ele = SOAPNil.new
132       ele.elename = qname
133     elsif obj.is_a?(::String)
134       ele = SOAPElement.new(qname, obj)
135     else
136       ele = SOAPElement.new(qname)
137     end
138     ele.qualified = definition.qualified
139     if definition.type
140       ele.type = definition.type
141       if definition.basetype or Mapping.root_type_hint
142         Mapping.reset_root_type_hint
143         ele.force_typed = true
144       end
145     end
146     if qname.nil? and definition.elename
147       ele.elename = definition.elename
148     end
149     return ele if obj.nil?
150     stubobj2soap_elements(obj, ele, definition.elements)
151     add_definedattributes2soap(obj, ele, definition)
152     ele
153   end
154
155   def stubobj2soap_elements(obj, ele, definition, is_choice = false)
156     added = false
157     case definition
158     when SchemaSequenceDefinition, SchemaEmptyDefinition
159       definition.each do |eledef|
160         ele_added = stubobj2soap_elements(obj, ele, eledef, is_choice)
161         added = true if ele_added
162       end
163     when SchemaChoiceDefinition
164       definition.each do |eledef|
165         added = stubobj2soap_elements(obj, ele, eledef, true)
166         break if added
167       end
168     else
169       added = true
170       if definition.as_any?
171         any = Mapping.get_attributes_for_any(obj)
172         SOAPElement.from_objs(any).each do |child|
173           ele.add(child)
174         end
175       elsif obj.is_a?(::Array) and definition.as_array?
176         obj.each do |item|
177           ele.add(definedobj2soap(item, definition))
178         end
179       else
180         child = Mapping.get_attribute(obj, definition.varname)
181         if child.nil? and (is_choice or definition.minoccurs == 0)
182           added = false
183         else
184           if child.is_a?(::Array) and definition.as_array?
185             if child.empty?
186               added = false
187             else
188               child.each do |item|
189                 ele.add(definedobj2soap(item, definition))
190               end
191             end
192           else
193             ele.add(definedobj2soap(child, definition))
194           end
195         end
196       end
197     end
198     added
199   end
200
201   def mappingobj2soap(obj, qname)
202     ele = SOAPElement.new(qname)
203     obj.__xmlele.each do |key, value|
204       if value.is_a?(::Array)
205         value.each do |item|
206           ele.add(obj2soap(item, key))
207         end
208       else
209         ele.add(obj2soap(value, key))
210       end
211     end
212     obj.__xmlattr.each do |key, value|
213       ele.extraattr[key] = value
214     end
215     ele
216   end
217
218   def any2obj(node, obj_class = nil)
219     is_compound = node.is_a?(::SOAP::SOAPCompoundtype)
220     # trust xsi:type first
221     if is_compound and node.type
222       definition = schema_definition_from_type(node.type)
223     end
224     # element name next
225     definition ||= schema_definition_from_elename(node.elename)
226     # class defined in parent type last
227     if obj_class
228       definition ||= schema_definition_from_class(obj_class)
229     end
230     if definition
231       obj_class = definition.class_for
232     end
233     if is_compound
234       if definition
235         return elesoap2stubobj(node, obj_class, definition)
236       elsif node.is_a?(::SOAP::SOAPNameAccessible)
237         return elesoap2plainobj(node)
238       end
239     end
240     obj = Mapping.soap2obj(node, nil, obj_class, MAPPING_OPT)
241     add_attributes2obj(node, obj)
242     obj
243   end
244
245   def elesoap2stubobj(node, obj_class, definition)
246     obj = nil
247     if obj_class == ::String
248       obj = node.text
249     elsif obj_class < ::String and node.respond_to?(:text)
250       obj = obj_class.new(node.text)
251     else
252       obj = Mapping.create_empty_object(obj_class)
253       add_elesoap2stubobj(node, obj, definition)
254     end
255     add_attributes2stubobj(node, obj, definition)
256     obj
257   end
258
259   def elesoap2plainobj(node)
260     obj = nil
261     if !node.have_member
262       obj = base2obj(node, ::SOAP::SOAPString)
263     else
264       obj = anytype2obj(node)
265       add_elesoap2plainobj(node, obj)
266     end
267     add_attributes2obj(node, obj)
268     obj
269   end
270
271   def anytype2obj(node)
272     if node.is_a?(::SOAP::SOAPBasetype)
273       return node.data
274     end
275     ::SOAP::Mapping::Object.new
276   end
277
278   def add_elesoap2stubobj(node, obj, definition)
279     vars = {}
280     node.each do |name, value|
281       item = definition.elements.find_element(value.elename)
282       if item
283         child = elesoapchild2obj(value, item)
284       else
285         # unknown element is treated as anyType.
286         child = any2obj(value)
287       end
288       if item and item.as_array?
289         (vars[name] ||= []) << child
290       elsif vars.key?(name)
291         vars[name] = [vars[name], child].flatten
292       else
293         vars[name] = child
294       end
295     end
296     if obj.is_a?(::Array) and is_stubobj_elements_for_array(vars)
297       Array.instance_method(:replace).bind(obj).call(vars.values[0])
298     else
299       Mapping.set_attributes(obj, vars)
300     end
301   end
302
303   def elesoapchild2obj(value, eledef)
304     if eledef.mapped_class
305       if eledef.mapped_class.include?(::SOAP::SOAPBasetype)
306         base2obj(value, eledef.mapped_class)
307       else
308         any2obj(value, eledef.mapped_class)
309       end
310     else
311       child_definition = schema_definition_from_elename(eledef.elename)
312       if child_definition
313         any2obj(value, child_definition.class_for)
314       else
315         # untyped element is treated as anyType.
316         any2obj(value)
317       end
318     end
319   end
320
321   def add_attributes2stubobj(node, obj, definition)
322     return if obj.nil? or node.extraattr.empty?
323     if attributes = definition.attributes
324       define_xmlattr(obj)
325       attributes.each do |qname, class_name|
326         child = node.extraattr[qname]
327         next if child.nil?
328         if class_name
329           klass = Mapping.class_from_name(class_name)
330           if klass.include?(::SOAP::SOAPBasetype)
331             child = klass.to_data(child)
332           end
333         end
334         obj.__xmlattr[qname] = child
335         define_xmlattr_accessor(obj, qname)
336       end
337     end
338   end
339
340   def add_elesoap2plainobj(node, obj)
341     node.each do |name, value|
342       obj.__add_xmlele_value(value.elename, any2obj(value))
343     end
344   end
345
346   def add_attributes2obj(node, obj)
347     return if obj.nil? or node.extraattr.empty?
348     define_xmlattr(obj)
349     node.extraattr.each do |qname, value|
350       obj.__xmlattr[qname] = value
351       define_xmlattr_accessor(obj, qname)
352     end
353   end
354
355   # Mapping.define_attr_accessor calls define_method with proc and it exhausts
356   # much memory for each singleton Object.  just instance_eval instead of it.
357   def define_xmlattr_accessor(obj, qname)
358     # untaint depends GenSupport.safemethodname
359     name = Mapping.safemethodname('xmlattr_' + qname.name).untaint
360     unless obj.respond_to?(name)
361       # untaint depends QName#dump
362       qnamedump = qname.dump.untaint
363       obj.instance_eval <<-EOS
364         def #{name}
365           @__xmlattr[#{qnamedump}]
366         end
367
368         def #{name}=(value)
369           @__xmlattr[#{qnamedump}] = value
370         end
371       EOS
372     end
373   end
374
375   # Mapping.define_attr_accessor calls define_method with proc and it exhausts
376   # much memory for each singleton Object.  just instance_eval instead of it.
377   def define_xmlattr(obj)
378     obj.instance_variable_set('@__xmlattr', {})
379     unless obj.respond_to?(:__xmlattr)
380       obj.instance_eval <<-EOS
381         def __xmlattr
382           @__xmlattr
383         end
384       EOS
385     end
386   end
387 end
388
389
390 end
391 end
Note: See TracBrowser for help on using the browser.