PageRenderTime 4993ms CodeModel.GetById 4919ms app.highlight 71ms RepoModel.GetById 1ms app.codeStats 0ms

/tools/Ruby/lib/ruby/1.8/soap/mapping/wsdlliteralregistry.rb

http://github.com/agross/netopenspace
Ruby | 418 lines | 364 code | 37 blank | 17 comment | 38 complexity | 75531d025e845e746c7c804a03df4709 MD5 | raw file
  1# SOAP4R - WSDL literal mapping registry.
  2# Copyright (C) 2004, 2005  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
  9require 'soap/baseData'
 10require 'soap/mapping/mapping'
 11require 'soap/mapping/typeMap'
 12require 'xsd/codegen/gensupport'
 13require 'xsd/namedelements'
 14
 15
 16module SOAP
 17module Mapping
 18
 19
 20class WSDLLiteralRegistry < Registry
 21  attr_reader :definedelements
 22  attr_reader :definedtypes
 23  attr_accessor :excn_handler_obj2soap
 24  attr_accessor :excn_handler_soap2obj
 25
 26  def initialize(definedtypes = XSD::NamedElements::Empty,
 27      definedelements = XSD::NamedElements::Empty)
 28    @definedtypes = definedtypes
 29    @definedelements = definedelements
 30    @excn_handler_obj2soap = nil
 31    @excn_handler_soap2obj = nil
 32    @schema_element_cache = {}
 33    @schema_attribute_cache = {}
 34  end
 35
 36  def obj2soap(obj, qname)
 37    soap_obj = nil
 38    if ele = @definedelements[qname]
 39      soap_obj = obj2elesoap(obj, ele)
 40    elsif type = @definedtypes[qname]
 41      soap_obj = obj2typesoap(obj, type, true)
 42    else
 43      soap_obj = any2soap(obj, qname)
 44    end
 45    return soap_obj if soap_obj
 46    if @excn_handler_obj2soap
 47      soap_obj = @excn_handler_obj2soap.call(obj) { |yield_obj|
 48        Mapping.obj2soap(yield_obj, nil, nil, MAPPING_OPT)
 49      }
 50      return soap_obj if soap_obj
 51    end
 52    raise MappingError.new("cannot map #{obj.class.name} as #{qname}")
 53  end
 54
 55  # node should be a SOAPElement
 56  def soap2obj(node, obj_class = nil)
 57    # obj_class is given when rpc/literal service.  but ignored for now.
 58    begin
 59      return any2obj(node)
 60    rescue MappingError
 61    end
 62    if @excn_handler_soap2obj
 63      begin
 64        return @excn_handler_soap2obj.call(node) { |yield_node|
 65	    Mapping.soap2obj(yield_node, nil, nil, MAPPING_OPT)
 66	  }
 67      rescue Exception
 68      end
 69    end
 70    if node.respond_to?(:type)
 71      raise MappingError.new("cannot map #{node.type.name} to Ruby object")
 72    else
 73      raise MappingError.new("cannot map #{node.elename.name} to Ruby object")
 74    end
 75  end
 76
 77private
 78
 79  MAPPING_OPT = { :no_reference => true }
 80
 81  def obj2elesoap(obj, ele)
 82    o = nil
 83    qualified = (ele.elementform == 'qualified')
 84    if ele.type
 85      if type = @definedtypes[ele.type]
 86        o = obj2typesoap(obj, type, qualified)
 87      elsif type = TypeMap[ele.type]
 88        o = base2soap(obj, type)
 89      else
 90        raise MappingError.new("cannot find type #{ele.type}")
 91      end
 92    elsif ele.local_complextype
 93      o = obj2typesoap(obj, ele.local_complextype, qualified)
 94      add_attributes2soap(obj, o)
 95    elsif ele.local_simpletype
 96      o = obj2typesoap(obj, ele.local_simpletype, qualified)
 97    else
 98      raise MappingError.new('illegal schema?')
 99    end
