summaryrefslogtreecommitdiff
path: root/lib/soap
diff options
context:
space:
mode:
authornahi <nahi@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2003-09-24 15:18:44 +0000
committernahi <nahi@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2003-09-24 15:18:44 +0000
commitdb9445103c082a306ba085f7677da02ea94b8841 (patch)
treea311d59f031ae5def87f68be71ed1f58abadc031 /lib/soap
parent8c2fb77787d1f20b4c19c9c52552856c339b86e9 (diff)
* lib/soap/* (29 files): SOAP4R added.
* lib/wsdl/* (42 files): WSDL4R added. * lib/xsd/* (12 files): XSD4R added. * test/soap/* (16 files): added. * test/wsdl/* (2 files): added. * test/xsd/* (3 files): added. * sample/soap/* (27 files): added. * sample/wsdl/* (13 files): added. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@4591 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'lib/soap')
-rw-r--r--lib/soap/baseData.rb782
-rw-r--r--lib/soap/element.rb232
-rw-r--r--lib/soap/encodingstyle/aspDotNetHandler.rb232
-rw-r--r--lib/soap/encodingstyle/handler.rb111
-rw-r--r--lib/soap/encodingstyle/literalHandler.rb218
-rw-r--r--lib/soap/encodingstyle/soapHandler.rb548
-rw-r--r--lib/soap/generator.rb206
-rw-r--r--lib/soap/mapping.rb21
-rw-r--r--lib/soap/mapping/factory.rb310
-rw-r--r--lib/soap/mapping/mapping.rb218
-rw-r--r--lib/soap/mapping/registry.rb408
-rw-r--r--lib/soap/mapping/rubytypeFactory.rb437
-rw-r--r--lib/soap/mapping/typeMap.rb52
-rw-r--r--lib/soap/mapping/wsdlRegistry.rb146
-rw-r--r--lib/soap/marshal.rb71
-rw-r--r--lib/soap/netHttpClient.rb101
-rw-r--r--lib/soap/parser.rb252
-rw-r--r--lib/soap/processor.rb79
-rw-r--r--lib/soap/rpc/cgistub.rb214
-rw-r--r--lib/soap/rpc/driver.rb189
-rw-r--r--lib/soap/rpc/element.rb278
-rw-r--r--lib/soap/rpc/proxy.rb147
-rw-r--r--lib/soap/rpc/router.rb176
-rw-r--r--lib/soap/rpc/rpc.rb36
-rw-r--r--lib/soap/rpc/soaplet.rb167
-rw-r--r--lib/soap/rpc/standaloneServer.rb116
-rw-r--r--lib/soap/soap.rb112
-rw-r--r--lib/soap/streamHandler.rb188
-rw-r--r--lib/soap/wsdlDriver.rb490
29 files changed, 6537 insertions, 0 deletions
diff --git a/lib/soap/baseData.rb b/lib/soap/baseData.rb
new file mode 100644
index 0000000000..30f3bce767
--- /dev/null
+++ b/lib/soap/baseData.rb
@@ -0,0 +1,782 @@
+=begin
+SOAP4R - Base type library
+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
+
+
+require 'xsd/datatypes'
+require 'soap/soap'
+
+
+module SOAP
+
+
+###
+## Mix-in module for SOAP base type classes.
+#
+module SOAPModuleUtils
+ include SOAP
+
+public
+
+ def decode(elename)
+ d = self.new
+ d.elename = elename
+ d
+ end
+end
+
+
+###
+## Mix-in module for SOAP base type instances.
+#
+module SOAPBasetype
+ include SOAP
+
+ attr_accessor :encodingstyle
+
+ attr_accessor :elename
+ attr_accessor :id
+ attr_reader :precedents
+ attr_accessor :root
+ attr_accessor :parent
+ attr_accessor :position
+ attr_reader :extraattr
+
+public
+
+ def initialize(*vars)
+ super(*vars)
+ @encodingstyle = nil
+ @elename = XSD::QName.new
+ @id = nil
+ @precedents = []
+ @parent = nil
+ @position = nil
+ @extraattr = {}
+ end
+end
+
+
+###
+## Mix-in module for SOAP compound type instances.
+#
+module SOAPCompoundtype
+ include SOAP
+
+ attr_accessor :encodingstyle
+
+ attr_accessor :elename
+ attr_accessor :id
+ attr_reader :precedents
+ attr_accessor :root
+ attr_accessor :parent
+ attr_accessor :position
+ attr_reader :extraattr
+
+ attr_accessor :definedtype
+
+public
+
+ def initialize(type)
+ super()
+ @type = type
+ @encodingstyle = nil
+ @elename = XSD::QName.new
+ @id = nil
+ @precedents = []
+ @root = false
+ @parent = nil
+ @position = nil
+ @definedtype = nil
+ @extraattr = {}
+ end
+end
+
+
+###
+## Convenience datatypes.
+#
+class SOAPReference < XSD::NSDBase
+ include SOAPBasetype
+ extend SOAPModuleUtils
+
+public
+
+ attr_accessor :refid
+ attr_accessor :elename
+
+ # Override the definition in SOAPBasetype.
+ def initialize(refid = nil)
+ super()
+ @type = XSD::QName.new
+ @encodingstyle = nil
+ @elename = XSD::QName.new
+ @id = nil
+ @precedents = []
+ @root = false
+ @parent = nil
+ @refid = refid
+ @obj = nil
+ end
+
+ def __getobj__
+ @obj
+ end
+
+ def __setobj__(obj)
+ @obj = obj
+ @refid = SOAPReference.create_refid(@obj)
+ @obj.id = @refid unless @obj.id
+ @obj.precedents << self
+ # Copies NSDBase information
+ @obj.type = @type unless @obj.type
+ end
+
+ # Why don't I use delegate.rb?
+ # -> delegate requires target object type at initialize time.
+ # Why don't I use forwardable.rb?
+ # -> forwardable requires a list of forwarding methods.
+ #
+ # ToDo: Maybe I should use forwardable.rb and give it a methods list like
+ # delegate.rb...
+ #
+ def method_missing(msg_id, *params)
+ if @obj
+ @obj.send(msg_id, *params)
+ else
+ nil
+ end
+ end
+
+ def self.decode(elename, refid)
+ d = super(elename)
+ d.refid = refid
+ d
+ end
+
+ def SOAPReference.create_refid(obj)
+ 'id' << obj.__id__.to_s
+ end
+end
+
+class SOAPNil < XSD::XSDNil
+ include SOAPBasetype
+ extend SOAPModuleUtils
+end
+
+# SOAPRawString is for sending raw string. In contrast to SOAPString,
+# SOAP4R does not do XML encoding and does not convert its CES. The string it
+# holds is embedded to XML instance directly as a 'xsd:string'.
+class SOAPRawString < XSD::XSDString
+ include SOAPBasetype
+ extend SOAPModuleUtils
+end
+
+
+###
+## Basic datatypes.
+#
+class SOAPAnySimpleType < XSD::XSDAnySimpleType
+ include SOAPBasetype
+ extend SOAPModuleUtils
+end
+
+class SOAPString < XSD::XSDString
+ include SOAPBasetype
+ extend SOAPModuleUtils
+end
+
+class SOAPBoolean < XSD::XSDBoolean
+ include SOAPBasetype
+ extend SOAPModuleUtils
+end
+
+class SOAPDecimal < XSD::XSDDecimal
+ include SOAPBasetype
+ extend SOAPModuleUtils
+end
+
+class SOAPFloat < XSD::XSDFloat
+ include SOAPBasetype
+ extend SOAPModuleUtils
+end
+
+class SOAPDouble < XSD::XSDDouble
+ include SOAPBasetype
+ extend SOAPModuleUtils
+end
+
+class SOAPDuration < XSD::XSDDuration
+ include SOAPBasetype
+ extend SOAPModuleUtils
+end
+
+class SOAPDateTime < XSD::XSDDateTime
+ include SOAPBasetype
+ extend SOAPModuleUtils
+end
+
+class SOAPTime < XSD::XSDTime
+ include SOAPBasetype
+ extend SOAPModuleUtils
+end
+
+class SOAPDate < XSD::XSDDate
+ include SOAPBasetype
+ extend SOAPModuleUtils
+end
+
+class SOAPGYearMonth < XSD::XSDGYearMonth
+ include SOAPBasetype
+ extend SOAPModuleUtils
+end
+
+class SOAPGYear < XSD::XSDGYear
+ include SOAPBasetype
+ extend SOAPModuleUtils
+end
+
+class SOAPGMonthDay < XSD::XSDGMonthDay
+ include SOAPBasetype
+ extend SOAPModuleUtils
+end
+
+class SOAPGDay < XSD::XSDGDay
+ include SOAPBasetype
+ extend SOAPModuleUtils
+end
+
+class SOAPGMonth < XSD::XSDGMonth
+ include SOAPBasetype
+ extend SOAPModuleUtils
+end
+
+class SOAPHexBinary < XSD::XSDHexBinary
+ include SOAPBasetype
+ extend SOAPModuleUtils
+end
+
+class SOAPBase64 < XSD::XSDBase64Binary
+ include SOAPBasetype
+ extend SOAPModuleUtils
+ Type = QName.new(EncodingNamespace, Base64Literal)
+
+public
+ # Override the definition in SOAPBasetype.
+ def initialize(value = nil)
+ super(value)
+ @type = Type
+ end
+
+ def as_xsd
+ @type = XSD::XSDBase64Binary::Type
+ end
+end
+
+class SOAPAnyURI < XSD::XSDAnyURI
+ include SOAPBasetype
+ extend SOAPModuleUtils
+end
+
+class SOAPQName < XSD::XSDQName
+ include SOAPBasetype
+ extend SOAPModuleUtils
+end
+
+
+class SOAPInteger < XSD::XSDInteger
+ include SOAPBasetype
+ extend SOAPModuleUtils
+end
+
+class SOAPLong < XSD::XSDLong
+ include SOAPBasetype
+ extend SOAPModuleUtils
+end
+
+class SOAPInt < XSD::XSDInt
+ include SOAPBasetype
+ extend SOAPModuleUtils
+end
+
+class SOAPShort < XSD::XSDShort
+ include SOAPBasetype
+ extend SOAPModuleUtils
+end
+
+
+###
+## Compound datatypes.
+#
+class SOAPStruct < XSD::NSDBase
+ include SOAPCompoundtype
+ include Enumerable
+
+public
+
+ def initialize(type = nil)
+ super(type || XSD::QName.new)
+ @array = []
+ @data = []
+ end
+
+ def to_s()
+ str = ''
+ self.each do |key, data|
+ str << "#{ key }: #{ data }\n"
+ end
+ str
+ end
+
+ def add(name, value)
+ add_member(name, value)
+ end
+
+ def [](idx)
+ if idx.is_a?(Range)
+ @data[idx]
+ elsif idx.is_a?(Integer)
+ if (idx > @array.size)
+ raise ArrayIndexOutOfBoundsError.new('In ' << @type.name)
+ end
+ @data[idx]
+ else
+ if @array.include?(idx)
+ @data[@array.index(idx)]
+ else
+ nil
+ end
+ end
+ end
+
+ def []=(idx, data)
+ if @array.include?(idx)
+ @data[@array.index(idx)] = data
+ else
+ add(idx, data)
+ end
+ end
+
+ def key?(name)
+ @array.include?(name)
+ end
+
+ def members
+ @array
+ end
+
+ def each
+ for i in 0..(@array.length - 1)
+ yield(@array[i], @data[i])
+ end
+ end
+
+ def replace
+ members.each do |member|
+ self[member] = yield(self[member])
+ end
+ end
+
+ def self.decode(elename, type)
+ s = SOAPStruct.new(type)
+ s.elename = elename
+ s
+ end
+
+private
+
+ def add_member(name, value = nil)
+ value = SOAPNil.new() unless value
+ @array.push(name)
+ value.elename = value.elename.dup_name(name)
+ @data.push(value)
+ end
+end
+
+
+# SOAPElement is not typed so it does not derive NSDBase.
+class SOAPElement
+ include Enumerable
+
+ attr_accessor :encodingstyle
+ attr_accessor :extraattr
+ attr_reader :precedents
+
+ attr_accessor :qualified
+ attr_accessor :elename
+
+ def initialize(elename, text = nil)
+ if !elename.is_a?(XSD::QName)
+ elename = XSD::QName.new(nil, elename)
+ end
+ @encodingstyle = LiteralNamespace
+ @extraattr = {}
+ @precedents = []
+
+ @qualified = false
+ @elename = elename
+
+ @array = []
+ @data = []
+ @text = text
+ end
+
+ # Text interface.
+ attr_accessor :text
+
+ # Element interfaces.
+ def add(value)
+ add_member(value.elename.name, value)
+ end
+
+ def [](idx)
+ if @array.include?(idx)
+ @data[@array.index(idx)]
+ else
+ nil
+ end
+ end
+
+ def []=(idx, data)
+ if @array.include?(idx)
+ @data[@array.index(idx)] = data
+ else
+ add(data)
+ end
+ end
+
+ def key?(name)
+ @array.include?(name)
+ end
+
+ def members
+ @array
+ end
+
+ def to_obj
+ if members.empty?
+ @text
+ else
+ hash = {}
+ each do |k, v|
+ hash[k] = v.to_obj
+ end
+ hash
+ end
+ end
+
+ def each
+ for i in 0..(@array.length - 1)
+ yield(@array[i], @data[i])
+ end
+ end
+
+ def self.decode(elename)
+ o = SOAPElement.new
+ o.elename = elename
+ o
+ end
+
+ def self.from_obj(hash_or_string)
+ o = SOAPElement.new(nil)
+ if hash_or_string.is_a?(Hash)
+ hash_or_string.each do |k, v|
+ child = self.from_obj(v)
+ child.elename = XSD::QName.new(nil, k)
+ o.add(child)
+ end
+ else
+ o.text = hash_or_string
+ end
+ o
+ end
+
+private
+
+ def add_member(name, value)
+ add_accessor(name)
+ @array.push(name)
+ @data.push(value)
+ end
+
+ def add_accessor(name)
+ methodname = name
+ if self.respond_to?(methodname)
+ methodname = safe_accessor_name(methodname)
+ end
+ begin
+ instance_eval <<-EOS
+ def #{ methodname }()
+ @data[@array.index('#{ name }')]
+ end
+
+ def #{ methodname }=(value)
+ @data[@array.index('#{ name }')] = value
+ end
+ EOS
+ rescue SyntaxError
+ methodname = safe_accessor_name(methodname)
+ retry
+ end
+ end
+
+ def safe_accessor_name(name)
+ "var_" << name.gsub(/[^a-zA-Z0-9_]/, '')
+ end
+end
+
+
+class SOAPArray < XSD::NSDBase
+ include SOAPCompoundtype
+ include Enumerable
+
+public
+
+ attr_accessor :sparse
+
+ attr_reader :offset, :rank
+ attr_accessor :size, :size_fixed
+ attr_reader :arytype
+
+ def initialize(type = nil, rank = 1, arytype = nil)
+ super(type || XSD::QName.new)
+ @rank = rank
+ @data = Array.new
+ @sparse = false
+ @offset = Array.new(rank, 0)
+ @size = Array.new(rank, 0)
+ @size_fixed = false
+ @position = nil
+ @arytype = arytype
+ end
+
+ def offset=(var)
+ @offset = var
+ @sparse = true
+ end
+
+ def add(value)
+ self[*(@offset)] = value
+ end
+
+ def [](*idxary)
+ if idxary.size != @rank
+ raise ArgumentError.new("Given #{ idxary.size } params does not match rank: #{ @rank }")
+ end
+
+ retrieve(idxary)
+ end
+
+ def []=(*idxary)
+ value = idxary.slice!(-1)
+
+ if idxary.size != @rank
+ raise ArgumentError.new("Given #{ idxary.size } params(#{ idxary }) does not match rank: #{ @rank }")
+ end
+
+ for i in 0..(idxary.size - 1)
+ if idxary[i] + 1 > @size[i]
+ @size[i] = idxary[i] + 1
+ end
+ end
+
+ data = retrieve(idxary[0, idxary.size - 1])
+ data[idxary.last] = value
+
+ if value.is_a?(SOAPBasetype) || value.is_a?(SOAPCompoundtype)
+ value.elename = value.elename.dup_name('item')
+
+ # Sync type
+ unless @type.name
+ @type = XSD::QName.new(value.type.namespace,
+ SOAPArray.create_arytype(value.type.name, @rank))
+ end
+
+ unless value.type
+ value.type = @type
+ end
+ end
+
+ @offset = idxary
+ offsetnext
+ end
+
+ def each
+ @data.each do |data|
+ yield(data)
+ end
+ end
+
+ def to_a
+ @data.dup
+ end
+
+ def replace
+ @data = deep_map(@data) do |ele|
+ yield(ele)
+ end
+ end
+
+ def deep_map(ary, &block)
+ ary.collect do |ele|
+ if ele.is_a?(Array)
+ deep_map(ele, &block)
+ else
+ new_obj = block.call(ele)
+ new_obj.elename = new_obj.elename.dup_name('item')
+ new_obj
+ end
+ end
+ end
+
+ def include?(var)
+ traverse_data(@data) do |v, *rank|
+ if v.is_a?(SOAPBasetype) && v.data == var
+ return true
+ end
+ end
+ false
+ end
+
+ def traverse
+ traverse_data(@data) do |v, *rank|
+ unless @sparse
+ yield(v)
+ else
+ yield(v, *rank) if v && !v.is_a?(SOAPNil)
+ end
+ end
+ end
+
+ def soap2array(ary)
+ traverse_data(@data) do |v, *position|
+ iteary = ary
+ for rank in 1..(position.size - 1)
+ idx = position[rank - 1]
+ if iteary[idx].nil?
+ iteary = iteary[idx] = Array.new
+ else
+ iteary = iteary[idx]
+ end
+ end
+ if block_given?
+ iteary[position.last] = yield(v)
+ else
+ iteary[position.last] = v
+ end
+ end
+ end
+
+ def position
+ @position
+ end
+
+private
+
+ def retrieve(idxary)
+ data = @data
+ for rank in 1..(idxary.size)
+ idx = idxary[rank - 1]
+ if data[idx].nil?
+ data = data[idx] = Array.new
+ else
+ data = data[idx]
+ end
+ end
+ data
+ end
+
+ def traverse_data(data, rank = 1)
+ for idx in 0..(ranksize(rank) - 1)
+ if rank < @rank
+ traverse_data(data[idx], rank + 1) do |*v|
+ v[1, 0] = idx
+ yield(*v)
+ end
+ else
+ yield(data[idx], idx)
+ end
+ end
+ end
+
+ def ranksize(rank)
+ @size[rank - 1]
+ end
+
+ def offsetnext
+ move = false
+ idx = @offset.size - 1
+ while !move && idx >= 0
+ @offset[idx] += 1
+ if @size_fixed
+ if @offset[idx] < @size[idx]
+ move = true
+ else
+ @offset[idx] = 0
+ idx -= 1
+ end
+ else
+ move = true
+ end
+ end
+ end
+
+ # Module function
+
+public
+
+ def self.decode(elename, type, arytype)
+ typestr, nofary = parse_type(arytype.name)
+ rank = nofary.count(',') + 1
+ plain_arytype = XSD::QName.new(arytype.namespace, typestr)
+ o = SOAPArray.new(type, rank, plain_arytype)
+ size = []
+ nofary.split(',').each do |s|
+ if s.empty?
+ size.clear
+ break
+ else
+ size << s.to_i
+ end
+ end
+ unless size.empty?
+ o.size = size
+ o.size_fixed = true
+ end
+ o.elename = elename
+ o
+ end
+
+private
+
+ def self.create_arytype(typename, rank)
+ "#{ typename }[" << ',' * (rank - 1) << ']'
+ end
+
+ TypeParseRegexp = Regexp.new('^(.+)\[([\d,]*)\]$')
+
+ def self.parse_type(string)
+ TypeParseRegexp =~ string
+ return $1, $2
+ end
+end
+
+
+require 'soap/mapping/typeMap'
+
+
+end
diff --git a/lib/soap/element.rb b/lib/soap/element.rb
new file mode 100644
index 0000000000..640eafb0e4
--- /dev/null
+++ b/lib/soap/element.rb
@@ -0,0 +1,232 @@
+=begin
+SOAP4R - SOAP elements library
+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
+
+
+require 'xsd/qname'
+require 'soap/baseData'
+
+
+module SOAP
+
+
+###
+## SOAP elements
+#
+module SOAPEnvelopeElement; end
+
+class SOAPFault < SOAPStruct
+ include SOAPEnvelopeElement
+ include SOAPCompoundtype
+
+public
+
+ def faultcode
+ self['faultcode']
+ end
+
+ def faultstring
+ self['faultstring']
+ end
+
+ def faultactor
+ self['faultactor']
+ end
+
+ def detail
+ self['detail']
+ end
+
+ def faultcode=(rhs)
+ self['faultcode'] = rhs
+ end
+
+ def faultstring=(rhs)
+ self['faultstring'] = rhs
+ end
+
+ def faultactor=(rhs)
+ self['faultactor'] = rhs
+ end
+
+ def detail=(rhs)
+ self['detail'] = rhs
+ end
+
+ def initialize(faultcode = nil, faultstring = nil, faultactor = nil, detail = nil)
+ super(EleFaultName)
+ @elename = EleFaultName
+ @encodingstyle = EncodingNamespace
+
+ if faultcode
+ self.faultcode = faultcode
+ self.faultstring = faultstring
+ self.faultactor = faultactor
+ self.detail = detail
+ self.faultcode.elename = EleFaultCodeName if self.faultcode
+ self.faultstring.elename = EleFaultStringName if self.faultstring
+ self.faultactor.elename = EleFaultActorName if self.faultactor
+ self.detail.elename = EleFaultDetailName if self.detail
+ end
+ end
+
+ def encode(buf, ns, attrs = {}, indent = '')
+ 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)
+ 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)
+ end
+end
+
+
+class SOAPBody < SOAPStruct
+ include SOAPEnvelopeElement
+
+public
+
+ def initialize(data = nil, is_fault = false)
+ super(nil)
+ @elename = EleBodyName
+ @encodingstyle = nil
+ add(data.elename.name, data) if data
+ @is_fault = is_fault
+ end
+
+ def encode(buf, ns, attrs = {}, indent = '')
+ name = ns.name(@elename)
+ SOAPGenerator.encode_tag(buf, name, attrs, indent)
+ if @is_fault
+ yield(@data, true)
+ else
+ @data.each do |data|
+ yield(data, true)
+ end
+ end
+ SOAPGenerator.encode_tag_end(buf, name, indent, true)
+ end
+
+ def root_node
+ @data.each do |node|
+ if node.root == 1
+ return node
+ end
+ end
+ # No specified root...
+ @data.each do |node|
+ if node.root != 0
+ return node
+ end
+ end
+
+ raise SOAPParser::FormatDecodeError.new('No root element.')
+ end
+end
+
+
+class SOAPHeaderItem < XSD::NSDBase
+ include SOAPEnvelopeElement
+ include SOAPCompoundtype
+
+public
+
+ attr_accessor :content
+ attr_accessor :mustunderstand
+ attr_accessor :encodingstyle
+
+ def initialize(content, mustunderstand = true, encodingstyle = nil)
+ super(nil)
+ @content = content
+ @mustunderstand = mustunderstand
+ @encodingstyle = encodingstyle || LiteralNamespace
+ end
+
+ def encode(buf, ns, attrs = {}, indent = '')
+ attrs.each do |key, value|
+ @content.attr[key] = value
+ end
+ @content.attr[ns.name(EnvelopeNamespace, AttrMustUnderstand)] =
+ (@mustunderstand ? '1' : '0')
+ if @encodingstyle
+ @content.attr[ns.name(EnvelopeNamespace, AttrEncodingStyle)] =
+ @encodingstyle
+ end
+ @content.encodingstyle = @encodingstyle if !@content.encodingstyle
+ yield(@content, true)
+ end
+end
+
+
+class SOAPHeader < SOAPArray
+ include SOAPEnvelopeElement
+
+ def initialize()
+ super(nil, 1) # rank == 1
+ @elename = EleHeaderName
+ @encodingstyle = nil
+ end
+
+ def encode(buf, ns, attrs = {}, indent = '')
+ name = ns.name(@elename)
+ SOAPGenerator.encode_tag(buf, name, attrs, indent)
+ @data.each do |data|
+ yield(data, true)
+ end
+ SOAPGenerator.encode_tag_end(buf, name, indent, true)
+ end
+
+ def length
+ @data.length
+ end
+end
+
+
+class SOAPEnvelope < XSD::NSDBase
+ include SOAPEnvelopeElement
+ include SOAPCompoundtype
+
+ attr_accessor :header
+ attr_accessor :body
+
+ def initialize(header = nil, body = nil)
+ super(nil)
+ @elename = EleEnvelopeName
+ @encodingstyle = nil
+ @header = header
+ @body = body
+ end
+
+ def encode(buf, ns, attrs = {}, indent = '')
+ SOAPGenerator.assign_ns(attrs, ns, EnvelopeNamespace,
+ SOAPNamespaceTag)
+ name = ns.name(@elename)
+ SOAPGenerator.encode_tag(buf, name, attrs, indent)
+
+ yield(@header, true) if @header and @header.length > 0
+ yield(@body, true)
+
+ SOAPGenerator.encode_tag_end(buf, name, indent, true)
+ end
+end
+
+
+end
diff --git a/lib/soap/encodingstyle/aspDotNetHandler.rb b/lib/soap/encodingstyle/aspDotNetHandler.rb
new file mode 100644
index 0000000000..fdce42a48a
--- /dev/null
+++ b/lib/soap/encodingstyle/aspDotNetHandler.rb
@@ -0,0 +1,232 @@
+=begin
+SOAP4R - ASP.NET EncodingStyle handler library
+Copyright (C) 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
+
+
+require 'soap/encodingstyle/handler'
+
+
+module SOAP
+module EncodingStyle
+
+
+class ASPDotNetHandler < Handler
+ Namespace = 'http://tempuri.org/ASP.NET'
+ add_handler
+
+ def initialize(charset = nil)
+ super(charset)
+ @textbuf = ''
+ @decode_typemap = nil
+ end
+
+
+ ###
+ ## encode interface.
+ #
+ def encode_data(buf, ns, qualified, data, parent, indent = '')
+ attrs = {}
+ name = if qualified and data.elename.namespace
+ SOAPGenerator.assign_ns(attrs, ns, data.elename.namespace)
+ ns.name(data.elename)
+ else
+ data.elename.name
+ end
+
+ case data
+ when SOAPRawString
+ SOAPGenerator.encode_tag(buf, name, attrs, indent)
+ buf << 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)
+ when XSD::XSDAnySimpleType
+ SOAPGenerator.encode_tag(buf, name, attrs, indent)
+ buf << SOAPGenerator.encode_str(data.to_s)
+ when SOAPStruct
+ SOAPGenerator.encode_tag(buf, name, attrs, indent)
+ data.each do |key, value|
+ if !value.elename.namespace
+ value.elename.namespace = data.elename.namespace
+ end
+ yield(value, true)
+ end
+ when SOAPArray
+ SOAPGenerator.encode_tag(buf, name, attrs, indent)
+ data.traverse do |child, *rank|
+ data.position = nil
+ yield(child, true)
+ end
+ else
+ raise EncodingStyleError.new("Unknown object:#{ data } in this encodingSt
+yle.")
+ end
+ end
+
+ def encode_data_end(buf, ns, qualified, data, parent, indent = "")
+ 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)
+ end
+
+
+ ###
+ ## decode interface.
+ #
+ class SOAPTemporalObject
+ attr_accessor :parent
+
+ def initialize
+ @parent = nil
+ end
+ end
+
+ class SOAPUnknown < SOAPTemporalObject
+ def initialize(handler, elename)
+ super()
+ @handler = handler
+ @elename = elename
+ end
+
+ def as_struct
+ o = SOAPStruct.decode(@elename, XSD::AnyTypeName)
+ o.parent = @parent
+ o.type.name = @name
+ @handler.decode_parent(@parent, o)
+ o
+ end
+
+ def as_string
+ o = SOAPString.decode(@elename)
+ o.parent = @parent
+ @handler.decode_parent(@parent, o)
+ o
+ end
+
+ def as_nil
+ o = SOAPNil.decode(@elename)
+ o.parent = @parent
+ @handler.decode_parent(@parent, o)
+ o
+ end
+ end
+
+ def decode_tag(ns, elename, attrs, parent)
+ # ToDo: check if @textbuf is empty...
+ @textbuf = ''
+ o = SOAPUnknown.new(self, elename)
+ o.parent = parent
+ o
+ end
+
+ def decode_tag_end(ns, node)
+ o = node.node
+ if o.is_a?(SOAPUnknown)
+ newnode = o.as_string
+# if /\A\s*\z/ =~ @textbuf
+# o.as_struct
+# else
+# o.as_string
+# end
+ node.replace_node(newnode)
+ o = node.node
+ end
+
+ decode_textbuf(o)
+ @textbuf = ''
+ end
+
+ def decode_text(ns, text)
+ # @textbuf is set at decode_tag_end.
+ @textbuf << text
+ end
+
+ def decode_prologue
+ end
+
+ def decode_epilogue
+ end
+
+ def decode_parent(parent, node)
+ case parent.node
+ when SOAPUnknown
+ newparent = parent.node.as_struct
+ node.parent = newparent
+ parent.replace_node(newparent)
+ decode_parent(parent, node)
+
+ when SOAPStruct
+ data = parent.node[node.elename.name]
+ case data
+ when nil
+ parent.node.add(node.elename.name, node)
+ when SOAPArray
+ name, type_ns = node.elename.name, node.type.namespace
+ data.add(node)
+ node.elename, node.type.namespace = name, type_ns
+ else
+ parent.node[node.elename.name] = SOAPArray.new
+ name, type_ns = data.elename.name, data.type.namespace
+ parent.node[node.elename.name].add(data)
+ data.elename.name, data.type.namespace = name, type_ns
+ name, type_ns = node.elename.name, node.type.namespace
+ parent.node[node.elename.name].add(node)
+ node.elename.name, node.type.namespace = name, type_ns
+ end
+
+ when SOAPArray
+ if node.position
+ parent.node[*(decode_arypos(node.position))] = node
+ parent.node.sparse = true
+ else
+ parent.node.add(node)
+ end
+
+ when SOAPBasetype
+ raise EncodingStyleError.new("SOAP base type must not have a child.")
+
+ else
+ # SOAPUnknown does not have parent.
+ # raise EncodingStyleError.new("Illegal parent: #{ parent }.")
+ end
+ end
+
+private
+
+ def decode_textbuf(node)
+ if node.is_a?(XSD::XSDString)
+ if @charset
+ node.set(XSD::Charset.encoding_from_xml(@textbuf, @charset))
+ else
+ node.set(@textbuf)
+ end
+ else
+ # Nothing to do...
+ end
+ end
+end
+
+ASPDotNetHandler.new
+
+
+end
+end
diff --git a/lib/soap/encodingstyle/handler.rb b/lib/soap/encodingstyle/handler.rb
new file mode 100644
index 0000000000..1ab9d86ec5
--- /dev/null
+++ b/lib/soap/encodingstyle/handler.rb
@@ -0,0 +1,111 @@
+=begin
+SOAP4R - EncodingStyle handler library
+Copyright (C) 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
+
+
+require 'soap/soap'
+require 'soap/baseData'
+require 'soap/element'
+
+
+module SOAP
+module EncodingStyle
+
+
+class Handler
+ @@handlers = {}
+
+ class EncodingStyleError < Error; end
+
+ class << self
+ def uri
+ self::Namespace
+ end
+
+ def handler(uri)
+ @@handlers[uri]
+ end
+
+ def each
+ @@handlers.each do |key, value|
+ yield(value)
+ end
+ end
+
+ private
+
+ def add_handler
+ @@handlers[self.uri] = self
+ end
+ end
+
+ attr_reader :charset
+ attr_accessor :generate_explicit_type
+ def decode_typemap=(complextypes)
+ @decode_typemap = complextypes
+ end
+
+ def initialize(charset)
+ @charset = charset
+ @generate_explicit_type = true
+ @decode_typemap = nil
+ end
+
+ ###
+ ## encode interface.
+ #
+ # Returns a XML instance as a string.
+ def encode_data(buf, ns, qualified, data, parent, indent)
+ raise NotImplementError.new('Method encode_data must be defined in derived class.')
+ end
+
+ def encode_data_end(buf, ns, qualified, data, parent, indent)
+ raise NotImplementError.new('Method encode_data must be defined in derived class.')
+ end
+
+ def encode_prologue
+ end
+
+ def encode_epilogue
+ end
+
+ ###
+ ## decode interface.
+ #
+ # Returns SOAP/OM data.
+ def decode_tag(ns, name, attrs, parent)
+ raise NotImplementError.new('Method decode_tag must be defined in derived class.')
+ end
+
+ def decode_tag_end(ns, name)
+ raise NotImplementError.new('Method decode_tag_end must be defined in derived class.')
+ end
+
+ def decode_text(ns, text)
+ raise NotImplementError.new('Method decode_text must be defined in derived class.')
+ end
+
+ def decode_prologue
+ end
+
+ def decode_epilogue
+ end
+end
+
+
+end
+end
diff --git a/lib/soap/encodingstyle/literalHandler.rb b/lib/soap/encodingstyle/literalHandler.rb
new file mode 100644
index 0000000000..b5d0d464d7
--- /dev/null
+++ b/lib/soap/encodingstyle/literalHandler.rb
@@ -0,0 +1,218 @@
+=begin
+SOAP4R - XML Literal EncodingStyle handler library
+Copyright (C) 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
+
+
+require 'soap/encodingstyle/handler'
+
+
+module SOAP
+module EncodingStyle
+
+
+class LiteralHandler < Handler
+ Namespace = SOAP::LiteralNamespace
+ add_handler
+
+ def initialize(charset = nil)
+ super(charset)
+ @textbuf = ''
+ end
+
+
+ ###
+ ## encode interface.
+ #
+ def encode_data(buf, ns, qualified, data, parent, indent = '')
+ attrs = {}
+ name = if qualified and data.elename.namespace
+ SOAPGenerator.assign_ns(attrs, ns, data.elename.namespace)
+ ns.name(data.elename)
+ else
+ data.elename.name
+ end
+
+ case data
+ when SOAPRawString
+ SOAPGenerator.encode_tag(buf, name, attrs, indent)
+ buf << 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)
+ when XSD::XSDAnySimpleType
+ SOAPGenerator.encode_tag(buf, name, attrs, indent)
+ buf << SOAPGenerator.encode_str(data.to_s)
+ when SOAPStruct
+ SOAPGenerator.encode_tag(buf, name, attrs, indent)
+ 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)
+ 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
+ data.each do |key, value|
+ value.elename.namespace = data.elename.namespace if !value.elename.namespace
+ #yield(value, data.qualified)
+ yield(value, qualified)
+ end
+ else
+ raise EncodingStyleError.new("Unknown object:#{ data } in this encodingStyle.")
+ end
+ end
+
+ def encode_data_end(buf, ns, qualified, data, parent, indent)
+ name = if qualified and data.elename.namespace
+ ns.name(data.elename)
+ else
+ data.elename.name
+ end
+ SOAPGenerator.encode_tag_end(buf, name, indent)
+ end
+
+
+ ###
+ ## decode interface.
+ #
+ class SOAPTemporalObject
+ attr_accessor :parent
+
+ def initialize
+ @parent = nil
+ end
+ end
+
+ class SOAPUnknown < SOAPTemporalObject
+ def initialize(handler, elename)
+ super()
+ @handler = handler
+ @elename = elename
+ end
+
+ def as_struct
+ o = SOAPStruct.decode(@elename, XSD::AnyTypeName)
+ o.parent = @parent
+ @handler.decode_parent(@parent, o)
+ o
+ end
+
+ def as_string
+ o = SOAPString.decode(@elename)
+ o.parent = @parent
+ @handler.decode_parent(@parent, o)
+ o
+ end
+
+ def as_nil
+ o = SOAPNil.decode(@elename)
+ o.parent = @parent
+ @handler.decode_parent(@parent, o)
+ o
+ end
+ end
+
+ def decode_tag(ns, elename, attrs, parent)
+ # ToDo: check if @textbuf is empty...
+ @textbuf = ''
+ o = SOAPUnknown.new(self, elename)
+ o.parent = parent
+ o
+ end
+
+ def decode_tag_end(ns, node)
+ o = node.node
+ if o.is_a?(SOAPUnknown)
+ newnode = if /\A\s*\z/ =~ @textbuf
+ o.as_struct
+ else
+ o.as_string
+ end
+ node.replace_node(newnode)
+ o = node.node
+ end
+
+ decode_textbuf(o)
+ @textbuf = ''
+ end
+
+ def decode_text(ns, text)
+ # @textbuf is set at decode_tag_end.
+ @textbuf << text
+ end
+
+ def decode_prologue
+ end
+
+ def decode_epilogue
+ end
+
+ def decode_parent(parent, node)
+ case parent.node
+ when SOAPUnknown
+ newparent = parent.node.as_struct
+ node.parent = newparent
+ parent.replace_node(newparent)
+ decode_parent(parent, node)
+
+ when SOAPStruct
+ parent.node.add(node.name, node)
+
+ when SOAPArray
+ if node.position
+ parent.node[*(decode_arypos(node.position))] = node
+ parent.node.sparse = true
+ else
+ parent.node.add(node)
+ end
+
+ when SOAPBasetype
+ raise EncodingStyleError.new("SOAP base type must not have a child.")
+
+ else
+ # SOAPUnknown does not have parent.
+ # raise EncodingStyleError.new("Illegal parent: #{ parent }.")
+ end
+ end
+
+private
+
+ def decode_textbuf(node)
+ if node.is_a?(XSD::XSDString)
+ if @charset
+ node.set(XSD::Charset.encoding_from_xml(@textbuf, @charset))
+ else
+ node.set(@textbuf)
+ end
+ else
+ # Nothing to do...
+ end
+ end
+end
+
+LiteralHandler.new
+
+
+end
+end
diff --git a/lib/soap/encodingstyle/soapHandler.rb b/lib/soap/encodingstyle/soapHandler.rb
new file mode 100644
index 0000000000..b1b5072e49
--- /dev/null
+++ b/lib/soap/encodingstyle/soapHandler.rb
@@ -0,0 +1,548 @@
+=begin
+SOAP4R - SOAP EncodingStyle handler library
+Copyright (C) 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
+
+
+require 'soap/encodingstyle/handler'
+
+
+module SOAP
+module EncodingStyle
+
+
+class SOAPHandler < Handler
+ Namespace = SOAP::EncodingNamespace
+ add_handler
+
+ def initialize(charset = nil)
+ super(charset)
+ @refpool = []
+ @idpool = []
+ @textbuf = ''
+ @is_first_top_ele = true
+ end
+
+
+ ###
+ ## encode interface.
+ #
+ def encode_data(buf, ns, qualified, data, parent, indent = '')
+ attrs = encode_attrs(ns, qualified, data, parent)
+
+ if parent && parent.is_a?(SOAPArray) && parent.position
+ attrs[ns.name(AttrPositionName)] = '[' << parent.position.join(',') << ']'
+ end
+
+ name = nil
+ if qualified and data.elename.namespace
+ SOAPGenerator.assign_ns(attrs, ns, data.elename.namespace)
+ name = ns.name(data.elename)
+ else
+ 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)
+ when SOAPRawString
+ SOAPGenerator.encode_tag(buf, name, attrs, indent)
+ buf << 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)
+ when XSD::XSDAnySimpleType
+ SOAPGenerator.encode_tag(buf, name, attrs, indent)
+ buf << SOAPGenerator.encode_str(data.to_s)
+ when SOAPStruct
+ SOAPGenerator.encode_tag(buf, name, attrs, indent)
+ data.each do |key, value|
+ yield(value, false)
+ end
+ when SOAPArray
+ SOAPGenerator.encode_tag(buf, name, attrs, indent)
+ data.traverse do |child, *rank|
+ data.position = data.sparse ? rank : nil
+ yield(child, false)
+ end
+ else
+ raise EncodingStyleError.new(
+ "Unknown object:#{ data } in this encodingStyle.")
+ end
+ end
+
+ def encode_data_end(buf, ns, qualified, data, parent, indent = '')
+ 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)
+ end
+
+
+ ###
+ ## decode interface.
+ #
+ class SOAPTemporalObject
+ attr_accessor :parent
+ attr_accessor :position
+ attr_accessor :id
+ attr_accessor :root
+
+ def initialize
+ @parent = nil
+ @position = nil
+ @id = nil
+ @root = nil
+ end
+ end
+
+ class SOAPUnknown < SOAPTemporalObject
+ attr_reader :type
+ attr_accessor :definedtype
+ attr_reader :extraattr
+
+ def initialize(handler, elename, type, extraattr)
+ super()
+ @handler = handler
+ @elename = elename
+ @type = type
+ @extraattr = extraattr
+ @definedtype = nil
+ end
+
+ def as_struct
+ o = SOAPStruct.decode(@elename, @type)
+ o.id = @id
+ o.root = @root
+ o.parent = @parent
+ o.position = @position
+ o.extraattr.update(@extraattr)
+ @handler.decode_parent(@parent, o)
+ o
+ end
+
+ def as_string
+ o = SOAPString.decode(@elename)
+ o.id = @id
+ o.root = @root
+ o.parent = @parent
+ o.position = @position
+ o.extraattr.update(@extraattr)
+ @handler.decode_parent(@parent, o)
+ o
+ end
+
+ def as_nil
+ o = SOAPNil.decode(@elename)
+ o.id = @id
+ o.root = @root
+ o.parent = @parent
+ o.position = @position
+ o.extraattr.update(@extraattr)
+ @handler.decode_parent(@parent, o)
+ o
+ end
+ end
+
+ def decode_tag(ns, elename, attrs, parent)
+ # ToDo: check if @textbuf is empty...
+ @textbuf = ''
+ is_nil, type, arytype, root, offset, position, href, id, extraattr =
+ decode_attrs(ns, attrs)
+ o = nil
+ if is_nil
+ o = SOAPNil.decode(elename)
+ elsif href
+ o = SOAPReference.decode(elename, href)
+ @refpool << o
+ elsif @decode_typemap &&
+ (parent.node.class != SOAPBody || @is_first_top_ele)
+ # multi-ref element should be parsed by decode_tag_by_type.
+ @is_first_top_ele = false
+ o = decode_tag_by_wsdl(ns, elename, type, parent.node, arytype, extraattr)
+ else
+ o = decode_tag_by_type(ns, elename, type, parent.node, arytype, extraattr)
+ end
+
+ if o.is_a?(SOAPArray)
+ if offset
+ o.offset = decode_arypos(offset)
+ o.sparse = true
+ else
+ o.sparse = false
+ end
+ end
+
+ o.parent = parent
+ o.id = id
+ o.root = root
+ o.position = position
+
+ unless o.is_a?(SOAPTemporalObject)
+ @idpool << o if o.id
+ decode_parent(parent, o)
+ end
+ o
+ end
+
+ def decode_tag_end(ns, node)
+ o = node.node
+ if o.is_a?(SOAPUnknown)
+ newnode = if /\A\s*\z/ =~ @textbuf
+ o.as_struct
+ else
+ o.as_string
+ end
+ if newnode.id
+ @idpool << newnode
+ end
+ node.replace_node(newnode)
+ o = node.node
+ end
+ if o.is_a?(SOAPCompoundtype)
+ o.definedtype = nil
+ end
+
+ decode_textbuf(o)
+ @textbuf = ''
+ end
+
+ def decode_text(ns, text)
+ # @textbuf is set at decode_tag_end.
+ @textbuf << text
+ end
+
+ def decode_prologue
+ @refpool.clear
+ @idpool.clear
+ @is_first_top_ele = true
+ end
+
+ def decode_epilogue
+ decode_resolve_id
+ end
+
+ def decode_parent(parent, node)
+ case parent.node
+ when SOAPUnknown
+ newparent = parent.node.as_struct
+ node.parent = newparent
+ if newparent.id
+ @idpool << newparent
+ end
+ parent.replace_node(newparent)
+ decode_parent(parent, node)
+
+ when SOAPStruct
+ parent.node.add(node.elename.name, node)
+ node.parent = parent.node
+
+ when SOAPArray
+ if node.position
+ parent.node[*(decode_arypos(node.position))] = node
+ parent.node.sparse = true
+ else
+ parent.node.add(node)
+ end
+ node.parent = parent.node
+
+ when SOAPBasetype
+ raise EncodingStyleError.new("SOAP base type must not have a child.")
+
+ else
+ raise EncodingStyleError.new("Illegal parent: #{ parent.node }.")
+ end
+ end
+
+private
+
+ def content_ranksize(typename)
+ typename.scan(/\[[\d,]*\]$/)[0]
+ end
+
+ def content_typename(typename)
+ typename.sub(/\[,*\]$/, '')
+ end
+
+ def create_arytype(ns, data)
+ XSD::QName.new(data.arytype.namespace,
+ content_typename(data.arytype.name) << '[' << data.size.join(',') << ']')
+ end
+
+ def encode_attrs(ns, qualified, data, parent)
+ return {} if data.is_a?(SOAPReference)
+ attrs = {}
+
+ if !parent || parent.encodingstyle != EncodingNamespace
+ if @generate_explicit_type
+ SOAPGenerator.assign_ns(attrs, ns, EnvelopeNamespace)
+ SOAPGenerator.assign_ns(attrs, ns, EncodingNamespace)
+ attrs[ns.name(AttrEncodingStyleName)] = EncodingNamespace
+ end
+ data.encodingstyle = EncodingNamespace
+ end
+
+ if data.is_a?(SOAPNil)
+ attrs[ns.name(XSD::AttrNilName)] = XSD::NilValue
+ elsif @generate_explicit_type
+ if data.type.namespace
+ SOAPGenerator.assign_ns(attrs, ns, data.type.namespace)
+ end
+ if data.is_a?(SOAPArray)
+ if data.arytype.namespace
+ SOAPGenerator.assign_ns(attrs, ns, data.arytype.namespace)
+ end
+ attrs[ns.name(AttrArrayTypeName)] = ns.name(create_arytype(ns, data))
+ if data.type.name
+ attrs[ns.name(XSD::AttrTypeName)] = ns.name(data.type)
+ end
+ elsif parent && parent.is_a?(SOAPArray) && (parent.arytype == data.type)
+ # No need to add.
+ elsif !data.type.namespace
+ # No need to add.
+ else
+ attrs[ns.name(XSD::AttrTypeName)] = ns.name(data.type)
+ end
+ end
+
+ data.extraattr.each do |key, value|
+ SOAPGenerator.assign_ns(attrs, ns, key.namespace)
+ attrs[ns.name(key)] = value # ns.name(value) ?
+ end
+ if data.id
+ attrs['id'] = data.id
+ end
+ attrs
+ end
+
+ def decode_tag_by_wsdl(ns, elename, typestr, parent, arytypestr, extraattr)
+ if parent.class == SOAPBody
+ # Unqualified name is allowed here.
+ type = @decode_typemap[elename] || @decode_typemap.find_name(elename.name)
+ unless type
+ raise EncodingStyleError.new("Unknown operation '#{ elename }'.")
+ end
+ o = SOAPStruct.new(elename)
+ o.definedtype = type
+ return o
+ end
+
+ if parent.type == XSD::AnyTypeName
+ return decode_tag_by_type(ns, elename, typestr, parent, arytypestr,
+ extraattr)
+ end
+
+ # parent.definedtype is nil means the parent is SOAPUnknown. SOAPUnknown is
+ # generated by decode_tag_by_type when its type is anyType.
+ parenttype = parent.definedtype || @decode_typemap[parent.type]
+ unless parenttype
+ raise EncodingStyleError.new("Unknown type '#{ parent.type }'.")
+ end
+ typename = parenttype.child_type(elename)
+ if typename
+ if (klass = TypeMap[typename])
+ return klass.decode(elename)
+ elsif typename == XSD::AnyTypeName
+ return decode_tag_by_type(ns, elename, typestr, parent, arytypestr,
+ extraattr)
+ end
+ end
+
+ type = if typename
+ @decode_typemap[typename]
+ else
+ parenttype.child_defined_complextype(elename)
+ end
+ unless type
+ raise EncodingStyleError.new("Unknown type '#{ typename }'.")
+ end
+
+ case type.compoundtype
+ when :TYPE_STRUCT
+ o = SOAPStruct.decode(elename, typename)
+ o.definedtype = type
+ return o
+ when :TYPE_ARRAY
+ expected_arytype = type.find_arytype
+ actual_arytype = if arytypestr
+ XSD::QName.new(expected_arytype.namespace,
+ content_typename(expected_arytype.name) <<
+ content_ranksize(arytypestr))
+ else
+ expected_arytype
+ end
+ o = SOAPArray.decode(elename, typename, actual_arytype)
+ o.definedtype = type
+ return o
+ end
+ return nil
+ end
+
+ def decode_tag_by_type(ns, elename, typestr, parent, arytypestr, extraattr)
+ if arytypestr
+ type = typestr ? ns.parse(typestr) : ValueArrayName
+ node = SOAPArray.decode(elename, type, ns.parse(arytypestr))
+ node.extraattr.update(extraattr)
+ return node
+ end
+
+ type = nil
+ if typestr
+ type = ns.parse(typestr)
+ elsif parent.is_a?(SOAPArray)
+ type = parent.arytype
+ else
+ # Since it's in dynamic(without any type) encoding process,
+ # assumes entity as its type itself.
+ # <SOAP-ENC:Array ...> => type Array in SOAP-ENC.
+ # <Country xmlns="foo"> => type Country in foo.
+ type = elename
+ end
+
+ if (klass = TypeMap[type])
+ klass.decode(elename)
+ else
+ # Unknown type... Struct or String
+ SOAPUnknown.new(self, elename, type, extraattr)
+ end
+ end
+
+ def decode_textbuf(node)
+ case node
+ when XSD::XSDHexBinary, XSD::XSDBase64Binary
+ node.set_encoded(@textbuf)
+ when XSD::XSDString
+ if @charset
+ node.set(XSD::Charset.encoding_from_xml(@textbuf, @charset))
+ else
+ node.set(@textbuf)
+ end
+ when SOAPNil
+ # Nothing to do.
+ when SOAPBasetype
+ node.set(@textbuf)
+ else
+ # Nothing to do...
+ end
+ end
+
+ NilLiteralMap = {
+ 'true' => true,
+ '1' => true,
+ 'false' => false,
+ '0' => false
+ }
+ RootLiteralMap = {
+ '1' => 1,
+ '0' => 0
+ }
+ def decode_attrs(ns, attrs)
+ is_nil = false
+ type = nil
+ arytype = nil
+ root = nil
+ offset = nil
+ position = nil
+ href = nil
+ id = nil
+ extraattr = {}
+
+ attrs.each do |key, value|
+ qname = ns.parse(key)
+ case qname.namespace
+ when XSD::InstanceNamespace
+ case qname.name
+ when XSD::NilLiteral
+ is_nil = NilLiteralMap[value] or
+ raise EncodingStyleError.new("Cannot accept attribute value: #{ value } as the value of xsi:#{ XSD::NilLiteral } (expected 'true', 'false', '1', or '0').")
+ next
+ when XSD::AttrType
+ type = value
+ next
+ end
+ when EncodingNamespace
+ case qname.name
+ when AttrArrayType
+ arytype = value
+ next
+ when AttrRoot
+ root = RootLiteralMap[value] or
+ raise EncodingStyleError.new(
+ "Illegal root attribute value: #{ value }.")
+ next
+ when AttrOffset
+ offset = value
+ next
+ when AttrPosition
+ position = value
+ next
+ end
+ end
+ if key == 'href'
+ href = value
+ next
+ elsif key == 'id'
+ id = value
+ next
+ end
+ extraattr[qname] = value
+ end
+
+ return is_nil, type, arytype, root, offset, position, href, id, extraattr
+ end
+
+ def decode_arypos(position)
+ /^\[(.+)\]$/ =~ position
+ $1.split(',').collect { |s| s.to_i }
+ end
+
+ def decode_resolve_id
+ count = @refpool.length # To avoid infinite loop
+ while !@refpool.empty? && count > 0
+ @refpool = @refpool.find_all { |ref|
+ o = @idpool.find { |item|
+ ('#' << item.id == ref.refid)
+ }
+ unless o
+ raise EncodingStyleError.new("Unresolved reference: #{ ref.refid }.")
+ end
+ if o.is_a?(SOAPReference)
+ true
+ else
+ ref.__setobj__(o)
+ false
+ end
+ }
+ count -= 1
+ end
+ end
+end
+
+SOAPHandler.new
+
+
+end
+end
diff --git a/lib/soap/generator.rb b/lib/soap/generator.rb
new file mode 100644
index 0000000000..6707aef195
--- /dev/null
+++ b/lib/soap/generator.rb
@@ -0,0 +1,206 @@
+=begin
+SOAP4R - SOAP XML Instance Generator library.
+Copyright (C) 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
+
+
+require 'xsd/ns'
+require 'soap/soap'
+require 'soap/baseData'
+require 'soap/encodingstyle/handler'
+
+
+module SOAP
+
+
+###
+## CAUTION: MT-unsafe
+#
+class SOAPGenerator
+ include SOAP
+
+ class FormatEncodeError < Error; end
+
+public
+
+ attr_accessor :charset
+ attr_accessor :default_encodingstyle
+ attr_accessor :generate_explicit_type
+ attr_accessor :pretty
+
+ def initialize(opt = {})
+ @reftarget = nil
+ @handlers = {}
+ @charset = opt[:charset] || XSD::Charset.encoding_label
+ @default_encodingstyle = opt[:default_encodingstyle] || EncodingNamespace
+ @generate_explicit_type =
+ opt.key?(:generate_explicit_type) ? opt[:generate_explicit_type] : true
+ @pretty = true # opt[:pretty]
+ end
+
+ def generate(obj, io = nil)
+ 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)
+
+ @handlers.each do |uri, handler|
+ handler.encode_epilogue
+ end
+ epilogue
+
+ io
+ end
+
+ def encode_data(buf, ns, qualified, obj, parent, indent)
+ if obj.is_a?(SOAPEnvelopeElement)
+ encode_element(buf, ns, qualified, obj, parent, indent)
+ return
+ end
+
+ if @reftarget && !obj.precedents.empty?
+ @reftarget.add(obj.elename.name, obj)
+ ref = SOAPReference.new
+ ref.elename.name = obj.elename.name
+ ref.__setobj__(obj)
+ obj.precedents.clear # Avoid cyclic delay.
+ obj.encodingstyle = parent.encodingstyle
+ # SOAPReference is encoded here.
+ obj = ref
+ end
+
+ encodingstyle = obj.encodingstyle
+ # Children's encodingstyle is derived from its parent.
+ encodingstyle ||= parent.encodingstyle if parent
+ obj.encodingstyle = encodingstyle
+
+ handler = find_handler(encodingstyle || @default_encodingstyle)
+ unless handler
+ raise FormatEncodeError.new("Unknown encodingStyle: #{ encodingstyle }.")
+ end
+
+ if !obj.elename.name
+ 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)
+ end
+ handler.encode_data_end(buf, ns, qualified, obj, parent, indent_str)
+ end
+
+ def encode_element(buf, ns, qualified, obj, parent, indent)
+ indent_str = ' ' * indent
+ child_indent = @pretty ? indent + 2 : indent
+ 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)
+ end
+ @reftarget = nil
+ else
+ if obj.is_a?(SOAPEnvelope)
+ # xsi:nil="true" can appear even if dumping without explicit type.
+ SOAPGenerator.assign_ns(attrs, ns,
+ XSD::InstanceNamespace, XSINamespaceTag)
+ if @generate_explicit_type
+ 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)
+ 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 = '')
+ if attrs
+ buf << "\n#{ indent }<#{ elename }" <<
+ attrs.collect { |key, value|
+ %Q[ #{ key }="#{ value }"]
+ }.join <<
+ '>'
+ else
+ buf << "\n#{ indent }<#{ elename }>"
+ end
+ end
+
+ def self.encode_tag_end(buf, elename, indent = '', cr = nil)
+ if cr
+ buf << "\n#{ indent }</#{ elename }>"
+ else
+ buf << "</#{ elename }>"
+ end
+ end
+
+ EncodeMap = {
+ '&' => '&amp;',
+ '<' => '&lt;',
+ '>' => '&gt;',
+ '"' => '&quot;',
+ '\'' => '&apos;',
+ "\r" => '&#xd;'
+ }
+ EncodeCharRegexp = Regexp.new("[#{EncodeMap.keys.join}]")
+ def self.encode_str(str)
+ str.gsub(EncodeCharRegexp) { |c| EncodeMap[c] }
+ end
+
+private
+
+ def prologue
+ end
+
+ def epilogue
+ end
+
+ def find_handler(encodingstyle)
+ unless @handlers.key?(encodingstyle)
+ handler = SOAP::EncodingStyle::Handler.handler(encodingstyle).new(@charset)
+ handler.generate_explicit_type = @generate_explicit_type
+ handler.encode_prologue
+ @handlers[encodingstyle] = handler
+ end
+ @handlers[encodingstyle]
+ end
+
+ def xmldecl
+ if @charset
+ %Q[<?xml version="1.0" encoding="#{ @charset }" ?>]
+ else
+ %Q[<?xml version="1.0" ?>]
+ end
+ end
+end
+
+
+end
diff --git a/lib/soap/mapping.rb b/lib/soap/mapping.rb
new file mode 100644
index 0000000000..8da1978a4f
--- /dev/null
+++ b/lib/soap/mapping.rb
@@ -0,0 +1,21 @@
+=begin
+SOAP4R - Ruby type mapping utility.
+Copyright (C) 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
+
+
+require 'soap/mapping/mapping'
+require 'soap/mapping/registry'
diff --git a/lib/soap/mapping/factory.rb b/lib/soap/mapping/factory.rb
new file mode 100644
index 0000000000..2e5ddc1f15
--- /dev/null
+++ b/lib/soap/mapping/factory.rb
@@ -0,0 +1,310 @@
+=begin
+SOAP4R - Mapping factory.
+Copyright (C) 2000, 2001, 2002, 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
+
+
+class Factory
+ include TraverseSupport
+
+ def obj2soap(soap_class, obj, info, map)
+ raise NotImplementError.new
+ # return soap_obj
+ end
+
+ def soap2obj(obj_class, node, info, map)
+ raise NotImplementError.new
+ # return convert_succeeded_or_not, obj
+ end
+
+ if Object.respond_to?(:allocate)
+ # ruby/1.7 or later.
+ def create_empty_object(klass)
+ klass.allocate
+ end
+ else
+ def create_empty_object(klass)
+ name = klass.name
+ # Below line is from TANAKA, Akira's amarshal.rb.
+ # See http://cvs.m17n.org/cgi-bin/viewcvs/amarshal/?cvsroot=ruby
+ ::Marshal.load(sprintf("\004\006o:%c%s\000", name.length + 5, name))
+ 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)
+ end
+
+ def setiv2soap(node, obj, map)
+ obj.instance_variables.each do |var|
+ name = var.sub(/^@/, '')
+ node.add(Mapping.name2elename(name),
+ Mapping._obj2soap(obj.instance_eval(var), map))
+ 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
+ end
+
+ # It breaks Thread.current[:SOAPMarshalDataKey].
+ def mark_unmarshalled_obj(node, obj)
+ Thread.current[:SOAPMarshalDataKey][node.id] = obj
+ end
+
+ def name2typename(name)
+ capitalize(name)
+ end
+
+ def capitalize(target)
+ target.gsub(/^([a-z])/) { $1.tr!('[a-z]', '[A-Z]') }
+ end
+end
+
+class StringFactory_ < Factory
+ def obj2soap(soap_class, obj, info, map)
+ 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
+ end
+ rescue XSD::ValueSpaceError
+ return nil
+ end
+ mark_marshalled_obj(obj, soap_obj)
+ soap_obj
+ end
+
+ def soap2obj(obj_class, node, info, map)
+ obj = XSD::Charset.encoding_conv(node.data, XSD::Charset.encoding, $KCODE)
+ mark_unmarshalled_obj(node, obj)
+ return true, obj
+ end
+end
+
+class BasetypeFactory_ < Factory
+ def obj2soap(soap_class, obj, info, map)
+ soap_obj = nil
+ begin
+ soap_obj = soap_class.new(obj)
+ rescue XSD::ValueSpaceError
+ return nil
+ end
+ # Basetype except String should not be multiref-ed in SOAP/1.1.
+ soap_obj
+ end
+
+ def soap2obj(obj_class, node, info, map)
+ obj = node.data
+ mark_unmarshalled_obj(node, obj)
+ return true, obj
+ end
+end
+
+class DateTimeFactory_ < Factory
+ def obj2soap(soap_class, obj, info, map)
+ soap_obj = nil
+ begin
+ soap_obj = soap_class.new(obj)
+ rescue XSD::ValueSpaceError
+ return nil
+ end
+ mark_marshalled_obj(obj, soap_obj)
+ soap_obj
+ end
+
+ def soap2obj(obj_class, node, info, map)
+ obj = nil
+ if obj_class == Time
+ obj = node.to_time
+ if obj.nil?
+ # Is out of range as a Time
+ return false
+ end
+ elsif obj_class == Date
+ obj = node.data
+ else
+ return false
+ end
+ mark_unmarshalled_obj(node, obj)
+ return true, obj
+ end
+end
+
+class Base64Factory_ < Factory
+ def obj2soap(soap_class, obj, info, map)
+ soap_obj = soap_class.new(obj)
+ mark_marshalled_obj(obj, soap_obj) if soap_obj
+ soap_obj
+ end
+
+ def soap2obj(obj_class, node, info, map)
+ obj = node.string
+ mark_unmarshalled_obj(node, obj)
+ return true, obj
+ end
+end
+
+class ArrayFactory_ < Factory
+ # [[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)
+ arytype = Mapping.obj2element(obj)
+ if arytype.name
+ arytype.namespace ||= RubyTypeNamespace
+ else
+ arytype = XSD::AnyTypeName
+ end
+ param = SOAPArray.new(ValueArrayName, 1, arytype)
+ mark_marshalled_obj(obj, param)
+ obj.each do |var|
+ param.add(Mapping._obj2soap(var, map))
+ end
+ param
+ end
+
+ def soap2obj(obj_class, node, info, map)
+ obj = create_empty_object(obj_class)
+ mark_unmarshalled_obj(node, obj)
+ node.soap2array(obj) do |elem|
+ elem ? Mapping._soap2obj(elem, map) : nil
+ end
+ return true, obj
+ end
+end
+
+class TypedArrayFactory_ < Factory
+ def obj2soap(soap_class, obj, info, map)
+ arytype = info[:type] || info[0]
+ param = SOAPArray.new(ValueArrayName, 1, arytype)
+ mark_marshalled_obj(obj, param)
+ obj.each do |var|
+ param.add(Mapping._obj2soap(var, map))
+ end
+ param
+ end
+
+ def soap2obj(obj_class, node, info, map)
+ if node.rank > 1
+ return false
+ end
+ arytype = info[:type] || info[0]
+ unless node.arytype == arytype
+ return false
+ end
+ obj = create_empty_object(obj_class)
+ mark_unmarshalled_obj(node, obj)
+ node.soap2array(obj) do |elem|
+ elem ? Mapping._soap2obj(elem, map) : nil
+ end
+ return true, obj
+ end
+end
+
+class TypedStructFactory_ < Factory
+ def obj2soap(soap_class, obj, info, map)
+ type = info[:type] || info[0]
+ param = soap_class.new(type)
+ mark_marshalled_obj(obj, param)
+ if obj.class <= SOAP::Marshallable
+ setiv2soap(param, obj, map)
+ else
+ setiv2soap(param, obj, map)
+ end
+ param
+ end
+
+ def soap2obj(obj_class, node, info, map)
+ type = info[:type] || info[0]
+ unless node.type == type
+ return false
+ end
+ obj = create_empty_object(obj_class)
+ mark_unmarshalled_obj(node, obj)
+ setiv2obj(obj, node, map)
+ return true, obj
+ end
+end
+
+MapQName = XSD::QName.new(ApacheSOAPTypeNamespace, 'Map')
+class HashFactory_ < Factory
+ def obj2soap(soap_class, obj, info, map)
+ if obj.default or
+ (obj.respond_to?(:default_proc) and obj.default_proc)
+ return nil
+ end
+ param = SOAPStruct.new(MapQName)
+ mark_marshalled_obj(obj, param)
+ obj.each do |key, value|
+ elem = SOAPStruct.new
+ elem.add("key", Mapping._obj2soap(key, map))
+ elem.add("value", Mapping._obj2soap(value, map))
+ # ApacheAxis allows only 'item' here.
+ param.add("item", elem)
+ end
+ param
+ end
+
+ def soap2obj(obj_class, node, info, map)
+ unless node.type == MapQName
+ return false
+ end
+ if node.key?('default')
+ return false
+ end
+ obj = create_empty_object(obj_class)
+ mark_unmarshalled_obj(node, obj)
+ node.each do |key, value|
+ obj[Mapping._soap2obj(value['key'], map)] =
+ Mapping._soap2obj(value['value'], map)
+ end
+ return true, obj
+ end
+end
+
+
+end
+end
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
diff --git a/lib/soap/mapping/registry.rb b/lib/soap/mapping/registry.rb
new file mode 100644
index 0000000000..568f34ac3e
--- /dev/null
+++ b/lib/soap/mapping/registry.rb
@@ -0,0 +1,408 @@
+=begin
+SOAP4R - Mapping registry.
+Copyright (C) 2000, 2001, 2002, 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
+
+
+require 'soap/baseData'
+require 'soap/mapping/mapping'
+require 'soap/mapping/typeMap'
+require 'soap/mapping/factory'
+require 'soap/mapping/rubytypeFactory'
+
+
+module SOAP
+
+
+module Marshallable
+ # @@type_ns = Mapping::RubyCustomTypeNamespace
+end
+
+
+module Mapping
+
+
+module MappedException; end
+
+
+RubyTypeName = XSD::QName.new(RubyTypeInstanceNamespace, 'rubyType')
+
+
+# Inner class to pass an exception.
+class SOAPException; include Marshallable
+ attr_reader :excn_type_name, :message, :backtrace, :cause
+ def initialize(e)
+ @excn_type_name = Mapping.name2elename(e.class.to_s)
+ @message = e.message
+ @backtrace = e.backtrace
+ @cause = e
+ end
+
+ def to_e
+ if @cause.is_a?(::Exception)
+ @cause.extend(::SOAP::Mapping::MappedException)
+ return @cause
+ end
+ klass = Mapping.class_from_name(
+ Mapping.elename2name(@excn_type_name.to_s))
+ if klass.nil?
+ raise RuntimeError.new(@message)
+ end
+ unless klass <= ::Exception
+ raise NameError.new
+ end
+ obj = klass.new(@message)
+ obj.extend(::SOAP::Mapping::MappedException)
+ obj
+ end
+
+ def set_backtrace(e)
+ e.set_backtrace(
+ if @backtrace.is_a?(Array)
+ @backtrace
+ else
+ [@backtrace.inspect]
+ end
+ )
+ end
+end
+
+
+# For anyType object: SOAP::Mapping::Object not ::Object
+class Object; include Marshallable
+ def set_property(name, value)
+ var_name = name
+ begin
+ instance_eval <<-EOS
+ def #{ var_name }
+ @#{ var_name }
+ end
+
+ def #{ var_name }=(value)
+ @#{ var_name } = value
+ end
+ EOS
+ self.send(var_name + '=', value)
+ rescue SyntaxError
+ var_name = safe_name(var_name)
+ retry
+ end
+
+ var_name
+ end
+
+ def members
+ instance_variables.collect { |str| str[1..-1] }
+ end
+
+ def [](name)
+ if self.respond_to?(name)
+ self.send(name)
+ else
+ self.send(safe_name(name))
+ end
+ end
+
+ def []=(name, value)
+ if self.respond_to?(name)
+ self.send(name + '=', value)
+ else
+ self.send(safe_name(name) + '=', value)
+ end
+ end
+
+private
+
+ def safe_name(name)
+ require 'md5'
+ "var_" << MD5.new(name).hexdigest
+ end
+end
+
+
+class MappingError < Error; end
+
+
+class Registry
+ class Map
+ def initialize(registry)
+ @map = []
+ @registry = registry
+ end
+
+ def obj2soap(klass, obj)
+ @map.each do |obj_class, soap_class, factory, info|
+ if klass == obj_class or
+ (info[:derived_class] and klass <= obj_class)
+ ret = factory.obj2soap(soap_class, obj, info, @registry)
+ return ret if ret
+ end
+ end
+ nil
+ end
+
+ def soap2obj(klass, node)
+ @map.each do |obj_class, soap_class, factory, info|
+ if klass == soap_class or
+ (info[:derived_class] and klass <= soap_class)
+ conv, obj = factory.soap2obj(obj_class, node, info, @registry)
+ return true, obj if conv
+ end
+ end
+ return false
+ end
+
+ # Give priority to former entry.
+ def init(init_map = [])
+ clear
+ init_map.reverse_each do |obj_class, soap_class, factory, info|
+ add(obj_class, soap_class, factory, info)
+ end
+ end
+
+ # Give priority to latter entry.
+ def add(obj_class, soap_class, factory, info)
+ info ||= {}
+ @map.unshift([obj_class, soap_class, factory, info])
+ end
+
+ def clear
+ @map.clear
+ end
+
+ def find_mapped_soap_class(target_obj_class)
+ @map.each do |obj_class, soap_class, factory, info|
+ if obj_class == target_obj_class
+ return soap_class
+ end
+ end
+ nil
+ end
+
+ def find_mapped_obj_class(target_soap_class)
+ @map.each do |obj_class, soap_class, factory, info|
+ if soap_class == target_soap_class
+ return obj_class
+ end
+ end
+ nil
+ end
+ end
+
+ StringFactory = StringFactory_.new
+ BasetypeFactory = BasetypeFactory_.new
+ DateTimeFactory = DateTimeFactory_.new
+ ArrayFactory = ArrayFactory_.new
+ Base64Factory = Base64Factory_.new
+ TypedArrayFactory = TypedArrayFactory_.new
+ TypedStructFactory = TypedStructFactory_.new
+
+ HashFactory = HashFactory_.new
+
+ SOAPBaseMap = [
+ [::NilClass, ::SOAP::SOAPNil, BasetypeFactory],
+ [::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],
+ [::Float, ::SOAP::SOAPDouble, BasetypeFactory,
+ {:derived_class => true}],
+ [::Float, ::SOAP::SOAPFloat, BasetypeFactory,
+ {:derived_class => true}],
+ [::Integer, ::SOAP::SOAPInt, BasetypeFactory,
+ {:derived_class => true}],
+ [::Integer, ::SOAP::SOAPLong, BasetypeFactory,
+ {:derived_class => true}],
+ [::Integer, ::SOAP::SOAPInteger, BasetypeFactory,
+ {:derived_class => true}],
+ [::Integer, ::SOAP::SOAPShort, BasetypeFactory,
+ {:derived_class => true}],
+ [::URI::Generic, ::SOAP::SOAPAnyURI, BasetypeFactory,
+ {:derived_class => true}],
+ [::String, ::SOAP::SOAPBase64, Base64Factory],
+ [::String, ::SOAP::SOAPHexBinary, Base64Factory],
+ [::String, ::SOAP::SOAPDecimal, BasetypeFactory],
+ [::String, ::SOAP::SOAPDuration, BasetypeFactory],
+ [::String, ::SOAP::SOAPGYearMonth, BasetypeFactory],
+ [::String, ::SOAP::SOAPGYear, BasetypeFactory],
+ [::String, ::SOAP::SOAPGMonthDay, BasetypeFactory],
+ [::String, ::SOAP::SOAPGDay, BasetypeFactory],
+ [::String, ::SOAP::SOAPGMonth, BasetypeFactory],
+ [::String, ::SOAP::SOAPQName, BasetypeFactory],
+
+ [::Array, ::SOAP::SOAPArray, ArrayFactory,
+ {:derived_class => true}],
+
+ [::Hash, ::SOAP::SOAPStruct, HashFactory],
+ [::SOAP::Mapping::SOAPException,
+ ::SOAP::SOAPStruct, TypedStructFactory,
+ {:type => XSD::QName.new(RubyCustomTypeNamespace, "SOAPException")}],
+ ]
+
+ RubyOriginalMap = [
+ [::NilClass, ::SOAP::SOAPNil, BasetypeFactory],
+ [::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],
+ [::Float, ::SOAP::SOAPDouble, BasetypeFactory,
+ {:derived_class => true}],
+ [::Float, ::SOAP::SOAPFloat, BasetypeFactory,
+ {:derived_class => true}],
+ [::Integer, ::SOAP::SOAPInt, BasetypeFactory,
+ {:derived_class => true}],
+ [::Integer, ::SOAP::SOAPLong, BasetypeFactory,
+ {:derived_class => true}],
+ [::Integer, ::SOAP::SOAPInteger, BasetypeFactory,
+ {:derived_class => true}],
+ [::Integer, ::SOAP::SOAPShort, BasetypeFactory,
+ {:derived_class => true}],
+ [::URI::Generic, ::SOAP::SOAPAnyURI, BasetypeFactory,
+ {:derived_class => true}],
+ [::String, ::SOAP::SOAPBase64, Base64Factory],
+ [::String, ::SOAP::SOAPHexBinary, Base64Factory],
+ [::String, ::SOAP::SOAPDecimal, BasetypeFactory],
+ [::String, ::SOAP::SOAPDuration, BasetypeFactory],
+ [::String, ::SOAP::SOAPGYearMonth, BasetypeFactory],
+ [::String, ::SOAP::SOAPGYear, BasetypeFactory],
+ [::String, ::SOAP::SOAPGMonthDay, BasetypeFactory],
+ [::String, ::SOAP::SOAPGDay, BasetypeFactory],
+ [::String, ::SOAP::SOAPGMonth, BasetypeFactory],
+ [::String, ::SOAP::SOAPQName, BasetypeFactory],
+
+ # Does not allow Array's subclass here.
+ [::Array, ::SOAP::SOAPArray, ArrayFactory],
+
+ [::Hash, ::SOAP::SOAPStruct, HashFactory],
+ [::SOAP::Mapping::SOAPException,
+ ::SOAP::SOAPStruct, TypedStructFactory,
+ {:type => XSD::QName.new(RubyCustomTypeNamespace, "SOAPException")}],
+ ]
+
+ def initialize(config = {})
+ @config = config
+ @map = Map.new(self)
+ if @config[:allow_original_mapping]
+ allow_original_mapping = true
+ @map.init(RubyOriginalMap)
+ else
+ allow_original_mapping = false
+ @map.init(SOAPBaseMap)
+ end
+
+ allow_untyped_struct = @config.key?(:allow_untyped_struct) ?
+ @config[:allow_untyped_struct] : true
+ @rubytype_factory = RubytypeFactory.new(
+ :allow_untyped_struct => allow_untyped_struct,
+ :allow_original_mapping => allow_original_mapping
+ )
+ @default_factory = @rubytype_factory
+ @excn_handler_obj2soap = nil
+ @excn_handler_soap2obj = nil
+ end
+
+ def add(obj_class, soap_class, factory, info = nil)
+ @map.add(obj_class, soap_class, factory, info)
+ end
+ alias :set :add
+
+ # This mapping registry ignores type hint.
+ def obj2soap(klass, obj, type = nil)
+ ret = nil
+ if obj.is_a?(SOAPStruct) || obj.is_a?(SOAPArray)
+ obj.replace do |ele|
+ Mapping._obj2soap(ele, self)
+ end
+ return obj
+ elsif obj.is_a?(SOAPBasetype)
+ return obj
+ end
+ begin
+ ret = @map.obj2soap(klass, obj) ||
+ @default_factory.obj2soap(klass, obj, nil, self)
+ 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)
+ if node.extraattr.key?(RubyTypeName)
+ conv, obj = @rubytype_factory.soap2obj(klass, node, nil, self)
+ return obj if conv
+ else
+ conv, obj = @map.soap2obj(klass, node)
+ return obj if conv
+ conv, obj = @default_factory.soap2obj(klass, node, nil, self)
+ return obj if conv
+ end
+
+ if @excn_handler_soap2obj
+ begin
+ return @excn_handler_soap2obj.call(node) { |yield_node|
+ Mapping._soap2obj(yield_node, self)
+ }
+ rescue Exception
+ end
+ end
+
+ raise MappingError.new("Cannot map #{ node.type.name } to Ruby object.")
+ end
+
+ 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
+end
+
+
+DefaultRegistry = Registry.new
+RubyOriginalRegistry = Registry.new(:allow_original_mapping => true)
+
+
+end
+end
diff --git a/lib/soap/mapping/rubytypeFactory.rb b/lib/soap/mapping/rubytypeFactory.rb
new file mode 100644
index 0000000000..0a3f502dfe
--- /dev/null
+++ b/lib/soap/mapping/rubytypeFactory.rb
@@ -0,0 +1,437 @@
+=begin
+SOAP4R - Ruby type mapping factory.
+Copyright (C) 2000, 2001, 2002, 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
+
+
+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'
+
+ 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
+ end
+
+ def obj2soap(soap_class, obj, info, map)
+ param = nil
+ case obj
+ when String
+ unless @allow_original_mapping
+ return nil
+ end
+ unless XSD::Charset.is_ces(obj, $KCODE)
+ 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.extraattr[RubyTypeName] = obj.class.name
+ end
+ addiv2soap(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
+ 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)
+ end
+ if obj.class != Array
+ param.extraattr[RubyTypeName] = obj.class.name
+ end
+ when Regexp
+ param = SOAPStruct.new(XSD::QName.new(RubyTypeNamespace, TYPE_REGEXP))
+ mark_marshalled_obj(obj, param)
+ if obj.class != Regexp
+ param.extraattr[RubyTypeName] = obj.class.name
+ end
+ param.add('source', SOAPBase64.new(obj.source))
+ if obj.respond_to?('options')
+ # Regexp#options is from Ruby/1.7
+ options = obj.options
+ else
+ options = 0
+ obj.inspect.sub(/^.*\//, '').each_byte do |c|
+ options += case c
+ when ?i
+ 1
+ when ?x
+ 2
+ when ?m
+ 4
+ when ?n
+ 16
+ when ?e
+ 32
+ when ?s
+ 48
+ when ?u
+ 64
+ end
+ end
+ end
+ param.add('options', SOAPInt.new(options))
+ addiv2soap(param, obj, map)
+ when Range
+ param = SOAPStruct.new(XSD::QName.new(RubyTypeNamespace, TYPE_RANGE))
+ mark_marshalled_obj(obj, param)
+ if obj.class != Range
+ param.extraattr[RubyTypeName] = obj.class.name
+ end
+ 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
+ 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))
+ mark_marshalled_obj(obj, param)
+ param.add('name', SOAPString.new(obj.name))
+ addiv2soap(param, obj, map)
+ when Module
+ if obj.name.empty?
+ raise TypeError.new("Can't dump anonymous module #{ obj }.")
+ end
+ param = SOAPStruct.new(XSD::QName.new(RubyTypeNamespace, TYPE_MODULE))
+ mark_marshalled_obj(obj, param)
+ param.add('name', SOAPString.new(obj.name))
+ addiv2soap(param, obj, map)
+ when Symbol
+ param = SOAPStruct.new(XSD::QName.new(RubyTypeNamespace, 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)
+ when Struct
+ param = SOAPStruct.new(XSD::QName.new(RubyTypeNamespace, TYPE_STRUCT))
+ mark_marshalled_obj(obj, param)
+ param.add('type', ele_type = SOAPString.new(obj.class.to_s))
+ ele_member = SOAPStruct.new
+ obj.members.each do |member|
+ ele_member.add(Mapping.name2elename(member),
+ Mapping._obj2soap(obj[member], map))
+ end
+ param.add('member', ele_member)
+ addiv2soap(param, obj, map)
+ when IO, Binding, Continuation, Data, Dir, File::Stat, MatchData, Method,
+ Proc, Thread, ThreadGroup
+ 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)
+ mark_marshalled_obj(obj, param)
+ if obj.class <= Marshallable
+ setiv2soap(param, obj, map)
+ else
+ setiv2soap(param, obj, map) # Should not be marshalled?
+ end
+ end
+ param
+ end
+
+ def soap2obj(obj_class, node, info, map)
+ rubytype = node.extraattr[RubyTypeName]
+ if rubytype or node.type.namespace == RubyTypeNamespace
+ rubytype2obj(node, map, rubytype)
+ elsif node.type == XSD::AnyTypeName or node.type == XSD::AnySimpleTypeName
+ anytype2obj(node, map)
+ else
+ unknowntype2obj(node, map)
+ end
+ end
+
+private
+
+ def check_singleton(obj)
+ unless singleton_methods_true(obj).empty?
+ return true
+ end
+ singleton_class = class << obj; self; end
+ if !singleton_class.instance_variables.empty? or
+ !(singleton_class.ancestors - obj.class.ancestors).empty?
+ return true
+ end
+ false
+ end
+
+ if RUBY_VERSION >= '1.8.0'
+ def singleton_methods_true(obj)
+ obj.singleton_methods(true)
+ end
+ else
+ def singleton_methods_true(obj)
+ obj.singleton_methods
+ end
+ end
+
+ def rubytype2obj(node, map, rubytype)
+ obj = nil
+ case node.class
+ when SOAPString
+ obj = string2obj(node, map, rubytype)
+ obj.replace(node.data)
+ 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
+ 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)
+ 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)
+ when TYPE_RANGE
+ klass = rubytype ? Mapping.class_from_name(rubytype) : Range
+ obj = create_empty_object(klass)
+ mark_unmarshalled_obj(node, obj)
+ 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)
+ 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)
+ if klass.nil?
+ klass = Mapping.class_from_name(name2typename(typestr))
+ end
+ if klass.nil?
+ return false
+ end
+ unless klass <= ::Struct
+ return false
+ end
+ obj = create_empty_object(klass)
+ mark_unmarshalled_obj(node, obj)
+ node['member'].each do |name, value|
+ 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)
+ 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)
+ case node
+ when SOAPBasetype
+ return true, node.data
+ when SOAPStruct
+ klass = ::SOAP::Mapping::Object
+ obj = klass.new
+ mark_unmarshalled_obj(node, obj)
+ node.each do |name, value|
+ obj.set_property(name, Mapping._soap2obj(value, map))
+ end
+ return true, obj
+ else
+ return false
+ end
+ end
+
+ def unknowntype2obj(node, map)
+ if node.is_a?(SOAPStruct)
+ obj = struct2obj(node, map)
+ return true, obj if obj
+ if !@allow_untyped_struct
+ return false
+ end
+ return anytype2obj(node, map)
+ else
+ # Basetype which is not defined...
+ return false
+ end
+ end
+
+ def struct2obj(node, map)
+ obj = nil
+ typestr = Mapping.elename2name(node.type.name)
+ klass = Mapping.class_from_name(typestr)
+ if klass.nil?
+ klass = Mapping.class_from_name(name2typename(typestr))
+ end
+ if klass.nil?
+ return nil
+ end
+ klass_type = Mapping.class2qname(klass)
+ return nil unless node.type.match(klass_type)
+ obj = create_empty_object(klass)
+ mark_unmarshalled_obj(node, obj)
+ setiv2obj(obj, node, map)
+ 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
+ obj = create_empty_object(klass)
+ mark_unmarshalled_obj(node, obj)
+ obj
+ end
+
+ # Only creates empty string. Do String#replace it with real string.
+ def string2obj(node, map, rubytype)
+ klass = rubytype ? Mapping.class_from_name(rubytype) : String
+ obj = create_empty_object(klass)
+ mark_unmarshalled_obj(node, obj)
+ obj
+ end
+end
+
+
+end
+end
diff --git a/lib/soap/mapping/typeMap.rb b/lib/soap/mapping/typeMap.rb
new file mode 100644
index 0000000000..c62f1482c1
--- /dev/null
+++ b/lib/soap/mapping/typeMap.rb
@@ -0,0 +1,52 @@
+=begin
+SOAP4R - Base type mapping definition
+Copyright (C) 2000, 2001, 2002, 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
+
+
+TypeMap = {
+ XSD::XSDAnySimpleType::Type => SOAPAnySimpleType,
+ XSD::XSDString::Type => SOAPString,
+ XSD::XSDBoolean::Type => SOAPBoolean,
+ XSD::XSDDecimal::Type => SOAPDecimal,
+ XSD::XSDFloat::Type => SOAPFloat,
+ XSD::XSDDouble::Type => SOAPDouble,
+ XSD::XSDDuration::Type => SOAPDuration,
+ XSD::XSDDateTime::Type => SOAPDateTime,
+ XSD::XSDTime::Type => SOAPTime,
+ XSD::XSDDate::Type => SOAPDate,
+ XSD::XSDGYearMonth::Type => SOAPGYearMonth,
+ XSD::XSDGYear::Type => SOAPGYear,
+ XSD::XSDGMonthDay::Type => SOAPGMonthDay,
+ XSD::XSDGDay::Type => SOAPGDay,
+ XSD::XSDGMonth::Type => SOAPGMonth,
+ XSD::XSDHexBinary::Type => SOAPHexBinary,
+ XSD::XSDBase64Binary::Type => SOAPBase64,
+ XSD::XSDAnyURI::Type => SOAPAnyURI,
+ XSD::XSDQName::Type => SOAPQName,
+ XSD::XSDInteger::Type => SOAPInteger,
+ XSD::XSDLong::Type => SOAPLong,
+ XSD::XSDInt::Type => SOAPInt,
+ XSD::XSDShort::Type => SOAPShort,
+
+ SOAP::SOAPBase64::Type => SOAPBase64,
+}
+
+
+end
diff --git a/lib/soap/mapping/wsdlRegistry.rb b/lib/soap/mapping/wsdlRegistry.rb
new file mode 100644
index 0000000000..4b5beabe72
--- /dev/null
+++ b/lib/soap/mapping/wsdlRegistry.rb
@@ -0,0 +1,146 @@
+=begin
+SOAP4R - WSDL mapping registry.
+Copyright (C) 2000, 2001, 2002, 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
+
+
+require 'soap/baseData'
+require 'soap/mapping/mapping'
+require 'soap/mapping/typeMap'
+
+
+module SOAP
+module Mapping
+
+
+class WSDLRegistry
+ include TraverseSupport
+
+ attr_reader :complextypes
+
+ def initialize(complextypes, config = {})
+ @complextypes = complextypes
+ @config = config
+ @excn_handler_obj2soap = nil
+ # For mapping AnyType element.
+ @rubytype_factory = RubytypeFactory.new(
+ :allow_untyped_struct => true,
+ :allow_original_mapping => true
+ )
+ end
+
+ def obj2soap(klass, obj, type_qname)
+ soap_obj = nil
+ if obj.nil?
+ soap_obj = SOAPNil.new
+ elsif obj.is_a?(XSD::NSDBase)
+ soap_obj = soap2soap(obj, type_qname)
+ elsif (type = @complextypes[type_qname])
+ case type.compoundtype
+ when :TYPE_STRUCT
+ soap_obj = struct2soap(obj, type_qname, type)
+ when :TYPE_ARRAY
+ soap_obj = array2soap(obj, type_qname, type)
+ end
+ elsif (type = TypeMap[type_qname])
+ soap_obj = base2soap(obj, type)
+ elsif type_qname == XSD::AnyTypeName
+ soap_obj = @rubytype_factory.obj2soap(nil, obj, nil, nil)
+ end
+ return soap_obj if soap_obj
+
+ if @excn_handler_obj2soap
+ soap_obj = @excn_handler_obj2soap.call(obj) { |yield_obj|
+ Mapping._obj2soap(yield_obj, self)
+ }
+ end
+ return soap_obj if soap_obj
+
+ raise MappingError.new("Cannot map #{ klass.name } to SOAP/OM.")
+ end
+
+ def soap2obj(klass, node)
+ raise RuntimeError.new("#{ self } is for obj2soap only.")
+ end
+
+ def excn_handler_obj2soap=(handler)
+ @excn_handler_obj2soap = handler
+ end
+
+private
+
+ def soap2soap(obj, type_qname)
+ if obj.is_a?(SOAPBasetype)
+ obj
+ elsif obj.is_a?(SOAPStruct) && (type = @complextypes[type_qname])
+ soap_obj = obj
+ mark_marshalled_obj(obj, soap_obj)
+ elements2soap(obj, soap_obj, type.content.elements)
+ soap_obj
+ elsif obj.is_a?(SOAPArray) && (type = @complextypes[type_qname])
+ soap_obj = obj
+ contenttype = type.child_type
+ mark_marshalled_obj(obj, soap_obj)
+ obj.replace do |ele|
+ Mapping._obj2soap(ele, self, contenttype)
+ end
+ soap_obj
+ else
+ nil
+ end
+ end
+
+ def base2soap(obj, type)
+ soap_obj = nil
+ if type <= XSD::XSDString
+ soap_obj = type.new(XSD::Charset.is_ces(obj, $KCODE) ?
+ XSD::Charset.encoding_conv(obj, $KCODE, XSD::Charset.encoding) : obj)
+ mark_marshalled_obj(obj, soap_obj)
+ else
+ soap_obj = type.new(obj)
+ end
+ soap_obj
+ end
+
+ def struct2soap(obj, type_qname, type)
+ soap_obj = SOAPStruct.new(type_qname)
+ mark_marshalled_obj(obj, soap_obj)
+ elements2soap(obj, soap_obj, type.content.elements)
+ soap_obj
+ end
+
+ def array2soap(obj, type_qname, type)
+ contenttype = type.child_type
+ soap_obj = SOAPArray.new(ValueArrayName, 1, contenttype)
+ mark_marshalled_obj(obj, soap_obj)
+ obj.each do |item|
+ soap_obj.add(Mapping._obj2soap(item, self, contenttype))
+ end
+ soap_obj
+ end
+
+ def elements2soap(obj, soap_obj, elements)
+ elements.each do |element|
+ name = element.name.name
+ child_obj = obj.instance_eval("@#{ name }")
+ soap_obj.add(name, Mapping._obj2soap(child_obj, self, element.type))
+ end
+ end
+end
+
+
+end
+end
diff --git a/lib/soap/marshal.rb b/lib/soap/marshal.rb
new file mode 100644
index 0000000000..1dc4e13558
--- /dev/null
+++ b/lib/soap/marshal.rb
@@ -0,0 +1,71 @@
+=begin
+SOAP4R - Marshalling/Unmarshalling Ruby's object using SOAP Encoding.
+Copyright (C) 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
+
+
+# The original version of the marshal.rb to marshal/unmarshal Ruby's object
+# using SOAP Encoding was written by Michael Neumann. His valuable comments
+# and his program inspired me to write this. Thanks.
+
+
+require "soap/mapping"
+require "soap/processor"
+
+
+module SOAP
+
+
+module Marshal
+ # Trying xsd:dateTime data to be recovered as aTime. aDateTime if it fails.
+ MarshalMappingRegistry = Mapping::Registry.new(:allow_original_mapping => true)
+ MarshalMappingRegistry.add(
+ Time,
+ ::SOAP::SOAPDateTime,
+ ::SOAP::Mapping::Registry::DateTimeFactory
+)
+
+ class << self
+ public
+ def dump(obj, io = nil)
+ marshal(obj, MarshalMappingRegistry, io)
+ end
+
+ def load(stream)
+ unmarshal(stream, MarshalMappingRegistry)
+ end
+
+ def marshal(obj, mapping_registry = MarshalMappingRegistry, io = nil)
+ elename = Mapping.name2elename(obj.class.to_s)
+ soap_obj = Mapping.obj2soap(obj, mapping_registry)
+ body = SOAPBody.new
+ body.add(elename, soap_obj)
+ SOAP::Processor.marshal(nil, body, {}, io)
+ end
+
+ def unmarshal(stream, mapping_registry = MarshalMappingRegistry)
+ header, body = SOAP::Processor.unmarshal(stream)
+ Mapping.soap2obj(body.root_node, mapping_registry)
+ end
+ end
+
+end
+
+
+end
+
+
+SOAPMarshal = SOAP::Marshal
diff --git a/lib/soap/netHttpClient.rb b/lib/soap/netHttpClient.rb
new file mode 100644
index 0000000000..14003b34d0
--- /dev/null
+++ b/lib/soap/netHttpClient.rb
@@ -0,0 +1,101 @@
+=begin
+SOAP4R - net/http wrapper
+Copyright (C) 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
+
+
+require 'net/http'
+
+
+module SOAP
+
+
+class NetHttpClient
+
+ attr_accessor :proxy
+ attr_accessor :debug_dev
+ attr_reader :session_manager
+
+ class SessionManager
+ attr_accessor :connect_timeout
+ attr_accessor :send_timeout
+ attr_accessor :receive_timeout
+ end
+
+ class Response
+ attr_reader :content
+ attr_reader :status
+ attr_reader :reason
+ attr_reader :contenttype
+
+ def initialize(res)
+ @status = res.code.to_i
+ @reason = res.message
+ @contenttype = res['content-type']
+ @content = res.body
+ end
+ end
+
+ def initialize(proxy = nil, agent = nil)
+ @proxy = proxy ? URI.parse(proxy) : nil
+ @agent = agent
+ @debug_dev = nil
+ @session_manager = SessionManager.new
+ end
+
+ def reset(url)
+ # ignored.
+ end
+
+ def post(url, req_body, header = {})
+ url = URI.parse(url)
+ extra = header.dup
+ extra['User-Agent'] = @agent if @agent
+ res = start(url) { |http|
+ http.post(url.instance_eval('path_query'), req_body, extra)
+ }
+ Response.new(res)
+ end
+
+ def get_content(url, header = {})
+ url = URI.parse(url)
+ extra = header.dup
+ extra['User-Agent'] = @agent if @agent
+ res = start(url) { |http|
+ http.get(url.instance_eval('path_query'), extra)
+ }
+ res.body
+ end
+
+private
+
+ def start(url)
+ proxy_host = @proxy ? @proxy.host : nil
+ proxy_port = @proxy ? @proxy.port : nil
+ response = nil
+ Net::HTTP::Proxy(proxy_host, proxy_port).start(url.host, url.port) { |http|
+ if http.respond_to?(:set_debug_output)
+ http.set_debug_output(@debug_dev)
+ end
+ response, = yield(http)
+ http.finish
+ }
+ response
+ end
+end
+
+
+end
diff --git a/lib/soap/parser.rb b/lib/soap/parser.rb
new file mode 100644
index 0000000000..67782566c3
--- /dev/null
+++ b/lib/soap/parser.rb
@@ -0,0 +1,252 @@
+=begin
+SOAP4R - SOAP XML Instance Parser library.
+Copyright (C) 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
+
+
+require 'xsd/ns'
+require 'xsd/xmlparser'
+require 'soap/soap'
+require 'soap/baseData'
+require 'soap/encodingstyle/handler'
+
+
+module SOAP
+
+
+class Parser
+ include SOAP
+
+ class ParseError < Error; end
+ class FormatDecodeError < ParseError; end
+ class UnexpectedElementError < ParseError; end
+
+private
+
+ class ParseFrame
+ attr_reader :node
+ attr_reader :name
+ attr_reader :ns, :encodingstyle
+
+ class NodeContainer
+ def initialize(node)
+ @node = node
+ end
+
+ def node
+ @node
+ end
+
+ def replace_node(node)
+ @node = node
+ end
+ end
+
+ public
+
+ def initialize(ns, name, node, encodingstyle)
+ @ns = ns
+ @name = name
+ self.node = node
+ @encodingstyle = encodingstyle
+ end
+
+ def node=(node)
+ @node = NodeContainer.new(node)
+ end
+ end
+
+public
+
+ attr_accessor :default_encodingstyle
+ attr_accessor :decode_typemap
+ attr_accessor :allow_unqualified_element
+
+ def initialize(opt = {})
+ @parser = XSD::XMLParser.create_parser(self, opt)
+ @parsestack = nil
+ @lastnode = nil
+ @handlers = {}
+ @default_encodingstyle = opt[:default_encodingstyle] || EncodingNamespace
+ @decode_typemap = opt[:decode_typemap] || nil
+ @allow_unqualified_element = opt[:allow_unqualified_element] || false
+ end
+
+ def charset
+ @parser.charset
+ end
+
+ def parse(string_or_readable)
+ @parsestack = []
+ @lastnode = nil
+
+ @handlers.each do |uri, handler|
+ handler.decode_prologue
+ end
+
+ @parser.do_parse(string_or_readable)
+
+ unless @parsestack.empty?
+ raise FormatDecodeError.new("Unbalanced tag in XML.")
+ end
+
+ @handlers.each do |uri, handler|
+ handler.decode_epilogue
+ end
+
+ @lastnode
+ end
+
+ def start_element(name, attrs)
+ lastframe = @parsestack.last
+ ns = parent = parent_encodingstyle = nil
+ if lastframe
+ ns = lastframe.ns.clone_ns
+ parent = lastframe.node
+ parent_encodingstyle = lastframe.encodingstyle
+ else
+ ns = XSD::NS.new
+ parent = ParseFrame::NodeContainer.new(nil)
+ parent_encodingstyle = nil
+ end
+
+ attrs = XSD::XMLParser.filter_ns(ns, attrs)
+ encodingstyle = find_encodingstyle(ns, attrs)
+
+ # Children's encodingstyle is derived from its parent.
+ encodingstyle ||= parent_encodingstyle || @default_encodingstyle
+
+ node = decode_tag(ns, name, attrs, parent, encodingstyle)
+
+ @parsestack << ParseFrame.new(ns, name, node, encodingstyle)
+ end
+
+ def characters(text)
+ lastframe = @parsestack.last
+ if lastframe
+ # Need not to be cloned because character does not have attr.
+ ns = lastframe.ns
+ parent = lastframe.node
+ encodingstyle = lastframe.encodingstyle
+ decode_text(ns, text, encodingstyle)
+ else
+ # Ignore Text outside of SOAP Envelope.
+ p text if $DEBUG
+ end
+ end
+
+ def end_element(name)
+ lastframe = @parsestack.pop
+ unless name == lastframe.name
+ raise UnexpectedElementError.new("Closing element name '#{ name }' does not match with opening element '#{ lastframe.name }'.")
+ end
+ decode_tag_end(lastframe.ns, lastframe.node, lastframe.encodingstyle)
+ @lastnode = lastframe.node.node
+ end
+
+private
+
+ def find_encodingstyle(ns, attrs)
+ attrs.each do |key, value|
+ if (ns.compare(EnvelopeNamespace, AttrEncodingStyle, key))
+ return value
+ end
+ end
+ nil
+ end
+
+ def decode_tag(ns, name, attrs, parent, encodingstyle)
+ ele = ns.parse(name)
+
+ # Envelope based parsing.
+ if ((ele.namespace == EnvelopeNamespace) ||
+ (@allow_unqualified_element && ele.namespace.nil?))
+ o = decode_soap_envelope(ns, ele, attrs, parent)
+ return o if o
+ end
+
+ # Encoding based parsing.
+ handler = find_handler(encodingstyle)
+ if handler
+ return handler.decode_tag(ns, ele, attrs, parent)
+ else
+ raise FormatDecodeError.new("Unknown encodingStyle: #{ encodingstyle }.")
+ end
+ end
+
+ def decode_tag_end(ns, node, encodingstyle)
+ return unless encodingstyle
+
+ handler = find_handler(encodingstyle)
+ if handler
+ return handler.decode_tag_end(ns, node)
+ else
+ raise FormatDecodeError.new("Unknown encodingStyle: #{ encodingstyle }.")
+ end
+ end
+
+ def decode_text(ns, text, encodingstyle)
+ handler = find_handler(encodingstyle)
+
+ if handler
+ handler.decode_text(ns, text)
+ else
+ # How should I do?
+ end
+ end
+
+ def decode_soap_envelope(ns, ele, attrs, parent)
+ o = nil
+ if ele.name == EleEnvelope
+ o = SOAPEnvelope.new
+ elsif ele.name == EleHeader
+ unless parent.node.is_a?(SOAPEnvelope)
+ raise FormatDecodeError.new("Header should be a child of Envelope.")
+ end
+ o = SOAPHeader.new
+ parent.node.header = o
+ elsif ele.name == EleBody
+ unless parent.node.is_a?(SOAPEnvelope)
+ raise FormatDecodeError.new("Body should be a child of Envelope.")
+ end
+ o = SOAPBody.new
+ parent.node.body = o
+ elsif ele.name == EleFault
+ unless parent.node.is_a?(SOAPBody)
+ raise FormatDecodeError.new("Fault should be a child of Body.")
+ end
+ o = SOAPFault.new
+ parent.node.fault = o
+ end
+ o.parent = parent if o
+ o
+ end
+
+ def find_handler(encodingstyle)
+ unless @handlers.key?(encodingstyle)
+ handler_factory = SOAP::EncodingStyle::Handler.handler(encodingstyle) ||
+ SOAP::EncodingStyle::Handler.handler(EncodingNamespace)
+ handler = handler_factory.new(@parser.charset)
+ handler.decode_typemap = @decode_typemap
+ handler.decode_prologue
+ @handlers[encodingstyle] = handler
+ end
+ @handlers[encodingstyle]
+ end
+end
+
+
+end
diff --git a/lib/soap/processor.rb b/lib/soap/processor.rb
new file mode 100644
index 0000000000..47df772a9d
--- /dev/null
+++ b/lib/soap/processor.rb
@@ -0,0 +1,79 @@
+=begin
+SOAP4R - marshal/unmarshal interface.
+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
+
+
+require 'xsd/datatypes'
+require 'soap/soap'
+require 'soap/element'
+require 'soap/parser'
+require 'soap/generator'
+require 'soap/encodingstyle/soapHandler'
+require 'soap/encodingstyle/literalHandler'
+require 'soap/encodingstyle/aspDotNetHandler'
+
+
+module SOAP
+
+
+module Processor
+ @@default_parser_option = {}
+
+ class << self
+ public
+
+ def marshal(header, body, opt = {}, io = nil)
+ env = SOAPEnvelope.new(header, body)
+ generator = create_generator(opt)
+ generator.generate(env, io)
+ end
+
+ def unmarshal(stream, opt = {})
+ parser = create_parser(opt)
+ env = parser.parse(stream)
+ if env
+ return env.header, env.body
+ else
+ return nil, nil
+ end
+ end
+
+ def default_parser_option=(rhs)
+ @@default_parser_option = rhs
+ end
+
+ def default_parser_option
+ @@default_parser_option
+ end
+
+ private
+
+ def create_generator(opt)
+ SOAPGenerator.new(opt)
+ end
+
+ def create_parser(opt)
+ if opt.empty?
+ opt = @@default_parser_option
+ end
+ ::SOAP::Parser.new(opt)
+ end
+ end
+end
+
+
+end
diff --git a/lib/soap/rpc/cgistub.rb b/lib/soap/rpc/cgistub.rb
new file mode 100644
index 0000000000..f016e31057
--- /dev/null
+++ b/lib/soap/rpc/cgistub.rb
@@ -0,0 +1,214 @@
+=begin
+SOAP4R - CGI stub library
+Copyright (C) 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
+
+
+require 'soap/streamHandler'
+require 'webrick/httpresponse'
+require 'webrick/httpstatus'
+require 'logger'
+require 'soap/rpc/router'
+
+
+module SOAP
+module RPC
+
+
+###
+# SYNOPSIS
+# CGIStub.new
+#
+# DESCRIPTION
+# To be written...
+#
+class CGIStub < Logger::Application
+ include SOAP
+
+ # There is a client which does not accept the media-type which is defined in
+ # SOAP spec.
+ attr_accessor :mediatype
+
+ class CGIError < Error; end
+
+ class SOAPRequest
+ ALLOWED_LENGTH = 1024 * 1024
+
+ def initialize(stream = $stdin)
+ @method = ENV['REQUEST_METHOD']
+ @size = ENV['CONTENT_LENGTH'].to_i || 0
+ @contenttype = ENV['CONTENT_TYPE']
+ @charset = nil
+ @soapaction = ENV['HTTP_SOAPAction']
+ @source = stream
+ @body = nil
+ end
+
+ def init
+ validate
+ @charset = StreamHandler.parse_media_type(@contenttype)
+ @body = @source.read(@size)
+ self
+ end
+
+ def dump
+ @body.dup
+ end
+
+ def soapaction
+ @soapaction
+ end
+
+ def charset
+ @charset
+ end
+
+ def to_s
+ "method: #{ @method }, size: #{ @size }"
+ end
+
+ private
+
+ def validate # raise CGIError
+ if @method != 'POST'
+ raise CGIError.new("Method '#{ @method }' not allowed.")
+ end
+
+ if @size > ALLOWED_LENGTH
+ raise CGIError.new("Content-length too long.")
+ end
+ end
+ end
+
+ def initialize(appname, default_namespace)
+ super(appname)
+ set_log(STDERR)
+ self.level = INFO
+ @default_namespace = default_namespace
+ @router = SOAP::RPC::Router.new(appname)
+ @remote_user = ENV['REMOTE_USER'] || 'anonymous'
+ @remote_host = ENV['REMOTE_HOST'] || ENV['REMOTE_ADDR'] || 'unknown'
+ @request = nil
+ @response = nil
+ @mediatype = MediaType
+ on_init
+ end
+
+ def add_servant(obj, namespace = @default_namespace, soapaction = nil)
+ RPC.defined_methods(obj).each do |name|
+ qname = XSD::QName.new(namespace, name)
+ param_size = obj.method(name).arity.abs
+ params = (1..param_size).collect { |i| "p#{ i }" }
+ param_def = SOAP::RPC::SOAPMethod.create_param_def(params)
+ @router.add_method(obj, qname, soapaction, name, param_def)
+ end
+ end
+
+ def on_init
+ # Override this method in derived class to call 'add_method' to add methods.
+ end
+
+ def mapping_registry
+ @router.mapping_registry
+ end
+
+ def mapping_registry=(value)
+ @router.mapping_registry = value
+ end
+
+ def add_method(receiver, name, *param)
+ add_method_with_namespace_as(@default_namespace, receiver,
+ name, name, *param)
+ end
+
+ def add_method_as(receiver, name, name_as, *param)
+ add_method_with_namespace_as(@default_namespace, receiver,
+ name, name_as, *param)
+ end
+
+ def add_method_with_namespace(namespace, receiver, name, *param)
+ add_method_with_namespace_as(namespace, receiver, name, name, *param)
+ end
+
+ def add_method_with_namespace_as(namespace, receiver, name, name_as, *param)
+ param_def = if param.size == 1 and param[0].is_a?(Array)
+ param[0]
+ else
+ SOAP::RPC::SOAPMethod.create_param_def(param)
+ end
+ qname = XSD::QName.new(namespace, name_as)
+ @router.add_method(receiver, qname, nil, name, param_def)
+ end
+
+ def route(request_string, charset)
+ @router.route(request_string, charset)
+ end
+
+ def create_fault_response(e)
+ @router.create_fault_response(e)
+ end
+
+private
+
+ def run
+ prologue
+
+ httpversion = WEBrick::HTTPVersion.new('1.0')
+ @response = WEBrick::HTTPResponse.new({:HTTPVersion => httpversion})
+ begin
+ log(INFO) { "Received a request from '#{ @remote_user }@#{ @remote_host }'." }
+ # SOAP request parsing.
+ @request = SOAPRequest.new.init
+ req_charset = @request.charset
+ req_string = @request.dump
+ log(DEBUG) { "XML Request: #{req_string}" }
+ res_string, is_fault = route(req_string, req_charset)
+ log(DEBUG) { "XML Response: #{res_string}" }
+
+ @response['Cache-Control'] = 'private'
+ if req_charset
+ @response['content-type'] = "#{@mediatype}; charset=\"#{req_charset}\""
+ else
+ @response['content-type'] = @mediatype
+ end
+ if is_fault
+ @response.status = WEBrick::HTTPStatus::RC_INTERNAL_SERVER_ERROR
+ end
+ @response.body = res_string
+ rescue Exception
+ res_string = create_fault_response($!)
+ @response['Cache-Control'] = 'private'
+ @response['content-type'] = @mediatype
+ @response.status = WEBrick::HTTPStatus::RC_INTERNAL_SERVER_ERROR
+ ensure
+ buf = ''
+ @response.send_response(buf)
+ buf.sub!(/^[^\r]+\r\n/, '') # Trim status line.
+ log(DEBUG) { "SOAP CGI Response:\n#{ buf }" }
+ print buf
+ epilogue
+ end
+
+ 0
+ end
+
+ def prologue; end
+ def epilogue; end
+end
+
+
+end
+end
diff --git a/lib/soap/rpc/driver.rb b/lib/soap/rpc/driver.rb
new file mode 100644
index 0000000000..76fd14e34b
--- /dev/null
+++ b/lib/soap/rpc/driver.rb
@@ -0,0 +1,189 @@
+=begin
+SOAP4R - SOAP RPC driver
+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
+
+
+require 'soap/soap'
+require 'soap/mapping'
+require 'soap/rpc/rpc'
+require 'soap/rpc/proxy'
+require 'soap/rpc/element'
+require 'soap/streamHandler'
+
+
+module SOAP
+module RPC
+
+
+class Driver
+public
+ class EmptyResponseError < Error; end
+
+ attr_accessor :mapping_registry
+ attr_accessor :soapaction
+ attr_reader :endpoint_url
+ attr_reader :wiredump_dev
+ attr_reader :wiredump_file_base
+ attr_reader :httpproxy
+
+ def initialize(endpoint_url, namespace, soapaction = nil)
+ @endpoint_url = endpoint_url
+ @namespace = namespace
+ @mapping_registry = nil # for unmarshal
+ @soapaction = soapaction
+ @wiredump_dev = nil
+ @wiredump_file_base = nil
+ @httpproxy = ENV['httpproxy'] || ENV['HTTP_PROXY']
+ @handler = HTTPPostStreamHandler.new(@endpoint_url, @httpproxy,
+ XSD::Charset.encoding_label)
+ @proxy = Proxy.new(@handler, @soapaction)
+ @proxy.allow_unqualified_element = true
+ end
+
+ def endpoint_url=(endpoint_url)
+ @endpoint_url = endpoint_url
+ if @handler
+ @handler.endpoint_url = @endpoint_url
+ @handler.reset
+ end
+ end
+
+ def wiredump_dev=(dev)
+ @wiredump_dev = dev
+ if @handler
+ @handler.wiredump_dev = @wiredump_dev
+ @handler.reset
+ end
+ end
+
+ def wiredump_file_base=(base)
+ @wiredump_file_base = base
+ end
+
+ def httpproxy=(httpproxy)
+ @httpproxy = httpproxy
+ if @handler
+ @handler.proxy = @httpproxy
+ @handler.reset
+ end
+ end
+
+ def default_encodingstyle
+ @proxy.default_encodingstyle
+ end
+
+ def default_encodingstyle=(encodingstyle)
+ @proxy.default_encodingstyle = encodingstyle
+ end
+
+
+ ###
+ ## Method definition interfaces.
+ #
+ # params: [[param_def...]] or [paramname, paramname, ...]
+ # param_def: See proxy.rb. Sorry.
+
+ def add_method(name, *params)
+ add_method_with_soapaction_as(name, name, @soapaction, *params)
+ end
+
+ def add_method_as(name, name_as, *params)
+ add_method_with_soapaction_as(name, name_as, @soapaction, *params)
+ end
+
+ def add_method_with_soapaction(name, soapaction, *params)
+ add_method_with_soapaction_as(name, name, soapaction, *params)
+ end
+
+ def add_method_with_soapaction_as(name, name_as, soapaction, *params)
+ param_def = if params.size == 1 and params[0].is_a?(Array)
+ params[0]
+ else
+ SOAPMethod.create_param_def(params)
+ end
+ qname = XSD::QName.new(@namespace, name_as)
+ @proxy.add_method(qname, soapaction, name, param_def)
+ add_rpc_method_interface(name, param_def)
+ end
+
+
+ ###
+ ## Driving interface.
+ #
+ def invoke(headers, body)
+ if @wiredump_file_base
+ @handler.wiredump_file_base =
+ @wiredump_file_base + '_' << body.elename.name
+ end
+ @proxy.invoke(headers, body)
+ end
+
+ def call(name, *params)
+ # Convert parameters: params array => SOAPArray => members array
+ params = Mapping.obj2soap(params, @mapping_registry).to_a
+ if @wiredump_file_base
+ @handler.wiredump_file_base = @wiredump_file_base + '_' << name
+ end
+
+ # Then, call @proxy.call like the following.
+ header, body = @proxy.call(nil, name, *params)
+ unless body
+ raise EmptyResponseError.new("Empty response.")
+ end
+
+ begin
+ @proxy.check_fault(body)
+ rescue SOAP::FaultError => e
+ Mapping.fault2exception(e)
+ end
+
+ ret = body.response ? Mapping.soap2obj(body.response, @mapping_registry) : nil
+ if body.outparams
+ outparams = body.outparams.collect { |outparam| Mapping.soap2obj(outparam) }
+ return [ret].concat(outparams)
+ else
+ return ret
+ end
+ end
+
+ def reset_stream
+ @handler.reset
+ end
+
+private
+
+ def add_rpc_method_interface(name, param_def)
+ param_names = []
+ i = 0
+ @proxy.method[name].each_param_name(RPC::SOAPMethod::IN,
+ RPC::SOAPMethod::INOUT) do |param_name|
+ i += 1
+ param_names << "arg#{ i }"
+ end
+
+ callparam = (param_names.collect { |pname| ", " + pname }).join
+ self.instance_eval <<-EOS
+ def #{ name }(#{ param_names.join(", ") })
+ call("#{ name }"#{ callparam })
+ end
+ EOS
+ end
+end
+
+
+end
+end
diff --git a/lib/soap/rpc/element.rb b/lib/soap/rpc/element.rb
new file mode 100644
index 0000000000..d1e1931fa3
--- /dev/null
+++ b/lib/soap/rpc/element.rb
@@ -0,0 +1,278 @@
+=begin
+SOAP4R - RPC element definition.
+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
+
+
+require 'soap/baseData'
+
+
+module SOAP
+
+# Add method definitions for RPC to common definition in element.rb
+class SOAPBody < SOAPStruct
+ public
+
+ def request
+ root_node
+ end
+
+ def response
+ if !@is_fault
+ if void?
+ nil
+ else
+ # Initial element is [retval].
+ root_node[0]
+ end
+ else
+ root_node
+ end
+ end
+
+ def outparams
+ if !@is_fault and !void?
+ op = root_node[1..-1]
+ op = nil if op && op.empty?
+ op
+ else
+ nil
+ end
+ end
+
+ def void?
+ root_node.nil? # || root_node.is_a?(SOAPNil)
+ end
+
+ def fault
+ if @is_fault
+ self['fault']
+ else
+ nil
+ end
+ end
+
+ def fault=(fault)
+ @is_fault = true
+ add_member('fault', fault)
+ end
+end
+
+
+module RPC
+
+
+class RPCError < Error; end
+class MethodDefinitionError < RPCError; end
+class ParameterError < RPCError; end
+
+class SOAPMethod < SOAPStruct
+ RETVAL = 'retval'
+ IN = 'in'
+ OUT = 'out'
+ INOUT = 'inout'
+
+ attr_reader :param_def
+ attr_reader :inparam
+ attr_reader :outparam
+
+ def initialize(qname, param_def = nil)
+ super(nil)
+ @elename = qname
+ @encodingstyle = nil
+
+ @param_def = param_def
+
+ @signature = []
+ @inparam_names = []
+ @inoutparam_names = []
+ @outparam_names = []
+
+ @inparam = {}
+ @outparam = {}
+ @retval_name = nil
+
+ init_param(@param_def) if @param_def
+ end
+
+ def have_outparam?
+ @outparam_names.size > 0
+ end
+
+ def each_param_name(*type)
+ @signature.each do |io_type, name, param_type|
+ if type.include?(io_type)
+ yield(name)
+ end
+ end
+ end
+
+ def set_param(params)
+ params.each do |param, data|
+ @inparam[param] = data
+ data.elename.name = param
+ end
+ end
+
+ def set_outparam(params)
+ params.each do |param, data|
+ @outparam[param] = data
+ data.elename.name = param
+ end
+ end
+
+ def SOAPMethod.create_param_def(param_names)
+ param_def = []
+ param_names.each do |param_name|
+ param_def.push([IN, param_name, nil])
+ end
+ param_def.push([RETVAL, 'return', nil])
+ param_def
+ end
+
+private
+
+ def init_param(param_def)
+ param_def.each do |io_type, name, param_type|
+ case io_type
+ when IN
+ @signature.push([IN, name, param_type])
+ @inparam_names.push(name)
+ when OUT
+ @signature.push([OUT, name, param_type])
+ @outparam_names.push(name)
+ when INOUT
+ @signature.push([INOUT, name, param_type])
+ @inoutparam_names.push(name)
+ when RETVAL
+ if (@retval_name)
+ raise MethodDefinitionError.new('Duplicated retval')
+ end
+ @retval_name = name
+ else
+ raise MethodDefinitionError.new("Unknown type: #{ io_type }")
+ end
+ end
+ end
+end
+
+
+class SOAPMethodRequest < SOAPMethod
+ attr_accessor :soapaction
+
+ def SOAPMethodRequest.create_request(qname, *params)
+ param_def = []
+ param_value = []
+ i = 0
+ params.each do |param|
+ param_name = "p#{ i }"
+ i += 1
+ param_def << [IN, nil, param_name]
+ param_value << [param_name, param]
+ end
+ param_def << [RETVAL, nil, 'return']
+ o = new(qname, param_def)
+ o.set_param(param_value)
+ o
+ end
+
+ def initialize(qname, param_def = nil, soapaction = nil)
+ check_elename(qname)
+ super(qname, param_def)
+ @soapaction = soapaction
+ end
+
+ def each
+ each_param_name(IN, INOUT) do |name|
+ unless @inparam[name]
+ raise ParameterError.new("Parameter: #{ name } was not given.")
+ end
+ yield(name, @inparam[name])
+ end
+ end
+
+ def dup
+ req = self.class.new(@elename.dup, @param_def, @soapaction)
+ req.encodingstyle = @encodingstyle
+ req
+ end
+
+ def create_method_response
+ SOAPMethodResponse.new(
+ XSD::QName.new(@elename.namespace, @elename.name + 'Response'),
+ @param_def)
+ end
+
+private
+
+ def check_elename(qname)
+ # NCName & ruby's method name
+ unless /\A[\w_][\w\d_\-]*\z/ =~ qname.name
+ raise MethodDefinitionError.new("Element name '#{qname.name}' not allowed")
+ end
+ end
+end
+
+
+class SOAPMethodResponse < SOAPMethod
+
+ def initialize(qname, param_def = nil)
+ super(qname, param_def)
+ @retval = nil
+ end
+
+ def retval=(retval)
+ @retval = retval
+ @retval.elename = @retval.elename.dup_name('return')
+ end
+
+ def each
+ if @retval_name and !@retval.is_a?(SOAPVoid)
+ yield(@retval_name, @retval)
+ end
+
+ each_param_name(OUT, INOUT) do |param_name|
+ unless @outparam[param_name]
+ raise ParameterError.new("Parameter: #{ param_name } was not given.")
+ end
+ yield(param_name, @outparam[param_name])
+ end
+ end
+end
+
+
+# To return(?) void explicitly.
+# def foo(input_var)
+# ...
+# return SOAP::RPC::SOAPVoid.new
+# end
+class SOAPVoid < XSD::XSDAnySimpleType
+ include SOAPBasetype
+ extend SOAPModuleUtils
+ Name = XSD::QName.new(Mapping::RubyCustomTypeNamespace, nil)
+
+public
+ def initialize()
+ @elename = Name
+ @id = nil
+ @precedents = []
+ @parent = nil
+ end
+end
+
+
+end
+end
diff --git a/lib/soap/rpc/proxy.rb b/lib/soap/rpc/proxy.rb
new file mode 100644
index 0000000000..39a095838c
--- /dev/null
+++ b/lib/soap/rpc/proxy.rb
@@ -0,0 +1,147 @@
+=begin
+SOAP4R - RPC Proxy library.
+Copyright (C) 2000, 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
+
+
+require 'soap/soap'
+require 'soap/processor'
+require 'soap/mapping'
+require 'soap/rpc/rpc'
+require 'soap/rpc/element'
+require 'soap/streamHandler'
+
+
+module SOAP
+module RPC
+
+
+class Proxy
+ include SOAP
+
+public
+
+ attr_accessor :soapaction
+ attr_accessor :allow_unqualified_element, :default_encodingstyle
+ attr_reader :method
+
+ def initialize(stream_handler, soapaction = nil)
+ @handler = stream_handler
+ @soapaction = soapaction
+ @method = {}
+ @allow_unqualified_element = false
+ @default_encodingstyle = nil
+ end
+
+ class Request
+ include RPC
+
+ public
+
+ attr_reader :method
+ attr_reader :namespace
+ attr_reader :name
+
+ def initialize(model, values)
+ @method = model.dup
+ @namespace = @method.elename.namespace
+ @name = @method.elename.name
+
+ params = {}
+
+ if ((values.size == 1) and (values[0].is_a?(Hash)))
+ params = values[0]
+ else
+ i = 0
+ @method.each_param_name(SOAPMethod::IN, SOAPMethod::INOUT) do |name|
+ params[name] = values[i] || SOAPNil.new
+ i += 1
+ end
+ end
+ @method.set_param(params)
+ end
+ end
+
+ def add_method(qname, soapaction, name, param_def)
+ @method[name] = SOAPMethodRequest.new(qname, param_def, soapaction)
+ end
+
+ def create_request(name, *values)
+ if (@method.key?(name))
+ method = @method[name]
+ method.encodingstyle = @default_encodingstyle if @default_encodingstyle
+ else
+ raise SOAP::RPC::MethodDefinitionError.new(
+ "Method: #{ name } not defined.")
+ end
+
+ Request.new(method, values)
+ end
+
+ def invoke(req_header, req_body, soapaction = nil)
+ if req_header and !req_header.is_a?(SOAPHeader)
+ req_header = create_header(req_header)
+ end
+ if !req_body.is_a?(SOAPBody)
+ req_body = SOAPBody.new(req_body)
+ end
+ opt = create_options
+ send_string = Processor.marshal(req_header, req_body, opt)
+ data = @handler.send(send_string, soapaction)
+ if data.receive_string.empty?
+ return nil, nil
+ end
+ res_charset = StreamHandler.parse_media_type(data.receive_contenttype)
+ opt = create_options
+ opt[:charset] = res_charset
+ res_header, res_body = Processor.unmarshal(data.receive_string, opt)
+ return res_header, res_body
+ end
+
+ def call(headers, name, *values)
+ req = create_request(name, *values)
+ return invoke(headers, req.method, req.method.soapaction || @soapaction)
+ end
+
+ def check_fault(body)
+ if body.fault
+ raise SOAP::FaultError.new(body.fault)
+ end
+ end
+
+private
+
+ def create_header(headers)
+ header = SOAPHeader.new()
+ headers.each do |content, mustunderstand, encodingstyle|
+ header.add(SOAPHeaderItem.new(content, mustunderstand, encodingstyle))
+ end
+ header
+ end
+
+ def create_options
+ opt = {}
+ opt[:default_encodingstyle] = @default_encodingstyle
+ if @allow_unqualified_element
+ opt[:allow_unqualified_element] = true
+ end
+ opt
+ end
+end
+
+
+end
+end
diff --git a/lib/soap/rpc/router.rb b/lib/soap/rpc/router.rb
new file mode 100644
index 0000000000..20396a4a7d
--- /dev/null
+++ b/lib/soap/rpc/router.rb
@@ -0,0 +1,176 @@
+=begin
+SOAP4R - RPC Routing library
+Copyright (C) 2001, 2002 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
+
+
+require 'soap/soap'
+require 'soap/processor'
+require 'soap/mapping'
+require 'soap/rpc/rpc'
+require 'soap/rpc/element'
+
+
+module SOAP
+module RPC
+
+
+class Router
+ include SOAP
+
+ attr_reader :actor
+ attr_accessor :allow_unqualified_element
+ attr_accessor :default_encodingstyle
+ attr_accessor :mapping_registry
+
+ def initialize(actor)
+ @actor = actor
+ @receiver = {}
+ @method_name = {}
+ @method = {}
+ @allow_unqualified_element = false
+ @default_encodingstyle = nil
+ @mapping_registry = nil
+ end
+
+ def add_method(receiver, qname, soapaction, name, param_def)
+ fqname = fqname(qname)
+ @receiver[fqname] = receiver
+ @method_name[fqname] = name
+ @method[fqname] = RPC::SOAPMethodRequest.new(qname, param_def, soapaction)
+ end
+
+ def add_header_handler
+ raise NotImplementedError.new
+ end
+
+ # Routing...
+ def route(soap_string, charset = nil)
+ opt = options
+ opt[:charset] = charset
+ is_fault = false
+ begin
+ header, body = Processor.unmarshal(soap_string, opt)
+ # So far, header is omitted...
+ soap_request = body.request
+ unless soap_request.is_a?(SOAPStruct)
+ raise RPCRoutingError.new("Not an RPC style.")
+ end
+ soap_response = dispatch(soap_request)
+ rescue Exception
+ soap_response = fault($!)
+ is_fault = true
+ end
+
+ header = SOAPHeader.new
+ body = SOAPBody.new(soap_response)
+ response_string = Processor.marshal(header, body, opt)
+
+ return response_string, is_fault
+ end
+
+ # Create fault response string.
+ def create_fault_response(e, charset = nil)
+ header = SOAPHeader.new
+ soap_response = fault(e)
+ body = SOAPBody.new(soap_response)
+ opt = options
+ opt[:charset] = charset
+ Processor.marshal(header, body, opt)
+ end
+
+private
+
+ # Create new response.
+ def create_response(qname, result)
+ name = fqname(qname)
+ if (@method.key?(name))
+ method = @method[name]
+ else
+ raise RPCRoutingError.new("Method: #{ name } not defined.")
+ end
+
+ soap_response = method.create_method_response
+ if soap_response.have_outparam?
+ unless result.is_a?(Array)
+ raise RPCRoutingError.new("Out parameter was not returned.")
+ end
+ outparams = {}
+ i = 1
+ soap_response.each_param_name('out', 'inout') do |outparam|
+ outparams[outparam] = Mapping.obj2soap(result[i], @mapping_registry)
+ i += 1
+ end
+ soap_response.set_outparam(outparams)
+ soap_response.retval = Mapping.obj2soap(result[0], @mapping_registry)
+ else
+ soap_response.retval = Mapping.obj2soap(result, @mapping_registry)
+ end
+ soap_response
+ end
+
+ # Create fault response.
+ def fault(e)
+ detail = Mapping::SOAPException.new(e)
+ SOAPFault.new(
+ SOAPString.new('Server'),
+ SOAPString.new(e.to_s),
+ SOAPString.new(@actor),
+ Mapping.obj2soap(detail, @mapping_registry))
+ end
+
+ # Dispatch to defined method.
+ def dispatch(soap_method)
+ request_struct = Mapping.soap2obj(soap_method, @mapping_registry)
+ values = soap_method.collect { |key, value| request_struct[key] }
+ method = lookup(soap_method.elename, values)
+ unless method
+ raise RPCRoutingError.new(
+ "Method: #{ soap_method.elename } not supported.")
+ end
+
+ result = method.call(*values)
+ create_response(soap_method.elename, result)
+ end
+
+ # Method lookup
+ def lookup(qname, values)
+ name = fqname(qname)
+ # It may be necessary to check all part of method signature...
+ if @method.member?(name)
+ @receiver[name].method(@method_name[name].intern)
+ else
+ nil
+ end
+ end
+
+ def fqname(qname)
+ "#{ qname.namespace }:#{ qname.name }"
+ end
+
+ def options
+ opt = {}
+ opt[:default_encodingstyle] = @default_encodingstyle
+ if @allow_unqualified_element
+ opt[:allow_unqualified_element] = true
+ end
+ opt
+ end
+end
+
+
+end
+end
diff --git a/lib/soap/rpc/rpc.rb b/lib/soap/rpc/rpc.rb
new file mode 100644
index 0000000000..c3cb3228f5
--- /dev/null
+++ b/lib/soap/rpc/rpc.rb
@@ -0,0 +1,36 @@
+=begin
+SOAP4R - RPC utility.
+Copyright (C) 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 RPC
+ ServerException = Mapping::MappedException
+
+ def self.defined_methods(obj)
+ if obj.is_a?(Module)
+ obj.methods - Module.methods
+ else
+ obj.methods - Kernel.instance_methods(true)
+ end
+ end
+end
+
+
+end
diff --git a/lib/soap/rpc/soaplet.rb b/lib/soap/rpc/soaplet.rb
new file mode 100644
index 0000000000..1a4ef99b76
--- /dev/null
+++ b/lib/soap/rpc/soaplet.rb
@@ -0,0 +1,167 @@
+=begin
+SOAP4R - SOAP handler servlet for WEBrick
+Copyright (C) 2001, 2002, 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
+
+
+require 'webrick/httpservlet/abstract'
+require 'webrick/httpstatus'
+require 'soap/rpc/router'
+require 'soap/streamHandler'
+
+module SOAP
+module RPC
+
+
+class SOAPlet < WEBrick::HTTPServlet::AbstractServlet
+public
+ attr_reader :app_scope_router
+
+ def initialize
+ @router_map = {}
+ @app_scope_router = ::SOAP::RPC::Router.new(self.class.name)
+ end
+
+ # Add servant klass whose object has request scope. A servant object is
+ # instanciated for each request.
+ #
+ # Bare in mind that servant klasses are distinguished by HTTP SOAPAction
+ # header in request. Client which calls request-scoped servant must have a
+ # SOAPAction header which is a namespace of the servant klass.
+ # I mean, use Driver#add_method_with_soapaction instead of Driver#add_method
+ # at client side.
+ #
+ def add_rpc_request_servant(klass, namespace, mapping_registry = nil)
+ router = RequestRouter.new(klass, namespace, mapping_registry)
+ add_router(namespace, router)
+ end
+
+ # Add servant object which has application scope.
+ def add_rpc_servant(obj, namespace)
+ router = @app_scope_router
+ SOAPlet.add_servant_to_router(router, obj, namespace)
+ add_router(namespace, router)
+ end
+ alias add_servant add_rpc_servant
+
+
+ ###
+ ## Servlet interfaces for WEBrick.
+ #
+ def get_instance(config, *options)
+ @config = config
+ self
+ end
+
+ def require_path_info?
+ false
+ end
+
+ def do_GET(req, res)
+ res.header['Allow'] = 'POST'
+ raise WEBrick::HTTPStatus::MethodNotAllowed, "GET request not allowed."
+ end
+
+ def do_POST(req, res)
+ namespace = parse_soapaction(req.meta_vars['HTTP_SOAPACTION'])
+ router = lookup_router(namespace)
+
+ is_fault = false
+
+ charset = ::SOAP::StreamHandler.parse_media_type(req['content-type'])
+ begin
+ response_stream, is_fault = router.route(req.body, charset)
+ rescue Exception => e
+ response_stream = router.create_fault_response(e)
+ is_fault = true
+ end
+
+ res.body = response_stream
+ res['content-type'] = "text/xml; charset=\"#{charset}\""
+ if response_stream.is_a?(IO)
+ res.chunked = true
+ end
+
+ if is_fault
+ res.status = WEBrick::HTTPStatus::RC_INTERNAL_SERVER_ERROR
+ end
+ end
+
+private
+
+ class RequestRouter < ::SOAP::RPC::Router
+ def initialize(klass, namespace, mapping_registry = nil)
+ super(namespace)
+ if mapping_registry
+ self.mapping_registry = mapping_registry
+ end
+ @klass = klass
+ @namespace = namespace
+ end
+
+ def route(soap_string)
+ obj = @klass.new
+ namespace = self.actor
+ router = ::SOAP::RPC::Router.new(@namespace)
+ SOAPlet.add_servant_to_router(router, obj, namespace)
+ router.route(soap_string)
+ end
+ end
+
+ def add_router(namespace, router)
+ @router_map[namespace] = router
+ end
+
+ def parse_soapaction(soapaction)
+ if /^"(.*)"$/ =~ soapaction
+ soapaction = $1
+ end
+ if soapaction.empty?
+ return nil
+ end
+ soapaction
+ end
+
+ def lookup_router(namespace)
+ if namespace
+ @router_map[namespace] || @app_scope_router
+ else
+ @app_scope_router
+ end
+ end
+
+ class << self
+ public
+ def add_servant_to_router(router, obj, namespace)
+ ::SOAP::RPC.defined_methods(obj).each do |name|
+ add_servant_method_to_router(router, obj, namespace, name)
+ end
+ end
+
+ def add_servant_method_to_router(router, obj, namespace, name)
+ qname = XSD::QName.new(namespace, name)
+ soapaction = nil
+ method = obj.method(name)
+ param_def = ::SOAP::RPC::SOAPMethod.create_param_def(
+ (1..method.arity.abs).collect { |i| "p#{ i }" })
+ router.add_method(obj, qname, soapaction, name, param_def)
+ end
+ end
+end
+
+
+end
+end
diff --git a/lib/soap/rpc/standaloneServer.rb b/lib/soap/rpc/standaloneServer.rb
new file mode 100644
index 0000000000..bc8ab18f4c
--- /dev/null
+++ b/lib/soap/rpc/standaloneServer.rb
@@ -0,0 +1,116 @@
+=begin
+SOAP4R - WEBrick Server
+Copyright (C) 2003 by 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
+
+
+require 'logger'
+require 'soap/rpc/soaplet'
+require 'soap/streamHandler'
+
+# require 'webrick'
+require 'webrick/compat.rb'
+require 'webrick/version.rb'
+require 'webrick/config.rb'
+require 'webrick/log.rb'
+require 'webrick/server.rb'
+require 'webrick/utils.rb'
+require 'webrick/accesslog'
+# require 'webrick/htmlutils.rb'
+require 'webrick/httputils.rb'
+# require 'webrick/cookie.rb'
+require 'webrick/httpversion.rb'
+require 'webrick/httpstatus.rb'
+require 'webrick/httprequest.rb'
+require 'webrick/httpresponse.rb'
+require 'webrick/httpserver.rb'
+# require 'webrick/httpservlet.rb'
+# require 'webrick/httpauth.rb'
+
+
+module SOAP
+module RPC
+
+
+class StandaloneServer < Logger::Application
+ attr_reader :server
+
+ def initialize(app_name, namespace, host = "0.0.0.0", port = 8080)
+ super(app_name)
+ @logdev = Logger.new(STDERR)
+ @logdev.level = INFO
+ @namespace = namespace
+ @server = WEBrick::HTTPServer.new(
+ :BindAddress => host,
+ :Logger => logdev,
+ :AccessLog => [[logdev, WEBrick::AccessLog::COMBINED_LOG_FORMAT]],
+ :Port => port
+ )
+ @soaplet = ::SOAP::RPC::SOAPlet.new
+ on_init
+ @server.mount('/', @soaplet)
+ end
+
+ def on_init
+ # define extra methods in derived class.
+ end
+
+ def add_rpc_request_servant(klass, namespace = @namespace, mapping_registry = nil)
+ @soaplet.add_rpc_request_servant(klass, namespace, mapping_registry)
+ end
+
+ def add_rpc_servant(obj, namespace = @namespace)
+ @soaplet.add_rpc_servant(obj, namespace)
+ end
+ alias add_servant add_rpc_servant
+
+ def mapping_registry
+ @soaplet.app_scope_router.mapping_registry
+ end
+
+ def mapping_registry=(mapping_registry)
+ @soaplet.app_scope_router.mapping_registry = mapping_registry
+ end
+
+ def add_method(obj, name, *param)
+ add_method_as(obj, name, name, *param)
+ end
+
+ def add_method_as(obj, name, name_as, *param)
+ qname = XSD::QName.new(@namespace, name_as)
+ soapaction = nil
+ method = obj.method(name)
+ param_def = if param.size == 1 and param[0].is_a?(Array)
+ param[0]
+ elsif param.empty?
+ ::SOAP::RPC::SOAPMethod.create_param_def(
+ (1..method.arity.abs).collect { |i| "p#{ i }" })
+ else
+ SOAP::RPC::SOAPMethod.create_param_def(param)
+ end
+ @soaplet.app_scope_router.add_method(obj, qname, soapaction, name, param_def)
+ end
+
+private
+
+ def run
+ @server.start
+ end
+end
+
+
+end
+end
diff --git a/lib/soap/soap.rb b/lib/soap/soap.rb
new file mode 100644
index 0000000000..313af3a059
--- /dev/null
+++ b/lib/soap/soap.rb
@@ -0,0 +1,112 @@
+=begin
+SOAP4R - Base definitions.
+Copyright (C) 2000, 2001, 2002, 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
+
+
+require 'xsd/qname'
+require 'xsd/charset'
+
+
+module SOAP
+
+
+Version = '1.5.0'
+
+EnvelopeNamespace = 'http://schemas.xmlsoap.org/soap/envelope/'
+EncodingNamespace = 'http://schemas.xmlsoap.org/soap/encoding/'
+LiteralNamespace = 'http://xml.apache.org/xml-soap/literalxml'
+
+NextActor = 'http://schemas.xmlsoap.org/soap/actor/next'
+
+EleEnvelope = 'Envelope'
+EleHeader = 'Header'
+EleBody = 'Body'
+EleFault = 'Fault'
+EleFaultString = 'faultstring'
+EleFaultActor = 'faultactor'
+EleFaultCode = 'faultcode'
+EleFaultDetail = 'detail'
+
+AttrMustUnderstand = 'mustUnderstand'
+AttrEncodingStyle = 'encodingStyle'
+AttrActor = 'actor'
+AttrRoot = 'root'
+AttrArrayType = 'arrayType'
+AttrOffset = 'offset'
+AttrPosition = 'position'
+ValueArray = 'Array'
+
+EleEnvelopeName = XSD::QName.new(EnvelopeNamespace, EleEnvelope)
+EleHeaderName = XSD::QName.new(EnvelopeNamespace, EleHeader)
+EleBodyName = XSD::QName.new(EnvelopeNamespace, EleBody)
+EleFaultName = XSD::QName.new(EnvelopeNamespace, EleFault)
+EleFaultStringName = XSD::QName.new(nil, EleFaultString)
+EleFaultActorName = XSD::QName.new(nil, EleFaultActor)
+EleFaultCodeName = XSD::QName.new(nil, EleFaultCode)
+EleFaultDetailName = XSD::QName.new(nil, EleFaultDetail)
+AttrEncodingStyleName = XSD::QName.new(EnvelopeNamespace, AttrEncodingStyle)
+AttrRootName = XSD::QName.new(EncodingNamespace, AttrRoot)
+AttrArrayTypeName = XSD::QName.new(EncodingNamespace, AttrArrayType)
+AttrOffsetName = XSD::QName.new(EncodingNamespace, AttrOffset)
+AttrPositionName = XSD::QName.new(EncodingNamespace, AttrPosition)
+ValueArrayName = XSD::QName.new(EncodingNamespace, ValueArray)
+
+Base64Literal = 'base64'
+
+SOAPNamespaceTag = 'env'
+XSDNamespaceTag = 'xsd'
+XSINamespaceTag = 'xsi'
+
+MediaType = 'text/xml'
+
+class Error < StandardError; end
+
+class StreamError < Error; end
+class HTTPStreamError < StreamError; end
+class PostUnavailableError < HTTPStreamError; end
+class MPostUnavailableError < HTTPStreamError; end
+
+class ArrayIndexOutOfBoundsError < Error; end
+class ArrayStoreError < Error; end
+
+class RPCRoutingError < Error; end
+
+class FaultError < Error
+ attr_reader :faultcode
+ attr_reader :faultstring
+ attr_reader :faultactor
+ attr_accessor :detail
+
+ def initialize(fault)
+ @faultcode = fault.faultcode
+ @faultstring = fault.faultstring
+ @faultactor = fault.faultactor
+ @detail = fault.detail
+ super(self.to_s)
+ end
+
+ def to_s
+ str = nil
+ if @faultstring && @faultstring.respond_to?('data')
+ str = @faultstring.data
+ end
+ str || '(No faultstring)'
+ end
+end
+
+
+end
diff --git a/lib/soap/streamHandler.rb b/lib/soap/streamHandler.rb
new file mode 100644
index 0000000000..c0ca3955f7
--- /dev/null
+++ b/lib/soap/streamHandler.rb
@@ -0,0 +1,188 @@
+=begin
+SOAP4R - Stream handler.
+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
+
+
+require 'soap/soap'
+
+
+module SOAP
+
+
+class StreamHandler
+ Client = begin
+ require 'http-access2'
+ if HTTPAccess2::VERSION < "2.0"
+ raise LoadError.new("http-access/2.0 or later is required.")
+ end
+ HTTPAccess2::Client
+ rescue LoadError
+ STDERR.puts "Loading http-access2 failed. Net/http is used." if $DEBUG
+ require 'soap/netHttpClient'
+ SOAP::NetHttpClient
+ end
+
+ RUBY_VERSION_STRING = "ruby #{ RUBY_VERSION } (#{ RUBY_RELEASE_DATE }) [#{ RUBY_PLATFORM }]"
+ %q$Id$ =~ /: (\S+),v (\S+)/
+ RCS_FILE, RCS_REVISION = $1, $2
+
+ class ConnectionData
+ attr_accessor :send_string
+ attr_accessor :send_contenttype
+ attr_accessor :receive_string
+ attr_accessor :receive_contenttype
+
+ def initialize
+ @send_string = nil
+ @send_contenttype = nil
+ @receive_string = nil
+ @receive_contenttype = nil
+ @bag = {}
+ end
+
+ def [](idx)
+ @bag[idx]
+ end
+
+ def []=(idx, value)
+ @bag[idx] = value
+ end
+ end
+
+ attr_accessor :endpoint_url
+
+ def initialize(endpoint_url)
+ @endpoint_url = endpoint_url
+ end
+
+ def self.parse_media_type(str)
+ if /^#{ MediaType }(?:\s*;\s*charset=([^"]+|"[^"]+"))?$/i !~ str
+ raise StreamError.new("Illegal media type.");
+ end
+ charset = $1
+ charset.gsub!(/"/, '') if charset
+ charset
+ end
+
+ def self.create_media_type(charset)
+ "#{ MediaType }; charset=#{ charset }"
+ end
+end
+
+
+class HTTPPostStreamHandler < StreamHandler
+ include SOAP
+
+public
+
+ attr_accessor :wiredump_dev
+ attr_accessor :wiredump_file_base
+ attr_accessor :charset
+
+ NofRetry = 10 # [times]
+ ConnectTimeout = 20 # [sec]
+ SendTimeout = 60 # [sec]
+ ReceiveTimeout = 60 # [sec]
+
+ def initialize(endpoint_url, proxy = nil, charset = nil)
+ super(endpoint_url)
+ @proxy = proxy || ENV['http_proxy'] || ENV['HTTP_PROXY']
+ @charset = charset || XSD::Charset.charset_label($KCODE)
+ @wiredump_dev = nil # Set an IO to get wiredump.
+ @wiredump_file_base = nil
+ @client = Client.new(@proxy, "SOAP4R/#{ Version }")
+ @client.session_manager.connect_timeout = ConnectTimeout
+ @client.session_manager.send_timeout = SendTimeout
+ @client.session_manager.receive_timeout = ReceiveTimeout
+ end
+
+ def proxy=(proxy)
+ @proxy = proxy
+ @client.proxy = @proxy
+ end
+
+ def send(soap_string, soapaction = nil, charset = @charset)
+ send_post(soap_string, soapaction, charset)
+ end
+
+ def reset
+ @client.reset(@endpoint_url)
+ end
+
+private
+
+ def send_post(soap_string, soapaction, charset)
+ data = ConnectionData.new
+ data.send_string = soap_string
+ data.send_contenttype = StreamHandler.create_media_type(charset)
+
+ wiredump_dev = if @wiredump_dev && @wiredump_dev.respond_to?("<<")
+ @wiredump_dev
+ else
+ nil
+ end
+ @client.debug_dev = wiredump_dev
+
+ if @wiredump_file_base
+ filename = @wiredump_file_base + '_request.xml'
+ f = File.open(filename, "w")
+ f << soap_string
+ f.close
+ end
+
+ extra = {}
+ extra['Content-Type'] = data.send_contenttype
+ extra['SOAPAction'] = "\"#{ soapaction }\""
+
+ wiredump_dev << "Wire dump:\n\n" if wiredump_dev
+ begin
+ res = @client.post(@endpoint_url, soap_string, extra)
+ rescue
+ @client.reset(@endpoint_url)
+ raise
+ end
+ wiredump_dev << "\n\n" if wiredump_dev
+
+ receive_string = res.content
+
+ if @wiredump_file_base
+ filename = @wiredump_file_base + '_response.xml'
+ f = File.open(filename, "w")
+ f << receive_string
+ f.close
+ end
+
+ case res.status
+ when 405
+ raise PostUnavailableError.new("#{ res.status }: #{ res.reason }")
+ when 200, 500
+ # Nothing to do.
+ else
+ raise HTTPStreamError.new("#{ res.status }: #{ res.reason }")
+ end
+
+ data.receive_string = receive_string
+ data.receive_contenttype = res.contenttype
+
+ return data
+ end
+
+ CRLF = "\r\n"
+end
+
+
+end
diff --git a/lib/soap/wsdlDriver.rb b/lib/soap/wsdlDriver.rb
new file mode 100644
index 0000000000..849effcb0b
--- /dev/null
+++ b/lib/soap/wsdlDriver.rb
@@ -0,0 +1,490 @@
+=begin
+SOAP4R - SOAP WSDL driver
+Copyright (C) 2002, 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
+
+
+require 'wsdl/parser'
+require 'wsdl/importer'
+require 'xsd/qname'
+require 'soap/element'
+require 'soap/baseData'
+require 'soap/streamHandler'
+require 'soap/mapping'
+require 'soap/mapping/wsdlRegistry'
+require 'soap/rpc/rpc'
+require 'soap/rpc/element'
+require 'soap/processor'
+require 'logger'
+
+
+module SOAP
+
+
+class WSDLDriverFactory
+ class FactoryError < StandardError; end
+
+ attr_reader :wsdl
+
+ def initialize(wsdl, logdev = nil)
+ @logdev = logdev
+ @wsdl = import(wsdl)
+ end
+
+ def create_driver(servicename = nil, portname = nil, opt = {})
+ service = if servicename
+ @wsdl.service(XSD::QName.new(@wsdl.targetnamespace, servicename))
+ else
+ @wsdl.services[0]
+ end
+ if service.nil?
+ raise FactoryError.new("Service #{ servicename } not found in WSDL.")
+ end
+ port = if portname
+ service.ports[XSD::QName.new(@wsdl.targetnamespace, portname)]
+ else
+ service.ports[0]
+ end
+ if port.nil?
+ raise FactoryError.new("Port #{ portname } not found in WSDL.")
+ end
+ if port.soap_address.nil?
+ raise FactoryError.new("soap:address element not found in WSDL.")
+ end
+ WSDLDriver.new(@wsdl, port, @logdev, opt)
+ end
+
+ # Backward compatibility.
+ alias createDriver create_driver
+
+private
+
+ def import(location)
+ WSDL::Importer.import(location)
+ end
+end
+
+
+class WSDLDriver
+ class << self
+ def __attr_proxy(symbol, assignable = false)
+ name = symbol.to_s
+ module_eval <<-EOD
+ def #{name}
+ @servant.#{name}
+ end
+ EOD
+ if assignable
+ module_eval <<-EOD
+ def #{name}=(rhs)
+ @servant.#{name} = rhs
+ end
+ EOD
+ end
+ end
+ end
+
+ __attr_proxy :opt
+ __attr_proxy :logdev, true
+ __attr_proxy :mapping_registry, true # for RPC unmarshal
+ __attr_proxy :wsdl_mapping_registry, true # for RPC marshal
+ __attr_proxy :endpoint_url, true
+ __attr_proxy :wiredump_dev, true
+ __attr_proxy :wiredump_file_base, true
+ __attr_proxy :httpproxy, true
+
+ __attr_proxy :default_encodingstyle, true
+ __attr_proxy :allow_unqualified_element, true
+ __attr_proxy :generate_explicit_type, true
+
+ def reset_stream
+ @servant.reset_stream
+ end
+
+ # Backward compatibility.
+ alias generateEncodeType= generate_explicit_type=
+
+ class Servant__
+ include Logger::Severity
+ include SOAP
+
+ attr_reader :opt
+ attr_accessor :logdev
+ attr_accessor :mapping_registry
+ attr_accessor :wsdl_mapping_registry
+ attr_reader :endpoint_url
+ attr_reader :wiredump_dev
+ attr_reader :wiredump_file_base
+ attr_reader :httpproxy
+
+ attr_accessor :default_encodingstyle
+ attr_accessor :allow_unqualified_element
+ attr_accessor :generate_explicit_type
+
+ class Mapper
+ def initialize(elements, types)
+ @elements = elements
+ @types = types
+ end
+
+ def obj2ele(obj, name)
+ if ele = @elements[name]
+ _obj2ele(obj, ele)
+ elsif type = @types[name]
+ obj2type(obj, type)
+ else
+ raise RuntimeError.new("Cannot find name #{name} in schema.")
+ end
+ end
+
+ def ele2obj(ele, *arg)
+ raise NotImplementedError.new
+ end
+
+ private
+
+ def _obj2ele(obj, ele)
+ o = nil
+ if ele.type
+ if type = @types[ele.type]
+ o = obj2type(obj, type)
+ elsif type = TypeMap[ele.type]
+ o = base2soap(obj, type)
+ else
+ raise RuntimeError.new("Cannot find type #{ele.type}.")
+ end
+ o.elename = ele.name
+ elsif ele.local_complextype
+ o = SOAPElement.new(ele.name)
+ ele.local_complextype.each_element do |child_name, child_ele|
+ o.add(_obj2ele(find_attribute(obj, child_name.name), child_ele))
+ end
+ else
+ raise RuntimeError.new("Illegal schema?")
+ end
+ o
+ end
+
+ def obj2type(obj, type)
+ o = SOAPElement.new(type.name)
+ type.each_element do |child_name, child_ele|
+ o.add(_obj2ele(find_attribute(obj, child_name.name), child_ele))
+ end
+ o
+ end
+
+ def _ele2obj(ele)
+ raise NotImplementedError.new
+ end
+
+ def base2soap(obj, type)
+ soap_obj = nil
+ if type <= XSD::XSDString
+ soap_obj = type.new(XSD::Charset.is_ces(obj, $KCODE) ?
+ XSD::Charset.encoding_conv(obj, $KCODE, XSD::Charset.encoding) : obj)
+ else
+ soap_obj = type.new(obj)
+ end
+ soap_obj
+ end
+
+ def find_attribute(obj, attr_name)
+ if obj.respond_to?(attr_name)
+ obj.__send__(attr_name)
+ elsif obj.is_a?(Hash)
+ obj[attr_name] || obj[attr_name.intern]
+ else
+ obj.instance_eval("@#{ attr_name }")
+ end
+ end
+ end
+
+ def initialize(host, wsdl, port, logdev, opt)
+ @host = host
+ @wsdl = wsdl
+ @port = port
+ @logdev = logdev
+ @opt = opt.dup
+ @mapping_registry = nil # for rpc unmarshal
+ @wsdl_mapping_registry = nil # for rpc marshal
+ @endpoint_url = nil
+ @wiredump_dev = nil
+ @wiredump_file_base = nil
+ @httpproxy = ENV['http_proxy'] || ENV['HTTP_PROXY']
+
+ @wsdl_elements = @wsdl.collect_elements
+ @wsdl_types = @wsdl.collect_complextypes
+ @rpc_decode_typemap = @wsdl_types + @wsdl.soap_rpc_complextypes(port.find_binding)
+ @wsdl_mapping_registry = Mapping::WSDLRegistry.new(@rpc_decode_typemap)
+ @doc_mapper = Mapper.new(@wsdl_elements, @wsdl_types)
+ @default_encodingstyle = EncodingNamespace
+ @allow_unqualified_element = true
+ @generate_explicit_type = false
+
+ create_handler
+ @operations = {}
+ # Convert a map which key is QName, to a Hash which key is String.
+ @port.inputoperation_map.each do |op_name, op_info|
+ @operations[op_name.name] = op_info
+ add_method_interface(op_info)
+ end
+ end
+
+ def endpoint_url=(endpoint_url)
+ @endpoint_url = endpoint_url
+ if @handler
+ @handler.endpoint_url = @endpoint_url
+ @handler.reset
+ end
+ log(DEBUG) { "endpoint_url=: set endpoint_url #{ @endpoint_url }." }
+ end
+
+ def wiredump_dev=(dev)
+ @wiredump_dev = dev
+ if @handler
+ @handler.wiredump_dev = @wiredump_dev
+ @handler.reset
+ end
+ end
+
+ def wiredump_file_base=(base)
+ @wiredump_file_base = base
+ end
+
+ def httpproxy=(httpproxy)
+ @httpproxy = httpproxy
+ if @handler
+ @handler.proxy = @httpproxy
+ @handler.reset
+ end
+ log(DEBUG) { "httpproxy=: set httpproxy #{ @httpproxy }." }
+ end
+
+ def reset_stream
+ @handler.reset
+ end
+
+ def rpc_send(method_name, *params)
+ log(INFO) { "call: calling method '#{ method_name }'." }
+ log(DEBUG) { "call: parameters '#{ params.inspect }'." }
+
+ op_info = @operations[method_name]
+ parts_names = op_info.bodyparts.collect { |part| part.name }
+ obj = create_method_obj(parts_names, params)
+ method = Mapping.obj2soap(obj, @wsdl_mapping_registry, op_info.optype_name)
+ method.elename = op_info.op_name
+ method.type = XSD::QName.new # Request should not be typed.
+ req_header = nil
+ req_body = SOAPBody.new(method)
+
+ if @wiredump_file_base
+ @handler.wiredump_file_base = @wiredump_file_base + '_' << method_name
+ end
+
+ begin
+ opt = create_options
+ opt[:decode_typemap] = @rpc_decode_typemap
+ res_header, res_body = invoke(req_header, req_body, op_info, opt)
+ if res_body.fault
+ raise SOAP::FaultError.new(res_body.fault)
+ end
+ rescue SOAP::FaultError => e
+ Mapping.fault2exception(e)
+ end
+
+ ret = res_body.response ?
+ Mapping.soap2obj(res_body.response, @mapping_registry) : nil
+
+ if res_body.outparams
+ outparams = res_body.outparams.collect { |outparam|
+ Mapping.soap2obj(outparam)
+ }
+ return [ret].concat(outparams)
+ else
+ return ret
+ end
+ end
+
+ # req_header: [[element, mustunderstand, encodingstyle(QName/String)], ...]
+ # req_body: SOAPBasetype/SOAPCompoundtype
+ def document_send(name, header_obj, body_obj)
+ log(INFO) { "document_send: sending document '#{ name }'." }
+ op_info = @operations[name]
+ req_header = header_from_obj(header_obj, op_info)
+ req_body = body_from_obj(body_obj, op_info)
+ opt = create_options
+ res_header, res_body = invoke(req_header, req_body, op_info, opt)
+ if res_body.fault
+ raise SOAP::FaultError.new(res_body.fault)
+ end
+ res_body_obj = res_body.response ?
+ Mapping.soap2obj(res_body.response, @mapping_registry) : nil
+ return res_header, res_body_obj
+ end
+
+ private
+
+ def create_handler
+ endpoint_url = @endpoint_url || @port.soap_address.location
+ @handler = HTTPPostStreamHandler.new(endpoint_url, @httpproxy,
+ XSD::Charset.encoding_label)
+ @handler.wiredump_dev = @wiredump_dev
+ end
+
+ def create_method_obj(names, params)
+ o = Object.new
+ for idx in 0 ... params.length
+ o.instance_eval("@#{ names[idx] } = params[idx]")
+ end
+ o
+ end
+
+ def invoke(req_header, req_body, op_info, opt)
+ send_string = Processor.marshal(req_header, req_body, opt)
+ log(DEBUG) { "invoke: sending string #{ send_string }" }
+ data = @handler.send(send_string, op_info.soapaction)
+ log(DEBUG) { "invoke: received string #{ data.receive_string }" }
+ if data.receive_string.empty?
+ return nil, nil
+ end
+ res_charset = StreamHandler.parse_media_type(data.receive_contenttype)
+ opt[:charset] = res_charset
+ res_header, res_body = Processor.unmarshal(data.receive_string, opt)
+ return res_header, res_body
+ end
+
+ def header_from_obj(obj, op_info)
+ if obj.is_a?(SOAPHeader)
+ obj
+ elsif op_info.headerparts.empty?
+ if obj.nil?
+ nil
+ else
+ raise RuntimeError.new("No header definition in schema.")
+ end
+ elsif op_info.headerparts.size == 1
+ part = op_info.headerparts[0]
+ header = SOAPHeader.new()
+ header.add(headeritem_from_obj(obj, part.element || part.eletype))
+ header
+ else
+ header = SOAPHeader.new()
+ op_info.headerparts.each do |part|
+ child = obj[part.elename.name]
+ ele = headeritem_from_obj(child, part.element || part.eletype)
+ header.add(ele)
+ end
+ header
+ end
+ end
+
+ def headeritem_from_obj(obj, name)
+ if obj.nil?
+ SOAPElement.new(name)
+ elsif obj.is_a?(SOAPHeaderItem)
+ obj
+ else
+ @doc_mapper.obj2ele(obj, name)
+ end
+ end
+
+ def body_from_obj(obj, op_info)
+ if obj.is_a?(SOAPBody)
+ obj
+ elsif op_info.bodyparts.empty?
+ if obj.nil?
+ nil
+ else
+ raise RuntimeError.new("No body found in schema.")
+ end
+ elsif op_info.bodyparts.size == 1
+ part = op_info.bodyparts[0]
+ ele = bodyitem_from_obj(obj, part.element || part.type)
+ SOAPBody.new(ele)
+ else
+ body = SOAPBody.new
+ op_info.bodyparts.each do |part|
+ child = obj[part.elename.name]
+ ele = bodyitem_from_obj(child, part.element || part.type)
+ body.add(ele.elename.name, ele)
+ end
+ body
+ end
+ end
+
+ def bodyitem_from_obj(obj, name)
+ if obj.nil?
+ SOAPElement.new(name)
+ elsif obj.is_a?(SOAPElement)
+ obj
+ else
+ @doc_mapper.obj2ele(obj, name)
+ end
+ end
+
+ def add_method_interface(op_info)
+ case op_info.style
+ when :document
+ add_document_method_interface(op_info.op_name.name)
+ when :rpc
+ parts_names = op_info.bodyparts.collect { |part| part.name }
+ add_rpc_method_interface(op_info.op_name.name, parts_names)
+ else
+ raise RuntimeError.new("Unknown style: #{op_info.style}")
+ end
+ end
+
+ def add_document_method_interface(name)
+ @host.instance_eval <<-EOS
+ def #{ name }(headers, body)
+ @servant.document_send(#{ name.dump }, headers, body)
+ end
+ EOS
+ end
+
+ def add_rpc_method_interface(name, parts_names)
+ i = 0
+ param_names = parts_names.collect { |orgname| i += 1; "arg#{ i }" }
+ callparam_str = (param_names.collect { |pname| ", " + pname }).join
+ @host.instance_eval <<-EOS
+ def #{ name }(#{ param_names.join(", ") })
+ @servant.rpc_send(#{ name.dump }#{ callparam_str })
+ end
+ EOS
+ end
+
+ def create_options
+ opt = @opt.dup
+ opt[:default_encodingstyle] = @default_encodingstyle
+ opt[:allow_unqualified_element] = @allow_unqualified_element
+ opt[:generate_explicit_type] = @generate_explicit_type
+ opt
+ end
+
+ def log(sev)
+ @logdev.add(sev, nil, self.class) { yield } if @logdev
+ end
+ end
+
+ def initialize(wsdl, port, logdev, opt)
+ @servant = Servant__.new(self, wsdl, port, logdev, opt)
+ end
+end
+
+
+end
+
+