From 9cba39a1a1b09f94a5d890e0ad6f4c74bb9f36bf Mon Sep 17 00:00:00 2001 From: nahi Date: Tue, 14 Oct 2003 15:14:02 +0000 Subject: * lib/soap/baseData.rb: Introduce SOAPType as the common ancestor of SOAPBasetype and SOAPCompoundtype. * lib/soap/generator.rb, lib/soap/element.rb, lib/soap/encodingstyle/*: Encoding methods signature change. Pass SOAPGenerator as a parameter. * lib/soap/mapping/*, test/soap/marshal/test_marshal.rb: Refactoring for better marshalling/unmarshalling support. Now I think SOAP marshaller supports all kind of object graph which is supported by Ruby's original marshaller. Of course there could be bugs as always. Find it. :-) * lib/soap/rpc/standaloneServer.rb: Set severity threshould to INFO. DEBUG is too noisy. * lib/xsd/datatypes.rb: DateTime#of is obsoleted. Use DateTime#offset. * test/wsdl/emptycomplextype.wsdl, test/xsd/xmlschema.xml: Avoid useless warning. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@4760 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- lib/soap/mapping/factory.rb | 91 ++++++--- lib/soap/mapping/mapping.rb | 10 + lib/soap/mapping/registry.rb | 119 ++++++++---- lib/soap/mapping/rubytypeFactory.rb | 377 +++++++++++++++++++----------------- 4 files changed, 358 insertions(+), 239 deletions(-) (limited to 'lib/soap/mapping') diff --git a/lib/soap/mapping/factory.rb b/lib/soap/mapping/factory.rb index 2e5ddc1f15..6a34da55a8 100644 --- a/lib/soap/mapping/factory.rb +++ b/lib/soap/mapping/factory.rb @@ -24,6 +24,10 @@ module Mapping class Factory include TraverseSupport + def initialize + # nothing to do + end + def obj2soap(soap_class, obj, info, map) raise NotImplementError.new # return soap_obj @@ -48,24 +52,13 @@ class Factory end end - def set_instance_vars(obj, values) - values.each do |name, value| - setter = name + "=" - if obj.respond_to?(setter) - obj.__send__(setter, value) - else - obj.instance_eval("@#{ name } = value") - end - end - end - def setiv2obj(obj, node, map) return if node.nil? vars = {} node.each do |name, value| vars[Mapping.elename2name(name)] = Mapping._soap2obj(value, map) end - set_instance_vars(obj, vars) + Mapping.set_instance_vars(obj, vars) end def setiv2soap(node, obj, map) @@ -76,13 +69,6 @@ class Factory end end - def addiv2soap(node, obj, map) - return if obj.instance_variables.empty? - ivars = SOAPStruct.new # Undefined type. - setiv2soap(ivars, obj, map) - node.add('ivars', ivars) - end - # It breaks Thread.current[:SOAPMarshalDataKey]. def mark_marshalled_obj(obj, soap_obj) Thread.current[:SOAPMarshalDataKey][obj.__id__] = soap_obj @@ -103,14 +89,21 @@ class Factory end class StringFactory_ < Factory + def initialize(allow_original_mapping = false) + super() + @allow_original_mapping = allow_original_mapping + end + def obj2soap(soap_class, obj, info, map) + if !@allow_original_mapping and !obj.instance_variables.empty? + return nil + end begin - if XSD::Charset.is_ces(obj, $KCODE) - encoded = XSD::Charset.encoding_conv(obj, $KCODE, XSD::Charset.encoding) - soap_obj = soap_class.new(encoded) - else - return nil + unless XSD::Charset.is_ces(obj, $KCODE) + return nil end + encoded = XSD::Charset.encoding_conv(obj, $KCODE, XSD::Charset.encoding) + soap_obj = soap_class.new(encoded) rescue XSD::ValueSpaceError return nil end @@ -119,14 +112,24 @@ class StringFactory_ < Factory end def soap2obj(obj_class, node, info, map) - obj = XSD::Charset.encoding_conv(node.data, XSD::Charset.encoding, $KCODE) + obj = create_empty_object(obj_class) + decoded = XSD::Charset.encoding_conv(node.data, XSD::Charset.encoding, $KCODE) + obj.replace(decoded) mark_unmarshalled_obj(node, obj) return true, obj end end class BasetypeFactory_ < Factory + def initialize(allow_original_mapping = false) + super() + @allow_original_mapping = allow_original_mapping + end + def obj2soap(soap_class, obj, info, map) + if !@allow_original_mapping and !obj.instance_variables.empty? + return nil + end soap_obj = nil begin soap_obj = soap_class.new(obj) @@ -145,7 +148,16 @@ class BasetypeFactory_ < Factory end class DateTimeFactory_ < Factory + def initialize(allow_original_mapping = false) + super() + @allow_original_mapping = allow_original_mapping + end + def obj2soap(soap_class, obj, info, map) + if !@allow_original_mapping and + Time === obj and !obj.instance_variables.empty? + return nil + end soap_obj = nil begin soap_obj = soap_class.new(obj) @@ -176,6 +188,7 @@ end class Base64Factory_ < Factory def obj2soap(soap_class, obj, info, map) + return nil unless obj.instance_variables.empty? soap_obj = soap_class.new(obj) mark_marshalled_obj(obj, soap_obj) if soap_obj soap_obj @@ -189,9 +202,17 @@ class Base64Factory_ < Factory end class ArrayFactory_ < Factory + def initialize(allow_original_mapping = false) + super() + @allow_original_mapping = allow_original_mapping + end + # [[1], [2]] is converted to Array of Array, not 2-D Array. # To create M-D Array, you must call Mapping.ary2md. def obj2soap(soap_class, obj, info, map) + if !@allow_original_mapping and !obj.instance_variables.empty? + return nil + end arytype = Mapping.obj2element(obj) if arytype.name arytype.namespace ||= RubyTypeNamespace @@ -217,7 +238,15 @@ class ArrayFactory_ < Factory end class TypedArrayFactory_ < Factory + def initialize(allow_original_mapping = false) + super() + @allow_original_mapping = allow_original_mapping + end + def obj2soap(soap_class, obj, info, map) + if !@allow_original_mapping and !obj.instance_variables.empty? + return nil + end arytype = info[:type] || info[0] param = SOAPArray.new(ValueArrayName, 1, arytype) mark_marshalled_obj(obj, param) @@ -271,9 +300,17 @@ end MapQName = XSD::QName.new(ApacheSOAPTypeNamespace, 'Map') class HashFactory_ < Factory + def initialize(allow_original_mapping = false) + super() + @allow_original_mapping = allow_original_mapping + end + def obj2soap(soap_class, obj, info, map) - if obj.default or - (obj.respond_to?(:default_proc) and obj.default_proc) + if !@allow_original_mapping and !obj.instance_variables.empty? + return nil + end + if !obj.default.nil? or + (obj.respond_to?(:default_proc) and obj.default_proc) return nil end param = SOAPStruct.new(MapQName) diff --git a/lib/soap/mapping/mapping.rb b/lib/soap/mapping/mapping.rb index 19eca5dab0..f660c2c63f 100644 --- a/lib/soap/mapping/mapping.rb +++ b/lib/soap/mapping/mapping.rb @@ -129,6 +129,16 @@ module Mapping return registry.soap2obj(node.class, node) end + def self.set_instance_vars(obj, values) + values.each do |name, value| + setter = name + "=" + if obj.respond_to?(setter) + obj.__send__(setter, value) + else + obj.instance_eval("@#{ name } = value") + end + end + end # Allow only (Letter | '_') (Letter | Digit | '-' | '_')* here. # Caution: '.' is not allowed here. diff --git a/lib/soap/mapping/registry.rb b/lib/soap/mapping/registry.rb index 568f34ac3e..bdf14d4fc6 100644 --- a/lib/soap/mapping/registry.rb +++ b/lib/soap/mapping/registry.rb @@ -39,6 +39,8 @@ module MappedException; end RubyTypeName = XSD::QName.new(RubyTypeInstanceNamespace, 'rubyType') +RubyExtendName = XSD::QName.new(RubyTypeInstanceNamespace, 'extends') +RubyIVarName = XSD::QName.new(RubyTypeInstanceNamespace, 'ivars') # Inner class to pass an exception. @@ -217,11 +219,11 @@ class Registry [::TrueClass, ::SOAP::SOAPBoolean, BasetypeFactory], [::FalseClass, ::SOAP::SOAPBoolean, BasetypeFactory], [::String, ::SOAP::SOAPString, StringFactory], - [::DateTime, ::SOAP::SOAPDateTime, BasetypeFactory], - [::Date, ::SOAP::SOAPDateTime, BasetypeFactory], - [::Date, ::SOAP::SOAPDate, BasetypeFactory], - [::Time, ::SOAP::SOAPDateTime, BasetypeFactory], - [::Time, ::SOAP::SOAPTime, BasetypeFactory], + [::DateTime, ::SOAP::SOAPDateTime, DateTimeFactory], + [::Date, ::SOAP::SOAPDateTime, DateTimeFactory], + [::Date, ::SOAP::SOAPDate, DateTimeFactory], + [::Time, ::SOAP::SOAPDateTime, DateTimeFactory], + [::Time, ::SOAP::SOAPTime, DateTimeFactory], [::Float, ::SOAP::SOAPDouble, BasetypeFactory, {:derived_class => true}], [::Float, ::SOAP::SOAPFloat, BasetypeFactory, @@ -261,11 +263,11 @@ class Registry [::TrueClass, ::SOAP::SOAPBoolean, BasetypeFactory], [::FalseClass, ::SOAP::SOAPBoolean, BasetypeFactory], [::String, ::SOAP::SOAPString, StringFactory], - [::DateTime, ::SOAP::SOAPDateTime, BasetypeFactory], - [::Date, ::SOAP::SOAPDateTime, BasetypeFactory], - [::Date, ::SOAP::SOAPDate, BasetypeFactory], - [::Time, ::SOAP::SOAPDateTime, BasetypeFactory], - [::Time, ::SOAP::SOAPTime, BasetypeFactory], + [::DateTime, ::SOAP::SOAPDateTime, DateTimeFactory], + [::Date, ::SOAP::SOAPDateTime, DateTimeFactory], + [::Date, ::SOAP::SOAPDate, DateTimeFactory], + [::Time, ::SOAP::SOAPDateTime, DateTimeFactory], + [::Time, ::SOAP::SOAPTime, DateTimeFactory], [::Float, ::SOAP::SOAPDouble, BasetypeFactory, {:derived_class => true}], [::Float, ::SOAP::SOAPFloat, BasetypeFactory, @@ -304,18 +306,18 @@ class Registry @config = config @map = Map.new(self) if @config[:allow_original_mapping] - allow_original_mapping = true + @allow_original_mapping = true @map.init(RubyOriginalMap) else - allow_original_mapping = false + @allow_original_mapping = false @map.init(SOAPBaseMap) end - allow_untyped_struct = @config.key?(:allow_untyped_struct) ? + @allow_untyped_struct = @config.key?(:allow_untyped_struct) ? @config[:allow_untyped_struct] : true @rubytype_factory = RubytypeFactory.new( - :allow_untyped_struct => allow_untyped_struct, - :allow_original_mapping => allow_original_mapping + :allow_untyped_struct => @allow_untyped_struct, + :allow_original_mapping => @allow_original_mapping ) @default_factory = @rubytype_factory @excn_handler_obj2soap = nil @@ -329,8 +331,47 @@ class Registry # This mapping registry ignores type hint. def obj2soap(klass, obj, type = nil) + soap = _obj2soap(klass, obj, type) + if @allow_original_mapping + addextend2soap(soap, obj) + end + soap + end + + def soap2obj(klass, node) + obj = _soap2obj(klass, node) + if @allow_original_mapping + addextend2obj(obj, node.extraattr[RubyExtendName]) + addiv2obj(obj, node.extraattr[RubyIVarName]) + end + 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 + + def find_mapped_obj_class(soap_class) + @map.find_mapped_obj_class(soap_class) + end + +private + + def _obj2soap(klass, obj, type) ret = nil - if obj.is_a?(SOAPStruct) || obj.is_a?(SOAPArray) + if obj.is_a?(SOAPStruct) or obj.is_a?(SOAPArray) obj.replace do |ele| Mapping._obj2soap(ele, self) end @@ -344,18 +385,17 @@ class Registry rescue MappingError end return ret if ret - if @excn_handler_obj2soap ret = @excn_handler_obj2soap.call(obj) { |yield_obj| Mapping._obj2soap(yield_obj, self) } end return ret if ret - raise MappingError.new("Cannot map #{ klass.name } to SOAP/OM.") end - def soap2obj(klass, node) + # Might return nil as a mapping result. + def _soap2obj(klass, node) if node.extraattr.key?(RubyTypeName) conv, obj = @rubytype_factory.soap2obj(klass, node, nil, self) return obj if conv @@ -369,34 +409,43 @@ class Registry if @excn_handler_soap2obj begin return @excn_handler_soap2obj.call(node) { |yield_node| - Mapping._soap2obj(yield_node, self) - } + Mapping._soap2obj(yield_node, self) + } rescue Exception end end - raise MappingError.new("Cannot map #{ node.type.name } to Ruby object.") end - def default_factory=(factory) - @default_factory = factory - end - - def excn_handler_obj2soap=(handler) - @excn_handler_obj2soap = handler + def addiv2obj(obj, attr) + return unless attr + vars = {} + attr.__getobj__.each do |name, value| + vars[name] = Mapping._soap2obj(value, self) + end + Mapping.set_instance_vars(obj, vars) end - def excn_handler_soap2obj=(handler) - @excn_handler_soap2obj = handler + def addextend2obj(obj, attr) + return unless attr + attr.split(/ /).reverse_each do |mstr| + obj.extend(Mapping.class_from_name(mstr)) + end end - def find_mapped_soap_class(obj_class) - @map.find_mapped_soap_class(obj_class) + def addextend2soap(node, obj) + return if obj.is_a?(Symbol) or obj.is_a?(Fixnum) + list = (class << obj; self; end).ancestors - obj.class.ancestors + unless list.empty? + node.extraattr[RubyExtendName] = list.collect { |c| + if c.name.empty? + raise TypeError.new("singleton can't be dumped #{ obj }") + end + c.name + }.join(" ") + end end - def find_mapped_obj_class(soap_class) - @map.find_mapped_obj_class(soap_class) - end end diff --git a/lib/soap/mapping/rubytypeFactory.rb b/lib/soap/mapping/rubytypeFactory.rb index 0a3f502dfe..3ebb799220 100644 --- a/lib/soap/mapping/rubytypeFactory.rb +++ b/lib/soap/mapping/rubytypeFactory.rb @@ -22,22 +22,28 @@ module Mapping class RubytypeFactory < Factory - TYPE_STRING = 'String' - TYPE_ARRAY = 'Array' - TYPE_REGEXP = 'Regexp' - TYPE_RANGE = 'Range' - TYPE_CLASS = 'Class' - TYPE_MODULE = 'Module' - TYPE_SYMBOL = 'Symbol' - TYPE_STRUCT = 'Struct' - TYPE_HASH = 'Map' - + TYPE_STRING = XSD::QName.new(RubyTypeNamespace, 'String') + TYPE_TIME = XSD::QName.new(RubyTypeNamespace, 'Time') + TYPE_ARRAY = XSD::QName.new(RubyTypeNamespace, 'Array') + TYPE_REGEXP = XSD::QName.new(RubyTypeNamespace, 'Regexp') + TYPE_RANGE = XSD::QName.new(RubyTypeNamespace, 'Range') + TYPE_CLASS = XSD::QName.new(RubyTypeNamespace, 'Class') + TYPE_MODULE = XSD::QName.new(RubyTypeNamespace, 'Module') + TYPE_SYMBOL = XSD::QName.new(RubyTypeNamespace, 'Symbol') + TYPE_STRUCT = XSD::QName.new(RubyTypeNamespace, 'Struct') + TYPE_HASH = XSD::QName.new(RubyTypeNamespace, 'Map') + def initialize(config = {}) @config = config @allow_untyped_struct = @config.key?(:allow_untyped_struct) ? @config[:allow_untyped_struct] : true @allow_original_mapping = @config.key?(:allow_original_mapping) ? @config[:allow_original_mapping] : false + @string_factory = StringFactory_.new(true) + @basetype_factory = BasetypeFactory_.new(true) + @datetime_factory = DateTimeFactory_.new(true) + @array_factory = ArrayFactory_.new(true) + @hash_factory = HashFactory_.new(true) end def obj2soap(soap_class, obj, info, map) @@ -47,48 +53,83 @@ class RubytypeFactory < Factory unless @allow_original_mapping return nil end - unless XSD::Charset.is_ces(obj, $KCODE) + param = @string_factory.obj2soap(SOAPString, obj, info, map) + if obj.class != String + param.extraattr[RubyTypeName] = obj.class.name + end + addiv2soapattr(param, obj, map) + when Time + unless @allow_original_mapping return nil end - encoded = XSD::Charset.encoding_conv(obj, $KCODE, XSD::Charset.encoding) - param = SOAPStruct.new(XSD::QName.new(RubyTypeNamespace, TYPE_STRING)) - mark_marshalled_obj(obj, param) - param.add('string', SOAPString.new(encoded)) - if obj.class != String + param = @datetime_factory.obj2soap(SOAPDateTime, obj, info, map) + if obj.class != Time param.extraattr[RubyTypeName] = obj.class.name end - addiv2soap(param, obj, map) + addiv2soapattr(param, obj, map) when Array unless @allow_original_mapping return nil end - arytype = Mapping.obj2element(obj) - if arytype.name - arytype.namespace ||= RubyTypeNamespace - else - arytype = XSD::AnyTypeName + param = @array_factory.obj2soap(nil, obj, info, map) + if obj.class != Array + param.extraattr[RubyTypeName] = obj.class.name end - if obj.instance_variables.empty? - param = SOAPArray.new(ValueArrayName, 1, arytype) - mark_marshalled_obj(obj, param) - obj.each do |var| - param.add(Mapping._obj2soap(var, map)) - end - else - param = SOAPStruct.new(XSD::QName.new(RubyTypeNamespace, TYPE_ARRAY)) - mark_marshalled_obj(obj, param) - ary = SOAPArray.new(ValueArrayName, 1, arytype) - obj.each do |var| - ary.add(Mapping._obj2soap(var, map)) - end - param.add('array', ary) - addiv2soap(param, obj, map) + addiv2soapattr(param, obj, map) + when NilClass + unless @allow_original_mapping + return nil end - if obj.class != Array + param = @basetype_factory.obj2soap(SOAPNil, obj, info, map) + addiv2soapattr(param, obj, map) + when FalseClass, TrueClass + unless @allow_original_mapping + return nil + end + param = @basetype_factory.obj2soap(SOAPBoolean, obj, info, map) + addiv2soapattr(param, obj, map) + when Integer + unless @allow_original_mapping + return nil + end + param = @basetype_factory.obj2soap(SOAPInt, obj, info, map) + param ||= @basetype_factory.obj2soap(SOAPInteger, obj, info, map) + param ||= @basetype_factory.obj2soap(SOAPDecimal, obj, info, map) + addiv2soapattr(param, obj, map) + when Float + unless @allow_original_mapping + return nil + end + param = @basetype_factory.obj2soap(SOAPDouble, obj, info, map) + if obj.class != Float param.extraattr[RubyTypeName] = obj.class.name end + addiv2soapattr(param, obj, map) + when Hash + unless @allow_original_mapping + return nil + end + if obj.respond_to?(:default_proc) && obj.default_proc + raise TypeError.new("cannot dump hash with default proc") + end + param = SOAPStruct.new(TYPE_HASH) + mark_marshalled_obj(obj, param) + if obj.class != Hash + param.extraattr[RubyTypeName] = obj.class.name + end + obj.each do |key, value| + elem = SOAPStruct.new # Undefined type. + elem.add("key", Mapping._obj2soap(key, map)) + elem.add("value", Mapping._obj2soap(value, map)) + param.add("item", elem) + end + param.add('default', Mapping._obj2soap(obj.default, map)) + addiv2soapattr(param, obj, map) when Regexp - param = SOAPStruct.new(XSD::QName.new(RubyTypeNamespace, TYPE_REGEXP)) + unless @allow_original_mapping + return nil + end + param = SOAPStruct.new(TYPE_REGEXP) mark_marshalled_obj(obj, param) if obj.class != Regexp param.extraattr[RubyTypeName] = obj.class.name @@ -119,9 +160,12 @@ class RubytypeFactory < Factory end end param.add('options', SOAPInt.new(options)) - addiv2soap(param, obj, map) + addiv2soapattr(param, obj, map) when Range - param = SOAPStruct.new(XSD::QName.new(RubyTypeNamespace, TYPE_RANGE)) + unless @allow_original_mapping + return nil + end + param = SOAPStruct.new(TYPE_RANGE) mark_marshalled_obj(obj, param) if obj.class != Range param.extraattr[RubyTypeName] = obj.class.name @@ -129,57 +173,42 @@ class RubytypeFactory < Factory param.add('begin', Mapping._obj2soap(obj.begin, map)) param.add('end', Mapping._obj2soap(obj.end, map)) param.add('exclude_end', SOAP::SOAPBoolean.new(obj.exclude_end?)) - addiv2soap(param, obj, map) - when Hash + addiv2soapattr(param, obj, map) + when Class unless @allow_original_mapping return nil end - if obj.respond_to?(:default_proc) && obj.default_proc - raise TypeError.new("cannot dump hash with default proc") - end - param = SOAPStruct.new(XSD::QName.new(RubyTypeNamespace, TYPE_HASH)) - mark_marshalled_obj(obj, param) - if obj.class != Hash - param.extraattr[RubyTypeName] = obj.class.name - end - obj.each do |key, value| - elem = SOAPStruct.new # Undefined type. - elem.add("key", Mapping._obj2soap(key, map)) - elem.add("value", Mapping._obj2soap(value, map)) - param.add("item", elem) - end - param.add('default', Mapping._obj2soap(obj.default, map)) - addiv2soap(param, obj, map) - when Class if obj.name.empty? raise TypeError.new("Can't dump anonymous class #{ obj }.") end - param = SOAPStruct.new(XSD::QName.new(RubyTypeNamespace, TYPE_CLASS)) + param = SOAPStruct.new(TYPE_CLASS) mark_marshalled_obj(obj, param) param.add('name', SOAPString.new(obj.name)) - addiv2soap(param, obj, map) + addiv2soapattr(param, obj, map) when Module + unless @allow_original_mapping + return nil + end if obj.name.empty? raise TypeError.new("Can't dump anonymous module #{ obj }.") end - param = SOAPStruct.new(XSD::QName.new(RubyTypeNamespace, TYPE_MODULE)) + param = SOAPStruct.new(TYPE_MODULE) mark_marshalled_obj(obj, param) param.add('name', SOAPString.new(obj.name)) - addiv2soap(param, obj, map) + addiv2soapattr(param, obj, map) when Symbol - param = SOAPStruct.new(XSD::QName.new(RubyTypeNamespace, TYPE_SYMBOL)) + unless @allow_original_mapping + return nil + end + param = SOAPStruct.new(TYPE_SYMBOL) mark_marshalled_obj(obj, param) param.add('id', SOAPString.new(obj.id2name)) - addiv2soap(param, obj, map) - when Exception - typestr = Mapping.name2elename(obj.class.to_s) - param = SOAPStruct.new(XSD::QName.new(RubyTypeNamespace, typestr)) - mark_marshalled_obj(obj, param) - param.add('message', Mapping._obj2soap(obj.message, map)) - param.add('backtrace', Mapping._obj2soap(obj.backtrace, map)) - addiv2soap(param, obj, map) + addiv2soapattr(param, obj, map) when Struct - param = SOAPStruct.new(XSD::QName.new(RubyTypeNamespace, TYPE_STRUCT)) + unless @allow_original_mapping + return nil + end + param = SOAPStruct.new(TYPE_STRUCT) mark_marshalled_obj(obj, param) param.add('type', ele_type = SOAPString.new(obj.class.to_s)) ele_member = SOAPStruct.new @@ -188,29 +217,23 @@ class RubytypeFactory < Factory Mapping._obj2soap(obj[member], map)) end param.add('member', ele_member) - addiv2soap(param, obj, map) + addiv2soapattr(param, obj, map) when IO, Binding, Continuation, Data, Dir, File::Stat, MatchData, Method, - Proc, Thread, ThreadGroup + Proc, Thread, ThreadGroup # from 1.8: Process::Status, UnboundMethod return nil when ::SOAP::Mapping::Object param = SOAPStruct.new(XSD::AnyTypeName) mark_marshalled_obj(obj, param) - setiv2soap(param, obj, map) # addiv2soap? - else - if obj.class.name.empty? - raise TypeError.new("Can't dump anonymous class #{ obj }.") - end - if check_singleton(obj) - raise TypeError.new("singleton can't be dumped #{ obj }") - end - type = Mapping.class2element(obj.class) - param = SOAPStruct.new(type) + addiv2soapattr(param, obj, map) + when Exception + typestr = Mapping.name2elename(obj.class.to_s) + param = SOAPStruct.new(XSD::QName.new(RubyTypeNamespace, typestr)) mark_marshalled_obj(obj, param) - if obj.class <= Marshallable - setiv2soap(param, obj, map) - else - setiv2soap(param, obj, map) # Should not be marshalled? - end + param.add('message', Mapping._obj2soap(obj.message, map)) + param.add('backtrace', Mapping._obj2soap(obj.backtrace, map)) + addiv2soapattr(param, obj, map) + else + param = unknownobj2soap(soap_class, obj, info, map) end param end @@ -218,26 +241,42 @@ class RubytypeFactory < Factory def soap2obj(obj_class, node, info, map) rubytype = node.extraattr[RubyTypeName] if rubytype or node.type.namespace == RubyTypeNamespace - rubytype2obj(node, map, rubytype) + rubytype2obj(node, info, map, rubytype) elsif node.type == XSD::AnyTypeName or node.type == XSD::AnySimpleTypeName - anytype2obj(node, map) + anytype2obj(node, info, map) else - unknowntype2obj(node, map) + unknowntype2obj(node, info, map) end end private - def check_singleton(obj) - unless singleton_methods_true(obj).empty? - return true + def addiv2soapattr(node, obj, map) + return if obj.instance_variables.empty? + ivars = SOAPStruct.new # Undefined type. + setiv2soap(ivars, obj, map) + node.extraattr[RubyIVarName] = ivars + end + + def unknownobj2soap(soap_class, obj, info, map) + if obj.class.name.empty? + raise TypeError.new("Can't dump anonymous class #{ obj }.") end singleton_class = class << obj; self; end - if !singleton_class.instance_variables.empty? or - !(singleton_class.ancestors - obj.class.ancestors).empty? - return true + if !singleton_methods_true(obj).empty? or + !singleton_class.instance_variables.empty? + raise TypeError.new("singleton can't be dumped #{ obj }") + end + if !(singleton_class.ancestors - obj.class.ancestors).empty? + typestr = Mapping.name2elename(obj.class.to_s) + type = XSD::QName.new(RubyTypeNamespace, typestr) + else + type = Mapping.class2element(obj.class) end - false + param = SOAPStruct.new(type) + mark_marshalled_obj(obj, param) + setiv2soap(param, obj, map) + param end if RUBY_VERSION >= '1.8.0' @@ -250,40 +289,54 @@ private end end - def rubytype2obj(node, map, rubytype) + def rubytype2obj(node, info, map, rubytype) + klass = rubytype ? Mapping.class_from_name(rubytype) : nil obj = nil - case node.class + case node when SOAPString - obj = string2obj(node, map, rubytype) - obj.replace(node.data) + return @string_factory.soap2obj(klass || String, node, info, map) + when SOAPDateTime + #return @datetime_factory.soap2obj(klass || Time, node, info, map) + klass ||= Time + t = node.to_time + arg = [t.year, t.month, t.mday, t.hour, t.min, t.sec, t.usec] + obj = t.gmt? ? klass.gm(*arg) : klass.local(*arg) + mark_unmarshalled_obj(node, obj) return true, obj when SOAPArray - obj = array2obj(node, map, rubytype) - node.soap2array(obj) do |elem| - elem ? Mapping._soap2obj(elem, map) : nil - end - return true, obj + return @array_factory.soap2obj(klass || Array, node, info, map) + when SOAPNil, SOAPBoolean, SOAPInt, SOAPInteger, SOAPDecimal, SOAPDouble + return @basetype_factory.soap2obj(nil, node, info, map) + when SOAPStruct + return rubytypestruct2obj(node, info, map, rubytype) + else + raise end + end - case node.type.name - when TYPE_STRING - obj = string2obj(node, map, rubytype) - obj.replace(node['string'].data) - setiv2obj(obj, node['ivars'], map) - when TYPE_ARRAY - obj = array2obj(node, map, rubytype) - node['array'].soap2array(obj) do |elem| - elem ? Mapping._soap2obj(elem, map) : nil - end - setiv2obj(obj, node['ivars'], map) + def rubytypestruct2obj(node, info, map, rubytype) + klass = rubytype ? Mapping.class_from_name(rubytype) : nil + obj = nil + case node.type + when TYPE_HASH + klass = rubytype ? Mapping.class_from_name(rubytype) : Hash + obj = create_empty_object(klass) + mark_unmarshalled_obj(node, obj) + node.each do |key, value| + next unless key == 'item' + obj[Mapping._soap2obj(value['key'], map)] = + Mapping._soap2obj(value['value'], map) + end + if node.key?('default') + obj.default = Mapping._soap2obj(node['default'], map) + end when TYPE_REGEXP klass = rubytype ? Mapping.class_from_name(rubytype) : Regexp obj = create_empty_object(klass) mark_unmarshalled_obj(node, obj) source = node['source'].string options = node['options'].data || 0 - obj.instance_eval { initialize(source, options) } - setiv2obj(obj, node['ivars'], map) + 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) @@ -291,33 +344,13 @@ private first = Mapping._soap2obj(node['begin'], map) last = Mapping._soap2obj(node['end'], map) exclude_end = node['exclude_end'].data - obj.instance_eval { initialize(first, last, exclude_end) } - setiv2obj(obj, node['ivars'], map) - when TYPE_HASH - unless @allow_original_mapping - return false - end - klass = rubytype ? Mapping.class_from_name(rubytype) : Hash - obj = create_empty_object(klass) - mark_unmarshalled_obj(node, obj) - node.each do |key, value| - next unless key == 'item' - obj[Mapping._soap2obj(value['key'], map)] = - Mapping._soap2obj(value['value'], map) - end - if node.key?('default') - obj.default = Mapping._soap2obj(node['default'], map) - end - setiv2obj(obj, node['ivars'], map) + Range.instance_method(:initialize).bind(obj).call(first, last, exclude_end) when TYPE_CLASS obj = Mapping.class_from_name(node['name'].data) - setiv2obj(obj, node['ivars'], map) when TYPE_MODULE obj = Mapping.class_from_name(node['name'].data) - setiv2obj(obj, node['ivars'], map) when TYPE_SYMBOL obj = node['id'].data.intern - setiv2obj(obj, node['ivars'], map) when TYPE_STRUCT typestr = Mapping.elename2name(node['type'].data) klass = Mapping.class_from_name(typestr) @@ -333,40 +366,15 @@ private obj = create_empty_object(klass) mark_unmarshalled_obj(node, obj) node['member'].each do |name, value| - obj[Mapping.elename2name(name)] = - Mapping._soap2obj(value, map) + obj[Mapping.elename2name(name)] = Mapping._soap2obj(value, map) end - setiv2obj(obj, node['ivars'], map) else - conv, obj = exception2obj(node, map) - unless conv - return false - end - setiv2obj(obj, node['ivars'], map) + return unknowntype2obj(node, info, map) end return true, obj end - def exception2obj(node, map) - typestr = Mapping.elename2name(node.type.name) - klass = Mapping.class_from_name(typestr) - if klass.nil? - return false - end - unless klass <= Exception - return false - end - message = Mapping._soap2obj(node['message'], map) - backtrace = Mapping._soap2obj(node['backtrace'], map) - obj = create_empty_object(klass) - obj = obj.exception(message) - mark_unmarshalled_obj(node, obj) - obj.set_backtrace(backtrace) - setiv2obj(obj, node['ivars'], map) - return true, obj - end - - def anytype2obj(node, map) + def anytype2obj(node, info, map) case node when SOAPBasetype return true, node.data @@ -383,22 +391,24 @@ private end end - def unknowntype2obj(node, map) + def unknowntype2obj(node, info, map) if node.is_a?(SOAPStruct) - obj = struct2obj(node, map) + obj = unknownstruct2obj(node, info, map) return true, obj if obj if !@allow_untyped_struct return false end - return anytype2obj(node, map) + return anytype2obj(node, info, map) else # Basetype which is not defined... return false end end - def struct2obj(node, map) - obj = nil + def unknownstruct2obj(node, info, map) + unless node.type.name + return nil + end typestr = Mapping.elename2name(node.type.name) klass = Mapping.class_from_name(typestr) if klass.nil? @@ -407,6 +417,9 @@ private if klass.nil? return nil end + if klass <= ::Exception + return exception2obj(klass, node, map) + end klass_type = Mapping.class2qname(klass) return nil unless node.type.match(klass_type) obj = create_empty_object(klass) @@ -415,6 +428,16 @@ private obj end + def exception2obj(klass, node, map) + message = Mapping._soap2obj(node['message'], map) + backtrace = Mapping._soap2obj(node['backtrace'], map) + obj = create_empty_object(klass) + obj = obj.exception(message) + mark_unmarshalled_obj(node, obj) + obj.set_backtrace(backtrace) + obj + end + # Only creates empty array. Do String#replace it with real string. def array2obj(node, map, rubytype) klass = rubytype ? Mapping.class_from_name(rubytype) : Array -- cgit v1.2.3