summaryrefslogtreecommitdiff
path: root/lib
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
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')
-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
-rw-r--r--lib/wsdl/binding.rb76
-rw-r--r--lib/wsdl/data.rb73
-rw-r--r--lib/wsdl/definitions.rb233
-rw-r--r--lib/wsdl/documentation.rb43
-rw-r--r--lib/wsdl/import.rb81
-rw-r--r--lib/wsdl/importer.rb75
-rw-r--r--lib/wsdl/info.rb44
-rw-r--r--lib/wsdl/message.rb65
-rw-r--r--lib/wsdl/operation.rb144
-rw-r--r--lib/wsdl/operationBinding.rb91
-rw-r--r--lib/wsdl/param.rb85
-rw-r--r--lib/wsdl/parser.rb170
-rw-r--r--lib/wsdl/part.rb63
-rw-r--r--lib/wsdl/port.rb95
-rw-r--r--lib/wsdl/portType.rb83
-rw-r--r--lib/wsdl/service.rb72
-rw-r--r--lib/wsdl/soap/address.rb51
-rw-r--r--lib/wsdl/soap/binding.rb59
-rw-r--r--lib/wsdl/soap/body.rb63
-rw-r--r--lib/wsdl/soap/complexType.rb96
-rw-r--r--lib/wsdl/soap/data.rb52
-rw-r--r--lib/wsdl/soap/definitions.rb130
-rw-r--r--lib/wsdl/soap/fault.rb63
-rw-r--r--lib/wsdl/soap/header.rb90
-rw-r--r--lib/wsdl/soap/headerfault.rb67
-rw-r--r--lib/wsdl/soap/operation.rb131
-rw-r--r--lib/wsdl/types.rb54
-rw-r--r--lib/wsdl/wsdl.rb34
-rw-r--r--lib/wsdl/xmlSchema/all.rb76
-rw-r--r--lib/wsdl/xmlSchema/any.rb67
-rw-r--r--lib/wsdl/xmlSchema/attribute.rb85
-rw-r--r--lib/wsdl/xmlSchema/choice.rb76
-rw-r--r--lib/wsdl/xmlSchema/complexContent.rb90
-rw-r--r--lib/wsdl/xmlSchema/complexType.rb130
-rw-r--r--lib/wsdl/xmlSchema/content.rb107
-rw-r--r--lib/wsdl/xmlSchema/data.rb75
-rw-r--r--lib/wsdl/xmlSchema/element.rb115
-rw-r--r--lib/wsdl/xmlSchema/import.rb55
-rw-r--r--lib/wsdl/xmlSchema/parser.rb172
-rw-r--r--lib/wsdl/xmlSchema/schema.rb105
-rw-r--r--lib/wsdl/xmlSchema/sequence.rb76
-rw-r--r--lib/wsdl/xmlSchema/unique.rb45
-rw-r--r--lib/xsd/charset.rb175
-rw-r--r--lib/xsd/datatypes.rb1094
-rw-r--r--lib/xsd/datatypes1999.rb31
-rw-r--r--lib/xsd/iconvcharset.rb44
-rw-r--r--lib/xsd/namedelements.rb86
-rw-r--r--lib/xsd/ns.rb141
-rw-r--r--lib/xsd/qname.rb77
-rw-r--r--lib/xsd/xmlparser.rb72
-rw-r--r--lib/xsd/xmlparser/parser.rb107
-rw-r--r--lib/xsd/xmlparser/rexmlparser.rb65
-rw-r--r--lib/xsd/xmlparser/xmlparser.rb61
-rw-r--r--lib/xsd/xmlparser/xmlscanner.rb158
83 files changed, 12305 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
+
+
diff --git a/lib/wsdl/binding.rb b/lib/wsdl/binding.rb
new file mode 100644
index 0000000000..349d6edcb6
--- /dev/null
+++ b/lib/wsdl/binding.rb
@@ -0,0 +1,76 @@
+=begin
+WSDL4R - WSDL binding definition.
+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/info'
+require 'xsd/namedelements'
+
+
+module WSDL
+
+
+class Binding < Info
+ attr_reader :name # required
+ attr_reader :type # required
+ attr_reader :operations
+ attr_reader :soapbinding
+
+ def initialize
+ super
+ @name = nil
+ @type = nil
+ @operations = XSD::NamedElements.new
+ @soapbinding = nil
+ end
+
+ def targetnamespace
+ parent.targetnamespace
+ end
+
+ def parse_element(element)
+ case element
+ when OperationName
+ o = OperationBinding.new
+ @operations << o
+ o
+ when SOAPBindingName
+ o = WSDL::SOAP::Binding.new
+ @soapbinding = o
+ o
+ when DocumentationName
+ o = Documentation.new
+ o
+ else
+ nil
+ end
+ end
+
+ def parse_attr(attr, value)
+ case attr
+ when NameAttrName
+ @name = XSD::QName.new(targetnamespace, value)
+ when TypeAttrName
+ @type = value
+ else
+ nil
+ end
+ end
+end
+
+
+end
diff --git a/lib/wsdl/data.rb b/lib/wsdl/data.rb
new file mode 100644
index 0000000000..4f3b845316
--- /dev/null
+++ b/lib/wsdl/data.rb
@@ -0,0 +1,73 @@
+=begin
+WSDL4R - WSDL data definitions.
+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/documentation'
+require 'wsdl/definitions'
+require 'wsdl/types'
+require 'wsdl/message'
+require 'wsdl/part'
+require 'wsdl/portType'
+require 'wsdl/operation'
+require 'wsdl/param'
+require 'wsdl/binding'
+require 'wsdl/operationBinding'
+require 'wsdl/service'
+require 'wsdl/port'
+require 'wsdl/import'
+
+
+module WSDL
+
+
+BindingName = XSD::QName.new(Namespace, 'binding')
+DefinitionsName = XSD::QName.new(Namespace, 'definitions')
+DocumentationName = XSD::QName.new(Namespace, 'documentation')
+FaultName = XSD::QName.new(Namespace, 'fault')
+ImportName = XSD::QName.new(Namespace, 'import')
+InputName = XSD::QName.new(Namespace, 'input')
+MessageName = XSD::QName.new(Namespace, 'message')
+OperationName = XSD::QName.new(Namespace, 'operation')
+OutputName = XSD::QName.new(Namespace, 'output')
+PartName = XSD::QName.new(Namespace, 'part')
+PortName = XSD::QName.new(Namespace, 'port')
+PortTypeName = XSD::QName.new(Namespace, 'portType')
+ServiceName = XSD::QName.new(Namespace, 'service')
+TypesName = XSD::QName.new(Namespace, 'types')
+
+SchemaName = XSD::QName.new(XSD::Namespace, 'schema')
+
+SOAPAddressName = XSD::QName.new(SOAPBindingNamespace, 'address')
+SOAPBindingName = XSD::QName.new(SOAPBindingNamespace, 'binding')
+SOAPHeaderName = XSD::QName.new(SOAPBindingNamespace, 'header')
+SOAPBodyName = XSD::QName.new(SOAPBindingNamespace, 'body')
+SOAPFaultName = XSD::QName.new(SOAPBindingNamespace, 'fault')
+SOAPOperationName = XSD::QName.new(SOAPBindingNamespace, 'operation')
+
+BindingAttrName = XSD::QName.new(nil, 'binding')
+ElementAttrName = XSD::QName.new(nil, 'element')
+LocationAttrName = XSD::QName.new(nil, 'location')
+MessageAttrName = XSD::QName.new(nil, 'message')
+NameAttrName = XSD::QName.new(nil, 'name')
+NamespaceAttrName = XSD::QName.new(nil, 'namespace')
+ParameterOrderAttrName = XSD::QName.new(nil, 'parameterOrder')
+TargetNamespaceAttrName = XSD::QName.new(nil, 'targetNamespace')
+TypeAttrName = XSD::QName.new(nil, 'type')
+
+
+end
diff --git a/lib/wsdl/definitions.rb b/lib/wsdl/definitions.rb
new file mode 100644
index 0000000000..7a0cff01e7
--- /dev/null
+++ b/lib/wsdl/definitions.rb
@@ -0,0 +1,233 @@
+=begin
+WSDL4R - WSDL definitions.
+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/info'
+require 'xsd/namedelements'
+
+
+module WSDL
+
+
+class Definitions < Info
+ attr_reader :name
+ attr_reader :targetnamespace
+ attr_reader :imports
+
+ # Overrides Info#root
+ def root
+ @root
+ end
+
+ def root=(root)
+ @root = root
+ end
+
+ def initialize
+ super
+ @name = nil
+ @targetnamespace = nil
+ @types = nil
+ @imports = []
+ @messages = XSD::NamedElements.new
+ @porttypes = XSD::NamedElements.new
+ @bindings = XSD::NamedElements.new
+ @services = XSD::NamedElements.new
+
+ @anontypes = XSD::NamedElements.new
+ @root = self
+ end
+
+ def targetnamespace=(targetnamespace)
+ @targetnamespace = targetnamespace
+ if @name
+ @name = XSD::QName.new(@targetnamespace, @name.name)
+ end
+ end
+
+ def collect_elements
+ result = XSD::NamedElements.new
+ if @types
+ @types.schemas.each do |schema|
+ result.concat(schema.elements)
+ end
+ end
+ @imports.each do |import|
+ result.concat(import.content.collect_elements)
+ end
+ result
+ end
+
+ def collect_complextypes
+ result = @anontypes.dup
+ if @types
+ @types.schemas.each do |schema|
+ result.concat(schema.complextypes)
+ end
+ end
+ @imports.each do |import|
+ result.concat(import.content.collect_complextypes)
+ end
+ result
+ end
+
+ def add_type(complextype)
+ @anontypes << complextype
+ end
+
+ def messages
+ result = @messages.dup
+ @imports.each do |import|
+ result.concat(import.content.messages) if self.class === import.content
+ end
+ result
+ end
+
+ def porttypes
+ result = @porttypes.dup
+ @imports.each do |import|
+ result.concat(import.content.porttypes) if self.class === import.content
+ end
+ result
+ end
+
+ def bindings
+ result = @bindings.dup
+ @imports.each do |import|
+ result.concat(import.content.bindings) if self.class === import.content
+ end
+ result
+ end
+
+ def services
+ result = @services.dup
+ @imports.each do |import|
+ result.concat(import.content.services) if self.class === import.content
+ end
+ result
+ end
+
+ def message(name)
+ message = @messages[name]
+ return message if message
+ @imports.each do |import|
+ message = import.content.message(name) if self.class === import.content
+ return message if message
+ end
+ nil
+ end
+
+ def porttype(name)
+ porttype = @porttypes[name]
+ return porttype if porttype
+ @imports.each do |import|
+ porttype = import.content.porttype(name) if self.class === import.content
+ return porttype if porttype
+ end
+ nil
+ end
+
+ def binding(name)
+ binding = @bindings[name]
+ return binding if binding
+ @imports.each do |import|
+ binding = import.content.binding(name) if self.class === import.content
+ return binding if binding
+ end
+ nil
+ end
+
+ def service(name)
+ service = @services[name]
+ return service if service
+ @imports.each do |import|
+ service = import.content.service(name) if self.class === import.content
+ return service if service
+ end
+ nil
+ end
+
+ def porttype_binding(name)
+ binding = @bindings.find { |item| item.type == name }
+ return binding if binding
+ @imports.each do |import|
+ binding = import.content.porttype_binding(name) if self.class === import.content
+ return binding if binding
+ end
+ nil
+ end
+
+ def parse_element(element)
+ case element
+ when ImportName
+ o = Import.new
+ @imports << o
+ o
+ when TypesName
+ o = Types.new
+ @types = o
+ o
+ when MessageName
+ o = Message.new
+ @messages << o
+ o
+ when PortTypeName
+ o = PortType.new
+ @porttypes << o
+ o
+ when BindingName
+ o = Binding.new
+ @bindings << o
+ o
+ when ServiceName
+ o = Service.new
+ @services << o
+ o
+ when DocumentationName
+ o = Documentation.new
+ o
+ else
+ nil
+ end
+ end
+
+ def parse_attr(attr, value)
+ case attr
+ when NameAttrName
+ @name = XSD::QName.new(@targetnamespace, value)
+ when TargetNamespaceAttrName
+ self.targetnamespace = value
+ else
+ nil
+ end
+ end
+
+ def self.parse_element(element)
+ if element == DefinitionsName
+ Definitions.new
+ else
+ nil
+ end
+ end
+
+private
+
+end
+
+
+end
diff --git a/lib/wsdl/documentation.rb b/lib/wsdl/documentation.rb
new file mode 100644
index 0000000000..00e78a99df
--- /dev/null
+++ b/lib/wsdl/documentation.rb
@@ -0,0 +1,43 @@
+=begin
+WSDL4R - WSDL SOAP documentation element.
+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 'wsdl/info'
+
+
+module WSDL
+
+
+class Documentation < Info
+ def initialize
+ super
+ end
+
+ def parse_element(element)
+ # Accepts any element.
+ self
+ end
+
+ def parse_attr(attr, value)
+ # Accepts any attribute.
+ true
+ end
+end
+
+
+end
diff --git a/lib/wsdl/import.rb b/lib/wsdl/import.rb
new file mode 100644
index 0000000000..a0fa26afaf
--- /dev/null
+++ b/lib/wsdl/import.rb
@@ -0,0 +1,81 @@
+=begin
+WSDL4R - WSDL import definition.
+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/info'
+require 'wsdl/importer'
+
+
+module WSDL
+
+
+class Import < Info
+ attr_reader :namespace
+ attr_reader :location
+ attr_reader :content
+
+ def initialize
+ super
+ @namespace = nil
+ @location = nil
+ @content = nil
+ @web_client = nil
+ end
+
+ def parse_element(element)
+ case element
+ when DocumentationName
+ o = Documentation.new
+ o
+ else
+ nil
+ end
+ end
+
+ def parse_attr(attr, value)
+ case attr
+ when NamespaceAttrName
+ @namespace = value
+ if @content
+ @content.targetnamespace = @namespace
+ end
+ @namespace
+ when LocationAttrName
+ @location = value
+ @content = import(@location)
+ if @content.is_a?(Definitions)
+ @content.root = root
+ if @namespace
+ @content.targetnamespace = @namespace
+ end
+ end
+ @location
+ else
+ nil
+ end
+ end
+
+private
+
+ def import(location)
+ Importer.import(location)
+ end
+end
+
+
+end
diff --git a/lib/wsdl/importer.rb b/lib/wsdl/importer.rb
new file mode 100644
index 0000000000..794e7fc74d
--- /dev/null
+++ b/lib/wsdl/importer.rb
@@ -0,0 +1,75 @@
+=begin
+WSDL4R - WSDL importer library.
+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 'wsdl/info'
+
+
+module WSDL
+
+
+class Importer
+ def self.import(location)
+ new.import(location)
+ end
+
+ def initialize
+ @web_client = nil
+ end
+
+ def import(location)
+ content = nil
+ if FileTest.exist?(location)
+ content = File.open(location).read
+ else
+ proxy = ENV['http_proxy'] || ENV['HTTP_PROXY']
+ content = web_client.new(proxy, "WSDL4R").get_content(location)
+ end
+ opt = {} # charset?
+ begin
+ WSDL::Parser.new(opt).parse(content)
+ rescue WSDL::Parser::ParseError => orgexcn
+ begin
+ require 'wsdl/xmlSchema/parser'
+ WSDL::XMLSchema::Parser.new(opt).parse(content)
+ rescue
+ raise orgexcn
+ end
+ end
+ end
+
+private
+
+ def web_client
+ @web_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
+ @web_client
+ end
+end
+
+
+end
diff --git a/lib/wsdl/info.rb b/lib/wsdl/info.rb
new file mode 100644
index 0000000000..886b9255ae
--- /dev/null
+++ b/lib/wsdl/info.rb
@@ -0,0 +1,44 @@
+=begin
+WSDL4R - WSDL information base.
+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
+
+
+module WSDL
+
+
+class Info
+ attr_accessor :parent
+ attr_accessor :id
+
+ def initialize
+ @parent = nil
+ @id = nil
+ end
+
+ def root
+ @parent.root
+ end
+
+ def parse_element(element); end # abstract
+
+ def parse_attr(attr, value); end # abstract
+
+ def parse_epilogue; end # abstract
+end
+
+
+end
diff --git a/lib/wsdl/message.rb b/lib/wsdl/message.rb
new file mode 100644
index 0000000000..68f3bb89dd
--- /dev/null
+++ b/lib/wsdl/message.rb
@@ -0,0 +1,65 @@
+=begin
+WSDL4R - WSDL message definition.
+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/info'
+
+
+module WSDL
+
+
+class Message < Info
+ attr_reader :name # required
+ attr_reader :parts
+
+ def initialize
+ super
+ @name = nil
+ @parts = []
+ end
+
+ def targetnamespace
+ parent.targetnamespace
+ end
+
+ def parse_element(element)
+ case element
+ when PartName
+ o = Part.new
+ @parts << o
+ o
+ when DocumentationName
+ o = Documentation.new
+ o
+ else
+ nil
+ end
+ end
+
+ def parse_attr(attr, value)
+ case attr
+ when NameAttrName
+ @name = XSD::QName.new(parent.targetnamespace, value)
+ else
+ nil
+ end
+ end
+end
+
+
+end
diff --git a/lib/wsdl/operation.rb b/lib/wsdl/operation.rb
new file mode 100644
index 0000000000..ff3450d234
--- /dev/null
+++ b/lib/wsdl/operation.rb
@@ -0,0 +1,144 @@
+=begin
+WSDL4R - WSDL operation definition.
+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/info'
+
+
+module WSDL
+
+
+class Operation < Info
+ class NameInfo
+ attr_reader :op_name
+ attr_reader :optype_name
+ attr_reader :parts
+ def initialize(op_name, optype_name, parts)
+ @op_name = op_name
+ @optype_name = optype_name
+ @parts = parts
+ end
+ end
+
+ attr_reader :name # required
+ attr_reader :parameter_order # optional
+ attr_reader :input
+ attr_reader :output
+ attr_reader :fault
+ attr_reader :type # required
+
+ def initialize
+ super
+ @name = nil
+ @type = nil
+ @parameter_order = nil
+ @input = nil
+ @output = nil
+ @fault = nil
+ end
+
+ def targetnamespace
+ parent.targetnamespace
+ end
+
+ def input_info
+ op_name = @name
+ optype_name = XSD::QName.new(targetnamespace, input.name ? input.name.name : @name.name)
+ NameInfo.new(op_name, optype_name, inputparts)
+ end
+
+ def output_info
+ op_name = @name
+ optype_name = XSD::QName.new(targetnamespace, output.name ? output.name.name : @name.name)
+ NameInfo.new(op_name, optype_name, outputparts)
+ end
+
+ def inputparts
+ sort_parts(input.find_message.parts)
+ end
+
+ def outputparts
+ sort_parts(output.find_message.parts)
+ end
+
+ def faultparts
+ sort_parts(fault.find_message.parts)
+ end
+
+ def outputname
+ XSD::QName.new(targetnamespace,
+ output.name ? output.name.name : @name.name + 'Response')
+ end
+
+ def parse_element(element)
+ case element
+ when InputName
+ o = Param.new
+ @input = o
+ o
+ when OutputName
+ o = Param.new
+ @output = o
+ o
+ when FaultName
+ o = Param.new
+ @fault = o
+ o
+ when DocumentationName
+ o = Documentation.new
+ o
+ else
+ nil
+ end
+ end
+
+ def parse_attr(attr, value)
+ case attr
+ when NameAttrName
+ @name = XSD::QName.new(targetnamespace, value)
+ when TypeAttrName
+ @type = value
+ when ParameterOrderAttrName
+ @parameter_order = value.split(/\s+/)
+ else
+ nil
+ end
+ end
+
+private
+
+ def sort_parts(parts)
+ return parts.dup unless parameter_order
+ result = []
+ parameter_order.each do |orderitem|
+ if (ele = parts.find { |part| part.name == orderitem })
+ result << ele
+ end
+ end
+ if result.length == 0
+ return parts.dup
+ end
+ if parts.length != result.length
+ raise RuntimeError.new("Incomplete prarmeterOrder list.")
+ end
+ result
+ end
+end
+
+
+end
diff --git a/lib/wsdl/operationBinding.rb b/lib/wsdl/operationBinding.rb
new file mode 100644
index 0000000000..0bd0d9b105
--- /dev/null
+++ b/lib/wsdl/operationBinding.rb
@@ -0,0 +1,91 @@
+=begin
+WSDL4R - WSDL bound operation definition.
+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/info'
+
+
+module WSDL
+
+
+class OperationBinding < Info
+ attr_reader :name # required
+ attr_reader :input
+ attr_reader :output
+ attr_reader :fault
+ attr_reader :soapoperation
+
+ def initialize
+ super
+ @name = nil
+ @input = nil
+ @output = nil
+ @fault = nil
+ @soapoperation = nil
+ end
+
+ def targetnamespace
+ parent.targetnamespace
+ end
+
+ def porttype
+ root.porttype(parent.type)
+ end
+
+ def find_operation
+ porttype.operations[@name]
+ end
+
+ def parse_element(element)
+ case element
+ when InputName
+ o = Param.new
+ @input = o
+ o
+ when OutputName
+ o = Param.new
+ @output = o
+ o
+ when FaultName
+ o = Param.new
+ @fault = o
+ o
+ when SOAPOperationName
+ o = WSDL::SOAP::Operation.new
+ @soapoperation = o
+ o
+ when DocumentationName
+ o = Documentation.new
+ o
+ else
+ nil
+ end
+ end
+
+ def parse_attr(attr, value)
+ case attr
+ when NameAttrName
+ @name = XSD::QName.new(targetnamespace, value)
+ else
+ nil
+ end
+ end
+end
+
+
+end
diff --git a/lib/wsdl/param.rb b/lib/wsdl/param.rb
new file mode 100644
index 0000000000..10111677d2
--- /dev/null
+++ b/lib/wsdl/param.rb
@@ -0,0 +1,85 @@
+=begin
+WSDL4R - WSDL param definition.
+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/info'
+
+
+module WSDL
+
+
+class Param < Info
+ attr_reader :message # required
+ attr_reader :name # optional but required for fault.
+ attr_reader :soapbody
+ attr_reader :soapheader
+ attr_reader :soapfault
+
+ def initialize
+ super
+ @message = nil
+ @name = nil
+ @soapbody = nil
+ @soapheader = []
+ @soapfault = nil
+ end
+
+ def targetnamespace
+ parent.targetnamespace
+ end
+
+ def find_message
+ root.message(@message)
+ end
+
+ def parse_element(element)
+ case element
+ when SOAPBodyName
+ o = WSDL::SOAP::Body.new
+ @soapbody = o
+ o
+ when SOAPHeaderName
+ o = WSDL::SOAP::Header.new
+ @soapheader << o
+ o
+ when SOAPFaultName
+ o = WSDL::SOAP::Fault.new
+ @soap_fault = o
+ o
+ when DocumentationName
+ o = Documentation.new
+ o
+ else
+ nil
+ end
+ end
+
+ def parse_attr(attr, value)
+ case attr
+ when MessageAttrName
+ @message = value
+ when NameAttrName
+ @name = XSD::QName.new(targetnamespace, value)
+ else
+ nil
+ end
+ end
+end
+
+
+end
diff --git a/lib/wsdl/parser.rb b/lib/wsdl/parser.rb
new file mode 100644
index 0000000000..e0a8945bb0
--- /dev/null
+++ b/lib/wsdl/parser.rb
@@ -0,0 +1,170 @@
+=begin
+WSDL4R - WSDL XML Instance parser library.
+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 'xsd/qname'
+require 'xsd/ns'
+require 'xsd/charset'
+require 'xsd/datatypes'
+require 'xsd/xmlparser'
+require 'wsdl/wsdl'
+require 'wsdl/data'
+require 'wsdl/xmlSchema/data'
+require 'wsdl/soap/data'
+
+
+module WSDL
+
+
+class Parser
+ include WSDL
+
+ class ParseError < Error; end
+ class FormatDecodeError < ParseError; end
+ class UnknownElementError < FormatDecodeError; end
+ class UnknownAttributeError < FormatDecodeError; end
+ class UnexpectedElementError < FormatDecodeError; end
+ class ElementConstraintError < FormatDecodeError; end
+ class AttributeConstraintError < FormatDecodeError; end
+
+private
+
+ class ParseFrame
+ attr_reader :ns
+ attr_reader :name
+ attr_accessor :node
+
+ private
+
+ def initialize(ns, name, node)
+ @ns = ns
+ @name = name
+ @node = node
+ end
+ end
+
+public
+
+ def initialize(opt = {})
+ @parser = XSD::XMLParser.create_parser(self, opt)
+ @parsestack = nil
+ @lastnode = nil
+ end
+
+ def parse(string_or_readable)
+ @parsestack = []
+ @lastnode = nil
+ @textbuf = ''
+ @parser.do_parse(string_or_readable)
+ @lastnode
+ end
+
+ def charset
+ @parser.charset
+ end
+
+ def start_element(name, attrs)
+ lastframe = @parsestack.last
+ ns = parent = nil
+ if lastframe
+ ns = lastframe.ns.clone_ns
+ parent = lastframe.node
+ else
+ ns = XSD::NS.new
+ parent = nil
+ end
+ attrs = XSD::XMLParser.filter_ns(ns, attrs)
+ node = decode_tag(ns, name, attrs, parent)
+ @parsestack << ParseFrame.new(ns, name, node)
+ end
+
+ def characters(text)
+ lastframe = @parsestack.last
+ if lastframe
+ # Need not to be cloned because character does not have attr.
+ ns = lastframe.ns
+ decode_text(ns, text)
+ else
+ 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)
+ @lastnode = lastframe.node
+ end
+
+private
+
+ def decode_tag(ns, name, attrs, parent)
+ o = nil
+ element = ns.parse(name)
+ if !parent
+ if element == DefinitionsName
+ o = Definitions.parse_element(element)
+ else
+ raise UnknownElementError.new("Unknown element #{ element }.")
+ end
+ else
+ o = parent.parse_element(element)
+ unless o
+ STDERR.puts("Unknown element #{ element }.")
+ o = Documentation.new # which accepts any element.
+ end
+ o.parent = parent
+ end
+ attrs.each do |key, value|
+ attr = unless /:/ =~ key
+ XSD::QName.new(nil, key)
+ else
+ ns.parse(key)
+ end
+ value_ele = if /:/ !~ value
+ value
+ elsif /^http:\/\// =~ value # ToDo: ugly.
+ value
+ else
+ begin
+ ns.parse(value)
+ rescue
+ value
+ end
+ end
+ unless o.parse_attr(attr, value_ele)
+ STDERR.puts("Unknown attr #{ attr }.")
+ # raise UnknownAttributeError.new("Unknown attr #{ attr }.")
+ end
+ end
+ o
+ end
+
+ def decode_tag_end(ns, node)
+ node.parse_epilogue
+ end
+
+ def decode_text(ns, text)
+ @textbuf << text
+ end
+end
+
+
+end
diff --git a/lib/wsdl/part.rb b/lib/wsdl/part.rb
new file mode 100644
index 0000000000..2aeb457c4e
--- /dev/null
+++ b/lib/wsdl/part.rb
@@ -0,0 +1,63 @@
+=begin
+WSDL4R - WSDL part definition.
+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/info'
+
+
+module WSDL
+
+
+class Part < Info
+ attr_reader :name # required
+ attr_reader :element # optional
+ attr_reader :type # optional
+
+ def initialize
+ super
+ @name = nil
+ @element = nil
+ @type = nil
+ end
+
+ def parse_element(element)
+ case element
+ when DocumentationName
+ o = Documentation.new
+ o
+ else
+ nil
+ end
+ end
+
+ def parse_attr(attr, value)
+ case attr
+ when NameAttrName
+ @name = value
+ when ElementAttrName
+ @element = value
+ when TypeAttrName
+ @type = value
+ else
+ nil
+ end
+ end
+end
+
+
+end
diff --git a/lib/wsdl/port.rb b/lib/wsdl/port.rb
new file mode 100644
index 0000000000..b389f6ca02
--- /dev/null
+++ b/lib/wsdl/port.rb
@@ -0,0 +1,95 @@
+=begin
+WSDL4R - WSDL port definition.
+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/info'
+
+
+module WSDL
+
+
+class Port < Info
+ attr_reader :name # required
+ attr_reader :binding # required
+ attr_reader :soap_address
+
+ def initialize
+ super
+ @name = nil
+ @binding = nil
+ @soap_address = nil
+ end
+
+ def targetnamespace
+ parent.targetnamespace
+ end
+
+ def porttype
+ root.porttype(find_binding.type)
+ end
+
+ def find_binding
+ root.binding(@binding)
+ end
+
+ def inputoperation_map
+ result = {}
+ find_binding.operations.each do |op_bind|
+ op_info = op_bind.soapoperation.input_info
+ result[op_info.op_name] = op_info
+ end
+ result
+ end
+
+ def outputoperation_map
+ result = {}
+ find_binding.operations.each do |op_bind|
+ op_info = op_bind.soapoperation.output_info
+ result[op_info.op_name] = op_info
+ end
+ result
+ end
+
+ def parse_element(element)
+ case element
+ when SOAPAddressName
+ o = WSDL::SOAP::Address.new
+ @soap_address = o
+ o
+ when DocumentationName
+ o = Documentation.new
+ o
+ else
+ nil
+ end
+ end
+
+ def parse_attr(attr, value)
+ case attr
+ when NameAttrName
+ @name = XSD::QName.new(targetnamespace, value)
+ when BindingAttrName
+ @binding = value
+ else
+ nil
+ end
+ end
+end
+
+
+end
diff --git a/lib/wsdl/portType.rb b/lib/wsdl/portType.rb
new file mode 100644
index 0000000000..b3a99b3bc0
--- /dev/null
+++ b/lib/wsdl/portType.rb
@@ -0,0 +1,83 @@
+=begin
+WSDL4R - WSDL portType definition.
+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/info'
+require 'xsd/namedelements'
+
+
+module WSDL
+
+
+class PortType < Info
+ attr_reader :name # required
+ attr_reader :operations
+
+ def targetnamespace
+ parent.targetnamespace
+ end
+
+ def initialize
+ super
+ @name = nil
+ @operations = XSD::NamedElements.new
+ end
+
+ def find_binding
+ root.bindings.find { |item| item.type == @name }
+ end
+
+ def locations
+ bind_name = find_binding.name
+ result = []
+ root.services.each do |service|
+ service.ports.each do |port|
+ if port.binding == bind_name
+ result << port.soap_address.location if port.soap_address
+ end
+ end
+ end
+ result
+ end
+
+ def parse_element(element)
+ case element
+ when OperationName
+ o = Operation.new
+ @operations << o
+ o
+ when DocumentationName
+ o = Documentation.new
+ o
+ else
+ nil
+ end
+ end
+
+ def parse_attr(attr, value)
+ case attr
+ when NameAttrName
+ @name = XSD::QName.new(targetnamespace, value)
+ else
+ nil
+ end
+ end
+end
+
+
+end
diff --git a/lib/wsdl/service.rb b/lib/wsdl/service.rb
new file mode 100644
index 0000000000..efaeaa727a
--- /dev/null
+++ b/lib/wsdl/service.rb
@@ -0,0 +1,72 @@
+=begin
+WSDL4R - WSDL service definition.
+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/info'
+require 'xsd/namedelements'
+
+
+module WSDL
+
+
+class Service < Info
+ attr_reader :name # required
+ attr_reader :ports
+ attr_reader :soap_address
+
+ def initialize
+ super
+ @name = nil
+ @ports = XSD::NamedElements.new
+ @soap_address = nil
+ end
+
+ def targetnamespace
+ parent.targetnamespace
+ end
+
+ def parse_element(element)
+ case element
+ when PortName
+ o = Port.new
+ @ports << o
+ o
+ when SOAPAddressName
+ o = WSDL::SOAP::Address.new
+ @soap_address = o
+ o
+ when DocumentationName
+ o = Documentation.new
+ o
+ else
+ nil
+ end
+ end
+
+ def parse_attr(attr, value)
+ case attr
+ when NameAttrName
+ @name = XSD::QName.new(targetnamespace, value)
+ else
+ nil
+ end
+ end
+end
+
+
+end
diff --git a/lib/wsdl/soap/address.rb b/lib/wsdl/soap/address.rb
new file mode 100644
index 0000000000..759918c95b
--- /dev/null
+++ b/lib/wsdl/soap/address.rb
@@ -0,0 +1,51 @@
+=begin
+WSDL4R - WSDL SOAP address definition.
+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/info'
+
+
+module WSDL
+module SOAP
+
+
+class Address < Info
+ attr_reader :location
+
+ def initialize
+ super
+ @location = nil
+ end
+
+ def parse_element(element)
+ nil
+ end
+
+ def parse_attr(attr, value)
+ case attr
+ when LocationAttrName
+ @location = value
+ else
+ nil
+ end
+ end
+end
+
+
+end
+end
diff --git a/lib/wsdl/soap/binding.rb b/lib/wsdl/soap/binding.rb
new file mode 100644
index 0000000000..e9ba3a48d1
--- /dev/null
+++ b/lib/wsdl/soap/binding.rb
@@ -0,0 +1,59 @@
+=begin
+WSDL4R - WSDL SOAP binding definition.
+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/info'
+
+
+module WSDL
+module SOAP
+
+
+class Binding < Info
+ attr_reader :style
+ attr_reader :transport
+
+ def initialize
+ super
+ @style = nil
+ @transport = nil
+ end
+
+ def parse_element(element)
+ nil
+ end
+
+ def parse_attr(attr, value)
+ case attr
+ when StyleAttrName
+ if ["document", "rpc"].include?(value)
+ @style = value.intern
+ else
+ raise AttributeConstraintError.new("Unexpected value #{ value }.")
+ end
+ when TransportAttrName
+ @transport = value
+ else
+ nil
+ end
+ end
+end
+
+
+end
+end
diff --git a/lib/wsdl/soap/body.rb b/lib/wsdl/soap/body.rb
new file mode 100644
index 0000000000..f209622410
--- /dev/null
+++ b/lib/wsdl/soap/body.rb
@@ -0,0 +1,63 @@
+=begin
+WSDL4R - WSDL SOAP body definition.
+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/info'
+
+
+module WSDL
+module SOAP
+
+
+class Body < Info
+ attr_reader :parts
+ attr_reader :use # required
+ attr_reader :encodingstyle
+ attr_reader :namespace
+
+ def initialize
+ super
+ @parts = nil
+ @use = nil
+ @encodingstyle = nil
+ @namespace = nil
+ end
+
+ def parse_element(element)
+ nil
+ end
+
+ def parse_attr(attr, value)
+ case attr
+ when PartsAttrName
+ @parts = value
+ when UseAttrName
+ @use = value
+ when EncodingStyleAttrName
+ @encodingstyle = value
+ when NamespaceAttrName
+ @namespace = value
+ else
+ nil
+ end
+ end
+end
+
+
+end
+end
diff --git a/lib/wsdl/soap/complexType.rb b/lib/wsdl/soap/complexType.rb
new file mode 100644
index 0000000000..f47ddee449
--- /dev/null
+++ b/lib/wsdl/soap/complexType.rb
@@ -0,0 +1,96 @@
+=begin
+WSDL4R - SOAP complexType definition for WSDL.
+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/xmlSchema/complexType'
+
+
+module WSDL
+module XMLSchema
+
+
+class ComplexType < Info
+ def compoundtype
+ @compoundtype ||= check_type
+ end
+
+ def check_type
+ if content
+ :TYPE_STRUCT
+ elsif complexcontent and complexcontent.base == ::SOAP::ValueArrayName
+ :TYPE_ARRAY
+ else
+ raise NotImplementedError.new("Unknown kind of complexType.")
+ end
+ end
+
+ def child_type(name = nil)
+ case compoundtype
+ when :TYPE_STRUCT
+ if ele = find_element(name)
+ ele.type
+ elsif ele = find_element_by_name(name.name)
+ ele.type
+ else
+ nil
+ end
+ when :TYPE_ARRAY
+ @contenttype ||= content_arytype
+ end
+ end
+
+ def child_defined_complextype(name)
+ unless compoundtype == :TYPE_STRUCT
+ raise RuntimeError.new("Assert: not for struct")
+ end
+ unless ele = find_element(name)
+ if name.namespace.nil?
+ ele = find_element_by_name(name.name)
+ end
+ end
+ unless ele
+ raise RuntimeError.new("Cannot find #{name} as a children of #{@name}.")
+ end
+ ele.local_complextype
+ end
+
+ def find_arytype
+ complexcontent.attributes.each do |attribute|
+ if attribute.ref == ::SOAP::AttrArrayTypeName
+ return attribute.arytype
+ end
+ end
+ nil
+ end
+
+private
+
+ def content_arytype
+ unless compoundtype == :TYPE_ARRAY
+ raise RuntimeError.new("Assert: not for array")
+ end
+ arytype = find_arytype
+ ns = arytype.namespace
+ name = arytype.name.sub(/\[(?:,)*\]$/, '')
+ XSD::QName.new(ns, name)
+ end
+end
+
+
+end
+end
diff --git a/lib/wsdl/soap/data.rb b/lib/wsdl/soap/data.rb
new file mode 100644
index 0000000000..301ae9951d
--- /dev/null
+++ b/lib/wsdl/soap/data.rb
@@ -0,0 +1,52 @@
+=begin
+WSDL4R - WSDL SOAP binding data definitions.
+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 'xsd/qname'
+require 'wsdl/soap/definitions'
+require 'wsdl/soap/binding'
+require 'wsdl/soap/operation'
+require 'wsdl/soap/body'
+require 'wsdl/soap/header'
+require 'wsdl/soap/headerfault'
+require 'wsdl/soap/fault'
+require 'wsdl/soap/address'
+require 'wsdl/soap/complexType'
+
+
+module WSDL
+module SOAP
+
+
+HeaderFaultName = XSD::QName.new(SOAPBindingNamespace, 'headerfault')
+
+LocationAttrName = XSD::QName.new(nil, 'location')
+StyleAttrName = XSD::QName.new(nil, 'style')
+TransportAttrName = XSD::QName.new(nil, 'transport')
+UseAttrName = XSD::QName.new(nil, 'use')
+PartsAttrName = XSD::QName.new(nil, 'parts')
+PartAttrName = XSD::QName.new(nil, 'part')
+NameAttrName = XSD::QName.new(nil, 'name')
+MessageAttrName = XSD::QName.new(nil, 'message')
+EncodingStyleAttrName = XSD::QName.new(nil, 'encodingStyle')
+NamespaceAttrName = XSD::QName.new(nil, 'namespace')
+SOAPActionAttrName = XSD::QName.new(nil, 'soapAction')
+
+
+end
+end
diff --git a/lib/wsdl/soap/definitions.rb b/lib/wsdl/soap/definitions.rb
new file mode 100644
index 0000000000..08df0dcc68
--- /dev/null
+++ b/lib/wsdl/soap/definitions.rb
@@ -0,0 +1,130 @@
+=begin
+WSDL4R - WSDL additional definitions for SOAP.
+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/info'
+require 'xsd/namedelements'
+require 'soap/mapping'
+
+
+module WSDL
+
+
+class Definitions < Info
+ def soap_rpc_complextypes(binding)
+ types = rpc_operation_complextypes(binding)
+ types << array_complextype
+ types << fault_complextype
+ types << exception_complextype
+ types
+ end
+
+private
+
+ def rpc_operation_complextypes(binding)
+ types = XSD::NamedElements.new
+ binding.operations.each do |op_bind|
+ if op_bind_rpc?(op_bind)
+ operation = op_bind.find_operation
+ if op_bind.input
+ type = XMLSchema::ComplexType.new(operation_input_name(operation))
+ message = messages[operation.input.message]
+ type.sequence_elements = elements_from_message(message)
+ types << type
+ end
+ if op_bind.output
+ type = XMLSchema::ComplexType.new(operation_output_name(operation))
+ message = messages[operation.output.message]
+ type.sequence_elements = elements_from_message(message)
+ types << type
+ end
+ end
+ end
+ types
+ end
+
+ def operation_input_name(operation)
+ operation.input.name || operation.name
+ end
+
+ def operation_output_name(operation)
+ operation.output.name ||
+ XSD::QName.new(operation.name.namespace, operation.name.name + "Response")
+ end
+
+ def op_bind_rpc?(op_bind)
+ op_bind.soapoperation and op_bind.soapoperation.operation_style == :rpc
+ end
+
+ def elements_from_message(message)
+ message.parts.collect { |part|
+ qname = XSD::QName.new(nil, part.name)
+ XMLSchema::Element.new(qname, part.type)
+ }
+ end
+
+ def array_complextype
+ type = XMLSchema::ComplexType.new(::SOAP::ValueArrayName)
+ type.complexcontent = XMLSchema::ComplexContent.new
+ type.complexcontent.base = ::SOAP::ValueArrayName
+ attr = XMLSchema::Attribute.new
+ attr.ref = ::SOAP::AttrArrayTypeName
+ anytype = XSD::AnyTypeName.dup
+ anytype.name += '[]'
+ attr.arytype = anytype
+ type.complexcontent.attributes << attr
+ type
+ end
+
+=begin
+<xs:complexType name="Fault" final="extension">
+ <xs:sequence>
+ <xs:element name="faultcode" type="xs:QName" />
+ <xs:element name="faultstring" type="xs:string" />
+ <xs:element name="faultactor" type="xs:anyURI" minOccurs="0" />
+ <xs:element name="detail" type="tns:detail" minOccurs="0" />
+ </xs:sequence>
+</xs:complexType>
+=end
+ def fault_complextype
+ type = XMLSchema::ComplexType.new(::SOAP::EleFaultName)
+ faultcode = XMLSchema::Element.new(::SOAP::EleFaultCodeName, XSD::XSDQName::Type)
+ faultstring = XMLSchema::Element.new(::SOAP::EleFaultStringName, XSD::XSDString::Type)
+ faultactor = XMLSchema::Element.new(::SOAP::EleFaultActorName, XSD::XSDAnyURI::Type)
+ faultactor.minoccurs = 0
+ detail = XMLSchema::Element.new(::SOAP::EleFaultDetailName, XSD::AnyTypeName)
+ detail.minoccurs = 0
+ type.all_elements = [faultcode, faultstring, faultactor, detail]
+ type.final = 'extension'
+ type
+ end
+
+ def exception_complextype
+ type = XMLSchema::ComplexType.new(XSD::QName.new(
+ ::SOAP::Mapping::RubyCustomTypeNamespace, 'SOAPException'))
+ excn_name = XMLSchema::Element.new(XSD::QName.new(nil, 'exceptionTypeName'), XSD::XSDString::Type)
+ cause = XMLSchema::Element.new(XSD::QName.new(nil, 'cause'), XSD::AnyTypeName)
+ backtrace = XMLSchema::Element.new(XSD::QName.new(nil, 'backtrace'), ::SOAP::ValueArrayName)
+ message = XMLSchema::Element.new(XSD::QName.new(nil, 'message'), XSD::XSDString::Type)
+ type.all_elements = [excn_name, cause, backtrace, message]
+ type
+ end
+end
+
+
+end
diff --git a/lib/wsdl/soap/fault.rb b/lib/wsdl/soap/fault.rb
new file mode 100644
index 0000000000..eb57cb0233
--- /dev/null
+++ b/lib/wsdl/soap/fault.rb
@@ -0,0 +1,63 @@
+=begin
+WSDL4R - WSDL SOAP body definition.
+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/info'
+
+
+module WSDL
+module SOAP
+
+
+class Fault < Info
+ attr_reader :name # required
+ attr_reader :use # required
+ attr_reader :encodingstyle
+ attr_reader :namespace
+
+ def initialize
+ super
+ @name = nil
+ @use = nil
+ @encodingstyle = nil
+ @namespace = nil
+ end
+
+ def parse_element(element)
+ nil
+ end
+
+ def parse_attr(attr, value)
+ case attr
+ when NameAttrName
+ @name = value
+ when UseAttrName
+ @use = value
+ when EncodingStyleAttrName
+ @encodingstyle = value
+ when NamespaceAttrName
+ @namespace = value
+ else
+ nil
+ end
+ end
+end
+
+
+end
+end
diff --git a/lib/wsdl/soap/header.rb b/lib/wsdl/soap/header.rb
new file mode 100644
index 0000000000..f779ba5c08
--- /dev/null
+++ b/lib/wsdl/soap/header.rb
@@ -0,0 +1,90 @@
+=begin
+WSDL4R - WSDL SOAP body definition.
+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/info'
+
+
+module WSDL
+module SOAP
+
+
+class Header < Info
+ attr_reader :headerfault
+
+ attr_reader :message # required
+ attr_reader :part # required
+ attr_reader :use # required
+ attr_reader :encodingstyle
+ attr_reader :namespace
+
+ def initialize
+ super
+ @message = nil
+ @part = nil
+ @use = nil
+ @encodingstyle = nil
+ @namespace = nil
+ @headerfault = nil
+ end
+
+ def find_message
+ root.message(@message)
+ end
+
+ def find_part
+ find_message.parts.each do |part|
+ if part.name == @part
+ return part
+ end
+ end
+ nil
+ end
+
+ def parse_element(element)
+ case element
+ when HeaderFaultName
+ o = WSDL::SOAP::HeaderFault.new
+ @headerfault = o
+ o
+ else
+ nil
+ end
+ end
+
+ def parse_attr(attr, value)
+ case attr
+ when MessageAttrName
+ @message = value
+ when PartAttrName
+ @part = value
+ when UseAttrName
+ @use = value
+ when EncodingStyleAttrName
+ @encodingstyle = value
+ when NamespaceAttrName
+ @namespace = value
+ else
+ nil
+ end
+ end
+end
+
+
+end
+end
diff --git a/lib/wsdl/soap/headerfault.rb b/lib/wsdl/soap/headerfault.rb
new file mode 100644
index 0000000000..c0d58e2230
--- /dev/null
+++ b/lib/wsdl/soap/headerfault.rb
@@ -0,0 +1,67 @@
+=begin
+WSDL4R - WSDL SOAP body definition.
+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/info'
+
+
+module WSDL
+module SOAP
+
+
+class HeaderFault < Info
+ attr_reader :message # required
+ attr_reader :part # required
+ attr_reader :use # required
+ attr_reader :encodingstyle
+ attr_reader :namespace
+
+ def initialize
+ super
+ @message = nil
+ @part = nil
+ @use = nil
+ @encodingstyle = nil
+ @namespace = nil
+ end
+
+ def parse_element(element)
+ nil
+ end
+
+ def parse_attr(attr, value)
+ case attr
+ when MessageAttrName
+ @message = value
+ when PartAttrName
+ @part = value
+ when UseAttrName
+ @use = value
+ when EncodingStyleAttrName
+ @encodingstyle = value
+ when NamespaceAttrName
+ @namespace = value
+ else
+ nil
+ end
+ end
+end
+
+
+end
+end
diff --git a/lib/wsdl/soap/operation.rb b/lib/wsdl/soap/operation.rb
new file mode 100644
index 0000000000..2e88522f5c
--- /dev/null
+++ b/lib/wsdl/soap/operation.rb
@@ -0,0 +1,131 @@
+=begin
+WSDL4R - WSDL SOAP operation definition.
+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/info'
+
+
+module WSDL
+module SOAP
+
+
+class Operation < Info
+ class OperationInfo
+ attr_reader :style
+ attr_reader :op_name
+ attr_reader :optype_name
+ attr_reader :headerparts
+ attr_reader :bodyparts
+ attr_reader :faultpart
+ attr_reader :soapaction
+
+ def initialize(style, op_name, optype_name, headerparts, bodyparts, faultpart, soapaction)
+ @style = style
+ @op_name = op_name
+ @optype_name = optype_name
+ @headerparts = headerparts
+ @bodyparts = bodyparts
+ @faultpart = faultpart
+ @soapaction = soapaction
+ end
+ end
+
+ attr_reader :soapaction
+ attr_reader :style
+
+ def initialize
+ super
+ @soapaction = nil
+ @style = nil
+ end
+
+ def parse_element(element)
+ nil
+ end
+
+ def parse_attr(attr, value)
+ case attr
+ when StyleAttrName
+ if ["document", "rpc"].include?(value)
+ @style = value.intern
+ else
+ raise AttributeConstraintError.new("Unexpected value #{ value }.")
+ end
+ when SOAPActionAttrName
+ @soapaction = value
+ else
+ nil
+ end
+ end
+
+ def input_info
+ name_info = parent.find_operation.input_info
+ param_info(name_info, parent.input)
+ end
+
+ def output_info
+ name_info = parent.find_operation.output_info
+ param_info(name_info, parent.output)
+ end
+
+ def operation_style
+ return @style if @style
+ if parent_binding.soapbinding
+ return parent_binding.soapbinding.style
+ end
+ nil
+ end
+
+private
+
+ def parent_binding
+ parent.parent
+ end
+
+ def param_info(name_info, param)
+ op_name = name_info.op_name
+ optype_name = name_info.optype_name
+
+ soapheader = param.soapheader
+ headerparts = soapheader.collect { |item| item.find_part }
+
+ soapbody = param.soapbody
+ if soapbody.encodingstyle and
+ soapbody.encodingstyle != ::SOAP::EncodingNamespace
+ raise NotImplementedError.new(
+ "EncodingStyle '#{ soapbody.encodingstyle }' not supported.")
+ end
+ if soapbody.namespace
+ op_name = op_name.dup
+ op_name.namespace = soapbody.namespace
+ end
+ if soapbody.parts
+ raise NotImplementedError.new("soap:body parts")
+ else
+ bodyparts = name_info.parts
+ end
+
+ faultpart = nil
+ soapaction = parent.soapoperation.soapaction
+ OperationInfo.new(operation_style, op_name, optype_name, headerparts, bodyparts, faultpart, soapaction)
+ end
+end
+
+
+end
+end
diff --git a/lib/wsdl/types.rb b/lib/wsdl/types.rb
new file mode 100644
index 0000000000..420ab8d387
--- /dev/null
+++ b/lib/wsdl/types.rb
@@ -0,0 +1,54 @@
+=begin
+WSDL4R - WSDL types definition.
+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/info'
+
+
+module WSDL
+
+
+class Types < Info
+ attr_reader :schemas
+
+ def initialize
+ super
+ @schemas = []
+ end
+
+ def parse_element(element)
+ case element
+ when SchemaName
+ o = XMLSchema::Schema.new
+ @schemas << o
+ o
+ when DocumentationName
+ o = Documentation.new
+ o
+ else
+ nil
+ end
+ end
+
+ def parse_attr(attr, value)
+ nil
+ end
+end
+
+
+end
diff --git a/lib/wsdl/wsdl.rb b/lib/wsdl/wsdl.rb
new file mode 100644
index 0000000000..23fd764065
--- /dev/null
+++ b/lib/wsdl/wsdl.rb
@@ -0,0 +1,34 @@
+=begin
+WSDL4R - Base definitions.
+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'
+
+
+module WSDL
+
+
+Version = '0.0.2'
+
+Namespace = 'http://schemas.xmlsoap.org/wsdl/'
+SOAPBindingNamespace ='http://schemas.xmlsoap.org/wsdl/soap/'
+
+class Error < StandardError; end
+
+
+end
diff --git a/lib/wsdl/xmlSchema/all.rb b/lib/wsdl/xmlSchema/all.rb
new file mode 100644
index 0000000000..7db0fbc939
--- /dev/null
+++ b/lib/wsdl/xmlSchema/all.rb
@@ -0,0 +1,76 @@
+=begin
+WSDL4R - XMLSchema complexType definition for WSDL.
+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/info'
+
+
+module WSDL
+module XMLSchema
+
+
+class All < Info
+ attr_reader :minoccurs
+ attr_reader :maxoccurs
+ attr_reader :elements
+
+ def initialize
+ super()
+ @minoccurs = 1
+ @maxoccurs = 1
+ @elements = []
+ end
+
+ def targetnamespace
+ parent.targetnamespace
+ end
+
+ def <<(element)
+ @elements << element
+ end
+
+ def parse_element(element)
+ case element
+ when AnyName
+ o = Any.new
+ @elements << o
+ o
+ when ElementName
+ o = Element.new
+ @elements << o
+ o
+ else
+ nil
+ end
+ end
+
+ def parse_attr(attr, value)
+ case attr
+ when MaxOccursAttrName
+ @maxoccurs = value
+ when MinOccursAttrName
+ @minoccurs = value
+ else
+ nil
+ end
+ end
+end
+
+
+end
+end
diff --git a/lib/wsdl/xmlSchema/any.rb b/lib/wsdl/xmlSchema/any.rb
new file mode 100644
index 0000000000..46904c4107
--- /dev/null
+++ b/lib/wsdl/xmlSchema/any.rb
@@ -0,0 +1,67 @@
+=begin
+WSDL4R - XMLSchema any definition for WSDL.
+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 'wsdl/info'
+
+
+module WSDL
+module XMLSchema
+
+
+class Any < Info
+ attr_accessor :maxoccurs
+ attr_accessor :minoccurs
+ attr_accessor :namespace
+ attr_accessor :process_contents
+
+ def initialize
+ super()
+ @maxoccurs = 1
+ @minoccurs = 1
+ @namespace = '##any'
+ @process_contents = 'strict'
+ end
+
+ def targetnamespace
+ parent.targetnamespace
+ end
+
+ def parse_element(element)
+ nil
+ end
+
+ def parse_attr(attr, value)
+ case attr
+ when MaxOccursAttrName
+ @maxoccurs = value
+ when MinOccursAttrName
+ @minoccurs = value
+ when NamespaceAttrName
+ @namespace = value
+ when ProcessContentsAttrName
+ @process_contents = value
+ else
+ nil
+ end
+ end
+end
+
+
+end
+end
diff --git a/lib/wsdl/xmlSchema/attribute.rb b/lib/wsdl/xmlSchema/attribute.rb
new file mode 100644
index 0000000000..08cc9e931b
--- /dev/null
+++ b/lib/wsdl/xmlSchema/attribute.rb
@@ -0,0 +1,85 @@
+=begin
+WSDL4R - XMLSchema attribute definition for WSDL.
+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/info'
+
+
+module WSDL
+module XMLSchema
+
+
+class Attribute < Info
+ attr_accessor :ref
+ attr_accessor :use
+ attr_accessor :form
+ attr_accessor :name
+ attr_accessor :type
+ attr_accessor :default
+ attr_accessor :fixed
+
+ attr_accessor :arytype
+
+ def initialize
+ super
+ @ref = nil
+ @use = nil
+ @form = nil
+ @name = nil
+ @type = nil
+ @default = nil
+ @fixed = nil
+
+ @arytype = nil
+ end
+
+ def parse_element(element)
+ nil
+ end
+
+ def parse_attr(attr, value)
+ case attr
+ when RefAttrName
+ @ref = value
+ when UseAttrName
+ @use = value
+ when FormAttrName
+ @form = value
+ when NameAttrName
+ @name = value
+ when TypeAttrName
+ @type = value
+ when DefaultAttrName
+ @default = value
+ when FixedAttrName
+ @fixed = value
+ when ArrayTypeAttrName
+ @arytype = if value.is_a?(XSD::QName)
+ value
+ else
+ XSD::QName.new(XSD::Namespace, value)
+ end
+ else
+ nil
+ end
+ end
+end
+
+
+end
+end
diff --git a/lib/wsdl/xmlSchema/choice.rb b/lib/wsdl/xmlSchema/choice.rb
new file mode 100644
index 0000000000..f31e93b3f1
--- /dev/null
+++ b/lib/wsdl/xmlSchema/choice.rb
@@ -0,0 +1,76 @@
+=begin
+WSDL4R - XMLSchema complexType definition for WSDL.
+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/info'
+
+
+module WSDL
+module XMLSchema
+
+
+class Choice < Info
+ attr_reader :minoccurs
+ attr_reader :maxoccurs
+ attr_reader :elements
+
+ def initialize
+ super()
+ @minoccurs = 1
+ @maxoccurs = 1
+ @elements = []
+ end
+
+ def targetnamespace
+ parent.targetnamespace
+ end
+
+ def <<(element)
+ @elements << element
+ end
+
+ def parse_element(element)
+ case element
+ when AnyName
+ o = Any.new
+ @elements << o
+ o
+ when ElementName
+ o = Element.new
+ @elements << o
+ o
+ else
+ nil
+ end
+ end
+
+ def parse_attr(attr, value)
+ case attr
+ when MaxOccursAttrName
+ @maxoccurs = value
+ when MinOccursAttrName
+ @minoccurs = value
+ else
+ nil
+ end
+ end
+end
+
+
+end
+end
diff --git a/lib/wsdl/xmlSchema/complexContent.rb b/lib/wsdl/xmlSchema/complexContent.rb
new file mode 100644
index 0000000000..79c231ac2d
--- /dev/null
+++ b/lib/wsdl/xmlSchema/complexContent.rb
@@ -0,0 +1,90 @@
+=begin
+WSDL4R - XMLSchema complexContent definition for WSDL.
+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/info'
+require 'xsd/namedelements'
+
+
+module WSDL
+module XMLSchema
+
+
+class ComplexContent < Info
+ attr_accessor :base
+ attr_reader :derivetype
+ attr_reader :content
+ attr_reader :attributes
+
+ def initialize
+ super
+ @base = nil
+ @derivetype = nil
+ @content = nil
+ @attributes = XSD::NamedElements.new
+ end
+
+ def parse_element(element)
+ case element
+ when RestrictionName, ExtensionName
+ @derivetype = element.name
+ self
+ when AllName
+ if @derivetype.nil?
+ raise Parser::ElementConstraintError.new("base attr not found.")
+ end
+ @content = All.new
+ @content
+ when SequenceName
+ if @derivetype.nil?
+ raise Parser::ElementConstraintError.new("base attr not found.")
+ end
+ @content = Sequence.new
+ @content
+ when ChoiceName
+ if @derivetype.nil?
+ raise Parser::ElementConstraintError.new("base attr not found.")
+ end
+ @content = Choice.new
+ @content
+ when AttributeName
+ if @derivetype.nil?
+ raise Parser::ElementConstraintError.new("base attr not found.")
+ end
+ o = Attribute.new
+ @attributes << o
+ o
+ end
+ end
+
+ def parse_attr(attr, value)
+ if @derivetype.nil?
+ return nil
+ end
+ case attr
+ when BaseAttrName
+ @base = value
+ else
+ nil
+ end
+ end
+end
+
+
+end
+end
diff --git a/lib/wsdl/xmlSchema/complexType.rb b/lib/wsdl/xmlSchema/complexType.rb
new file mode 100644
index 0000000000..c34be3e57b
--- /dev/null
+++ b/lib/wsdl/xmlSchema/complexType.rb
@@ -0,0 +1,130 @@
+=begin
+WSDL4R - XMLSchema complexType definition for WSDL.
+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/info'
+require 'wsdl/xmlSchema/content'
+require 'xsd/namedelements'
+
+
+module WSDL
+module XMLSchema
+
+
+class ComplexType < Info
+ attr_accessor :name
+ attr_accessor :complexcontent
+ attr_accessor :content
+ attr_accessor :final
+ attr_accessor :mixed
+ attr_reader :attributes
+
+ def initialize(name = nil)
+ super()
+ @name = name
+ @complexcontent = nil
+ @content = nil
+ @final = nil
+ @mixed = false
+ @attributes = XSD::NamedElements.new
+ end
+
+ def targetnamespace
+ parent.targetnamespace
+ end
+
+ def each_element
+ if @content
+ @content.elements.each do |element|
+ yield(element.name, element)
+ end
+ end
+ end
+
+ def find_element(name)
+ if @content
+ @content.elements.each do |element|
+ return element if name == element.name
+ end
+ end
+ nil
+ end
+
+ def find_element_by_name(name)
+ if @content
+ @content.elements.each do |element|
+ return element if name == element.name.name
+ end
+ end
+ nil
+ end
+
+ def sequence_elements=(elements)
+ @content = Sequence.new
+ elements.each do |element|
+ @content << element
+ end
+ end
+
+ def all_elements=(elements)
+ @content = All.new
+ elements.each do |element|
+ @content << element
+ end
+ end
+
+ def parse_element(element)
+ case element
+ when AllName
+ @content = All.new
+ @content
+ when SequenceName
+ @content = Sequence.new
+ @content
+ when ChoiceName
+ @content = Choice.new
+ @content
+ when ComplexContentName
+ @complexcontent = ComplexContent.new
+ @complexcontent
+ when AttributeName
+ o = Attribute.new
+ @attributes << o
+ o
+ else
+ nil
+ end
+ end
+
+ def parse_attr(attr, value)
+ case attr
+ when FinalAttrName
+ @final = value
+ when MixedAttrName
+ @mixed = (value == 'true')
+ when NameAttrName
+ @name = XSD::QName.new(targetnamespace, value)
+ else
+ nil
+ end
+ end
+end
+
+
+end
+end
diff --git a/lib/wsdl/xmlSchema/content.rb b/lib/wsdl/xmlSchema/content.rb
new file mode 100644
index 0000000000..a1bd302701
--- /dev/null
+++ b/lib/wsdl/xmlSchema/content.rb
@@ -0,0 +1,107 @@
+=begin
+WSDL4R - XMLSchema complexType definition for WSDL.
+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/info'
+
+
+module WSDL
+module XMLSchema
+
+
+class Content < Info
+ attr_accessor :final
+ attr_accessor :mixed
+ attr_accessor :type
+ attr_reader :contents
+ attr_reader :elements
+
+ def initialize
+ super()
+ @final = nil
+ @mixed = false
+ @type = nil
+ @contents = []
+ @elements = []
+ end
+
+ def targetnamespace
+ parent.targetnamespace
+ end
+
+ def <<(content)
+ @contents << content
+ update_elements
+ end
+
+ def each
+ @contents.each do |content|
+ yield content
+ end
+ end
+
+ def parse_element(element)
+ case element
+ when AllName, SequenceName, ChoiceName
+ o = Content.new
+ o.type = element.name
+ @contents << o
+ o
+ when AnyName
+ o = Any.new
+ @contents << o
+ o
+ when ElementName
+ o = Element.new
+ @contents << o
+ o
+ else
+ nil
+ end
+ end
+
+ def parse_attr(attr, value)
+ case attr
+ when FinalAttrName
+ @final = value
+ when MixedAttrName
+ @mixed = (value == 'true')
+ else
+ nil
+ end
+ end
+
+ def parse_epilogue
+ update_elements
+ end
+
+private
+
+ def update_elements
+ @elements = []
+ @contents.each do |content|
+ if content.is_a?(Element)
+ @elements << [content.name, content]
+ end
+ end
+ end
+end
+
+
+end
+end
diff --git a/lib/wsdl/xmlSchema/data.rb b/lib/wsdl/xmlSchema/data.rb
new file mode 100644
index 0000000000..9c9820abbd
--- /dev/null
+++ b/lib/wsdl/xmlSchema/data.rb
@@ -0,0 +1,75 @@
+=begin
+WSDL4R - XMLSchema data definitions.
+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/xmlSchema/schema'
+require 'wsdl/xmlSchema/import'
+require 'wsdl/xmlSchema/complexType'
+require 'wsdl/xmlSchema/complexContent'
+require 'wsdl/xmlSchema/any'
+require 'wsdl/xmlSchema/element'
+require 'wsdl/xmlSchema/all'
+require 'wsdl/xmlSchema/choice'
+require 'wsdl/xmlSchema/sequence'
+require 'wsdl/xmlSchema/attribute'
+require 'wsdl/xmlSchema/unique'
+
+
+module WSDL
+module XMLSchema
+
+
+AllName = XSD::QName.new(XSD::Namespace, 'all')
+AnyName = XSD::QName.new(XSD::Namespace, 'any')
+ArrayTypeAttrName = XSD::QName.new(Namespace, 'arrayType')
+AttributeName = XSD::QName.new(XSD::Namespace, 'attribute')
+ChoiceName = XSD::QName.new(XSD::Namespace, 'choice')
+ComplexContentName = XSD::QName.new(XSD::Namespace, 'complexContent')
+ComplexTypeName = XSD::QName.new(XSD::Namespace, 'complexType')
+ElementName = XSD::QName.new(XSD::Namespace, 'element')
+ExtensionName = XSD::QName.new(XSD::Namespace, 'extension')
+ImportName = XSD::QName.new(XSD::Namespace, 'import')
+RestrictionName = XSD::QName.new(XSD::Namespace, 'restriction')
+SequenceName = XSD::QName.new(XSD::Namespace, 'sequence')
+SchemaName = XSD::QName.new(XSD::Namespace, 'schema')
+SimpleTypeName = XSD::QName.new(XSD::Namespace, 'simpleType')
+UniqueName = XSD::QName.new(XSD::Namespace, 'unique')
+
+AttributeFormDefaultAttrName = XSD::QName.new(nil, 'attributeFormDefault')
+BaseAttrName = XSD::QName.new(nil, 'base')
+DefaultAttrName = XSD::QName.new(nil, 'default')
+ElementFormDefaultAttrName = XSD::QName.new(nil, 'elementFormDefault')
+FinalAttrName = XSD::QName.new(nil, 'final')
+FixedAttrName = XSD::QName.new(nil, 'fixed')
+FormAttrName = XSD::QName.new(nil, 'form')
+IdAttrName = XSD::QName.new(nil, 'id')
+MaxOccursAttrName = XSD::QName.new(nil, 'maxOccurs')
+MinOccursAttrName = XSD::QName.new(nil, 'minOccurs')
+MixedAttrName = XSD::QName.new(nil, 'mixed')
+NameAttrName = XSD::QName.new(nil, 'name')
+NamespaceAttrName = XSD::QName.new(nil, 'namespace')
+NillableAttrName = XSD::QName.new(nil, 'nillable')
+RefAttrName = XSD::QName.new(nil, 'ref')
+SchemaLocationAttrName = XSD::QName.new(nil, 'schemaLocation')
+TargetNamespaceAttrName = XSD::QName.new(nil, 'targetNamespace')
+TypeAttrName = XSD::QName.new(nil, 'type')
+UseAttrName = XSD::QName.new(nil, 'use')
+
+
+end
+end
diff --git a/lib/wsdl/xmlSchema/element.rb b/lib/wsdl/xmlSchema/element.rb
new file mode 100644
index 0000000000..d6d17c08cf
--- /dev/null
+++ b/lib/wsdl/xmlSchema/element.rb
@@ -0,0 +1,115 @@
+=begin
+WSDL4R - XMLSchema element definition for WSDL.
+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/info'
+
+
+module WSDL
+module XMLSchema
+
+
+class Element < Info
+ attr_accessor :name # required
+ attr_accessor :type
+ attr_accessor :local_complextype
+ attr_accessor :constraint
+ attr_accessor :maxoccurs
+ attr_accessor :minoccurs
+ attr_accessor :nillable
+
+ def initialize(name = nil, type = XSD::AnyTypeName)
+ super()
+ @name = name
+ @type = type
+ @local_complextype = nil
+ @constraint = nil
+ @maxoccurs = 1
+ @minoccurs = 1
+ @nillable = nil
+ end
+
+ def targetnamespace
+ parent.targetnamespace
+ end
+
+ def parse_element(element)
+ case element
+ when ComplexTypeName
+ @type = nil
+ @local_complextype = ComplexType.new
+ @local_complextype
+ when UniqueName
+ @constraint = Unique.new
+ @constraint
+ else
+ nil
+ end
+ end
+
+ def parse_attr(attr, value)
+ case attr
+ when NameAttrName
+ #@name = XSD::QName.new(nil, value)
+ @name = XSD::QName.new(targetnamespace, value)
+ when TypeAttrName
+ @type = if value.is_a?(XSD::QName)
+ value
+ else
+ XSD::QName.new(XSD::Namespace, value)
+ end
+ when MaxOccursAttrName
+ case parent
+ when All
+ if value != '1'
+ raise Parser::AttrConstraintError.new(
+ "Cannot parse #{ value } for #{ attr }.")
+ end
+ @maxoccurs = value
+ when Sequence
+ @maxoccurs = value
+ else
+ raise NotImplementedError.new
+ end
+ @maxoccurs
+ when MinOccursAttrName
+ case parent
+ when All
+ if ['0', '1'].include?(value)
+ @minoccurs = value
+ else
+ raise Parser::AttrConstraintError.new(
+ "Cannot parse #{ value } for #{ attr }.")
+ end
+ when Sequence
+ @minoccurs = value
+ else
+ raise NotImplementedError.new
+ end
+ @minoccurs
+ when NillableAttrName
+ @nillable = (value == 'true')
+ else
+ nil
+ end
+ end
+end
+
+
+end
+end
diff --git a/lib/wsdl/xmlSchema/import.rb b/lib/wsdl/xmlSchema/import.rb
new file mode 100644
index 0000000000..2267125a70
--- /dev/null
+++ b/lib/wsdl/xmlSchema/import.rb
@@ -0,0 +1,55 @@
+=begin
+WSDL4R - XMLSchema import definition.
+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/info'
+
+
+module WSDL
+module XMLSchema
+
+
+class Import < Info
+ attr_reader :namespace
+ attr_reader :schemalocation
+
+ def initialize
+ super
+ @namespace = nil
+ @schemalocation = nil
+ end
+
+ def parse_element(element)
+ nil
+ end
+
+ def parse_attr(attr, value)
+ case attr
+ when NamespaceAttrName
+ @namespace = value
+ when SchemaLocationAttrName
+ @schemalocation = value
+ else
+ nil
+ end
+ end
+end
+
+
+end
+end
diff --git a/lib/wsdl/xmlSchema/parser.rb b/lib/wsdl/xmlSchema/parser.rb
new file mode 100644
index 0000000000..6e893cdc7e
--- /dev/null
+++ b/lib/wsdl/xmlSchema/parser.rb
@@ -0,0 +1,172 @@
+=begin
+WSDL4R - WSDL XML Instance parser library.
+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 'xsd/qname'
+require 'xsd/ns'
+require 'xsd/charset'
+require 'xsd/datatypes'
+require 'xsd/xmlparser'
+require 'wsdl/xmlSchema/data'
+
+
+module WSDL
+module XMLSchema
+
+
+class Parser
+ include XSD
+
+ class ParseError < Error; end
+ class FormatDecodeError < Error; end
+ class UnknownElementError < FormatDecodeError; end
+ class UnknownAttributeError < FormatDecodeError; end
+ class UnexpectedElementError < FormatDecodeError; end
+ class ElementConstraintError < FormatDecodeError; end
+ class AttributeConstraintError < FormatDecodeError; end
+
+private
+
+ class ParseFrame
+ attr_reader :ns
+ attr_reader :name
+ attr_accessor :node
+
+ private
+
+ def initialize(ns, name, node)
+ @ns = ns
+ @name = name
+ @node = node
+ end
+ end
+
+public
+
+ def initialize(opt = {})
+ @parser = XSD::XMLParser.create_parser(self, opt)
+ @parsestack = nil
+ @lastnode = nil
+ end
+
+ def parse(string_or_readable)
+ @parsestack = []
+ @lastnode = nil
+ @textbuf = ''
+ @parser.do_parse(string_or_readable)
+ @lastnode
+ end
+
+ def charset
+ @parser.charset
+ end
+
+ def start_element(name, attrs)
+ lastframe = @parsestack.last
+ ns = parent = nil
+ if lastframe
+ ns = lastframe.ns.clone_ns
+ parent = lastframe.node
+ else
+ ns = XSD::NS.new
+ parent = nil
+ end
+ attrs = XSD::XMLParser.filter_ns(ns, attrs)
+ node = decode_tag(ns, name, attrs, parent)
+ @parsestack << ParseFrame.new(ns, name, node)
+ end
+
+ def characters(text)
+ lastframe = @parsestack.last
+ if lastframe
+ # Need not to be cloned because character does not have attr.
+ ns = lastframe.ns
+ decode_text(ns, text)
+ else
+ 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)
+ @lastnode = lastframe.node
+ end
+
+private
+
+ def decode_tag(ns, name, attrs, parent)
+ o = nil
+ element = ns.parse(name)
+ if !parent
+ if element == SchemaName
+ o = Schema.parse_element(element)
+ else
+ raise UnknownElementError.new("Unknown element #{ element }.")
+ end
+ else
+ o = parent.parse_element(element)
+ unless o
+ raise UnknownElementError.new("Unknown element #{ element }.")
+ end
+ o.parent = parent
+ end
+ attrs.each do |key, value|
+ attr = unless /:/ =~ key
+ XSD::QName.new(nil, key)
+ else
+ ns.parse(key)
+ end
+ value_ele = if /:/ !~ value
+ value
+ elsif /^http:\/\// =~ value # ToDo: ugly.
+ value
+ else
+ begin
+ ns.parse(value)
+ rescue
+ value
+ end
+ end
+ if attr == IdAttrName
+ o.id = value_ele
+ else
+ unless o.parse_attr(attr, value_ele)
+ STDERR.puts("Unknown attr #{ attr }.")
+ # raise UnknownAttributeError.new("Unknown attr #{ attr }.")
+ end
+ end
+ end
+ o
+ end
+
+ def decode_tag_end(ns, node)
+ node.parse_epilogue
+ end
+
+ def decode_text(ns, text)
+ @textbuf << text
+ end
+end
+
+
+end
+end
diff --git a/lib/wsdl/xmlSchema/schema.rb b/lib/wsdl/xmlSchema/schema.rb
new file mode 100644
index 0000000000..3a9aa6842e
--- /dev/null
+++ b/lib/wsdl/xmlSchema/schema.rb
@@ -0,0 +1,105 @@
+=begin
+WSDL4R - XMLSchema schema definition for WSDL.
+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/info'
+require 'xsd/namedelements'
+
+
+module WSDL
+module XMLSchema
+
+
+class Schema < Info
+ attr_reader :targetnamespace # required
+ attr_reader :complextypes
+ attr_reader :elements
+ attr_reader :attributes
+ attr_reader :imports
+ attr_accessor :attributeformdefault
+ attr_accessor :elementformdefault
+
+ def initialize
+ super
+ @targetnamespace = nil
+ @complextypes = XSD::NamedElements.new
+ @elements = XSD::NamedElements.new
+ @attributes = XSD::NamedElements.new
+ @imports = []
+ @elementformdefault = nil
+ end
+
+ def parse_element(element)
+ case element
+ when ImportName
+ o = Import.new
+ @imports << o
+ o
+ when ComplexTypeName
+ o = ComplexType.new
+ @complextypes << o
+ o
+ when ElementName
+ o = Element.new
+ @elements << o
+ o
+ when AttributeName
+ o = Attribute.new
+ o
+ else
+ nil
+ end
+ end
+
+ def parse_attr(attr, value)
+ case attr
+ when TargetNamespaceAttrName
+ @targetnamespace = value
+ when AttributeFormDefaultAttrName
+ @attributeformdefault = value
+ when ElementFormDefaultAttrName
+ @elementformdefault = value
+ else
+ nil
+ end
+ end
+
+ def collect_elements
+ result = XSD::NamedElements.new
+ result.concat(@elements)
+ result
+ end
+
+ def collect_complextypes
+ result = XSD::NamedElements.new
+ result.concat(@complextypes)
+ result
+ end
+
+ def self.parse_element(element)
+ if element == SchemaName
+ Schema.new
+ else
+ nil
+ end
+ end
+end
+
+
+end
+end
diff --git a/lib/wsdl/xmlSchema/sequence.rb b/lib/wsdl/xmlSchema/sequence.rb
new file mode 100644
index 0000000000..fb5ca1aef6
--- /dev/null
+++ b/lib/wsdl/xmlSchema/sequence.rb
@@ -0,0 +1,76 @@
+=begin
+WSDL4R - XMLSchema complexType definition for WSDL.
+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/info'
+
+
+module WSDL
+module XMLSchema
+
+
+class Sequence < Info
+ attr_reader :minoccurs
+ attr_reader :maxoccurs
+ attr_reader :elements
+
+ def initialize
+ super()
+ @minoccurs = 1
+ @maxoccurs = 1
+ @elements = []
+ end
+
+ def targetnamespace
+ parent.targetnamespace
+ end
+
+ def <<(element)
+ @elements << element
+ end
+
+ def parse_element(element)
+ case element
+ when AnyName
+ o = Any.new
+ @elements << o
+ o
+ when ElementName
+ o = Element.new
+ @elements << o
+ o
+ else
+ nil
+ end
+ end
+
+ def parse_attr(attr, value)
+ case attr
+ when MaxOccursAttrName
+ @maxoccurs = value
+ when MinOccursAttrName
+ @minoccurs = value
+ else
+ nil
+ end
+ end
+end
+
+
+end
+end
diff --git a/lib/wsdl/xmlSchema/unique.rb b/lib/wsdl/xmlSchema/unique.rb
new file mode 100644
index 0000000000..1d2573f6b0
--- /dev/null
+++ b/lib/wsdl/xmlSchema/unique.rb
@@ -0,0 +1,45 @@
+=begin
+WSDL4R - XMLSchema unique element.
+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 'wsdl/info'
+
+
+module WSDL
+module XMLSchema
+
+
+class Unique < Info
+ def initialize
+ super
+ end
+
+ def parse_element(element)
+ # Accepts any element.
+ self
+ end
+
+ def parse_attr(attr, value)
+ # Accepts any attribute.
+ true
+ end
+end
+
+
+end
+end
diff --git a/lib/xsd/charset.rb b/lib/xsd/charset.rb
new file mode 100644
index 0000000000..6dda959155
--- /dev/null
+++ b/lib/xsd/charset.rb
@@ -0,0 +1,175 @@
+=begin
+XSD4R - Charset handling 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
+
+
+module XSD
+
+
+module Charset
+ @encoding = $KCODE
+
+ class XSDError < StandardError; end
+ class CharsetError < XSDError; end
+ class UnknownCharsetError < CharsetError; end
+ class CharsetConversionError < CharsetError; end
+
+public
+
+ ###
+ ## Maps
+ #
+ EncodingConvertMap = {}
+ def Charset.init
+ begin
+ require 'xsd/iconvcharset'
+ @encoding = 'UTF8'
+ EncodingConvertMap[['UTF8', 'EUC' ]] = Proc.new { |str| IconvCharset.safe_iconv("euc-jp", "utf-8", str) }
+ EncodingConvertMap[['EUC' , 'UTF8']] = Proc.new { |str| IconvCharset.safe_iconv("utf-8", "euc-jp", str) }
+ EncodingConvertMap[['EUC' , 'SJIS']] = Proc.new { |str| IconvCharset.safe_iconv("shift-jis", "euc-jp", str) }
+ if /(mswin|bccwin|mingw|cygwin)/ =~ RUBY_PLATFORM
+ EncodingConvertMap[['UTF8', 'SJIS']] = Proc.new { |str| IconvCharset.safe_iconv("cp932", "utf-8", str) }
+ EncodingConvertMap[['SJIS', 'UTF8']] = Proc.new { |str| IconvCharset.safe_iconv("utf-8", "cp932", str) }
+ EncodingConvertMap[['SJIS', 'EUC' ]] = Proc.new { |str| IconvCharset.safe_iconv("euc-jp", "cp932", str) }
+ else
+ EncodingConvertMap[['UTF8', 'SJIS']] = Proc.new { |str| IconvCharset.safe_iconv("shift-jis", "utf-8", str) }
+ EncodingConvertMap[['SJIS', 'UTF8']] = Proc.new { |str| IconvCharset.safe_iconv("utf-8", "shift-jis", str) }
+ EncodingConvertMap[['SJIS', 'EUC' ]] = Proc.new { |str| IconvCharset.safe_iconv("euc-jp", "shift-jis", str) }
+ end
+ rescue LoadError
+ begin
+ require 'nkf'
+ EncodingConvertMap[['EUC' , 'SJIS']] = Proc.new { |str| NKF.nkf('-sXm0', str) }
+ EncodingConvertMap[['SJIS', 'EUC' ]] = Proc.new { |str| NKF.nkf('-eXm0', str) }
+ rescue LoadError
+ end
+
+ begin
+ require 'uconv'
+ @encoding = 'UTF8'
+ EncodingConvertMap[['UTF8', 'EUC' ]] = Uconv.method(:u8toeuc)
+ EncodingConvertMap[['UTF8', 'SJIS']] = Uconv.method(:u8tosjis)
+ EncodingConvertMap[['EUC' , 'UTF8']] = Uconv.method(:euctou8)
+ EncodingConvertMap[['SJIS', 'UTF8']] = Uconv.method(:sjistou8)
+ rescue LoadError
+ end
+ end
+ end
+ self.init
+
+ CharsetMap = {
+ 'NONE' => 'us-ascii',
+ 'EUC' => 'euc-jp',
+ 'SJIS' => 'shift_jis',
+ 'UTF8' => 'utf-8',
+ }
+
+
+ ###
+ ## handlers
+ #
+ def Charset.encoding
+ @encoding
+ end
+
+ def Charset.encoding_label
+ charset_label(@encoding)
+ end
+
+ def Charset.encoding_to_xml(str, charset)
+ encoding_conv(str, @encoding, charset_str(charset))
+ end
+
+ def Charset.encoding_from_xml(str, charset)
+ encoding_conv(str, charset_str(charset), @encoding)
+ end
+
+ def Charset.encoding_conv(str, enc_from, enc_to)
+ if enc_from == enc_to or enc_from == 'NONE' or enc_to == 'NONE'
+ str
+ elsif converter = EncodingConvertMap[[enc_from, enc_to]]
+ converter.call(str)
+ else
+ raise CharsetConversionError.new(
+ "Converter not found: #{ enc_from } -> #{ enc_to }")
+ end
+ end
+
+ def Charset.charset_label(encoding)
+ CharsetMap[encoding.upcase]
+ end
+
+ def Charset.charset_str(label)
+ CharsetMap.index(label.downcase)
+ end
+
+ # Original regexps: http://www.din.or.jp/~ohzaki/perl.htm
+ # ascii_euc = '[\x00-\x7F]'
+ ascii_euc = '[\x9\xa\xd\x20-\x7F]' # XML 1.0 restricted.
+ twobytes_euc = '(?:[\x8E\xA1-\xFE][\xA1-\xFE])'
+ threebytes_euc = '(?:\x8F[\xA1-\xFE][\xA1-\xFE])'
+ character_euc = "(?:#{ ascii_euc }|#{ twobytes_euc }|#{ threebytes_euc })"
+ EUCRegexp = Regexp.new("\\A#{ character_euc }*\\z", nil, "NONE")
+
+ # onebyte_sjis = '[\x00-\x7F\xA1-\xDF]'
+ onebyte_sjis = '[\x9\xa\xd\x20-\x7F\xA1-\xDF]' # XML 1.0 restricted.
+ twobytes_sjis = '(?:[\x81-\x9F\xE0-\xFC][\x40-\x7E\x80-\xFC])'
+ character_sjis = "(?:#{ onebyte_sjis }|#{ twobytes_sjis })"
+ SJISRegexp = Regexp.new("\\A#{ character_sjis }*\\z", nil, "NONE")
+
+ # 0xxxxxxx
+ #ascii_utf8 = '[\0-\x7F]'
+ ascii_utf8 = '[\x9\xA\xD\x20-\x7F]' # XML 1.0 restricted.
+ # 110yyyyy 10xxxxxx
+ twobytes_utf8 = '(?:[\xC0-\xDF][\x80-\xBF])'
+ # 1110zzzz 10yyyyyy 10xxxxxx
+ threebytes_utf8 = '(?:[\xE0-\xEF][\x80-\xBF][\x80-\xBF])'
+ # 11110uuu 10uuuzzz 10yyyyyy 10xxxxxx
+ fourbytes_utf8 = '(?:[\xF0-\xF7][\x80-\xBF][\x80-\xBF][\x80-\xBF])'
+ character_utf8 = "(?:#{ ascii_utf8 }|#{ twobytes_utf8 }|#{ threebytes_utf8 }|#{ fourbytes_utf8 })"
+ UTF8Regexp = Regexp.new("\\A#{ character_utf8 }*\\z", nil, "NONE")
+
+ def Charset.is_utf8(str)
+ UTF8Regexp =~ str
+ end
+
+ def Charset.is_euc(str)
+ EUCRegexp =~ str
+ end
+
+ def Charset.is_sjis(str)
+ SJISRegexp =~ str
+ end
+
+ def Charset.is_ces(str, code = $KCODE)
+ case code
+ when 'NONE'
+ true
+ when 'UTF8'
+ is_utf8(str)
+ when 'EUC'
+ is_euc(str)
+ when 'SJIS'
+ is_sjis(str)
+ else
+ raise UnknownCharsetError.new("Unknown charset: #{ code }")
+ end
+ end
+end
+
+
+end
diff --git a/lib/xsd/datatypes.rb b/lib/xsd/datatypes.rb
new file mode 100644
index 0000000000..03e42d21d2
--- /dev/null
+++ b/lib/xsd/datatypes.rb
@@ -0,0 +1,1094 @@
+=begin
+XSD4R - XML Schema Datatype implementation.
+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'
+require 'uri'
+
+
+###
+## XMLSchamaDatatypes general definitions.
+#
+module XSD
+
+
+Namespace = 'http://www.w3.org/2001/XMLSchema'
+InstanceNamespace = 'http://www.w3.org/2001/XMLSchema-instance'
+
+AttrType = 'type'
+NilValue = 'true'
+
+AnyTypeLiteral = 'anyType'
+AnySimpleTypeLiteral = 'anySimpleType'
+NilLiteral = 'nil'
+StringLiteral = 'string'
+BooleanLiteral = 'boolean'
+DecimalLiteral = 'decimal'
+FloatLiteral = 'float'
+DoubleLiteral = 'double'
+DurationLiteral = 'duration'
+DateTimeLiteral = 'dateTime'
+TimeLiteral = 'time'
+DateLiteral = 'date'
+GYearMonthLiteral = 'gYearMonth'
+GYearLiteral = 'gYear'
+GMonthDayLiteral = 'gMonthDay'
+GDayLiteral = 'gDay'
+GMonthLiteral = 'gMonth'
+HexBinaryLiteral = 'hexBinary'
+Base64BinaryLiteral = 'base64Binary'
+AnyURILiteral = 'anyURI'
+QNameLiteral = 'QName'
+
+NormalizedStringLiteral = 'normalizedString'
+IntegerLiteral = 'integer'
+LongLiteral = 'long'
+IntLiteral = 'int'
+ShortLiteral = 'short'
+
+AttrTypeName = QName.new(InstanceNamespace, AttrType)
+AttrNilName = QName.new(InstanceNamespace, NilLiteral)
+
+AnyTypeName = QName.new(Namespace, AnyTypeLiteral)
+AnySimpleTypeName = QName.new(Namespace, AnySimpleTypeLiteral)
+
+class Error < StandardError; end
+class ValueSpaceError < Error; end
+
+
+###
+## The base class of all datatypes with Namespace.
+#
+class NSDBase
+ @@types = []
+
+ attr_accessor :type
+
+ def self.inherited(klass)
+ @@types << klass
+ end
+
+ def self.types
+ @@types
+ end
+
+ def initialize
+ @type = nil
+ end
+end
+
+
+###
+## The base class of XSD datatypes.
+#
+class XSDAnySimpleType < NSDBase
+ include XSD
+ Type = QName.new(Namespace, AnySimpleTypeLiteral)
+
+ # @data represents canonical space (ex. Integer: 123).
+ attr_reader :data
+ # @is_nil represents this data is nil or not.
+ attr_accessor :is_nil
+
+ def initialize(value = nil)
+ super()
+ @type = Type
+ @data = nil
+ @is_nil = true
+ set(value) if value
+ end
+
+ # set accepts a string which follows lexical space (ex. String: "+123"), or
+ # an object which follows canonical space (ex. Integer: 123).
+ def set(value)
+ if value.nil?
+ @is_nil = true
+ @data = nil
+ else
+ @is_nil = false
+ _set(value)
+ end
+ end
+
+ # to_s creates a string which follows lexical space (ex. String: "123").
+ def to_s()
+ if @is_nil
+ ""
+ else
+ _to_s
+ end
+ end
+
+ def trim(data)
+ data.sub(/\A\s*(\S*)\s*\z/, '\1')
+ end
+
+private
+
+ def _set(value)
+ @data = value
+ end
+
+ def _to_s
+ @data.to_s
+ end
+end
+
+class XSDNil < XSDAnySimpleType
+ Type = QName.new(Namespace, NilLiteral)
+ Value = 'true'
+
+ def initialize(value = nil)
+ super()
+ @type = Type
+ set(value)
+ end
+
+private
+
+ def _set(value)
+ @data = value
+ end
+end
+
+
+###
+## Primitive datatypes.
+#
+class XSDString < XSDAnySimpleType
+ Type = QName.new(Namespace, StringLiteral)
+
+ def initialize(value = nil)
+ super()
+ @type = Type
+ @encoding = nil
+ set(value) if value
+ end
+
+private
+
+ def _set(value)
+ unless XSD::Charset.is_ces(value, XSD::Charset.encoding)
+ raise ValueSpaceError.new("#{ type }: cannot accept '#{ value }'.")
+ end
+ @data = value
+ end
+end
+
+class XSDBoolean < XSDAnySimpleType
+ Type = QName.new(Namespace, BooleanLiteral)
+
+ def initialize(value = nil)
+ super()
+ @type = Type
+ set(value)
+ end
+
+private
+
+ def _set(value)
+ if value.is_a?(String)
+ str = trim(value)
+ if str == 'true' || str == '1'
+ @data = true
+ elsif str == 'false' || str == '0'
+ @data = false
+ else
+ raise ValueSpaceError.new("#{ type }: cannot accept '#{ str }'.")
+ end
+ else
+ @data = value ? true : false
+ end
+ end
+end
+
+class XSDDecimal < XSDAnySimpleType
+ Type = QName.new(Namespace, DecimalLiteral)
+
+ def initialize(value = nil)
+ super()
+ @type = Type
+ @sign = ''
+ @number = ''
+ @point = 0
+ set(value) if value
+ end
+
+ def nonzero?
+ (@number != '0')
+ end
+
+private
+
+ def _set(d)
+ if d.is_a?(String)
+ # Integer("00012") => 10 in Ruby.
+ d.sub!(/^([+\-]?)0*(?=\d)/, "\\1")
+ end
+ set_str(d)
+ end
+
+ def set_str(str)
+ /^([+\-]?)(\d*)(?:\.(\d*)?)?$/ =~ trim(str.to_s)
+ unless Regexp.last_match
+ raise ValueSpaceError.new("#{ type }: cannot accept '#{ str }'.")
+ end
+
+ @sign = $1 || '+'
+ int_part = $2
+ frac_part = $3
+
+ int_part = '0' if int_part.empty?
+ frac_part = frac_part ? frac_part.sub(/0+$/, '') : ''
+ @point = - frac_part.size
+ @number = int_part + frac_part
+
+ # normalize
+ if @sign == '+'
+ @sign = ''
+ elsif @sign == '-'
+ if @number == '0'
+ @sign = ''
+ end
+ end
+
+ @data = _to_s
+ end
+
+ # 0.0 -> 0; right?
+ def _to_s
+ str = @number.dup
+ if @point.nonzero?
+ str[@number.size + @point, 0] = '.'
+ end
+ @sign + str
+ end
+end
+
+class XSDFloat < XSDAnySimpleType
+ Type = QName.new(Namespace, FloatLiteral)
+
+ def initialize(value = nil)
+ super()
+ @type = Type
+ set(value) if value
+ end
+
+private
+
+ def _set(value)
+ # "NaN".to_f => 0 in some environment. libc?
+ if value.is_a?(Float)
+ @data = narrow32bit(value)
+ return
+ end
+
+ str = trim(value.to_s)
+ if str == 'NaN'
+ @data = 0.0/0.0
+ elsif str == 'INF'
+ @data = 1.0/0.0
+ elsif str == '-INF'
+ @data = -1.0/0.0
+ else
+ if /^[+\-\.\deE]+$/ !~ str
+ raise ValueSpaceError.new("#{ type }: cannot accept '#{ str }'.")
+ end
+ # Float("-1.4E") might fail on some system.
+ str << '0' if /e$/i =~ str
+ begin
+ @data = narrow32bit(Float(str))
+ rescue ArgumentError
+ raise ValueSpaceError.new("#{ type }: cannot accept '#{ str }'.")
+ end
+ end
+ end
+
+ # Do I have to convert 0.0 -> 0 and -0.0 -> -0 ?
+ def _to_s
+ if @data.nan?
+ 'NaN'
+ elsif @data.infinite? == 1
+ 'INF'
+ elsif @data.infinite? == -1
+ '-INF'
+ else
+ sprintf("%.10g", @data)
+ end
+ end
+
+ # Convert to single-precision 32-bit floating point value.
+ def narrow32bit(f)
+ if f.nan? || f.infinite?
+ f
+ else
+ packed = [f].pack("f")
+ (/\A\0*\z/ =~ packed)? 0.0 : f
+ end
+ end
+end
+
+# Ruby's Float is double-precision 64-bit floating point value.
+class XSDDouble < XSDAnySimpleType
+ Type = QName.new(Namespace, DoubleLiteral)
+
+ def initialize(value = nil)
+ super()
+ @type = Type
+ set(value) if value
+ end
+
+private
+
+ def _set(value)
+ # "NaN".to_f => 0 in some environment. libc?
+ if value.is_a?(Float)
+ @data = value
+ return
+ end
+
+ str = trim(value.to_s)
+ if str == 'NaN'
+ @data = 0.0/0.0
+ elsif str == 'INF'
+ @data = 1.0/0.0
+ elsif str == '-INF'
+ @data = -1.0/0.0
+ else
+ begin
+ @data = Float(str)
+ rescue ArgumentError
+ # '1.4e' cannot be parsed on some architecture.
+ if /e\z/i =~ str
+ begin
+ @data = Float(str + '0')
+ rescue ArgumentError
+ raise ValueSpaceError.new("#{ type }: cannot accept '#{ str }'.")
+ end
+ else
+ raise ValueSpaceError.new("#{ type }: cannot accept '#{ str }'.")
+ end
+ end
+ end
+ end
+
+ # Do I have to convert 0.0 -> 0 and -0.0 -> -0 ?
+ def _to_s
+ if @data.nan?
+ 'NaN'
+ elsif @data.infinite? == 1
+ 'INF'
+ elsif @data.infinite? == -1
+ '-INF'
+ else
+ sprintf("%.16g", @data)
+ end
+ end
+end
+
+class XSDDuration < XSDAnySimpleType
+ Type = QName.new(Namespace, DurationLiteral)
+
+ attr_accessor :sign
+ attr_accessor :year
+ attr_accessor :month
+ attr_accessor :day
+ attr_accessor :hour
+ attr_accessor :min
+ attr_accessor :sec
+
+ def initialize(value = nil)
+ super()
+ @type = Type
+ @sign = nil
+ @year = nil
+ @month = nil
+ @day = nil
+ @hour = nil
+ @min = nil
+ @sec = nil
+ set(value) if value
+ end
+
+private
+
+ def _set(value)
+ /^([+\-]?)P(?:(\d+)Y)?(?:(\d+)M)?(?:(\d+)D)?(T(?:(\d+)H)?(?:(\d+)M)?(?:(\d+(?:\.\d+)?)S)?)?$/ =~ trim(value.to_s)
+ unless Regexp.last_match
+ raise ValueSpaceError.new("#{ type }: cannot accept '#{ value }'.")
+ end
+
+ if ($5 and ((!$2 and !$3 and !$4) or (!$6 and !$7 and !$8)))
+ # Should we allow 'PT5S' here?
+ raise ValueSpaceError.new("#{ type }: cannot accept '#{ value }'.")
+ end
+
+ @sign = $1
+ @year = $2.to_i
+ @month = $3.to_i
+ @day = $4.to_i
+ @hour = $6.to_i
+ @min = $7.to_i
+ @sec = $8 ? XSDDecimal.new($8) : 0
+ @data = _to_s
+ end
+
+ def _to_s
+ str = ''
+ str << @sign if @sign
+ str << 'P'
+ l = ''
+ l << "#{ @year }Y" if @year.nonzero?
+ l << "#{ @month }M" if @month.nonzero?
+ l << "#{ @day }D" if @day.nonzero?
+ r = ''
+ r << "#{ @hour }H" if @hour.nonzero?
+ r << "#{ @min }M" if @min.nonzero?
+ r << "#{ @sec }S" if @sec.nonzero?
+ str << l
+ if l.empty?
+ str << "0D"
+ end
+ unless r.empty?
+ str << "T" << r
+ end
+ str
+ end
+end
+
+
+require 'rational'
+require 'date'
+unless Object.const_defined?('DateTime')
+ raise LoadError.new('XSD4R requires date2/3.2 or later to be installed. You can download it from http://www.funaba.org/en/ruby.html#date2')
+end
+
+module XSDDateTimeImpl
+ SecInDay = 86400 # 24 * 60 * 60
+
+ def to_time
+ begin
+ if @data.of * SecInDay == Time.now.utc_offset
+ d = @data
+ usec = (d.sec_fraction * SecInDay * 1000000).to_f
+ Time.local(d.year, d.month, d.mday, d.hour, d.min, d.sec, usec)
+ else
+ d = @data.newof
+ usec = (d.sec_fraction * SecInDay * 1000000).to_f
+ Time.gm(d.year, d.month, d.mday, d.hour, d.min, d.sec, usec)
+ end
+ rescue ArgumentError
+ nil
+ end
+ end
+
+ def tz2of(str)
+ /^(?:Z|(?:([+\-])(\d\d):(\d\d))?)$/ =~ str
+ sign = $1
+ hour = $2.to_i
+ min = $3.to_i
+
+ of = case sign
+ when '+'
+ of = +(hour.to_r * 60 + min) / 1440 # 24 * 60
+ when '-'
+ of = -(hour.to_r * 60 + min) / 1440 # 24 * 60
+ else
+ 0
+ end
+ of
+ end
+
+ def of2tz(offset)
+ diffmin = offset * 24 * 60
+ if diffmin.zero?
+ 'Z'
+ else
+ ((diffmin < 0) ? '-' : '+') << format('%02d:%02d',
+ (diffmin.abs / 60.0).to_i, (diffmin.abs % 60.0).to_i)
+ end
+ end
+
+ def _set(t)
+ if (t.is_a?(Date))
+ @data = t
+ elsif (t.is_a?(Time))
+ sec, min, hour, mday, month, year = t.to_a[0..5]
+ diffday = t.usec.to_r / 1000000 / SecInDay
+ of = t.utc_offset.to_r / SecInDay
+ @data = DateTime.civil(year, month, mday, hour, min, sec, of)
+ @data += diffday
+ else
+ set_str(t)
+ end
+ end
+
+ def add_tz(s)
+ s + of2tz(@data.offset)
+ end
+end
+
+class XSDDateTime < XSDAnySimpleType
+ include XSDDateTimeImpl
+ Type = QName.new(Namespace, DateTimeLiteral)
+
+ def initialize(value = nil)
+ super()
+ @type = Type
+ set(value) if value
+ end
+
+private
+
+ def set_str(t)
+ /^([+\-]?\d\d\d\d\d*)-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d(?:\.(\d*))?)(Z|(?:[+\-]\d\d:\d\d)?)?$/ =~ trim(t.to_s)
+ unless Regexp.last_match
+ raise ValueSpaceError.new("#{ type }: cannot accept '#{ t }'.")
+ end
+ if $1 == '0000'
+ raise ValueSpaceError.new("#{ type }: cannot accept '#{ t }'.")
+ end
+
+ year = $1.to_i
+ if year < 0
+ year += 1
+ end
+ mon = $2.to_i
+ mday = $3.to_i
+ hour = $4.to_i
+ min = $5.to_i
+ sec = $6.to_i
+ secfrac = $7
+ zonestr = $8
+
+ @data = DateTime.civil(year, mon, mday, hour, min, sec, tz2of(zonestr))
+
+ if secfrac
+ diffday = secfrac.to_i.to_r / (10 ** secfrac.size) / SecInDay
+ # jd = @data.jd
+ # day_fraction = @data.day_fraction + diffday
+ # @data = DateTime.new0(DateTime.jd_to_rjd(jd, day_fraction,
+ # @data.offset), @data.offset)
+ #
+ # Thanks to Funaba-san, above code can be simply written as below.
+ @data += diffday
+ # FYI: new0 and jd_to_rjd are not necessary to use if you don't have
+ # exceptional reason.
+ end
+ end
+
+ def _to_s
+ year = (@data.year > 0) ? @data.year : @data.year - 1
+ s = format('%.4d-%02d-%02dT%02d:%02d:%02d',
+ year, @data.mon, @data.mday, @data.hour, @data.min, @data.sec)
+ if @data.sec_fraction.nonzero?
+ fr = @data.sec_fraction * SecInDay
+ shiftsize = fr.denominator.to_s.size
+ fr_s = (fr * (10 ** shiftsize)).to_i.to_s
+ s << '.' << '0' * (shiftsize - fr_s.size) << fr_s.sub(/0+$/, '')
+ end
+ add_tz(s)
+ end
+end
+
+class XSDTime < XSDAnySimpleType
+ include XSDDateTimeImpl
+ Type = QName.new(Namespace, TimeLiteral)
+
+ def initialize(value = nil)
+ super()
+ @type = Type
+ set(value) if value
+ end
+
+private
+
+ def set_str(t)
+ /^(\d\d):(\d\d):(\d\d(?:\.(\d*))?)(Z|(?:([+\-])(\d\d):(\d\d))?)?$/ =~ trim(t.to_s)
+ unless Regexp.last_match
+ raise ValueSpaceError.new("#{ type }: cannot accept '#{ t }'.")
+ end
+
+ hour = $1.to_i
+ min = $2.to_i
+ sec = $3.to_i
+ secfrac = $4
+ zonestr = $5
+
+ @data = DateTime.civil(1, 1, 1, hour, min, sec, tz2of(zonestr))
+
+ if secfrac
+ @data += secfrac.to_i.to_r / (10 ** secfrac.size) / SecInDay
+ end
+ end
+
+ def _to_s
+ s = format('%02d:%02d:%02d', @data.hour, @data.min, @data.sec)
+ if @data.sec_fraction.nonzero?
+ fr = @data.sec_fraction * SecInDay
+ shiftsize = fr.denominator.to_s.size
+ fr_s = (fr * (10 ** shiftsize)).to_i.to_s
+ s << '.' << '0' * (shiftsize - fr_s.size) << fr_s.sub(/0+$/, '')
+ end
+ add_tz(s)
+ end
+end
+
+class XSDDate < XSDAnySimpleType
+ include XSDDateTimeImpl
+ Type = QName.new(Namespace, DateLiteral)
+
+ def initialize(value = nil)
+ super()
+ @type = Type
+ set(value) if value
+ end
+
+private
+
+ def set_str(t)
+ /^([+\-]?\d\d\d\d\d*)-(\d\d)-(\d\d)(Z|(?:([+\-])(\d\d):(\d\d))?)?$/ =~ trim(t.to_s)
+ unless Regexp.last_match
+ raise ValueSpaceError.new("#{ type }: cannot accept '#{ t }'.")
+ end
+
+ year = $1.to_i
+ if year < 0
+ year += 1
+ end
+ mon = $2.to_i
+ mday = $3.to_i
+ zonestr = $4
+
+ @data = DateTime.civil(year, mon, mday, 0, 0, 0, tz2of(zonestr))
+ end
+
+ def _to_s
+ year = (@data.year > 0) ? @data.year : @data.year - 1
+ s = format('%.4d-%02d-%02d', year, @data.mon, @data.mday)
+ add_tz(s)
+ end
+end
+
+class XSDGYearMonth < XSDAnySimpleType
+ include XSDDateTimeImpl
+ Type = QName.new(Namespace, GYearMonthLiteral)
+
+ def initialize(value = nil)
+ super()
+ @type = Type
+ set(value) if value
+ end
+
+private
+
+ def set_str(t)
+ /^([+\-]?\d\d\d\d\d*)-(\d\d)(Z|(?:([+\-])(\d\d):(\d\d))?)?$/ =~ trim(t.to_s)
+ unless Regexp.last_match
+ raise ValueSpaceError.new("#{ type }: cannot accept '#{ t }'.")
+ end
+
+ year = $1.to_i
+ if year < 0
+ year += 1
+ end
+ mon = $2.to_i
+ zonestr = $3
+
+ @data = DateTime.civil(year, mon, 1, 0, 0, 0, tz2of(zonestr))
+ end
+
+ def _to_s
+ year = (@data.year > 0) ? @data.year : @data.year - 1
+ s = format('%.4d-%02d', year, @data.mon)
+ add_tz(s)
+ end
+end
+
+class XSDGYear < XSDAnySimpleType
+ include XSDDateTimeImpl
+ Type = QName.new(Namespace, GYearLiteral)
+
+ def initialize(value = nil)
+ super()
+ @type = Type
+ set(value) if value
+ end
+
+private
+
+ def set_str(t)
+ /^([+\-]?\d\d\d\d\d*)(Z|(?:([+\-])(\d\d):(\d\d))?)?$/ =~ trim(t.to_s)
+ unless Regexp.last_match
+ raise ValueSpaceError.new("#{ type }: cannot accept '#{ t }'.")
+ end
+
+ year = $1.to_i
+ if year < 0
+ year += 1
+ end
+ zonestr = $2
+
+ @data = DateTime.civil(year, 1, 1, 0, 0, 0, tz2of(zonestr))
+ end
+
+ def _to_s
+ year = (@data.year > 0) ? @data.year : @data.year - 1
+ s = format('%.4d', year)
+ add_tz(s)
+ end
+end
+
+class XSDGMonthDay < XSDAnySimpleType
+ include XSDDateTimeImpl
+ Type = QName.new(Namespace, GMonthDayLiteral)
+
+ def initialize(value = nil)
+ super()
+ @type = Type
+ set(value) if value
+ end
+
+private
+
+ def set_str(t)
+ /^(\d\d)-(\d\d)(Z|(?:[+\-]\d\d:\d\d)?)?$/ =~ trim(t.to_s)
+ unless Regexp.last_match
+ raise ValueSpaceError.new("#{ type }: cannot accept '#{ t }'.")
+ end
+
+ mon = $1.to_i
+ mday = $2.to_i
+ zonestr = $3
+
+ @data = DateTime.civil(1, mon, mday, 0, 0, 0, tz2of(zonestr))
+ end
+
+ def _to_s
+ s = format('%02d-%02d', @data.mon, @data.mday)
+ add_tz(s)
+ end
+end
+
+class XSDGDay < XSDAnySimpleType
+ include XSDDateTimeImpl
+ Type = QName.new(Namespace, GDayLiteral)
+
+ def initialize(value = nil)
+ super()
+ @type = Type
+ set(value) if value
+ end
+
+private
+
+ def set_str(t)
+ /^(\d\d)(Z|(?:[+\-]\d\d:\d\d)?)?$/ =~ trim(t.to_s)
+ unless Regexp.last_match
+ raise ValueSpaceError.new("#{ type }: cannot accept '#{ t }'.")
+ end
+
+ mday = $1.to_i
+ zonestr = $2
+
+ @data = DateTime.civil(1, 1, mday, 0, 0, 0, tz2of(zonestr))
+ end
+
+ def _to_s
+ s = format('%02d', @data.mday)
+ add_tz(s)
+ end
+end
+
+class XSDGMonth < XSDAnySimpleType
+ include XSDDateTimeImpl
+ Type = QName.new(Namespace, GMonthLiteral)
+
+ def initialize(value = nil)
+ super()
+ @type = Type
+ set(value) if value
+ end
+
+private
+
+ def set_str(t)
+ /^(\d\d)(Z|(?:[+\-]\d\d:\d\d)?)?$/ =~ trim(t.to_s)
+ unless Regexp.last_match
+ raise ValueSpaceError.new("#{ type }: cannot accept '#{ t }'.")
+ end
+
+ mon = $1.to_i
+ zonestr = $2
+
+ @data = DateTime.civil(1, mon, 1, 0, 0, 0, tz2of(zonestr))
+ end
+
+ def _to_s
+ s = format('%02d', @data.mon)
+ add_tz(s)
+ end
+end
+
+class XSDHexBinary < XSDAnySimpleType
+ Type = QName.new(Namespace, HexBinaryLiteral)
+
+ # String in Ruby could be a binary.
+ def initialize(value = nil)
+ super()
+ @type = Type
+ set(value) if value
+ end
+
+ def set_encoded(value)
+ if /^[0-9a-fA-F]*$/ !~ value
+ raise ValueSpaceError.new("#{ type }: cannot accept '#{ value }'.")
+ end
+ @data = trim(String.new(value))
+ @is_nil = false
+ end
+
+ def string
+ [@data].pack("H*")
+ end
+
+private
+
+ def _set(value)
+ @data = value.unpack("H*")[0]
+ @data.tr!('a-f', 'A-F')
+ end
+end
+
+class XSDBase64Binary < XSDAnySimpleType
+ Type = QName.new(Namespace, Base64BinaryLiteral)
+
+ # String in Ruby could be a binary.
+ def initialize(value = nil)
+ super()
+ @type = Type
+ set(value) if value
+ end
+
+ def set_encoded(value)
+ if /^[A-Za-z0-9+\/=]*$/ !~ value
+ raise ValueSpaceError.new("#{ type }: cannot accept '#{ value }'.")
+ end
+ @data = trim(String.new(value))
+ @is_nil = false
+ end
+
+ def string
+ @data.unpack("m")[0]
+ end
+
+private
+
+ def _set(value)
+ @data = trim([value].pack("m"))
+ end
+end
+
+class XSDAnyURI < XSDAnySimpleType
+ Type = QName.new(Namespace, AnyURILiteral)
+
+ def initialize(value = nil)
+ super()
+ @type = Type
+ set(value) if value
+ end
+
+private
+
+ def _set(value)
+ begin
+ @data = URI.parse(trim(value.to_s))
+ rescue URI::InvalidURIError
+ raise ValueSpaceError.new("#{ type }: cannot accept '#{ value }'.")
+ end
+ end
+end
+
+class XSDQName < XSDAnySimpleType
+ Type = QName.new(Namespace, QNameLiteral)
+
+ def initialize(value = nil)
+ super()
+ @type = Type
+ set(value) if value
+ end
+
+private
+
+ def _set(value)
+ /^(?:([^:]+):)?([^:]+)$/ =~ trim(value.to_s)
+ unless Regexp.last_match
+ raise ValueSpaceError.new("#{ type }: cannot accept '#{ value }'.")
+ end
+
+ @prefix = $1
+ @localpart = $2
+ @data = _to_s
+ end
+
+ def _to_s
+ if @prefix
+ "#{ @prefix }:#{ @localpart }"
+ else
+ "#{ @localpart }"
+ end
+ end
+end
+
+
+###
+## Derived types
+#
+class XSDNormalizedString < XSDString
+ Type = QName.new(Namespace, NormalizedStringLiteral)
+
+ def initialize(value = nil)
+ super()
+ @type = Type
+ set(value) if value
+ end
+
+private
+
+ def _set(value)
+ if /[\t\r\n]/ =~ value
+ raise ValueSpaceError.new("#{ type }: cannot accept '#{ value }'.")
+ end
+ super
+ end
+end
+
+class XSDInteger < XSDDecimal
+ Type = QName.new(Namespace, IntegerLiteral)
+
+ def initialize(value = nil)
+ super()
+ @type = Type
+ set(value) if value
+ end
+
+private
+
+ def set_str(str)
+ begin
+ @data = Integer(str)
+ rescue ArgumentError
+ raise ValueSpaceError.new("#{ type }: cannot accept '#{ str }'.")
+ end
+ end
+
+ def _to_s()
+ @data.to_s
+ end
+end
+
+class XSDLong < XSDInteger
+ Type = QName.new(Namespace, LongLiteral)
+
+ def initialize(value = nil)
+ super()
+ @type = Type
+ set(value) if value
+ end
+
+private
+
+ def set_str(str)
+ begin
+ @data = Integer(str)
+ rescue ArgumentError
+ raise ValueSpaceError.new("#{ type }: cannot accept '#{ str }'.")
+ end
+ unless validate(@data)
+ raise ValueSpaceError.new("#{ type }: cannot accept '#{ str }'.")
+ end
+ end
+
+ MaxInclusive = +9223372036854775807
+ MinInclusive = -9223372036854775808
+ def validate(v)
+ ((MinInclusive <= v) && (v <= MaxInclusive))
+ end
+end
+
+class XSDInt < XSDLong
+ Type = QName.new(Namespace, IntLiteral)
+
+ def initialize(value = nil)
+ super()
+ @type = Type
+ set(value) if value
+ end
+
+private
+
+ def set_str(str)
+ begin
+ @data = Integer(str)
+ rescue ArgumentError
+ raise ValueSpaceError.new("#{ type }: cannot accept '#{ str }'.")
+ end
+ unless validate(@data)
+ raise ValueSpaceError.new("#{ type }: cannot accept '#{ str }'.")
+ end
+ end
+
+ MaxInclusive = +2147483647
+ MinInclusive = -2147483648
+ def validate(v)
+ ((MinInclusive <= v) && (v <= MaxInclusive))
+ end
+end
+
+class XSDShort < XSDInt
+ Type = QName.new(Namespace, ShortLiteral)
+
+ def initialize(value = nil)
+ super()
+ @type = Type
+ set(value) if value
+ end
+
+private
+
+ def set_str(str)
+ begin
+ @data = Integer(str)
+ rescue ArgumentError
+ raise ValueSpaceError.new("#{ type }: cannot accept '#{ str }'.")
+ end
+ unless validate(@data)
+ raise ValueSpaceError.new("#{ type }: cannot accept '#{ str }'.")
+ end
+ end
+
+ MaxInclusive = +32767
+ MinInclusive = -32768
+ def validate(v)
+ ((MinInclusive <= v) && (v <= MaxInclusive))
+ end
+end
+
+
+end
diff --git a/lib/xsd/datatypes1999.rb b/lib/xsd/datatypes1999.rb
new file mode 100644
index 0000000000..6b6b6be20a
--- /dev/null
+++ b/lib/xsd/datatypes1999.rb
@@ -0,0 +1,31 @@
+=begin
+XSD4R - XML Schema Datatype 1999 support
+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/datatypes'
+
+
+module XSD
+ Namespace.replace('http://www.w3.org/1999/XMLSchema')
+ InstanceNamespace.replace('http://www.w3.org/1999/XMLSchema-instance')
+ AnyTypeLiteral.replace('ur-type')
+ AnySimpleTypeLiteral.replace('ur-type')
+ NilLiteral.replace('null')
+ NilValue.replace('1')
+ DateTimeLiteral.replace('timeInstant')
+end
diff --git a/lib/xsd/iconvcharset.rb b/lib/xsd/iconvcharset.rb
new file mode 100644
index 0000000000..f607b7db45
--- /dev/null
+++ b/lib/xsd/iconvcharset.rb
@@ -0,0 +1,44 @@
+=begin
+XSD4R - Charset handling with iconv.
+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 'iconv'
+
+
+module XSD
+
+
+class IconvCharset
+ def self.safe_iconv(to, from, str)
+ iconv = Iconv.new(to, from)
+ out = ""
+ begin
+ out << iconv.iconv(str)
+ rescue Iconv::IllegalSequence => e
+ out << e.success
+ ch, str = e.failed.split(//, 2)
+ out << '?'
+ STDERR.puts("Failed to convert #{ch}")
+ retry
+ end
+ return out
+ end
+end
+
+
+end
diff --git a/lib/xsd/namedelements.rb b/lib/xsd/namedelements.rb
new file mode 100644
index 0000000000..76f958a9e5
--- /dev/null
+++ b/lib/xsd/namedelements.rb
@@ -0,0 +1,86 @@
+=begin
+XSD4R - WSDL named element collection.
+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
+
+
+module XSD
+
+
+class NamedElements
+ include Enumerable
+
+ def initialize
+ @elements = []
+ @cache = {}
+ end
+
+ def dup
+ o = NamedElements.new
+ o.elements = @elements.dup
+ o
+ end
+
+ def size
+ @elements.size
+ end
+
+ def [](idx)
+ if idx.is_a?(Numeric)
+ @elements[idx]
+ else
+ @cache[idx] ||= @elements.find { |item| item.name == idx }
+ end
+ end
+
+ def find_name(name)
+ @elements.find { |item| item.name.name == name }
+ end
+
+ def each
+ @elements.each do |element|
+ yield(element)
+ end
+ end
+
+ def <<(rhs)
+ @elements << rhs
+ self
+ end
+
+ def +(rhs)
+ o = NamedElements.new
+ o.elements = @elements + rhs.elements
+ o
+ end
+
+ def concat(rhs)
+ @elements.concat(rhs.elements)
+ self
+ end
+
+protected
+
+ def elements=(rhs)
+ @elements = rhs
+ end
+
+ def elements
+ @elements
+ end
+end
+
+end
diff --git a/lib/xsd/ns.rb b/lib/xsd/ns.rb
new file mode 100644
index 0000000000..0767b2c30d
--- /dev/null
+++ b/lib/xsd/ns.rb
@@ -0,0 +1,141 @@
+=begin
+XSD4R - XML Schema Namespace library
+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/datatypes'
+
+
+module XSD
+
+
+class NS
+ class Assigner
+ def initialize
+ @count = 0
+ end
+
+ def assign(ns)
+ @count += 1
+ "n#{ @count }"
+ end
+ end
+
+ attr_reader :default_namespace
+
+ class FormatError < Error; end
+
+public
+
+ def initialize(tag2ns = {})
+ @tag2ns = tag2ns
+ @assigner = nil
+ @ns2tag = {}
+ @tag2ns.each do |tag, ns|
+ @ns2tag[ns] = tag
+ end
+ @default_namespace = nil
+ end
+
+ def assign(ns, tag = nil)
+ if (tag == '')
+ @default_namespace = ns
+ tag
+ else
+ @assigner ||= Assigner.new
+ tag ||= @assigner.assign(ns)
+ @ns2tag[ns] = tag
+ @tag2ns[tag] = ns
+ tag
+ end
+ end
+
+ def assigned?(ns)
+ @ns2tag.key?(ns)
+ end
+
+ def assigned_tag?(tag)
+ @tag2ns.key?(tag)
+ end
+
+ def clone_ns
+ cloned = NS.new(@tag2ns.dup)
+ cloned.assigner = @assigner
+ cloned.assign(@default_namespace, '') if @default_namespace
+ cloned
+ end
+
+ def name(name)
+ if (name.namespace == @default_namespace)
+ name.name
+ elsif @ns2tag.key?(name.namespace)
+ @ns2tag[name.namespace] + ':' << name.name
+ else
+ raise FormatError.new('Namespace: ' << name.namespace << ' not defined yet.')
+ end
+ end
+
+ def compare(ns, name, rhs)
+ if (ns == @default_namespace)
+ return true if (name == rhs)
+ end
+ @tag2ns.each do |assigned_tag, assigned_ns|
+ if assigned_ns == ns && "#{ assigned_tag }:#{ name }" == rhs
+ return true
+ end
+ end
+ false
+ end
+
+ # $1 and $2 are necessary.
+ ParseRegexp = Regexp.new('^([^:]+)(?::(.+))?$')
+
+ def parse(elem)
+ ns = nil
+ name = nil
+ ParseRegexp =~ elem
+ if $2
+ ns = @tag2ns[$1]
+ name = $2
+ if !ns
+ raise FormatError.new('Unknown namespace qualifier: ' << $1)
+ end
+ elsif $1
+ ns = @default_namespace
+ name = $1
+ end
+ if !name
+ raise FormatError.new("Illegal element format: #{ elem }")
+ end
+ XSD::QName.new(ns, name)
+ end
+
+ def each_ns
+ @ns2tag.each do |ns, tag|
+ yield(ns, tag)
+ end
+ end
+
+protected
+
+ def assigner=(assigner)
+ @assigner = assigner
+ end
+end
+
+
+end
diff --git a/lib/xsd/qname.rb b/lib/xsd/qname.rb
new file mode 100644
index 0000000000..150a837c1d
--- /dev/null
+++ b/lib/xsd/qname.rb
@@ -0,0 +1,77 @@
+=begin
+XSD4R - XML QName definition.
+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
+
+
+module XSD
+
+
+class QName
+ attr_accessor :namespace
+ attr_accessor :name
+
+ def initialize(namespace = nil, name = nil)
+ @namespace = namespace
+ @name = name
+ end
+
+ def dup_name(name)
+ self.class.new(@namespace, name)
+ end
+
+ def match(rhs)
+ unless self.class === rhs
+ return false
+ end
+ if rhs.namespace and (rhs.namespace != @namespace)
+ return false
+ end
+ if rhs.name and (rhs.name != @name)
+ return false
+ end
+ true
+ end
+
+ def ==(rhs)
+ (self.class === rhs && @namespace == rhs.namespace && @name == rhs.name)
+ end
+
+ def ===(rhs)
+ (self == rhs)
+ end
+
+ def eql?(rhs)
+ (self == rhs)
+ end
+
+ def hash
+ @namespace.hash ^ @name.hash
+ end
+
+ def to_s
+ "{#{ namespace }}#{ name }"
+ end
+
+ NormalizedNameRegexp = /^\{([^}]*)\}(.*)$/
+ def parse(str)
+ NormalizedNameRegexp =~ str
+ self.new($1, $2)
+ end
+end
+
+
+end
diff --git a/lib/xsd/xmlparser.rb b/lib/xsd/xmlparser.rb
new file mode 100644
index 0000000000..7d6d389261
--- /dev/null
+++ b/lib/xsd/xmlparser.rb
@@ -0,0 +1,72 @@
+=begin
+XSD4R - XML Instance parser library.
+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 'xsd/xmlparser/parser'
+
+
+module XSD
+
+
+module XMLParser
+ def create_parser(host, opt)
+ XSD::XMLParser::Parser.create_parser(host, opt)
+ end
+ module_function :create_parser
+
+ # $1 is necessary.
+ NSParseRegexp = Regexp.new('^xmlns:?(.*)$')
+
+ def filter_ns(ns, attrs)
+ return attrs if attrs.nil? or attrs.empty?
+ newattrs = {}
+ attrs.each do |key, value|
+ if (NSParseRegexp =~ key)
+ # '' means 'default namespace'.
+ tag = $1 || ''
+ ns.assign(value, tag)
+ else
+ newattrs[key] = value
+ end
+ end
+ newattrs
+ end
+ module_function :filter_ns
+end
+
+
+end
+
+
+# Try to load XML processor.
+loaded = false
+[
+ 'xsd/xmlparser/xmlscanner',
+ 'xsd/xmlparser/xmlparser',
+ 'xsd/xmlparser/rexmlparser',
+].each do |lib|
+ begin
+ require lib
+ loaded = true
+ break
+ rescue LoadError
+ end
+end
+unless loaded
+ raise RuntimeError.new("XML processor module not found.")
+end
diff --git a/lib/xsd/xmlparser/parser.rb b/lib/xsd/xmlparser/parser.rb
new file mode 100644
index 0000000000..0c7fd48084
--- /dev/null
+++ b/lib/xsd/xmlparser/parser.rb
@@ -0,0 +1,107 @@
+=begin
+XSD4R - XML Instance parser library.
+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 'xsd/qname'
+require 'xsd/ns'
+require 'xsd/charset'
+
+
+module XSD
+module XMLParser
+
+
+class Parser
+ class ParseError < Error; end
+ class FormatDecodeError < ParseError; end
+ class UnknownElementError < FormatDecodeError; end
+ class UnknownAttributeError < FormatDecodeError; end
+ class UnexpectedElementError < FormatDecodeError; end
+ class ElementConstraintError < FormatDecodeError; end
+
+ @@parser_factory = nil
+
+ def self.factory
+ @@parser_factory
+ end
+
+ def self.create_parser(host, opt = {})
+ @@parser_factory.new(host, opt)
+ end
+
+ def self.add_factory(factory)
+ if $DEBUG
+ puts "Set #{ factory } as XML processor."
+ end
+ @@parser_factory = factory
+ end
+
+public
+
+ attr_accessor :charset
+
+ def initialize(host, opt = {})
+ @host = host
+ @charset = opt[:charset] || 'us-ascii'
+ end
+
+ def parse(string_or_readable)
+ @textbuf = ''
+ prologue
+ do_parse(string_or_readable)
+ epilogue
+ end
+
+private
+
+ def do_parse(string_or_readable)
+ raise NotImplementError.new(
+ 'Method do_parse must be defined in derived class.')
+ end
+
+ def start_element(name, attrs)
+ @host.start_element(name, attrs)
+ end
+
+ def characters(text)
+ @host.characters(text)
+ end
+
+ def end_element(name)
+ @host.end_element(name)
+ end
+
+ def prologue
+ end
+
+ def epilogue
+ end
+
+ def xmldecl_encoding=(charset)
+ if @charset.nil?
+ @charset = charset
+ else
+ # Definition in a stream (like HTTP) has a priority.
+ p "encoding definition: #{ charset } is ignored." if $DEBUG
+ end
+ end
+end
+
+
+end
+end
diff --git a/lib/xsd/xmlparser/rexmlparser.rb b/lib/xsd/xmlparser/rexmlparser.rb
new file mode 100644
index 0000000000..2500d432d8
--- /dev/null
+++ b/lib/xsd/xmlparser/rexmlparser.rb
@@ -0,0 +1,65 @@
+=begin
+XSD4R - REXMLParser XML parser library.
+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 'xsd/xmlparser'
+require 'rexml/streamlistener'
+require 'rexml/document'
+
+
+module XSD
+module XMLParser
+
+
+class REXMLParser < XSD::XMLParser::Parser
+ include REXML::StreamListener
+
+ def do_parse(string_or_readable)
+ source = nil
+ source = REXML::SourceFactory.create_from(string_or_readable)
+ source.encoding = charset if charset
+ # Listener passes a String in utf-8.
+ @charset = 'utf-8'
+ REXML::Document.parse_stream(source, self)
+ end
+
+ def epilogue
+ end
+
+ def tag_start(name, attrs)
+ start_element(name, attrs)
+ end
+
+ def tag_end(name)
+ end_element(name)
+ end
+
+ def text(text)
+ characters(text)
+ end
+
+ def xmldecl(version, encoding, standalone)
+ # Version should be checked.
+ end
+
+ add_factory(self)
+end
+
+
+end
+end
diff --git a/lib/xsd/xmlparser/xmlparser.rb b/lib/xsd/xmlparser/xmlparser.rb
new file mode 100644
index 0000000000..f555b99b26
--- /dev/null
+++ b/lib/xsd/xmlparser/xmlparser.rb
@@ -0,0 +1,61 @@
+=begin
+XSD4R - XMLParser XML 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/xmlparser'
+require 'xml/parser'
+
+
+module XSD
+module XMLParser
+
+
+class XMLParser < XSD::XMLParser::Parser
+ class Listener < XML::Parser
+ begin
+ require 'xml/encoding-ja'
+ include XML::Encoding_ja
+ rescue LoadError
+ # uconv may not be installed.
+ end
+ end
+
+ def do_parse(string_or_readable)
+ # XMLParser passes a String in utf-8.
+ @charset = 'utf-8'
+ @parser = Listener.new
+ @parser.parse(string_or_readable) do |type, name, data|
+ case type
+ when XML::Parser::START_ELEM
+ start_element(name, data)
+ when XML::Parser::END_ELEM
+ end_element(name)
+ when XML::Parser::CDATA
+ characters(data)
+ else
+ raise FormatDecodeError.new("Unexpected XML: #{ type }/#{ name }/#{ data }.")
+ end
+ end
+ end
+
+ add_factory(self)
+end
+
+
+end
+end
diff --git a/lib/xsd/xmlparser/xmlscanner.rb b/lib/xsd/xmlparser/xmlscanner.rb
new file mode 100644
index 0000000000..c10e275b9e
--- /dev/null
+++ b/lib/xsd/xmlparser/xmlscanner.rb
@@ -0,0 +1,158 @@
+=begin
+XSD4R - XMLScan XML parser library.
+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 'xsd/xmlparser'
+require 'xmlscan/scanner'
+
+
+module XSD
+module XMLParser
+
+
+class XMLScanner < XSD::XMLParser::Parser
+ include XMLScan::Visitor
+
+ def do_parse(string_or_readable)
+ @attrs = {}
+ @curattr = nil
+ @scanner = XMLScan::XMLScanner.new(self)
+ @scanner.kcode = ::XSD::Charset.charset_str(charset) if charset
+ @scanner.parse(string_or_readable)
+ end
+
+ def scanner_kcode=(charset)
+ @scanner.kcode = ::XSD::Charset.charset_str(charset) if charset
+ self.xmldecl_encoding = charset
+ end
+
+ ENTITY_REF_MAP = {
+ 'lt' => '<',
+ 'gt' => '>',
+ 'amp' => '&',
+ 'quot' => '"',
+ 'apos' => '\''
+ }
+
+ def parse_error(msg)
+ raise ParseError.new(msg)
+ end
+
+ def wellformed_error(msg)
+ raise NotWellFormedError.new(msg)
+ end
+
+ def valid_error(msg)
+ raise NotValidError.new(msg)
+ end
+
+ def warning(msg)
+ p msg if $DEBUG
+ end
+
+ # def on_xmldecl; end
+
+ def on_xmldecl_version(str)
+ # 1.0 expected.
+ end
+
+ def on_xmldecl_encoding(str)
+ self.scanner_kcode = str
+ end
+
+ # def on_xmldecl_standalone(str); end
+
+ # def on_xmldecl_other(name, value); end
+
+ # def on_xmldecl_end; end
+
+ # def on_doctype(root, pubid, sysid); end
+
+ # def on_prolog_space(str); end
+
+ # def on_comment(str); end
+
+ # def on_pi(target, pi); end
+
+ def on_chardata(str)
+ characters(str)
+ end
+
+ # def on_cdata(str); end
+
+ def on_etag(name)
+ end_element(name)
+ end
+
+ def on_entityref(ref)
+ characters(ENTITY_REF_MAP[ref])
+ end
+
+ def on_charref(code)
+ characters([code].pack('U'))
+ end
+
+ def on_charref_hex(code)
+ on_charref(code)
+ end
+
+ # def on_start_document; end
+
+ # def on_end_document; end
+
+ def on_stag(name)
+ @attrs = {}
+ end
+
+ def on_attribute(name)
+ @attrs[name] = @curattr = ''
+ end
+
+ def on_attr_value(str)
+ @curattr << str
+ end
+
+ def on_attr_entityref(ref)
+ @curattr << ENTITY_REF_MAP[ref]
+ end
+
+ def on_attr_charref(code)
+ @curattr << [code].pack('U')
+ end
+
+ def on_attr_charref_hex(code)
+ on_attr_charref(code)
+ end
+
+ # def on_attribute_end(name); end
+
+ def on_stag_end_empty(name)
+ on_stag_end(name)
+ on_etag(name)
+ end
+
+ def on_stag_end(name)
+ start_element(name, @attrs)
+ end
+
+ add_factory(self)
+end
+
+
+end
+end