summaryrefslogtreecommitdiff
path: root/lib/soap/mapping/mapping.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/soap/mapping/mapping.rb')
-rw-r--r--lib/soap/mapping/mapping.rb218
1 files changed, 218 insertions, 0 deletions
diff --git a/lib/soap/mapping/mapping.rb b/lib/soap/mapping/mapping.rb
new file mode 100644
index 0000000000..19eca5dab0
--- /dev/null
+++ b/lib/soap/mapping/mapping.rb
@@ -0,0 +1,218 @@
+=begin
+SOAP4R - Ruby type mapping utility.
+Copyright (C) 2000, 2001, 2003 NAKAMURA Hiroshi.
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+PRATICULAR PURPOSE. See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 675 Mass
+Ave, Cambridge, MA 02139, USA.
+=end
+
+
+module SOAP
+
+
+module Mapping
+ RubyTypeNamespace = 'http://www.ruby-lang.org/xmlns/ruby/type/1.6'
+ RubyTypeInstanceNamespace =
+ 'http://www.ruby-lang.org/xmlns/ruby/type-instance'
+ RubyCustomTypeNamespace = 'http://www.ruby-lang.org/xmlns/ruby/type/custom'
+ ApacheSOAPTypeNamespace = 'http://xml.apache.org/xml-soap'
+
+
+ # TraverseSupport breaks Thread.current[:SOAPMarshalDataKey].
+ module TraverseSupport
+ def mark_marshalled_obj(obj, soap_obj)
+ Thread.current[:SOAPMarshalDataKey][obj.__id__] = soap_obj
+ end
+
+ def mark_unmarshalled_obj(node, obj)
+ # node.id is not Object#id but SOAPReference#id
+ Thread.current[:SOAPMarshalDataKey][node.id] = obj
+ end
+ end
+
+
+ def self.obj2soap(obj, registry = nil, type = nil)
+ registry ||= Mapping::DefaultRegistry
+ Thread.current[:SOAPMarshalDataKey] = {}
+ soap_obj = _obj2soap(obj, registry, type)
+ Thread.current[:SOAPMarshalDataKey] = nil
+ soap_obj
+ end
+
+ def self.soap2obj(node, registry = nil)
+ registry ||= Mapping::DefaultRegistry
+ Thread.current[:SOAPMarshalDataKey] = {}
+ obj = _soap2obj(node, registry)
+ Thread.current[:SOAPMarshalDataKey] = nil
+ obj
+ end
+
+ def self.ary2soap(ary, type_ns = XSD::Namespace, typename = XSD::AnyTypeLiteral, registry = nil)
+ registry ||= Mapping::DefaultRegistry
+ type = XSD::QName.new(type_ns, typename)
+ soap_ary = SOAPArray.new(ValueArrayName, 1, type)
+ Thread.current[:SOAPMarshalDataKey] = {}
+ ary.each do |ele|
+ soap_ary.add(_obj2soap(ele, registry, type))
+ end
+ Thread.current[:SOAPMarshalDataKey] = nil
+ soap_ary
+ end
+
+ def self.ary2md(ary, rank, type_ns = XSD::Namespace, typename = XSD::AnyTypeLiteral, registry = nil)
+ registry ||= Mapping::DefaultRegistry
+ type = XSD::QName.new(type_ns, typename)
+ md_ary = SOAPArray.new(ValueArrayName, rank, type)
+ Thread.current[:SOAPMarshalDataKey] = {}
+ add_md_ary(md_ary, ary, [], registry)
+ Thread.current[:SOAPMarshalDataKey] = nil
+ md_ary
+ end
+
+ def self.fault2exception(e, registry = nil)
+ registry ||= Mapping::DefaultRegistry
+ detail = if e.detail
+ soap2obj(e.detail, registry) || ""
+ else
+ ""
+ end
+ if detail.is_a?(Mapping::SOAPException)
+ begin
+ raise detail.to_e
+ rescue Exception => e2
+ detail.set_backtrace(e2)
+ raise
+ end
+ else
+ e.detail = detail
+ e.set_backtrace(
+ if detail.is_a?(Array)
+ detail
+ else
+ [detail.to_s]
+ end
+ )
+ raise
+ end
+ end
+
+ def self._obj2soap(obj, registry, type = nil)
+ if referent = Thread.current[:SOAPMarshalDataKey][obj.__id__]
+ soap_obj = SOAPReference.new
+ soap_obj.__setobj__(referent)
+ soap_obj
+ else
+ registry.obj2soap(obj.class, obj, type)
+ end
+ end
+
+ def self._soap2obj(node, registry)
+ if node.is_a?(SOAPReference)
+ target = node.__getobj__
+ # target.id is not Object#id but SOAPReference#id
+ if referent = Thread.current[:SOAPMarshalDataKey][target.id]
+ return referent
+ else
+ return _soap2obj(target, registry)
+ end
+ end
+ return registry.soap2obj(node.class, node)
+ end
+
+
+ # Allow only (Letter | '_') (Letter | Digit | '-' | '_')* here.
+ # Caution: '.' is not allowed here.
+ # To follow XML spec., it should be NCName.
+ # (denied chars) => .[0-F][0-F]
+ # ex. a.b => a.2eb
+ #
+ def self.name2elename(name)
+ name.gsub(/([^a-zA-Z0-9:_\-]+)/n) {
+ '.' << $1.unpack('H2' * $1.size).join('.')
+ }.gsub(/::/n, '..')
+ end
+
+ def self.elename2name(name)
+ name.gsub(/\.\./n, '::').gsub(/((?:\.[0-9a-fA-F]{2})+)/n) {
+ [$1.delete('.')].pack('H*')
+ }
+ end
+
+ def self.class_from_name(name)
+ if /^[A-Z]/ !~ name
+ return nil
+ end
+ klass = ::Object
+ name.split('::').each do |klass_str|
+ if klass.const_defined?(klass_str)
+ klass = klass.const_get(klass_str)
+ else
+ return nil
+ end
+ end
+ klass
+ end
+
+ def self.class2qname(klass)
+ name = if klass.class_variables.include?("@@schema_type")
+ klass.class_eval("@@schema_type")
+ else
+ nil
+ end
+ namespace = if klass.class_variables.include?("@@schema_ns")
+ klass.class_eval("@@schema_ns")
+ else
+ nil
+ end
+ XSD::QName.new(namespace, name)
+ end
+
+ def self.class2element(klass)
+ type = Mapping.class2qname(klass)
+ type.name ||= Mapping.name2elename(klass.name)
+ type.namespace ||= RubyCustomTypeNamespace
+ type
+ end
+
+ def self.obj2element(obj)
+ name = namespace = nil
+ ivars = obj.instance_variables
+ if ivars.include?("@schema_type")
+ name = obj.instance_eval("@schema_type")
+ end
+ if ivars.include?("@schema_ns")
+ namespace = obj.instance_eval("@schema_ns")
+ end
+ if !name or !namespace
+ class2qname(obj.class)
+ else
+ XSD::QName.new(namespace, name)
+ end
+ end
+
+ class << Mapping
+ private
+ def add_md_ary(md_ary, ary, indices, registry)
+ for idx in 0..(ary.size - 1)
+ if ary[idx].is_a?(Array)
+ add_md_ary(md_ary, ary[idx], indices + [idx], registry)
+ else
+ md_ary[*(indices + [idx])] = _obj2soap(ary[idx], registry)
+ end
+ end
+ end
+ end
+end
+
+
+end