diff options
-rw-r--r-- | lib/soap/baseData.rb | 12 | ||||
-rw-r--r-- | lib/soap/element.rb | 26 | ||||
-rw-r--r-- | lib/soap/encodingstyle/aspDotNetHandler.rb | 23 | ||||
-rw-r--r-- | lib/soap/encodingstyle/handler.rb | 4 | ||||
-rw-r--r-- | lib/soap/encodingstyle/literalHandler.rb | 28 | ||||
-rw-r--r-- | lib/soap/encodingstyle/soapHandler.rb | 71 | ||||
-rw-r--r-- | lib/soap/generator.rb | 87 | ||||
-rw-r--r-- | lib/soap/mapping/factory.rb | 91 | ||||
-rw-r--r-- | lib/soap/mapping/mapping.rb | 10 | ||||
-rw-r--r-- | lib/soap/mapping/registry.rb | 119 | ||||
-rw-r--r-- | lib/soap/mapping/rubytypeFactory.rb | 377 | ||||
-rw-r--r-- | lib/soap/marshal.rb | 2 | ||||
-rw-r--r-- | lib/soap/rpc/standaloneServer.rb | 1 | ||||
-rw-r--r-- | lib/soap/wsdlDriver.rb | 3 | ||||
-rw-r--r-- | lib/xsd/datatypes.rb | 4 | ||||
-rw-r--r-- | lib/xsd/namedelements.rb | 4 | ||||
-rw-r--r-- | test/ruby/test_beginendblock.rb | 3 | ||||
-rw-r--r-- | test/ruby/test_marshal.rb | 13 | ||||
-rw-r--r-- | test/soap/marshal/test_marshal.rb | 172 | ||||
-rw-r--r-- | test/wsdl/emptycomplextype.wsdl | 1 | ||||
-rw-r--r-- | test/xsd/xmlschema.xml | 13 |
21 files changed, 689 insertions, 375 deletions
diff --git a/lib/soap/baseData.rb b/lib/soap/baseData.rb index 30f3bce767..c175072e4d 100644 --- a/lib/soap/baseData.rb +++ b/lib/soap/baseData.rb @@ -41,9 +41,16 @@ end ### +## Marker of SOAP/DM types. +# +module SOAPType; end + + +### ## Mix-in module for SOAP base type instances. # module SOAPBasetype + include SOAPType include SOAP attr_accessor :encodingstyle @@ -75,6 +82,7 @@ end ## Mix-in module for SOAP compound type instances. # module SOAPCompoundtype + include SOAPType include SOAP attr_accessor :encodingstyle @@ -168,7 +176,7 @@ public d end - def SOAPReference.create_refid(obj) + def self.create_refid(obj) 'id' << obj.__id__.to_s end end @@ -597,7 +605,7 @@ public data = retrieve(idxary[0, idxary.size - 1]) data[idxary.last] = value - if value.is_a?(SOAPBasetype) || value.is_a?(SOAPCompoundtype) + if value.is_a?(SOAPType) value.elename = value.elename.dup_name('item') # Sync type diff --git a/lib/soap/element.rb b/lib/soap/element.rb index 640eafb0e4..561cb1b071 100644 --- a/lib/soap/element.rb +++ b/lib/soap/element.rb @@ -84,17 +84,17 @@ public end end - def encode(buf, ns, attrs = {}, indent = '') + def encode(generator, ns, attrs = {}) SOAPGenerator.assign_ns(attrs, ns, EnvelopeNamespace) SOAPGenerator.assign_ns(attrs, ns, EncodingNamespace) attrs[ns.name(AttrEncodingStyleName)] = EncodingNamespace name = ns.name(@elename) - SOAPGenerator.encode_tag(buf, name, attrs, indent) + generator.encode_tag(name, attrs) yield(self.faultcode, false) yield(self.faultstring, false) yield(self.faultactor, false) yield(self.detail, false) if self.detail - SOAPGenerator.encode_tag_end(buf, name, indent, true) + generator.encode_tag_end(name, true) end end @@ -112,9 +112,9 @@ public @is_fault = is_fault end - def encode(buf, ns, attrs = {}, indent = '') + def encode(generator, ns, attrs = {}) name = ns.name(@elename) - SOAPGenerator.encode_tag(buf, name, attrs, indent) + generator.encode_tag(name, attrs) if @is_fault yield(@data, true) else @@ -122,7 +122,7 @@ public yield(data, true) end end - SOAPGenerator.encode_tag_end(buf, name, indent, true) + generator.encode_tag_end(name, true) end def root_node @@ -160,7 +160,7 @@ public @encodingstyle = encodingstyle || LiteralNamespace end - def encode(buf, ns, attrs = {}, indent = '') + def encode(generator, ns, attrs = {}) attrs.each do |key, value| @content.attr[key] = value end @@ -185,13 +185,13 @@ class SOAPHeader < SOAPArray @encodingstyle = nil end - def encode(buf, ns, attrs = {}, indent = '') + def encode(generator, ns, attrs = {}) name = ns.name(@elename) - SOAPGenerator.encode_tag(buf, name, attrs, indent) + generator.encode_tag(name, attrs) @data.each do |data| yield(data, true) end - SOAPGenerator.encode_tag_end(buf, name, indent, true) + generator.encode_tag_end(name, true) end def length @@ -215,16 +215,16 @@ class SOAPEnvelope < XSD::NSDBase @body = body end - def encode(buf, ns, attrs = {}, indent = '') + def encode(generator, ns, attrs = {}) SOAPGenerator.assign_ns(attrs, ns, EnvelopeNamespace, SOAPNamespaceTag) name = ns.name(@elename) - SOAPGenerator.encode_tag(buf, name, attrs, indent) + generator.encode_tag(name, attrs) yield(@header, true) if @header and @header.length > 0 yield(@body, true) - SOAPGenerator.encode_tag_end(buf, name, indent, true) + generator.encode_tag_end(name, true) end end diff --git a/lib/soap/encodingstyle/aspDotNetHandler.rb b/lib/soap/encodingstyle/aspDotNetHandler.rb index fdce42a48a..b00084e3a9 100644 --- a/lib/soap/encodingstyle/aspDotNetHandler.rb +++ b/lib/soap/encodingstyle/aspDotNetHandler.rb @@ -38,7 +38,7 @@ class ASPDotNetHandler < Handler ### ## encode interface. # - def encode_data(buf, ns, qualified, data, parent, indent = '') + def encode_data(generator, ns, qualified, data, parent) attrs = {} name = if qualified and data.elename.namespace SOAPGenerator.assign_ns(attrs, ns, data.elename.namespace) @@ -49,17 +49,16 @@ class ASPDotNetHandler < Handler case data when SOAPRawString - SOAPGenerator.encode_tag(buf, name, attrs, indent) - buf << data.to_s + generator.encode_tag(name, attrs) + generator.encode_rawstring(data.to_s) when XSD::XSDString - SOAPGenerator.encode_tag(buf, name, attrs, indent) - buf << SOAPGenerator.encode_str(@charset ? - XSD::Charset.encoding_to_xml(data.to_s, @charset) : data.to_s) + generator.encode_tag(name, attrs) + generator.encode_string(@charset ? XSD::Charset.encoding_to_xml(data.to_s, @charset) : data.to_s) when XSD::XSDAnySimpleType - SOAPGenerator.encode_tag(buf, name, attrs, indent) - buf << SOAPGenerator.encode_str(data.to_s) + generator.encode_tag(name, attrs) + generator.encode_string(data.to_s) when SOAPStruct - SOAPGenerator.encode_tag(buf, name, attrs, indent) + generator.encode_tag(name, attrs) data.each do |key, value| if !value.elename.namespace value.elename.namespace = data.elename.namespace @@ -67,7 +66,7 @@ class ASPDotNetHandler < Handler yield(value, true) end when SOAPArray - SOAPGenerator.encode_tag(buf, name, attrs, indent) + generator.encode_tag(name, attrs) data.traverse do |child, *rank| data.position = nil yield(child, true) @@ -78,14 +77,14 @@ yle.") end end - def encode_data_end(buf, ns, qualified, data, parent, indent = "") + def encode_data_end(generator, ns, qualified, data, parent) name = if qualified and data.elename.namespace ns.name(data.elename) else data.elename.name end cr = data.is_a?(SOAPCompoundtype) - SOAPGenerator.encode_tag_end(buf, name, indent, cr) + generator.encode_tag_end(name, cr) end diff --git a/lib/soap/encodingstyle/handler.rb b/lib/soap/encodingstyle/handler.rb index 1ab9d86ec5..2cbbd893d8 100644 --- a/lib/soap/encodingstyle/handler.rb +++ b/lib/soap/encodingstyle/handler.rb @@ -69,11 +69,11 @@ class Handler ## encode interface. # # Returns a XML instance as a string. - def encode_data(buf, ns, qualified, data, parent, indent) + def encode_data(generator, ns, qualified, data, parent) raise NotImplementError.new('Method encode_data must be defined in derived class.') end - def encode_data_end(buf, ns, qualified, data, parent, indent) + def encode_data_end(generator, ns, qualified, data, parent) raise NotImplementError.new('Method encode_data must be defined in derived class.') end diff --git a/lib/soap/encodingstyle/literalHandler.rb b/lib/soap/encodingstyle/literalHandler.rb index b5d0d464d7..5ff0e5dd66 100644 --- a/lib/soap/encodingstyle/literalHandler.rb +++ b/lib/soap/encodingstyle/literalHandler.rb @@ -37,7 +37,7 @@ class LiteralHandler < Handler ### ## encode interface. # - def encode_data(buf, ns, qualified, data, parent, indent = '') + def encode_data(generator, ns, qualified, data, parent) attrs = {} name = if qualified and data.elename.namespace SOAPGenerator.assign_ns(attrs, ns, data.elename.namespace) @@ -48,31 +48,29 @@ class LiteralHandler < Handler case data when SOAPRawString - SOAPGenerator.encode_tag(buf, name, attrs, indent) - buf << data.to_s + generator.encode_tag(name, attrs) + generator.encode_rawstring(data.to_s) when XSD::XSDString - SOAPGenerator.encode_tag(buf, name, attrs, indent) - buf << SOAPGenerator.encode_str(@charset ? - XSD::Charset.encoding_to_xml(data.to_s, @charset) : data.to_s) + generator.encode_tag(name, attrs) + generator.encode_string(@charset ? XSD::Charset.encoding_to_xml(data.to_s, @charset) : data.to_s) when XSD::XSDAnySimpleType - SOAPGenerator.encode_tag(buf, name, attrs, indent) - buf << SOAPGenerator.encode_str(data.to_s) + generator.encode_tag(name, attrs) + generator.encode_string(data.to_s) when SOAPStruct - SOAPGenerator.encode_tag(buf, name, attrs, indent) + generator.encode_tag(name, attrs) data.each do |key, value| value.elename.namespace = data.elename.namespace if !value.elename.namespace yield(value, true) end when SOAPArray - SOAPGenerator.encode_tag(buf, name, attrs, indent) + generator.encode_tag(name, attrs) data.traverse do |child, *rank| data.position = nil yield(child, true) end when SOAPElement - SOAPGenerator.encode_tag(buf, name, attrs.update(data.extraattr), - indent) - buf << data.text if data.text + generator.encode_tag(name, attrs.update(data.extraattr)) + generator.encode_rawstring(data.text) if data.text data.each do |key, value| value.elename.namespace = data.elename.namespace if !value.elename.namespace #yield(value, data.qualified) @@ -83,13 +81,13 @@ class LiteralHandler < Handler end end - def encode_data_end(buf, ns, qualified, data, parent, indent) + def encode_data_end(generator, ns, qualified, data, parent) name = if qualified and data.elename.namespace ns.name(data.elename) else data.elename.name end - SOAPGenerator.encode_tag_end(buf, name, indent) + generator.encode_tag_end(name) end diff --git a/lib/soap/encodingstyle/soapHandler.rb b/lib/soap/encodingstyle/soapHandler.rb index b1b5072e49..85d8642b8c 100644 --- a/lib/soap/encodingstyle/soapHandler.rb +++ b/lib/soap/encodingstyle/soapHandler.rb @@ -40,8 +40,8 @@ class SOAPHandler < Handler ### ## encode interface. # - def encode_data(buf, ns, qualified, data, parent, indent = '') - attrs = encode_attrs(ns, qualified, data, parent) + def encode_data(generator, ns, qualified, data, parent) + attrs = encode_attrs(generator, ns, data, parent) if parent && parent.is_a?(SOAPArray) && parent.position attrs[ns.name(AttrPositionName)] = '[' << parent.position.join(',') << ']' @@ -55,32 +55,26 @@ class SOAPHandler < Handler name = data.elename.name end - if data.respond_to?(:encode) - SOAPGenerator.encode_tag(buf, name, attrs, indent) - return data.encode(buf, ns, attrs, indent) - end - case data when SOAPReference attrs['href'] = '#' << data.refid - SOAPGenerator.encode_tag(buf, name, attrs, indent) + generator.encode_tag(name, attrs) when SOAPRawString - SOAPGenerator.encode_tag(buf, name, attrs, indent) - buf << data.to_s + generator.encode_tag(name, attrs) + generator.encode_rawstring(data.to_s) when XSD::XSDString - SOAPGenerator.encode_tag(buf, name, attrs, indent) - buf << SOAPGenerator.encode_str(@charset ? - XSD::Charset.encoding_to_xml(data.to_s, @charset) : data.to_s) + generator.encode_tag(name, attrs) + generator.encode_string(@charset ? XSD::Charset.encoding_to_xml(data.to_s, @charset) : data.to_s) when XSD::XSDAnySimpleType - SOAPGenerator.encode_tag(buf, name, attrs, indent) - buf << SOAPGenerator.encode_str(data.to_s) + generator.encode_tag(name, attrs) + generator.encode_string(data.to_s) when SOAPStruct - SOAPGenerator.encode_tag(buf, name, attrs, indent) + generator.encode_tag(name, attrs) data.each do |key, value| yield(value, false) end when SOAPArray - SOAPGenerator.encode_tag(buf, name, attrs, indent) + generator.encode_tag(name, attrs) data.traverse do |child, *rank| data.position = data.sparse ? rank : nil yield(child, false) @@ -91,14 +85,14 @@ class SOAPHandler < Handler end end - def encode_data_end(buf, ns, qualified, data, parent, indent = '') + def encode_data_end(generator, ns, qualified, data, parent) name = if qualified and data.elename.namespace ns.name(data.elename) else data.elename.name end cr = data.is_a?(SOAPCompoundtype) - SOAPGenerator.encode_tag_end(buf, name, indent, cr) + generator.encode_tag_end(name, cr) end @@ -292,7 +286,7 @@ private content_typename(data.arytype.name) << '[' << data.size.join(',') << ']') end - def encode_attrs(ns, qualified, data, parent) + def encode_attrs(generator, ns, data, parent) return {} if data.is_a?(SOAPReference) attrs = {} @@ -330,7 +324,7 @@ private data.extraattr.each do |key, value| SOAPGenerator.assign_ns(attrs, ns, key.namespace) - attrs[ns.name(key)] = value # ns.name(value) ? + attrs[ns.name(key)] = encode_attr_value(generator, ns, key, value) end if data.id attrs['id'] = data.id @@ -338,6 +332,17 @@ private attrs end + def encode_attr_value(generator, ns, qname, value) + if value.is_a?(SOAPType) + refid = SOAPReference.create_refid(value) + value.id = refid + generator.add_reftarget(qname.name, value) + '#' + refid + else + value.to_s + end + end + def decode_tag_by_wsdl(ns, elename, typestr, parent, arytypestr, extraattr) if parent.class == SOAPBody # Unqualified name is allowed here. @@ -423,11 +428,13 @@ private end if (klass = TypeMap[type]) - klass.decode(elename) - else - # Unknown type... Struct or String - SOAPUnknown.new(self, elename, type, extraattr) + node = klass.decode(elename) + node.extraattr.update(extraattr) + return node end + + # Unknown type... Struct or String + SOAPUnknown.new(self, elename, type, extraattr) end def decode_textbuf(node) @@ -508,12 +515,22 @@ private id = value next end - extraattr[qname] = value + extraattr[qname] = decode_attr_value(ns, qname, value) end return is_nil, type, arytype, root, offset, position, href, id, extraattr end + def decode_attr_value(ns, qname, value) + if /\A#/ =~ value + o = SOAPReference.new(value) + @refpool << o + o + else + value + end + end + def decode_arypos(position) /^\[(.+)\]$/ =~ position $1.split(',').collect { |s| s.to_i } @@ -524,7 +541,7 @@ private while !@refpool.empty? && count > 0 @refpool = @refpool.find_all { |ref| o = @idpool.find { |item| - ('#' << item.id == ref.refid) + '#' + item.id == ref.refid } unless o raise EncodingStyleError.new("Unresolved reference: #{ ref.refid }.") diff --git a/lib/soap/generator.rb b/lib/soap/generator.rb index 6707aef195..e0ac484c42 100644 --- a/lib/soap/generator.rb +++ b/lib/soap/generator.rb @@ -39,7 +39,6 @@ public attr_accessor :charset attr_accessor :default_encodingstyle attr_accessor :generate_explicit_type - attr_accessor :pretty def initialize(opt = {}) @reftarget = nil @@ -48,37 +47,38 @@ public @default_encodingstyle = opt[:default_encodingstyle] || EncodingNamespace @generate_explicit_type = opt.key?(:generate_explicit_type) ? opt[:generate_explicit_type] : true - @pretty = true # opt[:pretty] + @buf = @indent = @curr = nil end def generate(obj, io = nil) + @buf = io || '' + @indent = '' + prologue @handlers.each do |uri, handler| handler.encode_prologue end - io = '' if io.nil? - ns = XSD::NS.new - io << xmldecl - encode_data(io, ns, true, obj, nil, 0) + @buf << xmldecl + encode_data(ns, true, obj, nil) @handlers.each do |uri, handler| handler.encode_epilogue end epilogue - io + @buf end - def encode_data(buf, ns, qualified, obj, parent, indent) + def encode_data(ns, qualified, obj, parent) if obj.is_a?(SOAPEnvelopeElement) - encode_element(buf, ns, qualified, obj, parent, indent) + encode_element(ns, qualified, obj, parent) return end if @reftarget && !obj.precedents.empty? - @reftarget.add(obj.elename.name, obj) + add_reftarget(obj.elename.name, obj) ref = SOAPReference.new ref.elename.name = obj.elename.name ref.__setobj__(obj) @@ -102,22 +102,29 @@ public raise FormatEncodeError.new("Element name not defined: #{ obj }.") end - indent_str = ' ' * indent - child_indent = @pretty ? indent + 2 : indent - handler.encode_data(buf, ns, qualified, obj, parent, indent_str) do |child, child_q| - encode_data(buf, ns.clone_ns, child_q, child, obj, child_indent) + handler.encode_data(self, ns, qualified, obj, parent) do |child, child_q| + indent_backup, @indent = @indent, @indent + ' ' + encode_data(ns.clone_ns, child_q, child, obj) + @indent = indent_backup + end + handler.encode_data_end(self, ns, qualified, obj, parent) + end + + def add_reftarget(name, node) + unless @reftarget + raise FormatEncodeError.new("Reftarget is not defined.") end - handler.encode_data_end(buf, ns, qualified, obj, parent, indent_str) + @reftarget.add(name, node) end - def encode_element(buf, ns, qualified, obj, parent, indent) - indent_str = ' ' * indent - child_indent = @pretty ? indent + 2 : indent + def encode_element(ns, qualified, obj, parent) attrs = {} if obj.is_a?(SOAPBody) @reftarget = obj - obj.encode(buf, ns, attrs, indent_str) do |child, child_q| - encode_data(buf, ns.clone_ns, child_q, child, obj, child_indent) + obj.encode(self, ns, attrs) do |child, child_q| + indent_backup, @indent = @indent, @indent + ' ' + encode_data(ns.clone_ns, child_q, child, obj) + @indent = indent_backup end @reftarget = nil else @@ -129,39 +136,38 @@ public SOAPGenerator.assign_ns(attrs, ns, XSD::Namespace, XSDNamespaceTag) end end - obj.encode(buf, ns, attrs, indent_str) do |child, child_q| - encode_data(buf, ns.clone_ns, child_q, child, obj, child_indent) + obj.encode(self, ns, attrs) do |child, child_q| + indent_backup, @indent = @indent, @indent + ' ' + encode_data(ns.clone_ns, child_q, child, obj) + @indent = indent_backup end end end - def self.assign_ns(attrs, ns, namespace, tag = nil) - unless ns.assigned?(namespace) - tag = ns.assign(namespace, tag) - attrs['xmlns:' << tag] = namespace - end - end - - def self.encode_tag(buf, elename, attrs = nil, indent = '') + def encode_tag(elename, attrs = nil) if attrs - buf << "\n#{ indent }<#{ elename }" << + @buf << "\n#{ @indent }<#{ elename }" << attrs.collect { |key, value| %Q[ #{ key }="#{ value }"] }.join << '>' else - buf << "\n#{ indent }<#{ elename }>" + @buf << "\n#{ @indent }<#{ elename }>" end end - def self.encode_tag_end(buf, elename, indent = '', cr = nil) + def encode_tag_end(elename, cr = nil) if cr - buf << "\n#{ indent }</#{ elename }>" + @buf << "\n#{ @indent }</#{ elename }>" else - buf << "</#{ elename }>" + @buf << "</#{ elename }>" end end + def encode_rawstring(str) + @buf << str + end + EncodeMap = { '&' => '&', '<' => '<', @@ -171,8 +177,15 @@ public "\r" => '
' } EncodeCharRegexp = Regexp.new("[#{EncodeMap.keys.join}]") - def self.encode_str(str) - str.gsub(EncodeCharRegexp) { |c| EncodeMap[c] } + def encode_string(str) + @buf << str.gsub(EncodeCharRegexp) { |c| EncodeMap[c] } + end + + def self.assign_ns(attrs, ns, namespace, tag = nil) + if namespace and !ns.assigned?(namespace) + tag = ns.assign(namespace, tag) + attrs['xmlns:' << tag] = namespace + end end private 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 diff --git a/lib/soap/marshal.rb b/lib/soap/marshal.rb index f45f80a0de..5c25fad0de 100644 --- a/lib/soap/marshal.rb +++ b/lib/soap/marshal.rb @@ -58,10 +58,10 @@ module Marshal def unmarshal(stream, mapping_registry = MarshalMappingRegistry) header, body = SOAP::Processor.unmarshal(stream) + #Mapping.soap2obj(body.root_node, mapping_registry) Mapping.soap2obj(body.root_node, mapping_registry) end end - end diff --git a/lib/soap/rpc/standaloneServer.rb b/lib/soap/rpc/standaloneServer.rb index 346d6e196b..8862f7606e 100644 --- a/lib/soap/rpc/standaloneServer.rb +++ b/lib/soap/rpc/standaloneServer.rb @@ -50,6 +50,7 @@ class StandaloneServer < Logger::Application def initialize(app_name, namespace, host = "0.0.0.0", port = 8080) super(app_name) + self.level = Logger::Severity::INFO @namespace = namespace @host = host @port = port diff --git a/lib/soap/wsdlDriver.rb b/lib/soap/wsdlDriver.rb index 849effcb0b..6ecc4b4f6e 100644 --- a/lib/soap/wsdlDriver.rb +++ b/lib/soap/wsdlDriver.rb @@ -223,7 +223,8 @@ class WSDLDriver @endpoint_url = nil @wiredump_dev = nil @wiredump_file_base = nil - @httpproxy = ENV['http_proxy'] || ENV['HTTP_PROXY'] + name = 'http_proxy' + @httpproxy = ENV[name] || ENV[name.upcase] @wsdl_elements = @wsdl.collect_elements @wsdl_types = @wsdl.collect_complextypes diff --git a/lib/xsd/datatypes.rb b/lib/xsd/datatypes.rb index a1e3e55624..f6ec83be71 100644 --- a/lib/xsd/datatypes.rb +++ b/lib/xsd/datatypes.rb @@ -485,7 +485,7 @@ module XSDDateTimeImpl def to_time begin - if @data.of * SecInDay == Time.now.utc_offset + if @data.offset * SecInDay == Time.now.utc_offset d = @data usec = (d.sec_fraction * SecInDay * 1000000).to_f Time.local(d.year, d.month, d.mday, d.hour, d.min, d.sec, usec) @@ -643,7 +643,7 @@ private s = format('%02d:%02d:%02d', @data.hour, @data.min, @data.sec) if @data.sec_fraction.nonzero? fr = @data.sec_fraction * SecInDay - shiftsize = fr.denominator.to_s.size + shiftsize = fr.denominator.to_s.size + 1 fr_s = (fr * (10 ** shiftsize)).to_i.to_s s << '.' << '0' * (shiftsize - fr_s.size) << fr_s.sub(/0+$/, '') end diff --git a/lib/xsd/namedelements.rb b/lib/xsd/namedelements.rb index 76f958a9e5..df749fed72 100644 --- a/lib/xsd/namedelements.rb +++ b/lib/xsd/namedelements.rb @@ -60,6 +60,10 @@ class NamedElements @elements << rhs self end + + def delete(rhs) + @elements.delete(rhs) + end def +(rhs) o = NamedElements.new diff --git a/test/ruby/test_beginendblock.rb b/test/ruby/test_beginendblock.rb index c7296a4e85..deacc782b7 100644 --- a/test/ruby/test_beginendblock.rb +++ b/test/ruby/test_beginendblock.rb @@ -39,8 +39,7 @@ STDERR.reopen(File.open(errout, "w")) STDERR.sync = true Dir.chdir(#{q(DIR)}) cmd = "\\"#{ruby}\\" \\"endblockwarn.rb\\"" -exec(cmd) -exit!("must not reach here") +system(cmd) EOF launcher.close launcherpath = launcher.path diff --git a/test/ruby/test_marshal.rb b/test/ruby/test_marshal.rb index e4e4cc3c6a..ac804eb24c 100644 --- a/test/ruby/test_marshal.rb +++ b/test/ruby/test_marshal.rb @@ -252,6 +252,19 @@ module MarshalTestLib marshal_equal(o) {|obj| class << obj; ancestors end} end + def test_extend_string + o = String.new + o.extend Mod1 + marshal_equal(o) { |obj| obj.kind_of? Mod1 } + o = String.new + o.extend Module.new + assert_raises(TypeError) { marshaltest(o) } + o = String.new + o.extend Mod1 + o.extend Mod2 + marshal_equal(o) {|obj| class << obj; ancestors end} + end + def test_anonymous c = Class.new assert_raises(TypeError) { marshaltest(c) } diff --git a/test/soap/marshal/test_marshal.rb b/test/soap/marshal/test_marshal.rb index 21776c5fd1..cfdab39ab2 100644 --- a/test/soap/marshal/test_marshal.rb +++ b/test/soap/marshal/test_marshal.rb @@ -7,6 +7,10 @@ module Marshal module MarshalTestLib + + module Mod1; end + module Mod2; end + def encode(o) SOAPMarshal.dump(o) end @@ -23,19 +27,20 @@ module MarshalTestLib o2 end - def marshal_equal(o1) + def marshal_equal(o1, msg = nil) + msg = msg ? msg + "(#{ caller[0] })" : caller[0] o2 = marshaltest(o1) - assert_equal(o1.class, o2.class, caller[0]) + assert_equal(o1.class, o2.class, msg) iv1 = o1.instance_variables.sort iv2 = o2.instance_variables.sort assert_equal(iv1, iv2) val1 = iv1.map {|var| o1.instance_eval {eval var}} val2 = iv1.map {|var| o2.instance_eval {eval var}} - assert_equal(val1, val2, caller[0]) + assert_equal(val1, val2, msg) if block_given? - assert_equal(yield(o1), yield(o2), caller[0]) + assert_equal(yield(o1), yield(o2), msg) else - assert_equal(o1, o2, caller[0]) + assert_equal(o1, o2, msg) end end @@ -50,6 +55,30 @@ module MarshalTestLib marshal_equal(MyObject.new(2)) {|o| o.v} end + def test_object_extend + o1 = Object.new + o1.extend(Mod1) + marshal_equal(o1) { |o| + (class << self; self; end).ancestors + } + o1.extend(Mod2) + marshal_equal(o1) { |o| + (class << self; self; end).ancestors + } + end + + def test_object_subclass_extend + o1 = MyObject.new(2) + o1.extend(Mod1) + marshal_equal(o1) { |o| + (class << self; self; end).ancestors + } + o1.extend(Mod2) + marshal_equal(o1) { |o| + (class << self; self; end).ancestors + } + end + class MyArray < Array; def initialize(v, *args) super args; @v = v; end end def test_array marshal_equal([1,2,3]) @@ -59,6 +88,12 @@ module MarshalTestLib marshal_equal(MyArray.new(0, 1,2,3)) end + def test_array_ivar + o1 = Array.new + o1.instance_eval { @iv = 1 } + marshal_equal(o1) {|o| o.instance_eval { @iv }} + end + class MyException < Exception; def initialize(v, *args) super(*args); @v = v; end; attr_reader :v; end def test_exception marshal_equal(Exception.new('foo')) {|o| o.message} @@ -94,6 +129,36 @@ module MarshalTestLib assert_raises(TypeError) { marshaltest(h) } end + def test_hash_ivar + o1 = Hash.new + o1.instance_eval { @iv = 1 } + marshal_equal(o1) {|o| o.instance_eval { @iv }} + end + + def test_hash_extend + o1 = Hash.new + o1.extend(Mod1) + marshal_equal(o1) { |o| + (class << self; self; end).ancestors + } + o1.extend(Mod2) + marshal_equal(o1) { |o| + (class << self; self; end).ancestors + } + end + + def test_hash_subclass_extend + o1 = MyHash.new(2) + o1.extend(Mod1) + marshal_equal(o1) { |o| + (class << self; self; end).ancestors + } + o1.extend(Mod2) + marshal_equal(o1) { |o| + (class << self; self; end).ancestors + } + end + def test_bignum marshal_equal(-0x4000_0000_0000_0001) marshal_equal(-0x4000_0001) @@ -122,6 +187,24 @@ module MarshalTestLib marshal_equal(-0.0) {|o| 1.0/o} end + def test_float_ivar + o1 = 1.23 + o1.instance_eval { @iv = 1 } + marshal_equal(o1) {|o| o.instance_eval { @iv }} + end + + def test_float_extend + o1 = 0.0/0.0 + o1.extend(Mod1) + marshal_equal(o1) { |o| + (class << self; self; end).ancestors + } + o1.extend(Mod2) + marshal_equal(o1) { |o| + (class << self; self; end).ancestors + } + end + class MyRange < Range; def initialize(v, *args) super(*args); @v = v; end end def test_range marshal_equal(1..2) @@ -129,19 +212,17 @@ module MarshalTestLib end def test_range_subclass - STDERR.puts("test_range_subclass: known bug should be fixed.") - return marshal_equal(MyRange.new(4,5,8, false)) end class MyRegexp < Regexp; def initialize(v, *args) super(*args); @v = v; end end def test_regexp marshal_equal(/a/) + marshal_equal(/A/i) + marshal_equal(/A/mx) end def test_regexp_subclass - STDERR.puts("test_regexp_subclass: known bug should be fixed.") - return marshal_equal(MyRegexp.new(10, "a")) end @@ -150,10 +231,34 @@ module MarshalTestLib marshal_equal("abc") end + def test_string_ivar + o1 = String.new + o1.instance_eval { @iv = 1 } + marshal_equal(o1) {|o| o.instance_eval { @iv }} + end + def test_string_subclass marshal_equal(MyString.new(10, "a")) end + def test_string_subclass_cycle + str = MyString.new(10, "b") + str.instance_eval { @v = str } + marshal_equal(str) { |o| + assert_equal(o.__id__, o.instance_eval { @v }.__id__) + o.instance_eval { @v } + } + end + + def test_string_subclass_extend + o = "abc" + o.extend(Mod1) + str = MyString.new(o, "c") + marshal_equal(str) { |o| + assert(o.instance_eval { @v }).kind_of?(Mod1) + } + end + MyStruct = Struct.new("MyStruct", :a, :b) class MySubStruct < MyStruct; def initialize(v, *args) super(*args); @v = v; end end def test_struct @@ -164,6 +269,24 @@ module MarshalTestLib marshal_equal(MySubStruct.new(10,1,2)) end + def test_struct_ivar + o1 = MyStruct.new + o1.instance_eval { @iv = 1 } + marshal_equal(o1) {|o| o.instance_eval { @iv }} + end + + def test_struct_subclass_extend + o1 = MyStruct.new + o1.extend(Mod1) + marshal_equal(o1) { |o| + (class << self; self; end).ancestors + } + o1.extend(Mod2) + marshal_equal(o1) { |o| + (class << self; self; end).ancestors + } + end + def test_symbol marshal_equal(:a) marshal_equal(:a?) @@ -201,16 +324,21 @@ module MarshalTestLib def test_time # once there was a bug caused by usec overflow. try a little harder. 10.times do - marshal_equal(Time.now) + t = Time.now + marshal_equal(t, t.usec.to_s) end end def test_time_subclass - STDERR.puts("test_time_subclass: known bug should be fixed.") - return marshal_equal(MyTime.new(10)) end + def test_time_ivar + o1 = Time.now + o1.instance_eval { @iv = 1 } + marshal_equal(o1) {|o| o.instance_eval { @iv }} + end + def test_true marshal_equal(true) end @@ -250,22 +378,30 @@ module MarshalTestLib assert_raises(TypeError) { marshaltest(ENV) } end - module Mod1 end - module Mod2 end def test_extend o = Object.new + o.extend Mod1 + marshal_equal(o) { |obj| obj.kind_of? Mod1 } + o = Object.new + o.extend Mod1 + o.extend Mod2 + marshal_equal(o) {|obj| class << obj; ancestors end} + o = Object.new o.extend Module.new assert_raises(TypeError) { marshaltest(o) } + end - STDERR.puts("test_range_subclass: known bug should be fixed.") - return - o = Object.new + def test_extend_string + o = String.new o.extend Mod1 marshal_equal(o) { |obj| obj.kind_of? Mod1 } - o = Object.new + o = String.new o.extend Mod1 o.extend Mod2 marshal_equal(o) {|obj| class << obj; ancestors end} + o = String.new + o.extend Module.new + assert_raises(TypeError) { marshaltest(o) } end def test_anonymous diff --git a/test/wsdl/emptycomplextype.wsdl b/test/wsdl/emptycomplextype.wsdl index a4504c95ba..afd8dc239f 100644 --- a/test/wsdl/emptycomplextype.wsdl +++ b/test/wsdl/emptycomplextype.wsdl @@ -8,7 +8,6 @@ xmlns:i2="http://www.winfessor.com/SoapBoxWebService/ExceptionDataSet.xsd" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:i0="http://www.winfessor.com/SoapBoxWebService/MessageDataSet.xsd" - xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/" xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" targetNamespace="http://www.winfessor.com/SoapBoxWebService/SoapBoxWebService" xmlns="http://schemas.xmlsoap.org/wsdl/"> diff --git a/test/xsd/xmlschema.xml b/test/xsd/xmlschema.xml index f532e2934e..0e9914e64b 100644 --- a/test/xsd/xmlschema.xml +++ b/test/xsd/xmlschema.xml @@ -1,8 +1,15 @@ <?xml version="1.0" encoding="utf-8"?> -<xs:schema xmlns:mstns="http://www.winfessor.com/SoapBoxWebService/MessageDataSet.xsd" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns="http://www.winfessor.com/SoapBoxWebService/MessageDataSet.xsd" attributeFormDefault="qualified" elementFormDefault="qualified" targetNamespace="http://www.winfessor.com/SoapBoxWebService/MessageDataSet.xsd" id="MessageDataSet" xmlns:xs="http://www.w3.org/2001/XMLSchema"> - <xs:element msdata:IsDataSet="true" name="MessageDataSet"> +<xs:schema + xmlns:mstns="http://www.winfessor.com/SoapBoxWebService/MessageDataSet.xsd" + xmlns="http://www.winfessor.com/SoapBoxWebService/MessageDataSet.xsd" + attributeFormDefault="qualified" + elementFormDefault="qualified" + targetNamespace="http://www.winfessor.com/SoapBoxWebService/MessageDataSet.xsd" + id="MessageDataSet" + xmlns:xs="http://www.w3.org/2001/XMLSchema"> + <xs:element name="MessageDataSet"> <xs:complexType> <xs:choice maxOccurs="unbounded" /> </xs:complexType> </xs:element> -</xs:schema>
\ No newline at end of file +</xs:schema> |