100    o.elename = ele.name
101    o
102  end
103
104  def obj2typesoap(obj, type, qualified)
105    if type.is_a?(::WSDL::XMLSchema::SimpleType)
106      simpleobj2soap(obj, type)
107    else
108      complexobj2soap(obj, type, qualified)
109    end
110  end
111
112  def simpleobj2soap(obj, type)
113    type.check_lexical_format(obj)
114    return SOAPNil.new if obj.nil?      # ToDo: check nillable.
115    o = base2soap(obj, TypeMap[type.base])
116    o
117  end
118
119  def complexobj2soap(obj, type, qualified)
120    o = SOAPElement.new(type.name)
121    o.qualified = qualified
122    type.each_element do |child_ele|
123      child = Mapping.get_attribute(obj, child_ele.name.name)
124      if child.nil?
125        if child_ele.nillable
126          # ToDo: test
127          # add empty element
128          child_soap = obj2elesoap(nil, child_ele)
129          o.add(child_soap)
130        elsif Integer(child_ele.minoccurs) == 0
131          # nothing to do
132        else
133          raise MappingError.new("nil not allowed: #{child_ele.name.name}")
134        end
135      elsif child_ele.map_as_array?
136        child.each do |item|
137          child_soap = obj2elesoap(item, child_ele)
138          o.add(child_soap)
139        end
140      else
141        child_soap = obj2elesoap(child, child_ele)
142        o.add(child_soap)
143      end
144    end
145    o
146  end
147
148  def any2soap(obj, qname)
149    if obj.is_a?(SOAPElement)
150      obj
151    elsif obj.class.class_variables.include?('@@schema_element')
152      stubobj2soap(obj, qname)
153    elsif obj.is_a?(SOAP::Mapping::Object)
154      mappingobj2soap(obj, qname)
155    elsif obj.is_a?(Hash)
156      ele = SOAPElement.from_obj(obj)
157      ele.elename = qname
158      ele
159    else
160      # expected to be a basetype or an anyType.
161      # SOAPStruct, etc. is used instead of SOAPElement.
162      begin
163        ele = Mapping.obj2soap(obj, nil, nil, MAPPING_OPT)
164        ele.elename = qname
165        ele
166      rescue MappingError
167        ele = SOAPElement.new(qname, obj.to_s)
168      end
169      if obj.respond_to?(:__xmlattr)
170        obj.__xmlattr.each do |key, value|
171          ele.extraattr[key] = value
172        end
173      end
174      ele
175    end
176  end
177
178  def stubobj2soap(obj, qname)
179    ele = SOAPElement.new(qname)
180    ele.qualified =
181      (obj.class.class_variables.include?('@@schema_qualified') and
182      obj.class.class_eval('@@schema_qualified'))
183    add_elements2soap(obj, ele)
184    add_attributes2soap(obj, ele)
185    ele
186  end
187
188  def mappingobj2soap(obj, qname)
189    ele = SOAPElement.new(qname)
190    obj.__xmlele.each do |key, value|
191      if value.is_a?(::Array)
192        value.each do |item|
193          ele.add(obj2soap(item, key))
194        end
195      else
196        ele.add(obj2soap(value, key))
197      end
198    end
199    obj.__xmlattr.each do |key, value|
200      ele.extraattr[key] = value
201    end
202    ele
203  end
204
205  def add_elements2soap(obj, ele)
206    elements, as_array = schema_element_definition(obj.class)
207    if elements
208      elements.each do |elename, type|
209        if child = Mapping.get_attribute(obj, elename.name)
210          if as_array.include?(elename.name)
211            child.each do |item|
212              ele.add(obj2soap(item, elename))
213            end
214          else
215            ele.add(obj2soap(child, elename))
216          end
217        elsif obj.is_a?(::Array) and as_array.include?(elename.name)
218          obj.each do |item|
219            ele.add(obj2soap(item, elename))
220          end
221        end
222      end
223    end
224  end
225  
226  def add_attributes2soap(obj, ele)
227    attributes = schema_attribute_definition(obj.class)
228    if attributes
229      attributes.each do |qname, param|
230        attr = obj.__send__('xmlattr_' +
231          XSD::CodeGen::GenSupport.safevarname(qname.name))
232        ele.extraattr[qname] = attr
233      end
234    end
235  end
236
237  def base2soap(obj, type)
238    soap_obj = nil
239    if type <= XSD::XSDString
240      str = XSD::Charset.encoding_conv(obj.to_s,
241        Thread.current[:SOAPExternalCES], XSD::Charset.encoding)
242      soap_obj = type.new(str)
243    else
244      soap_obj = type.new(obj)
245    end
246    soap_obj
247  end
248
249  def anytype2obj(node)
250    if node.is_a?(::SOAP::SOAPBasetype)
251      return node.data
252    end
253    klass = ::SOAP::Mapping::Object
254    obj = klass.new
255    obj
256  end
257
258  def any2obj(node, obj_class = nil)
259    unless obj_class
260      typestr = XSD::CodeGen::GenSupport.safeconstname(node.elename.name)
261      obj_class = Mapping.class_from_name(typestr)
262    end
263    if obj_class and obj_class.class_variables.include?('@@schema_element')
264      soapele2stubobj(node, obj_class)
265    elsif node.is_a?(SOAPElement) or node.is_a?(SOAPStruct)
266        # SOAPArray for literal?
267      soapele2plainobj(node)
268    else
269      obj = Mapping.soap2obj(node, nil, obj_class, MAPPING_OPT)
270      add_attributes2plainobj(node, obj)
271      obj
272    end
273  end
274
275  def soapele2stubobj(node, obj_class)
276    obj = Mapping.create_empty_object(obj_class)
277    add_elements2stubobj(node, obj)
278    add_attributes2stubobj(node, obj)
279    obj
280  end
281
282  def soapele2plainobj(node)
283    obj = anytype2obj(node)
284    add_elements2plainobj(node, obj)
285    add_attributes2plainobj(node, obj)
286    obj
287  end
288
289  def add_elements2stubobj(node, obj)
290    elements, as_array = schema_element_definition(obj.class)
291    vars = {}
292    node.each do |name, value|
293      item = elements.find { |k, v| k.name == name }
294      if item
295        elename, class_name = item
296        if klass = Mapping.class_from_name(class_name)
297          # klass must be a SOAPBasetype or a class
298          if klass.ancestors.include?(::SOAP::SOAPBasetype)
299            if value.respond_to?(:data)
300              child = klass.new(value.data).data
301            else
302              child = klass.new(nil).data
303            end
304          else
305            child = any2obj(value, klass)
306          end
307        elsif klass = Mapping.module_from_name(class_name)
308          # simpletype
309          if value.respond_to?(:data)
310            child = value.data
311          else
312            raise MappingError.new(
313              "cannot map to a module value: #{class_name}")
314          end
315        else
316          raise MappingError.new("unknown class/module: #{class_name}")
317        end
318      else      # untyped element is treated as anyType.
319        child = any2obj(value)
320      end
321      if as_array.include?(elename.name)
322        (vars[name] ||= []) << child
323      else
324        vars[name] = child
325      end
326    end
327    Mapping.set_attributes(obj, vars)
328  end
329
330  def add_attributes2stubobj(node, obj)
331    if attributes = schema_attribute_definition(obj.class)
332      define_xmlattr(obj)
333      attributes.each do |qname, class_name|
334        attr = node.extraattr[qname]
335        next if attr.nil? or attr.empty?
336        klass = Mapping.class_from_name(class_name)
337        if klass.ancestors.include?(::SOAP::SOAPBasetype)
338          child = klass.new(attr).data
339        else
340          child = attr
341        end
342        obj.__xmlattr[qname] = child
343        define_xmlattr_accessor(obj, qname)
344      end
345    end
346  end
347
348  def add_elements2plainobj(node, obj)
349    node.each do |name, value|
350      obj.__add_xmlele_value(value.elename, any2obj(value))
351    end
352  end
353
354  def add_attributes2plainobj(node, obj)
355    return if node.extraattr.empty?
356    define_xmlattr(obj)
357    node.extraattr.each do |qname, value|
358      obj.__xmlattr[qname] = value
359      define_xmlattr_accessor(obj, qname)
360    end
361  end
362
363  if RUBY_VERSION > "1.7.0"
364    def define_xmlattr_accessor(obj, qname)
365      name = XSD::CodeGen::GenSupport.safemethodname(qname.name)
366      Mapping.define_attr_accessor(obj, 'xmlattr_' + name,
367        proc { @__xmlattr[qname] },
368        proc { |value| @__xmlattr[qname] = value })
369    end
370  else
371    def define_xmlattr_accessor(obj, qname)
372      name = XSD::CodeGen::GenSupport.safemethodname(qname.name)
373      obj.instance_eval <<-EOS
374        def #{name}
375          @__xmlattr[#{qname.dump}]
376        end
377
378        def #{name}=(value)
379          @__xmlattr[#{qname.dump}] = value
380        end
381      EOS
382    end
383  end
384
385  if RUBY_VERSION > "1.7.0"
386    def define_xmlattr(obj)
387      obj.instance_variable_set('@__xmlattr', {})
388      unless obj.respond_to?(:__xmlattr)
389        Mapping.define_attr_accessor(obj, :__xmlattr, proc { @__xmlattr })
390      end
391    end
392  else
393    def define_xmlattr(obj)
394      obj.instance_variable_set('@__xmlattr', {})
395      unless obj.respond_to?(:__xmlattr)
396        obj.instance_eval <<-EOS
397          def __xmlattr
398            @__xmlattr
399          end
400        EOS
401      end
402    end
403  end
404
405  # it caches @@schema_element.  this means that @@schema_element must not be
406  # changed while a lifetime of a WSDLLiteralRegistry.
407  def schema_element_definition(klass)
408    @schema_element_cache[klass] ||= Mapping.schema_element_definition(klass)
409  end
410
411  def schema_attribute_definition(klass)
412    @schema_attribute_cache[klass] ||= Mapping.schema_attribute_definition(klass)
413  end
414end
415
416
417end
418end