diff options
Diffstat (limited to 'lib/soap/mapping')
-rw-r--r-- | lib/soap/mapping/factory.rb | 79 | ||||
-rw-r--r-- | lib/soap/mapping/mapping.rb | 56 | ||||
-rw-r--r-- | lib/soap/mapping/registry.rb | 162 | ||||
-rw-r--r-- | lib/soap/mapping/rubytypeFactory.rb | 18 | ||||
-rw-r--r-- | lib/soap/mapping/typeMap.rb | 9 | ||||
-rw-r--r-- | lib/soap/mapping/wsdlencodedregistry.rb | 173 | ||||
-rw-r--r-- | lib/soap/mapping/wsdlliteralregistry.rb | 281 |
7 files changed, 642 insertions, 136 deletions
diff --git a/lib/soap/mapping/factory.rb b/lib/soap/mapping/factory.rb index 6b9ac1eeaa..8e82ae5efd 100644 --- a/lib/soap/mapping/factory.rb +++ b/lib/soap/mapping/factory.rb @@ -27,39 +27,6 @@ class Factory # return convert_succeeded_or_not, obj end - if Object.respond_to?(:allocate) - # ruby/1.7 or later. - def create_empty_object(klass) - klass.allocate - end - else - MARSHAL_TAG = { - String => ['"', 1], - Regexp => ['/', 2], - Array => ['[', 1], - Hash => ['{', 1] - } - def create_empty_object(klass) - if klass <= Struct - name = klass.name - return ::Marshal.load(sprintf("\004\006S:%c%s\000", name.length + 5, name)) - end - if MARSHAL_TAG.has_key?(klass) - tag, terminate = MARSHAL_TAG[klass] - return ::Marshal.load(sprintf("\004\006%s%s", tag, "\000" * terminate)) - end - MARSHAL_TAG.each do |k, v| - if klass < k - name = klass.name - tag, terminate = v - return ::Marshal.load(sprintf("\004\006C:%c%s%s%s", name.length + 5, name, tag, "\000" * terminate)) - end - end - name = klass.name - ::Marshal.load(sprintf("\004\006o:%c%s\000", name.length + 5, name)) - end - end - def setiv2obj(obj, node, map) return if node.nil? if obj.is_a?(Array) @@ -129,7 +96,7 @@ class StringFactory_ < Factory end def soap2obj(obj_class, node, info, map) - obj = create_empty_object(obj_class) + obj = Mapping.create_empty_object(obj_class) decoded = XSD::Charset.encoding_conv(node.data, XSD::Charset.encoding, $KCODE) obj.replace(decoded) mark_unmarshalled_obj(node, obj) @@ -253,16 +220,16 @@ class ArrayFactory_ < Factory else arytype = XSD::AnyTypeName end - param = SOAPArray.new(ValueArrayName, 1, arytype) - mark_marshalled_obj(obj, param) - obj.each do |var| - param.add(Mapping._obj2soap(var, map)) + soap_obj = SOAPArray.new(ValueArrayName, 1, arytype) + mark_marshalled_obj(obj, soap_obj) + obj.each do |item| + soap_obj.add(Mapping._obj2soap(item, map)) end - param + soap_obj end def soap2obj(obj_class, node, info, map) - obj = create_empty_object(obj_class) + obj = Mapping.create_empty_object(obj_class) mark_unmarshalled_obj(node, obj) node.soap2array(obj) do |elem| elem ? Mapping._soap2obj(elem, map) : nil @@ -282,12 +249,12 @@ class TypedArrayFactory_ < Factory return nil end arytype = info[:type] || info[0] - param = SOAPArray.new(ValueArrayName, 1, arytype) - mark_marshalled_obj(obj, param) + soap_obj = SOAPArray.new(ValueArrayName, 1, arytype) + mark_marshalled_obj(obj, soap_obj) obj.each do |var| - param.add(Mapping._obj2soap(var, map)) + soap_obj.add(Mapping._obj2soap(var, map)) end - param + soap_obj end def soap2obj(obj_class, node, info, map) @@ -298,7 +265,7 @@ class TypedArrayFactory_ < Factory unless node.arytype == arytype return false end - obj = create_empty_object(obj_class) + obj = Mapping.create_empty_object(obj_class) mark_unmarshalled_obj(node, obj) node.soap2array(obj) do |elem| elem ? Mapping._soap2obj(elem, map) : nil @@ -310,14 +277,14 @@ end class TypedStructFactory_ < Factory def obj2soap(soap_class, obj, info, map) type = info[:type] || info[0] - param = soap_class.new(type) - mark_marshalled_obj(obj, param) + soap_obj = soap_class.new(type) + mark_marshalled_obj(obj, soap_obj) if obj.class <= SOAP::Marshallable - setiv2soap(param, obj, map) + setiv2soap(soap_obj, obj, map) else - setiv2soap(param, obj, map) + setiv2soap(soap_obj, obj, map) end - param + soap_obj end def soap2obj(obj_class, node, info, map) @@ -325,7 +292,7 @@ class TypedStructFactory_ < Factory unless node.type == type return false end - obj = create_empty_object(obj_class) + obj = Mapping.create_empty_object(obj_class) mark_unmarshalled_obj(node, obj) setiv2obj(obj, node, map) return true, obj @@ -347,16 +314,16 @@ class HashFactory_ < Factory (obj.respond_to?(:default_proc) and obj.default_proc) return nil end - param = SOAPStruct.new(MapQName) - mark_marshalled_obj(obj, param) + soap_obj = SOAPStruct.new(MapQName) + mark_marshalled_obj(obj, soap_obj) obj.each do |key, value| elem = SOAPStruct.new elem.add("key", Mapping._obj2soap(key, map)) elem.add("value", Mapping._obj2soap(value, map)) # ApacheAxis allows only 'item' here. - param.add("item", elem) + soap_obj.add("item", elem) end - param + soap_obj end def soap2obj(obj_class, node, info, map) @@ -366,7 +333,7 @@ class HashFactory_ < Factory if node.class == SOAPStruct and node.key?('default') return false end - obj = create_empty_object(obj_class) + obj = Mapping.create_empty_object(obj_class) mark_unmarshalled_obj(node, obj) if node.class == SOAPStruct node.each do |key, value| diff --git a/lib/soap/mapping/mapping.rb b/lib/soap/mapping/mapping.rb index db7ea607fd..17a0d3bcf4 100644 --- a/lib/soap/mapping/mapping.rb +++ b/lib/soap/mapping/mapping.rb @@ -1,11 +1,14 @@ # SOAP4R - Ruby type mapping utility. -# Copyright (C) 2000, 2001, 2003 NAKAMURA Hiroshi <[email protected]>. +# Copyright (C) 2000, 2001, 2003, 2004 NAKAMURA Hiroshi <[email protected]>. # This program is copyrighted free software by NAKAMURA, Hiroshi. You can # redistribute it and/or modify it under the same terms of Ruby's license; # either the dual license version in 2003, or any later version. +require 'xsd/codegen/gensupport' + + module SOAP @@ -101,8 +104,10 @@ module Mapping def self._obj2soap(obj, registry, type = nil) if referent = Thread.current[:SOAPMarshalDataKey][obj.__id__] SOAPReference.new(referent) + elsif registry + registry.obj2soap(obj, type) else - registry.obj2soap(obj.class, obj, type) + raise MappingError.new("No mapping registry given.") end end @@ -116,9 +121,41 @@ module Mapping return _soap2obj(target, registry) end end - return registry.soap2obj(node.class, node) + return registry.soap2obj(node) end + if Object.respond_to?(:allocate) + # ruby/1.7 or later. + def self.create_empty_object(klass) + klass.allocate + end + else + MARSHAL_TAG = { + String => ['"', 1], + Regexp => ['/', 2], + Array => ['[', 1], + Hash => ['{', 1] + } + def self.create_empty_object(klass) + if klass <= Struct + name = klass.name + return ::Marshal.load(sprintf("\004\006S:%c%s\000", name.length + 5, name)) + end + if MARSHAL_TAG.has_key?(klass) + tag, terminate = MARSHAL_TAG[klass] + return ::Marshal.load(sprintf("\004\006%s%s", tag, "\000" * terminate)) + end + MARSHAL_TAG.each do |k, v| + if klass < k + name = klass.name + tag, terminate = v + return ::Marshal.load(sprintf("\004\006C:%c%s%s%s", name.length + 5, name, tag, "\000" * terminate)) + end + end + name = klass.name + ::Marshal.load(sprintf("\004\006o:%c%s\000", name.length + 5, name)) + end + end def self.set_instance_vars(obj, values) values.each do |name, value| setter = name + "=" @@ -200,6 +237,19 @@ module Mapping end end + def self.find_attribute(obj, attr_name) + if obj.is_a?(::Hash) + obj[attr_name] || obj[attr_name.intern] + else + name = ::XSD::CodeGen::GenSupport.safevarname(attr_name) + if obj.respond_to?(name) + obj.__send__(name) + else + obj.instance_eval("@#{name}") + end + end + end + class << Mapping private def add_md_ary(md_ary, ary, indices, registry) diff --git a/lib/soap/mapping/registry.rb b/lib/soap/mapping/registry.rb index 1317d40cd6..24cd57fa71 100644 --- a/lib/soap/mapping/registry.rb +++ b/lib/soap/mapping/registry.rb @@ -64,12 +64,12 @@ end # For anyType object: SOAP::Mapping::Object not ::Object class Object; include Marshallable def initialize - @__members = [] - @__value_type = {} + @__soap_members = [] + @__soap_value_type = {} end def [](name) - if @__members.include?(name) + if @__soap_members.include?(name) self.__send__(name) else self.__send__(Object.safe_name(name)) @@ -77,39 +77,35 @@ class Object; include Marshallable end def []=(name, value) - if @__members.include?(name) + if @__soap_members.include?(name) self.__send__(name + '=', value) else self.__send__(Object.safe_name(name) + '=', value) end end - def __set_property(name, value) + def __soap_set_property(name, value) var_name = name - unless @__members.include?(name) + unless @__soap_members.include?(name) var_name = __define_attr_accessor(var_name) end - __set_property_value(var_name, value) + __soap_set_property_value(var_name, value) var_name end - def __members - @__members - end - private - def __set_property_value(name, value) + def __soap_set_property_value(name, value) org = self.__send__(name) - case @__value_type[name] + case @__soap_value_type[name] when :single self.__send__(name + '=', [org, value]) - @__value_type[name] = :multi + @__soap_value_type[name] = :multi when :multi org << value else self.__send__(name + '=', value) - @__value_type[name] = :single + @__soap_value_type[name] = :single end value end @@ -130,7 +126,7 @@ private var_name = Object.safe_name(var_name) retry end - @__members << var_name + @__soap_members << var_name var_name end @@ -147,30 +143,42 @@ class MappingError < Error; end class Registry class Map def initialize(registry) - @map = [] + @obj2soap = {} + @soap2obj = {} @registry = registry end - def obj2soap(klass, obj, type_qname = nil) - @map.each do |obj_class, soap_class, factory, info| - if klass == obj_class or - (info[:derived_class] and klass <= obj_class) + def obj2soap(obj, type_qname = nil) + klass = obj.class + if map = @obj2soap[klass] + map.each do |soap_class, factory, info| ret = factory.obj2soap(soap_class, obj, info, @registry) return ret if ret end end + ancestors = klass.ancestors[1..-3] # except itself, Object and Kernel + ancestors.each do |klass| + if map = @obj2soap[klass] + map.each do |soap_class, factory, info| + if info[:derived_class] + ret = factory.obj2soap(soap_class, obj, info, @registry) + return ret if ret + end + end + end + end nil end - def soap2obj(klass, node) - @map.each do |obj_class, soap_class, factory, info| - if klass == soap_class or - (info[:derived_class] and klass <= soap_class) + def soap2obj(node) + klass = node.class + if map = @soap2obj[klass] + map.each do |obj_class, factory, info| conv, obj = factory.soap2obj(obj_class, node, info, @registry) return true, obj if conv end end - return false + return false, nil end # Give priority to former entry. @@ -184,29 +192,21 @@ class Registry # Give priority to latter entry. def add(obj_class, soap_class, factory, info) info ||= {} - @map.unshift([obj_class, soap_class, factory, info]) + (@obj2soap[obj_class] ||= []).unshift([soap_class, factory, info]) + (@soap2obj[soap_class] ||= []).unshift([obj_class, factory, info]) end def clear - @map.clear + @obj2soap.clear + @soap2obj.clear end def find_mapped_soap_class(target_obj_class) - @map.each do |obj_class, soap_class, factory, info| - if obj_class == target_obj_class - return soap_class - end - end - nil + @obj2soap[target_obj_class][0] end def find_mapped_obj_class(target_soap_class) - @map.each do |obj_class, soap_class, factory, info| - if soap_class == target_soap_class - return obj_class - end - end - nil + @soap2obj[target_soap_class][0] end end @@ -243,6 +243,24 @@ class Registry {:derived_class => true}], [::Integer, ::SOAP::SOAPShort, BasetypeFactory, {:derived_class => true}], + [::Integer, ::SOAP::SOAPByte, BasetypeFactory, + {:derived_class => true}], + [::Integer, ::SOAP::SOAPNonPositiveInteger, BasetypeFactory, + {:derived_class => true}], + [::Integer, ::SOAP::SOAPNegativeInteger, BasetypeFactory, + {:derived_class => true}], + [::Integer, ::SOAP::SOAPNonNegativeInteger, BasetypeFactory, + {:derived_class => true}], + [::Integer, ::SOAP::SOAPPositiveInteger, BasetypeFactory, + {:derived_class => true}], + [::Integer, ::SOAP::SOAPUnsignedLong, BasetypeFactory, + {:derived_class => true}], + [::Integer, ::SOAP::SOAPUnsignedInt, BasetypeFactory, + {:derived_class => true}], + [::Integer, ::SOAP::SOAPUnsignedShort, BasetypeFactory, + {:derived_class => true}], + [::Integer, ::SOAP::SOAPUnsignedByte, BasetypeFactory, + {:derived_class => true}], [::URI::Generic, ::SOAP::SOAPAnyURI, URIFactory, {:derived_class => true}], [::String, ::SOAP::SOAPBase64, Base64Factory], @@ -289,6 +307,24 @@ class Registry {:derived_class => true}], [::Integer, ::SOAP::SOAPShort, BasetypeFactory, {:derived_class => true}], + [::Integer, ::SOAP::SOAPByte, BasetypeFactory, + {:derived_class => true}], + [::Integer, ::SOAP::SOAPNonPositiveInteger, BasetypeFactory, + {:derived_class => true}], + [::Integer, ::SOAP::SOAPNegativeInteger, BasetypeFactory, + {:derived_class => true}], + [::Integer, ::SOAP::SOAPNonNegativeInteger, BasetypeFactory, + {:derived_class => true}], + [::Integer, ::SOAP::SOAPPositiveInteger, BasetypeFactory, + {:derived_class => true}], + [::Integer, ::SOAP::SOAPUnsignedLong, BasetypeFactory, + {:derived_class => true}], + [::Integer, ::SOAP::SOAPUnsignedInt, BasetypeFactory, + {:derived_class => true}], + [::Integer, ::SOAP::SOAPUnsignedShort, BasetypeFactory, + {:derived_class => true}], + [::Integer, ::SOAP::SOAPUnsignedByte, BasetypeFactory, + {:derived_class => true}], [::URI::Generic, ::SOAP::SOAPAnyURI, URIFactory, {:derived_class => true}], [::String, ::SOAP::SOAPBase64, Base64Factory], @@ -313,6 +349,10 @@ class Registry {:type => XSD::QName.new(RubyCustomTypeNamespace, "SOAPException")}], ] + attr_accessor :default_factory + attr_accessor :excn_handler_obj2soap + attr_accessor :excn_handler_soap2obj + def initialize(config = {}) @config = config @map = Map.new(self) @@ -323,7 +363,6 @@ class Registry @allow_original_mapping = false @map.init(SOAPBaseMap) end - @allow_untyped_struct = @config.key?(:allow_untyped_struct) ? @config[:allow_untyped_struct] : true @rubytype_factory = RubytypeFactory.new( @@ -341,16 +380,16 @@ class Registry alias set add # This mapping registry ignores type hint. - def obj2soap(klass, obj, type_qname = nil) - soap = _obj2soap(klass, obj, type_qname) + def obj2soap(obj, type_qname = nil) + soap = _obj2soap(obj, type_qname) if @allow_original_mapping addextend2soap(soap, obj) end soap end - def soap2obj(klass, node) - obj = _soap2obj(klass, node) + def soap2obj(node) + obj = _soap2obj(node) if @allow_original_mapping addextend2obj(obj, node.extraattr[RubyExtendName]) addiv2obj(obj, node.extraattr[RubyIVarName]) @@ -358,18 +397,6 @@ class Registry obj end - def default_factory=(factory) - @default_factory = factory - end - - def excn_handler_obj2soap=(handler) - @excn_handler_obj2soap = handler - end - - def excn_handler_soap2obj=(handler) - @excn_handler_soap2obj = handler - end - def find_mapped_soap_class(obj_class) @map.find_mapped_soap_class(obj_class) end @@ -380,7 +407,7 @@ class Registry private - def _obj2soap(klass, obj, type_qname) + def _obj2soap(obj, type_qname) ret = nil if obj.is_a?(SOAPStruct) or obj.is_a?(SOAPArray) obj.replace do |ele| @@ -391,32 +418,31 @@ private return obj end begin - ret = @map.obj2soap(klass, obj, type_qname) || - @default_factory.obj2soap(klass, obj, nil, self) + ret = @map.obj2soap(obj, type_qname) || + @default_factory.obj2soap(nil, obj, nil, self) + return ret if ret rescue MappingError end - return ret if ret if @excn_handler_obj2soap ret = @excn_handler_obj2soap.call(obj) { |yield_obj| Mapping._obj2soap(yield_obj, self) } + return ret if ret end - return ret if ret - raise MappingError.new("Cannot map #{ klass.name } to SOAP/OM.") + raise MappingError.new("Cannot map #{ obj.class.name } to SOAP/OM.") end # Might return nil as a mapping result. - def _soap2obj(klass, node) + def _soap2obj(node) if node.extraattr.key?(RubyTypeName) - conv, obj = @rubytype_factory.soap2obj(klass, node, nil, self) + conv, obj = @rubytype_factory.soap2obj(nil, node, nil, self) return obj if conv else - conv, obj = @map.soap2obj(klass, node) + conv, obj = @map.soap2obj(node) return obj if conv - conv, obj = @default_factory.soap2obj(klass, node, nil, self) + conv, obj = @default_factory.soap2obj(nil, node, nil, self) return obj if conv end - if @excn_handler_soap2obj begin return @excn_handler_soap2obj.call(node) { |yield_node| diff --git a/lib/soap/mapping/rubytypeFactory.rb b/lib/soap/mapping/rubytypeFactory.rb index a46d93275f..6266148b55 100644 --- a/lib/soap/mapping/rubytypeFactory.rb +++ b/lib/soap/mapping/rubytypeFactory.rb @@ -318,7 +318,7 @@ private case node.type when TYPE_HASH klass = rubytype ? Mapping.class_from_name(rubytype) : Hash - obj = create_empty_object(klass) + obj = Mapping.create_empty_object(klass) mark_unmarshalled_obj(node, obj) node.each do |key, value| next unless key == 'item' @@ -330,14 +330,14 @@ private end when TYPE_REGEXP klass = rubytype ? Mapping.class_from_name(rubytype) : Regexp - obj = create_empty_object(klass) + obj = Mapping.create_empty_object(klass) mark_unmarshalled_obj(node, obj) source = node['source'].string options = node['options'].data || 0 Regexp.instance_method(:initialize).bind(obj).call(source, options) when TYPE_RANGE klass = rubytype ? Mapping.class_from_name(rubytype) : Range - obj = create_empty_object(klass) + obj = Mapping.create_empty_object(klass) mark_unmarshalled_obj(node, obj) first = Mapping._soap2obj(node['begin'], map) last = Mapping._soap2obj(node['end'], map) @@ -358,7 +358,7 @@ private unless klass <= ::Struct return false end - obj = create_empty_object(klass) + obj = Mapping.create_empty_object(klass) mark_unmarshalled_obj(node, obj) node['member'].each do |name, value| obj[Mapping.elename2name(name)] = Mapping._soap2obj(value, map) @@ -378,7 +378,7 @@ private obj = klass.new mark_unmarshalled_obj(node, obj) node.each do |name, value| - obj.__set_property(name, Mapping._soap2obj(value, map)) + obj.__soap_set_property(name, Mapping._soap2obj(value, map)) end return true, obj else @@ -414,7 +414,7 @@ private end klass_type = Mapping.class2qname(klass) return nil unless node.type.match(klass_type) - obj = create_empty_object(klass) + obj = Mapping.create_empty_object(klass) mark_unmarshalled_obj(node, obj) setiv2obj(obj, node, map) obj @@ -423,7 +423,7 @@ private def exception2obj(klass, node, map) message = Mapping._soap2obj(node['message'], map) backtrace = Mapping._soap2obj(node['backtrace'], map) - obj = create_empty_object(klass) + obj = Mapping.create_empty_object(klass) obj = obj.exception(message) mark_unmarshalled_obj(node, obj) obj.set_backtrace(backtrace) @@ -433,7 +433,7 @@ private # Only creates empty array. Do String#replace it with real string. def array2obj(node, map, rubytype) klass = rubytype ? Mapping.class_from_name(rubytype) : Array - obj = create_empty_object(klass) + obj = Mapping.create_empty_object(klass) mark_unmarshalled_obj(node, obj) obj end @@ -441,7 +441,7 @@ private # Only creates empty string. Do String#replace it with real string. def string2obj(node, map, rubytype) klass = rubytype ? Mapping.class_from_name(rubytype) : String - obj = create_empty_object(klass) + obj = Mapping.create_empty_object(klass) mark_unmarshalled_obj(node, obj) obj end diff --git a/lib/soap/mapping/typeMap.rb b/lib/soap/mapping/typeMap.rb index 93f24b4bd8..34db19a5b6 100644 --- a/lib/soap/mapping/typeMap.rb +++ b/lib/soap/mapping/typeMap.rb @@ -30,9 +30,18 @@ TypeMap = { XSD::XSDAnyURI::Type => SOAPAnyURI, XSD::XSDQName::Type => SOAPQName, XSD::XSDInteger::Type => SOAPInteger, + XSD::XSDNonPositiveInteger::Type => SOAPNonPositiveInteger, + XSD::XSDNegativeInteger::Type => SOAPNegativeInteger, XSD::XSDLong::Type => SOAPLong, XSD::XSDInt::Type => SOAPInt, XSD::XSDShort::Type => SOAPShort, + XSD::XSDByte::Type => SOAPByte, + XSD::XSDNonNegativeInteger::Type => SOAPNonNegativeInteger, + XSD::XSDUnsignedLong::Type => SOAPUnsignedLong, + XSD::XSDUnsignedInt::Type => SOAPUnsignedInt, + XSD::XSDUnsignedShort::Type => SOAPUnsignedShort, + XSD::XSDUnsignedByte::Type => SOAPUnsignedByte, + XSD::XSDPositiveInteger::Type => SOAPPositiveInteger, SOAP::SOAPBase64::Type => SOAPBase64, } diff --git a/lib/soap/mapping/wsdlencodedregistry.rb b/lib/soap/mapping/wsdlencodedregistry.rb new file mode 100644 index 0000000000..4433c73cf0 --- /dev/null +++ b/lib/soap/mapping/wsdlencodedregistry.rb @@ -0,0 +1,173 @@ +# SOAP4R - WSDL encoded mapping registry. +# Copyright (C) 2000, 2001, 2002, 2003 NAKAMURA, Hiroshi <[email protected]>. + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'soap/baseData' +require 'soap/mapping/mapping' +require 'soap/mapping/typeMap' + + +module SOAP +module Mapping + + +class WSDLEncodedRegistry + include TraverseSupport + + attr_reader :definedtypes + attr_accessor :excn_handler_obj2soap + + def initialize(definedtypes, config = {}) + @definedtypes = definedtypes + @config = config + @excn_handler_obj2soap = nil + # For mapping AnyType element. + @rubytype_factory = RubytypeFactory.new( + :allow_untyped_struct => true, + :allow_original_mapping => true + ) + end + + def obj2soap(obj, type_qname = nil) + soap_obj = nil + if obj.nil? + soap_obj = SOAPNil.new + elsif type_qname.nil? or type_qname == XSD::AnyTypeName + soap_obj = @rubytype_factory.obj2soap(nil, obj, nil, self) + elsif obj.is_a?(XSD::NSDBase) + soap_obj = soap2soap(obj, type_qname) + elsif type = @definedtypes[type_qname] + soap_obj = obj2type(obj, type) + elsif (type = TypeMap[type_qname]) + soap_obj = base2soap(obj, type) + end + return soap_obj if soap_obj + if @excn_handler_obj2soap + soap_obj = @excn_handler_obj2soap.call(obj) { |yield_obj| + Mapping._obj2soap(yield_obj, self) + } + return soap_obj if soap_obj + end + raise MappingError.new("Cannot map #{ obj.class.name } to SOAP/OM.") + end + + def soap2obj(node) + raise RuntimeError.new("#{ self } is for obj2soap only.") + end + +private + + def soap2soap(obj, type_qname) + if obj.is_a?(SOAPBasetype) + obj + elsif obj.is_a?(SOAPStruct) && (type = @definedtypes[type_qname]) + soap_obj = obj + mark_marshalled_obj(obj, soap_obj) + elements2soap(obj, soap_obj, type.content.elements) + soap_obj + elsif obj.is_a?(SOAPArray) && (type = @definedtypes[type_qname]) + soap_obj = obj + contenttype = type.child_type + mark_marshalled_obj(obj, soap_obj) + obj.replace do |ele| + Mapping._obj2soap(ele, self, contenttype) + end + soap_obj + else + nil + end + end + + def obj2type(obj, type) + if type.is_a?(::WSDL::XMLSchema::SimpleType) + simple2soap(obj, type) + else + complex2soap(obj, type) + end + end + + def simple2soap(obj, type) + o = base2soap(obj, TypeMap[type.base]) + if type.restriction.enumeration.empty? + STDERR.puts("#{type.name}: simpleType which is not enum type not supported.") + return o + end + type.check_lexical_format(obj) + o + end + + def complex2soap(obj, type) + case type.compoundtype + when :TYPE_STRUCT + struct2soap(obj, type.name, type) + when :TYPE_ARRAY + array2soap(obj, type.name, type) + when :TYPE_MAP + map2soap(obj, type.name, type) + else + raise MappingError.new("Unknown compound type: #{ type.compoundtype }") + end + end + + def base2soap(obj, type) + soap_obj = nil + if type <= XSD::XSDString + soap_obj = type.new(XSD::Charset.is_ces(obj, $KCODE) ? + XSD::Charset.encoding_conv(obj, $KCODE, XSD::Charset.encoding) : obj) + mark_marshalled_obj(obj, soap_obj) + else + soap_obj = type.new(obj) + end + soap_obj + end + + def struct2soap(obj, type_qname, type) + soap_obj = SOAPStruct.new(type_qname) + mark_marshalled_obj(obj, soap_obj) + elements2soap(obj, soap_obj, type.content.elements) + soap_obj + end + + def array2soap(obj, type_qname, type) + arytype = type.child_type + soap_obj = SOAPArray.new(ValueArrayName, 1, arytype) + mark_marshalled_obj(obj, soap_obj) + obj.each do |item| + soap_obj.add(Mapping._obj2soap(item, self, arytype)) + end + soap_obj + end + + MapKeyName = XSD::QName.new(nil, "key") + MapValueName = XSD::QName.new(nil, "value") + def map2soap(obj, type_qname, type) + keytype = type.child_type(MapKeyName) || XSD::AnyTypeName + valuetype = type.child_type(MapValueName) || XSD::AnyTypeName + soap_obj = SOAPStruct.new(MapQName) + mark_marshalled_obj(obj, soap_obj) + obj.each do |key, value| + elem = SOAPStruct.new + elem.add("key", Mapping._obj2soap(key, self, keytype)) + elem.add("value", Mapping._obj2soap(value, self, valuetype)) + # ApacheAxis allows only 'item' here. + soap_obj.add("item", elem) + end + soap_obj + end + + def elements2soap(obj, soap_obj, elements) + elements.each do |element| + name = element.name.name + child_obj = obj.instance_eval("@#{ name }") + soap_obj.add(name, Mapping._obj2soap(child_obj, self, element.type)) + end + end +end + + +end +end diff --git a/lib/soap/mapping/wsdlliteralregistry.rb b/lib/soap/mapping/wsdlliteralregistry.rb new file mode 100644 index 0000000000..c190c9d904 --- /dev/null +++ b/lib/soap/mapping/wsdlliteralregistry.rb @@ -0,0 +1,281 @@ +# SOAP4R - WSDL literal mapping registry. +# Copyright (C) 2004 NAKAMURA, Hiroshi <[email protected]>. + +# This program is copyrighted free software by NAKAMURA, Hiroshi. You can +# redistribute it and/or modify it under the same terms of Ruby's license; +# either the dual license version in 2003, or any later version. + + +require 'soap/baseData' +require 'soap/mapping/mapping' +require 'soap/mapping/typeMap' +require 'xsd/codegen/gensupport' + + +module SOAP +module Mapping + + +class WSDLLiteralRegistry + attr_reader :definedelements + attr_reader :definedtypes + attr_accessor :excn_handler_obj2soap + attr_accessor :excn_handler_soap2obj + + def initialize(definedelements = nil, definedtypes = nil) + @definedelements = definedelements + @definedtypes = definedtypes + @rubytype_factory = RubytypeFactory.new(:allow_original_mapping => false) + @schema_element_cache = {} + end + + def obj2soap(obj, qname) + ret = nil + if [email protected]? && ele = @definedelements[qname] + ret = _obj2soap(obj, ele) + elsif [email protected]? && type = @definedtypes[qname] + ret = obj2type(obj, type) + else + ret = unknownobj2soap(obj, qname) + end + return ret if ret + if @excn_handler_obj2soap + ret = @excn_handler_obj2soap.call(obj) { |yield_obj| + Mapping._obj2soap(yield_obj, self) + } + return ret if ret + end + raise MappingError.new("Cannot map #{ obj.class.name } to SOAP/OM.") + end + + # node should be a SOAPElement + def soap2obj(node) + begin + return soapele2obj(node) + rescue MappingError + end + if @excn_handler_soap2obj + begin + return @excn_handler_soap2obj.call(node) { |yield_node| + Mapping._soap2obj(yield_node, self) + } + rescue Exception + end + end + raise MappingError.new("Cannot map #{ node.type.name } to Ruby object.") + end + +private + + def _obj2soap(obj, ele) + o = nil + if ele.type + if type = @definedtypes[ele.type] + o = obj2type(obj, type) + elsif type = TypeMap[ele.type] + o = base2soap(obj, type) + else + raise MappingError.new("Cannot find type #{ele.type}.") + end + o.elename = ele.name + elsif ele.local_complextype + o = SOAPElement.new(ele.name) + ele.local_complextype.each_element do |child_ele| + o.add(_obj2soap(Mapping.find_attribute(obj, child_ele.name.name), + child_ele)) + end + else + raise MappingError.new('Illegal schema?') + end + o + end + + def obj2type(obj, type) + if type.is_a?(::WSDL::XMLSchema::SimpleType) + simple2soap(obj, type) + else + complex2soap(obj, type) + end + end + + def simple2soap(obj, type) + o = base2soap(obj, TypeMap[type.base]) + if type.restriction.enumeration.empty? + STDERR.puts( + "#{type.name}: simpleType which is not enum type not supported.") + return o + end + type.check_lexical_format(obj) + o + end + + def complex2soap(obj, type) + o = SOAPElement.new(type.name) + type.each_element do |child_ele| + o.add(_obj2soap(Mapping.find_attribute(obj, child_ele.name.name), + child_ele)) + end + o + end + + def unknownobj2soap(obj, name) + if obj.class.class_variables.include?('@@schema_element') + ele = SOAPElement.new(name) + add_elements2soap(obj, ele) + add_attributes2soap(obj, ele) + ele + elsif obj.is_a?(Hash) + ele = SOAPElement.from_obj(obj) + ele.elename = name + ele + else # expected to be a basetype or an anyType. + o = Mapping.obj2soap(obj) + o.elename = name + o + end + end + + def add_elements2soap(obj, ele) + elements, as_array = schema_element_definition(obj.class) + elements.each do |elename, type| + child = Mapping.find_attribute(obj, elename) + name = ::XSD::QName.new(nil, elename) + if as_array.include?(type) + child.each do |item| + ele.add(obj2soap(item, name)) + end + else + ele.add(obj2soap(child, name)) + end + end + end + + def add_attributes2soap(obj, ele) + attributes = schema_attribute_definition(obj.class) + attributes.each do |attrname, param| + attr = Mapping.find_attribute(obj, 'attr_' + attrname) + ele.extraattr[attrname] = attr + end + end + + def base2soap(obj, type) + soap_obj = nil + if type <= ::XSD::XSDString + soap_obj = type.new(::XSD::Charset.is_ces(obj, $KCODE) ? + ::XSD::Charset.encoding_conv(obj, $KCODE, ::XSD::Charset.encoding) : + obj) + else + soap_obj = type.new(obj) + end + soap_obj + end + + def anytype2obj(node) + if node.is_a?(::SOAP::SOAPBasetype) + return node.data + end + klass = ::SOAP::Mapping::Object + obj = klass.new + node.each do |name, value| + obj.__soap_set_property(name, Mapping.soap2obj(value)) + end + obj + end + + def soapele2obj(node, obj_class = nil) + unless obj_class + typestr = ::XSD::CodeGen::GenSupport.safeconstname(node.elename.name) + obj_class = Mapping.class_from_name(typestr) + end + if obj_class and obj_class.class_variables.include?('@@schema_element') + soapele2definedobj(node, obj_class) + elsif node.is_a?(SOAPElement) + node.to_obj + else + result, obj = @rubytype_factory.soap2obj(nil, node, nil, self) + obj + end + end + + def soapele2definedobj(node, obj_class) + obj = Mapping.create_empty_object(obj_class) + add_elements2obj(node, obj) + add_attributes2obj(node, obj) + obj + end + + def add_elements2obj(node, obj) + elements, as_array = schema_element_definition(obj.class) + vars = {} + node.each do |name, value| + if class_name = elements[name] + if klass = Mapping.class_from_name(class_name) + if klass.ancestors.include?(::SOAP::SOAPBasetype) + if value.respond_to?(:data) + child = klass.new(value.data).data + else + child = klass.new(nil).data + end + else + child = soapele2obj(value, klass) + end + else + raise MappingError.new("Unknown class: #{class_name}") + end + else # untyped element is treated as anyType. + child = anytype2obj(value) + end + if as_array.include?(class_name) + (vars[name] ||= []) << child + else + vars[name] = child + end + end + Mapping.set_instance_vars(obj, vars) + end + + def add_attributes2obj(node, obj) + Mapping.set_instance_vars(obj, {'__soap_attribute' => {}}) + vars = {} + attributes = schema_attribute_definition(obj.class) + attributes.each do |attrname, class_name| + attr = node.extraattr[::XSD::QName.new(nil, attrname)] + next if attr.nil? or attr.empty? + klass = Mapping.class_from_name(class_name) + if klass.ancestors.include?(::SOAP::SOAPBasetype) + child = klass.new(attr).data + else + child = attr + end + vars['attr_' + attrname] = child + end + Mapping.set_instance_vars(obj, vars) + end + + # it caches @@schema_element. this means that @@schema_element must not be + # changed while a lifetime of a WSDLLiteralRegistry. + def schema_element_definition(klass) + if @schema_element_cache.key?(klass) + return @schema_element_cache[klass] + end + elements = {} + as_array = [] + klass.class_eval('@@schema_element').each do |name, class_name| + if /\[\]$/ =~ class_name + class_name = class_name.sub(/\[\]$/, '') + as_array << class_name + end + elements[name] = class_name + end + @schema_element_cache[klass] = [elements, as_array] + return @schema_element_cache[klass] + end + + def schema_attribute_definition(klass) + attributes = klass.class_eval('@@schema_attribute') + end +end + + +end +end |