From db9445103c082a306ba085f7677da02ea94b8841 Mon Sep 17 00:00:00 2001 From: nahi Date: Wed, 24 Sep 2003 15:18:44 +0000 Subject: * 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 --- ChangeLog | 18 + lib/soap/baseData.rb | 782 +++++ lib/soap/element.rb | 232 ++ lib/soap/encodingstyle/aspDotNetHandler.rb | 232 ++ lib/soap/encodingstyle/handler.rb | 111 + lib/soap/encodingstyle/literalHandler.rb | 218 ++ lib/soap/encodingstyle/soapHandler.rb | 548 ++++ lib/soap/generator.rb | 206 ++ lib/soap/mapping.rb | 21 + lib/soap/mapping/factory.rb | 310 ++ lib/soap/mapping/mapping.rb | 218 ++ lib/soap/mapping/registry.rb | 408 +++ lib/soap/mapping/rubytypeFactory.rb | 437 +++ lib/soap/mapping/typeMap.rb | 52 + lib/soap/mapping/wsdlRegistry.rb | 146 + lib/soap/marshal.rb | 71 + lib/soap/netHttpClient.rb | 101 + lib/soap/parser.rb | 252 ++ lib/soap/processor.rb | 79 + lib/soap/rpc/cgistub.rb | 214 ++ lib/soap/rpc/driver.rb | 189 ++ lib/soap/rpc/element.rb | 278 ++ lib/soap/rpc/proxy.rb | 147 + lib/soap/rpc/router.rb | 176 ++ lib/soap/rpc/rpc.rb | 36 + lib/soap/rpc/soaplet.rb | 167 ++ lib/soap/rpc/standaloneServer.rb | 116 + lib/soap/soap.rb | 112 + lib/soap/streamHandler.rb | 188 ++ lib/soap/wsdlDriver.rb | 490 ++++ lib/wsdl/binding.rb | 76 + lib/wsdl/data.rb | 73 + lib/wsdl/definitions.rb | 233 ++ lib/wsdl/documentation.rb | 43 + lib/wsdl/import.rb | 81 + lib/wsdl/importer.rb | 75 + lib/wsdl/info.rb | 44 + lib/wsdl/message.rb | 65 + lib/wsdl/operation.rb | 144 + lib/wsdl/operationBinding.rb | 91 + lib/wsdl/param.rb | 85 + lib/wsdl/parser.rb | 170 ++ lib/wsdl/part.rb | 63 + lib/wsdl/port.rb | 95 + lib/wsdl/portType.rb | 83 + lib/wsdl/service.rb | 72 + lib/wsdl/soap/address.rb | 51 + lib/wsdl/soap/binding.rb | 59 + lib/wsdl/soap/body.rb | 63 + lib/wsdl/soap/complexType.rb | 96 + lib/wsdl/soap/data.rb | 52 + lib/wsdl/soap/definitions.rb | 130 + lib/wsdl/soap/fault.rb | 63 + lib/wsdl/soap/header.rb | 90 + lib/wsdl/soap/headerfault.rb | 67 + lib/wsdl/soap/operation.rb | 131 + lib/wsdl/types.rb | 54 + lib/wsdl/wsdl.rb | 34 + lib/wsdl/xmlSchema/all.rb | 76 + lib/wsdl/xmlSchema/any.rb | 67 + lib/wsdl/xmlSchema/attribute.rb | 85 + lib/wsdl/xmlSchema/choice.rb | 76 + lib/wsdl/xmlSchema/complexContent.rb | 90 + lib/wsdl/xmlSchema/complexType.rb | 130 + lib/wsdl/xmlSchema/content.rb | 107 + lib/wsdl/xmlSchema/data.rb | 75 + lib/wsdl/xmlSchema/element.rb | 115 + lib/wsdl/xmlSchema/import.rb | 55 + lib/wsdl/xmlSchema/parser.rb | 172 ++ lib/wsdl/xmlSchema/schema.rb | 105 + lib/wsdl/xmlSchema/sequence.rb | 76 + lib/wsdl/xmlSchema/unique.rb | 45 + lib/xsd/charset.rb | 175 ++ lib/xsd/datatypes.rb | 1094 +++++++ lib/xsd/datatypes1999.rb | 31 + lib/xsd/iconvcharset.rb | 44 + lib/xsd/namedelements.rb | 86 + lib/xsd/ns.rb | 141 + lib/xsd/qname.rb | 77 + lib/xsd/xmlparser.rb | 72 + lib/xsd/xmlparser/parser.rb | 107 + lib/xsd/xmlparser/rexmlparser.rb | 65 + lib/xsd/xmlparser/xmlparser.rb | 61 + lib/xsd/xmlparser/xmlscanner.rb | 158 + sample/soap/babelfish.rb | 16 + sample/soap/calc/calc.rb | 17 + sample/soap/calc/calc2.rb | 29 + sample/soap/calc/client.rb | 26 + sample/soap/calc/client2.rb | 29 + sample/soap/calc/httpd.rb | 15 + sample/soap/calc/server.cgi | 15 + sample/soap/calc/server.rb | 17 + sample/soap/calc/server2.rb | 20 + sample/soap/digraph.rb | 43 + sample/soap/exchange/client.rb | 19 + sample/soap/exchange/exchange.rb | 17 + sample/soap/exchange/httpd.rb | 15 + sample/soap/exchange/server.cgi | 14 + sample/soap/exchange/server.rb | 16 + sample/soap/helloworld/hw_c.rb | 6 + sample/soap/helloworld/hw_s.rb | 17 + sample/soap/icd/IICD.rb | 17 + sample/soap/icd/icd.rb | 46 + sample/soap/raa/iRAA.rb | 154 + sample/soap/raa/soap4r.rb | 30 + sample/soap/sampleStruct/client.rb | 16 + sample/soap/sampleStruct/httpd.rb | 15 + sample/soap/sampleStruct/iSampleStruct.rb | 22 + sample/soap/sampleStruct/sampleStruct.rb | 13 + sample/soap/sampleStruct/server.cgi | 14 + sample/soap/sampleStruct/server.rb | 16 + sample/wsdl/amazon/AmazonSearch.rb | 3701 ++++++++++++++++++++++++ sample/wsdl/amazon/AmazonSearchDriver.rb | 445 +++ sample/wsdl/amazon/sampleClient.rb | 37 + sample/wsdl/amazon/wsdlDriver.rb | 48 + sample/wsdl/googleSearch/GoogleSearch.rb | 258 ++ sample/wsdl/googleSearch/GoogleSearchDriver.rb | 101 + sample/wsdl/googleSearch/README | 6 + sample/wsdl/googleSearch/httpd.rb | 15 + sample/wsdl/googleSearch/sampleClient.rb | 56 + sample/wsdl/googleSearch/sjissearch.sh | 3 + sample/wsdl/googleSearch/wsdlDriver.rb | 23 + sample/wsdl/raa/raa.wsdl | 264 ++ sample/wsdl/raa/soap4r.rb | 31 + test/soap/calc/calc.rb | 17 + test/soap/calc/calc2.rb | 29 + test/soap/calc/server.cgi | 15 + test/soap/calc/server.rb | 17 + test/soap/calc/server2.rb | 20 + test/soap/calc/test_calc.rb | 41 + test/soap/calc/test_calc2.rb | 43 + test/soap/calc/test_calc_cgi.rb | 42 + test/soap/helloworld/hw_s.rb | 17 + test/soap/helloworld/test_helloworld.rb | 31 + test/soap/marshal/cmarshal.rb | 142 + test/soap/marshal/test_digraph.rb | 45 + test/soap/marshal/test_marshal.rb | 301 ++ test/soap/marshal/test_struct.rb | 38 + test/soap/test_basetype.rb | 951 ++++++ test/soap/test_soapelement.rb | 114 + test/wsdl/emptycomplextype.wsdl | 78 + test/wsdl/test_emptycomplextype.rb | 14 + test/xsd/test_xmlschemaparser.rb | 14 + test/xsd/test_xsd.rb | 976 +++++++ test/xsd/xmlschema.xml | 8 + 145 files changed, 20938 insertions(+) create mode 100644 lib/soap/baseData.rb create mode 100644 lib/soap/element.rb create mode 100644 lib/soap/encodingstyle/aspDotNetHandler.rb create mode 100644 lib/soap/encodingstyle/handler.rb create mode 100644 lib/soap/encodingstyle/literalHandler.rb create mode 100644 lib/soap/encodingstyle/soapHandler.rb create mode 100644 lib/soap/generator.rb create mode 100644 lib/soap/mapping.rb create mode 100644 lib/soap/mapping/factory.rb create mode 100644 lib/soap/mapping/mapping.rb create mode 100644 lib/soap/mapping/registry.rb create mode 100644 lib/soap/mapping/rubytypeFactory.rb create mode 100644 lib/soap/mapping/typeMap.rb create mode 100644 lib/soap/mapping/wsdlRegistry.rb create mode 100644 lib/soap/marshal.rb create mode 100644 lib/soap/netHttpClient.rb create mode 100644 lib/soap/parser.rb create mode 100644 lib/soap/processor.rb create mode 100644 lib/soap/rpc/cgistub.rb create mode 100644 lib/soap/rpc/driver.rb create mode 100644 lib/soap/rpc/element.rb create mode 100644 lib/soap/rpc/proxy.rb create mode 100644 lib/soap/rpc/router.rb create mode 100644 lib/soap/rpc/rpc.rb create mode 100644 lib/soap/rpc/soaplet.rb create mode 100644 lib/soap/rpc/standaloneServer.rb create mode 100644 lib/soap/soap.rb create mode 100644 lib/soap/streamHandler.rb create mode 100644 lib/soap/wsdlDriver.rb create mode 100644 lib/wsdl/binding.rb create mode 100644 lib/wsdl/data.rb create mode 100644 lib/wsdl/definitions.rb create mode 100644 lib/wsdl/documentation.rb create mode 100644 lib/wsdl/import.rb create mode 100644 lib/wsdl/importer.rb create mode 100644 lib/wsdl/info.rb create mode 100644 lib/wsdl/message.rb create mode 100644 lib/wsdl/operation.rb create mode 100644 lib/wsdl/operationBinding.rb create mode 100644 lib/wsdl/param.rb create mode 100644 lib/wsdl/parser.rb create mode 100644 lib/wsdl/part.rb create mode 100644 lib/wsdl/port.rb create mode 100644 lib/wsdl/portType.rb create mode 100644 lib/wsdl/service.rb create mode 100644 lib/wsdl/soap/address.rb create mode 100644 lib/wsdl/soap/binding.rb create mode 100644 lib/wsdl/soap/body.rb create mode 100644 lib/wsdl/soap/complexType.rb create mode 100644 lib/wsdl/soap/data.rb create mode 100644 lib/wsdl/soap/definitions.rb create mode 100644 lib/wsdl/soap/fault.rb create mode 100644 lib/wsdl/soap/header.rb create mode 100644 lib/wsdl/soap/headerfault.rb create mode 100644 lib/wsdl/soap/operation.rb create mode 100644 lib/wsdl/types.rb create mode 100644 lib/wsdl/wsdl.rb create mode 100644 lib/wsdl/xmlSchema/all.rb create mode 100644 lib/wsdl/xmlSchema/any.rb create mode 100644 lib/wsdl/xmlSchema/attribute.rb create mode 100644 lib/wsdl/xmlSchema/choice.rb create mode 100644 lib/wsdl/xmlSchema/complexContent.rb create mode 100644 lib/wsdl/xmlSchema/complexType.rb create mode 100644 lib/wsdl/xmlSchema/content.rb create mode 100644 lib/wsdl/xmlSchema/data.rb create mode 100644 lib/wsdl/xmlSchema/element.rb create mode 100644 lib/wsdl/xmlSchema/import.rb create mode 100644 lib/wsdl/xmlSchema/parser.rb create mode 100644 lib/wsdl/xmlSchema/schema.rb create mode 100644 lib/wsdl/xmlSchema/sequence.rb create mode 100644 lib/wsdl/xmlSchema/unique.rb create mode 100644 lib/xsd/charset.rb create mode 100644 lib/xsd/datatypes.rb create mode 100644 lib/xsd/datatypes1999.rb create mode 100644 lib/xsd/iconvcharset.rb create mode 100644 lib/xsd/namedelements.rb create mode 100644 lib/xsd/ns.rb create mode 100644 lib/xsd/qname.rb create mode 100644 lib/xsd/xmlparser.rb create mode 100644 lib/xsd/xmlparser/parser.rb create mode 100644 lib/xsd/xmlparser/rexmlparser.rb create mode 100644 lib/xsd/xmlparser/xmlparser.rb create mode 100644 lib/xsd/xmlparser/xmlscanner.rb create mode 100644 sample/soap/babelfish.rb create mode 100644 sample/soap/calc/calc.rb create mode 100644 sample/soap/calc/calc2.rb create mode 100644 sample/soap/calc/client.rb create mode 100644 sample/soap/calc/client2.rb create mode 100644 sample/soap/calc/httpd.rb create mode 100644 sample/soap/calc/server.cgi create mode 100644 sample/soap/calc/server.rb create mode 100644 sample/soap/calc/server2.rb create mode 100644 sample/soap/digraph.rb create mode 100644 sample/soap/exchange/client.rb create mode 100644 sample/soap/exchange/exchange.rb create mode 100644 sample/soap/exchange/httpd.rb create mode 100644 sample/soap/exchange/server.cgi create mode 100644 sample/soap/exchange/server.rb create mode 100644 sample/soap/helloworld/hw_c.rb create mode 100644 sample/soap/helloworld/hw_s.rb create mode 100644 sample/soap/icd/IICD.rb create mode 100644 sample/soap/icd/icd.rb create mode 100644 sample/soap/raa/iRAA.rb create mode 100644 sample/soap/raa/soap4r.rb create mode 100644 sample/soap/sampleStruct/client.rb create mode 100644 sample/soap/sampleStruct/httpd.rb create mode 100644 sample/soap/sampleStruct/iSampleStruct.rb create mode 100644 sample/soap/sampleStruct/sampleStruct.rb create mode 100644 sample/soap/sampleStruct/server.cgi create mode 100644 sample/soap/sampleStruct/server.rb create mode 100644 sample/wsdl/amazon/AmazonSearch.rb create mode 100644 sample/wsdl/amazon/AmazonSearchDriver.rb create mode 100644 sample/wsdl/amazon/sampleClient.rb create mode 100644 sample/wsdl/amazon/wsdlDriver.rb create mode 100644 sample/wsdl/googleSearch/GoogleSearch.rb create mode 100644 sample/wsdl/googleSearch/GoogleSearchDriver.rb create mode 100644 sample/wsdl/googleSearch/README create mode 100644 sample/wsdl/googleSearch/httpd.rb create mode 100644 sample/wsdl/googleSearch/sampleClient.rb create mode 100644 sample/wsdl/googleSearch/sjissearch.sh create mode 100644 sample/wsdl/googleSearch/wsdlDriver.rb create mode 100644 sample/wsdl/raa/raa.wsdl create mode 100644 sample/wsdl/raa/soap4r.rb create mode 100644 test/soap/calc/calc.rb create mode 100644 test/soap/calc/calc2.rb create mode 100644 test/soap/calc/server.cgi create mode 100644 test/soap/calc/server.rb create mode 100644 test/soap/calc/server2.rb create mode 100644 test/soap/calc/test_calc.rb create mode 100644 test/soap/calc/test_calc2.rb create mode 100644 test/soap/calc/test_calc_cgi.rb create mode 100644 test/soap/helloworld/hw_s.rb create mode 100644 test/soap/helloworld/test_helloworld.rb create mode 100644 test/soap/marshal/cmarshal.rb create mode 100644 test/soap/marshal/test_digraph.rb create mode 100644 test/soap/marshal/test_marshal.rb create mode 100644 test/soap/marshal/test_struct.rb create mode 100644 test/soap/test_basetype.rb create mode 100644 test/soap/test_soapelement.rb create mode 100644 test/wsdl/emptycomplextype.wsdl create mode 100644 test/wsdl/test_emptycomplextype.rb create mode 100644 test/xsd/test_xmlschemaparser.rb create mode 100644 test/xsd/test_xsd.rb create mode 100644 test/xsd/xmlschema.xml diff --git a/ChangeLog b/ChangeLog index 3538f1125f..7b60e14f6f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,21 @@ +Mon Sep 25 00:13:15 2003 NAKAMURA, Hiroshi + + * 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. + Wed Sep 24 02:08:11 2003 GOTOU Yuuzou * lib/webrick/httpservlet/cgihandler.rb: conform to mswin32. 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. + # => type Array in SOAP-ENC. + # => 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 }" + else + buf << "" + end + end + + EncodeMap = { + '&' => '&', + '<' => '<', + '>' => '>', + '"' => '"', + '\'' => ''', + "\r" => ' ' + } + 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[] + else + %Q[] + 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 + + + + + + + + +=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 diff --git a/sample/soap/babelfish.rb b/sample/soap/babelfish.rb new file mode 100644 index 0000000000..eb2421449a --- /dev/null +++ b/sample/soap/babelfish.rb @@ -0,0 +1,16 @@ +#!/usr/bin/env ruby + +text = ARGV.shift || 'Hello world.' +lang = ARGV.shift || 'en_fr' + +require 'soap/rpc/driver' + +server = 'http://services.xmethods.net/perl/soaplite.cgi' +InterfaceNS = 'urn:xmethodsBabelFish' +wireDumpDev = nil # STDERR + +drv = SOAP::RPC::Driver.new(server, InterfaceNS) +drv.wiredump_dev = wireDumpDev +drv.add_method_with_soapaction('BabelFish', InterfaceNS + "#BabelFish", 'translationmode', 'sourcedata') + +p drv.BabelFish(lang, text) diff --git a/sample/soap/calc/calc.rb b/sample/soap/calc/calc.rb new file mode 100644 index 0000000000..6bc78803b3 --- /dev/null +++ b/sample/soap/calc/calc.rb @@ -0,0 +1,17 @@ +module CalcService + def self.add(lhs, rhs) + lhs + rhs + end + + def self.sub(lhs, rhs) + lhs - rhs + end + + def self.multi(lhs, rhs) + lhs * rhs + end + + def self.div(lhs, rhs) + lhs / rhs + end +end diff --git a/sample/soap/calc/calc2.rb b/sample/soap/calc/calc2.rb new file mode 100644 index 0000000000..e9cf6bbca7 --- /dev/null +++ b/sample/soap/calc/calc2.rb @@ -0,0 +1,29 @@ +class CalcService2 + def initialize(value = 0) + @value = value + end + + def set(value) + @value = value + end + + def get + @value + end + + def +(rhs) + @value + rhs + end + + def -(rhs) + @value - rhs + end + + def *(rhs) + @value * rhs + end + + def /(rhs) + @value / rhs + end +end diff --git a/sample/soap/calc/client.rb b/sample/soap/calc/client.rb new file mode 100644 index 0000000000..57a4c0ba5b --- /dev/null +++ b/sample/soap/calc/client.rb @@ -0,0 +1,26 @@ +require 'soap/rpc/driver' + +server = ARGV.shift || 'http://localhost:7000/' +# server = 'http://localhost:8808/server.cgi' + +calc = SOAP::RPC::Driver.new(server, 'http://tempuri.org/calcService') +#calc.wiredump_dev = STDERR +calc.add_method('add', 'lhs', 'rhs') +calc.add_method('sub', 'lhs', 'rhs') +calc.add_method('multi', 'lhs', 'rhs') +calc.add_method('div', 'lhs', 'rhs') + +puts 'add: 1 + 2 # => 3' +puts calc.add(1, 2) +puts 'sub: 1.1 - 2.2 # => -1.1' +puts calc.sub(1.1, 2.2) +puts 'multi: 1.1 * 2.2 # => 2.42' +puts calc.multi(1.1, 2.2) +puts 'div: 5 / 2 # => 2' +puts calc.div(5, 2) +puts 'div: 5.0 / 2 # => 2.5' +puts calc.div(5.0, 2) +puts 'div: 1.1 / 0 # => Infinity' +puts calc.div(1.1, 0) +puts 'div: 1 / 0 # => ZeroDivisionError' +puts calc.div(1, 0) diff --git a/sample/soap/calc/client2.rb b/sample/soap/calc/client2.rb new file mode 100644 index 0000000000..2c53f09d42 --- /dev/null +++ b/sample/soap/calc/client2.rb @@ -0,0 +1,29 @@ +require 'soap/rpc/driver' + +server = ARGV.shift || 'http://localhost:7000/' +# server = 'http://localhost:8808/server2.cgi' + +var = SOAP::RPC::Driver.new( server, 'http://tempuri.org/calcService' ) +var.add_method( 'set', 'newValue' ) +var.add_method( 'get' ) +var.add_method_as( '+', 'add', 'rhs' ) +var.add_method_as( '-', 'sub', 'rhs' ) +var.add_method_as( '*', 'multi', 'rhs' ) +var.add_method_as( '/', 'div', 'rhs' ) + +puts 'var.set( 1 )' +puts '# Bare in mind that another client set another value to this service.' +puts '# This is only a sample for proof of concept.' +var.set( 1 ) +puts 'var + 2 # => 1 + 2 = 3' +puts var + 2 +puts 'var - 2.2 # => 1 - 2.2 = -1.2' +puts var - 2.2 +puts 'var * 2.2 # => 1 * 2.2 = 2.2' +puts var * 2.2 +puts 'var / 2 # => 1 / 2 = 0' +puts var / 2 +puts 'var / 2.0 # => 1 / 2.0 = 0.5' +puts var / 2.0 +puts 'var / 0 # => 1 / 0 => ZeroDivisionError' +puts var / 0 diff --git a/sample/soap/calc/httpd.rb b/sample/soap/calc/httpd.rb new file mode 100644 index 0000000000..ee8ab09f50 --- /dev/null +++ b/sample/soap/calc/httpd.rb @@ -0,0 +1,15 @@ +#!/usr/bin/env ruby + +require 'webrick' +require 'getopts' + +getopts "", 'r:', 'p:8808' + +s = WEBrick::HTTPServer.new( + :BindAddress => "0.0.0.0", + :Port => $OPT_p.to_i, + :DocumentRoot => $OPT_r || ".", + :CGIPathEnv => ENV['PATH'] +) +trap(:INT){ s.shutdown } +s.start diff --git a/sample/soap/calc/server.cgi b/sample/soap/calc/server.cgi new file mode 100644 index 0000000000..c4fa687550 --- /dev/null +++ b/sample/soap/calc/server.cgi @@ -0,0 +1,15 @@ +#!/usr/bin/env ruby + +require 'soap/rpc/cgistub' + +class CalcServer < SOAP::RPC::CGIStub + def initialize(*arg) + super + + require 'calc' + servant = CalcService + add_servant(servant, 'http://tempuri.org/calcService') + end +end + +status = CalcServer.new('CalcServer', nil).start diff --git a/sample/soap/calc/server.rb b/sample/soap/calc/server.rb new file mode 100644 index 0000000000..12a3968b5a --- /dev/null +++ b/sample/soap/calc/server.rb @@ -0,0 +1,17 @@ +#!/usr/bin/env ruby + +require 'soap/rpc/standaloneServer' +require 'calc' + +class CalcServer < SOAP::RPC::StandaloneServer + def initialize(*arg) + super + + servant = CalcService + add_servant(servant, 'http://tempuri.org/calcService') + end +end + +if $0 == __FILE__ + status = CalcServer.new('CalcServer', nil, '0.0.0.0', 7000).start +end diff --git a/sample/soap/calc/server2.rb b/sample/soap/calc/server2.rb new file mode 100644 index 0000000000..735721de64 --- /dev/null +++ b/sample/soap/calc/server2.rb @@ -0,0 +1,20 @@ +#!/usr/bin/env ruby + +require 'soap/rpc/standaloneServer' +require 'calc2' + +class CalcServer2 < SOAP::RPC::StandaloneServer + def on_init + servant = CalcService2.new + add_method(servant, 'set', 'newValue') + add_method(servant, 'get') + add_method_as(servant, '+', 'add', 'lhs') + add_method_as(servant, '-', 'sub', 'lhs') + add_method_as(servant, '*', 'multi', 'lhs') + add_method_as(servant, '/', 'div', 'lhs') + end +end + +if $0 == __FILE__ + status = CalcServer2.new('CalcServer', 'http://tempuri.org/calcService', '0.0.0.0', 7000).start +end diff --git a/sample/soap/digraph.rb b/sample/soap/digraph.rb new file mode 100644 index 0000000000..bf4a650cfe --- /dev/null +++ b/sample/soap/digraph.rb @@ -0,0 +1,43 @@ +require 'soap/marshal' + +class Node; include SOAP::Marshallable + attr_reader :first, :second, :str + + def initialize(*init_next) + @first = init_next[0] + @second = init_next[1] + end +end + +n9 = Node.new +n81 = Node.new(n9) +n82 = Node.new(n9) +n7 = Node.new(n81, n82) +n61 = Node.new(n7) +n62 = Node.new(n7) +n5 = Node.new(n61, n62) +n41 = Node.new(n5) +n42 = Node.new(n5) +n3 = Node.new(n41, n42) +n21 = Node.new(n3) +n22 = Node.new(n3) +n1 = Node.new(n21, n22) + +File.open("digraph_marshalled_string.soap", "wb") do |f| + SOAP::Marshal.dump(n1, f) +end + +marshalledString = File.open("digraph_marshalled_string.soap").read + +puts marshalledString + +newnode = SOAP::Marshal.unmarshal(marshalledString) + +puts newnode.inspect + +p newnode.first.first.__id__ +p newnode.second.first.__id__ +p newnode.first.first.first.first.__id__ +p newnode.second.first.second.first.__id__ + +File.unlink("digraph_marshalled_string.soap") diff --git a/sample/soap/exchange/client.rb b/sample/soap/exchange/client.rb new file mode 100644 index 0000000000..2aa277afef --- /dev/null +++ b/sample/soap/exchange/client.rb @@ -0,0 +1,19 @@ +#!/usr/bin/env ruby + +require "soap/rpc/driver" + +ExchangeServiceNamespace = 'http://tempuri.org/exchangeService' + +server = ARGV.shift || "http://localhost:7000/" +# server = "http://localhost:8808/server.cgi" + +logger = nil +wiredump_dev = nil +# logger = Logger.new(STDERR) +# wiredump_dev = STDERR + +drv = SOAP::RPC::Driver.new(server, ExchangeServiceNamespace) +drv.wiredump_dev = wiredump_dev +drv.add_method("rate", "country1", "country2") + +p drv.rate("USA", "Japan") diff --git a/sample/soap/exchange/exchange.rb b/sample/soap/exchange/exchange.rb new file mode 100644 index 0000000000..00f930deb8 --- /dev/null +++ b/sample/soap/exchange/exchange.rb @@ -0,0 +1,17 @@ +require 'soap/rpc/driver' + +ExchangeServiceNamespace = 'http://tempuri.org/exchangeService' + +class Exchange + ForeignServer = "http://services.xmethods.net/soap" + Namespace = "urn:xmethods-CurrencyExchange" + + def initialize + @drv = SOAP::RPC::Driver.new(ForeignServer, Namespace) + @drv.add_method("getRate", "country1", "country2") + end + + def rate(country1, country2) + return @drv.getRate(country1, country2) + end +end diff --git a/sample/soap/exchange/httpd.rb b/sample/soap/exchange/httpd.rb new file mode 100644 index 0000000000..ee8ab09f50 --- /dev/null +++ b/sample/soap/exchange/httpd.rb @@ -0,0 +1,15 @@ +#!/usr/bin/env ruby + +require 'webrick' +require 'getopts' + +getopts "", 'r:', 'p:8808' + +s = WEBrick::HTTPServer.new( + :BindAddress => "0.0.0.0", + :Port => $OPT_p.to_i, + :DocumentRoot => $OPT_r || ".", + :CGIPathEnv => ENV['PATH'] +) +trap(:INT){ s.shutdown } +s.start diff --git a/sample/soap/exchange/server.cgi b/sample/soap/exchange/server.cgi new file mode 100644 index 0000000000..16bc85a042 --- /dev/null +++ b/sample/soap/exchange/server.cgi @@ -0,0 +1,14 @@ +#!/usr/local/bin/ruby + +require 'soap/rpc/cgistub' +require 'exchange' + +class ExchangeServer < SOAP::RPC::CGIStub + def initialize(*arg) + super + servant = Exchange.new + add_servant(servant) + end +end + +status = ExchangeServer.new('SampleStructServer', ExchangeServiceNamespace).start diff --git a/sample/soap/exchange/server.rb b/sample/soap/exchange/server.rb new file mode 100644 index 0000000000..d510d54a76 --- /dev/null +++ b/sample/soap/exchange/server.rb @@ -0,0 +1,16 @@ +#!/usr/bin/env ruby + +require 'soap/rpc/standaloneServer' +require 'exchange' + +class ExchangeServer < SOAP::RPC::StandaloneServer + def initialize(*arg) + super + servant = Exchange.new + add_servant(servant) + end +end + +if $0 == __FILE__ + status = ExchangeServer.new('SampleStructServer', ExchangeServiceNamespace, '0.0.0.0', 7000).start +end diff --git a/sample/soap/helloworld/hw_c.rb b/sample/soap/helloworld/hw_c.rb new file mode 100644 index 0000000000..253d0a409b --- /dev/null +++ b/sample/soap/helloworld/hw_c.rb @@ -0,0 +1,6 @@ +require 'soap/rpc/driver' + +s = SOAP::RPC::Driver.new('http://localhost:2000/', 'urn:hws') +s.add_method("hello_world", "from") + +p s.hello_world(self.to_s) diff --git a/sample/soap/helloworld/hw_s.rb b/sample/soap/helloworld/hw_s.rb new file mode 100644 index 0000000000..b917f72fc0 --- /dev/null +++ b/sample/soap/helloworld/hw_s.rb @@ -0,0 +1,17 @@ +require 'soap/rpc/standaloneServer' + +class HelloWorldServer < SOAP::RPC::StandaloneServer + def on_init + @log.level = Logger::Severity::DEBUG + add_method(self, 'hello_world', 'from') + end + + def hello_world(from) + "Hello World, from #{ from }" + end +end + +if $0 == __FILE__ + server = HelloWorldServer.new('hws', 'urn:hws', '0.0.0.0', 2000) + server.start +end diff --git a/sample/soap/icd/IICD.rb b/sample/soap/icd/IICD.rb new file mode 100644 index 0000000000..3b1fa9b32c --- /dev/null +++ b/sample/soap/icd/IICD.rb @@ -0,0 +1,17 @@ +module IICD + # All methods in a single namespace?! + InterfaceNS = 'http://www.iwebmethod.net' + + Methods = [ + ['SearchWord', 'query', 'partial'], + ['GetItemById', 'id'], + ['EnumWords'], + ['FullTextSearch', 'query'], + ] + + def IICD.add_method(drv) + Methods.each do |method, *param| + drv.add_method_with_soapaction(method, InterfaceNS + "/#{ method }", *param ) + end + end +end diff --git a/sample/soap/icd/icd.rb b/sample/soap/icd/icd.rb new file mode 100644 index 0000000000..6e1e51c996 --- /dev/null +++ b/sample/soap/icd/icd.rb @@ -0,0 +1,46 @@ +#!/usr/bin/env ruby + +$KCODE = 'SJIS' + +require 'soap/rpc/driver' +require 'IICD'; include IICD + +server = 'http://www.iwebmethod.net/icd1.0/icd.asmx' +wiredump_dev = nil # STDERR + +icd = SOAP::RPC::Driver.new(server, IICD::InterfaceNS) +icd.wiredump_dev = wiredump_dev +icd.default_encodingstyle = SOAP::EncodingStyle::ASPDotNetHandler::Namespace +IICD::add_method(icd) + +puts "ƒL[ƒ[ƒh: 'microsoft'‚ÅŒ©o‚µŒŸõ" +result = icd.SearchWord('microsoft', true) + +id = nil +result.WORD.each do |word| + puts "Title: " << word.title + puts "Id: " << word.id + puts "English: " << word.english + puts "Japanese: " << word.japanese + puts "----" + id = word.id +end + +item = icd.GetItemById(id) +puts +puts +puts "Title: " << item.word.title +puts "ˆÓ–¡: " << item.meaning + +#p icd.EnumWords + +puts +puts +puts "ƒL[ƒ[ƒh: 'IBM'‚Å‘S•¶ŒŸõ" +icd.FullTextSearch("IBM").WORD.each do |word| + puts "Title: " << word.title + puts "Id: " << word.id + puts "English: " << word.english + puts "Japanese: " << word.japanese + puts "----" +end diff --git a/sample/soap/raa/iRAA.rb b/sample/soap/raa/iRAA.rb new file mode 100644 index 0000000000..2b188fb887 --- /dev/null +++ b/sample/soap/raa/iRAA.rb @@ -0,0 +1,154 @@ +require 'soap/mapping' + + +module RAA; extend SOAP + + +InterfaceNS = "http://www.ruby-lang.org/xmlns/soap/interface/RAA/0.0.2/" +MappingRegistry = SOAP::Mapping::Registry.new + +Methods = [ + ['getAllListings', ['retval', 'return']], + ['getProductTree', ['retval', 'return']], + ['getInfoFromCategory', ['in', 'category'], [ 'retval', 'return']], + ['getModifiedInfoSince', ['in', 'time'], [ 'retval', 'return']], + ['getInfoFromName', ['in', 'name'], ['retval', 'return']], +] + + +class Category + include SOAP::Marshallable + + @@schema_type = 'Category' + @@schema_ns = InterfaceNS + + attr_reader :major, :minor + + def initialize(major, minor = nil) + @major = major + @minor = minor + end + + def to_s + "#{ @major }/#{ @minor }" + end + + def ==(rhs) + if @major != rhs.major + false + elsif !@minor or !rhs.minor + true + else + @minor == rhs.minor + end + end +end + +MappingRegistry.set( + ::RAA::Category, + ::SOAP::SOAPStruct, + ::SOAP::Mapping::Registry::TypedStructFactory, + { :type => XSD::QName.new(InterfaceNS, "Category") } +) + +class Product + include SOAP::Marshallable + + @@schema_type = 'Product' + @@schema_ns = InterfaceNS + + attr_reader :id, :name + attr_accessor :short_description, :version, :status, :homepage, :download, :license, :description + + def initialize(name, short_description = nil, version = nil, status = nil, homepage = nil, download = nil, license = nil, description = nil) + @name = name + @short_description = short_description + @version = version + @status = status + @homepage = homepage + @download = download + @license = license + @description = description + end +end + +MappingRegistry.set( + ::RAA::Product, + ::SOAP::SOAPStruct, + ::SOAP::Mapping::Registry::TypedStructFactory, + { :type => XSD::QName.new(InterfaceNS, "Product") } +) + +class Owner + include SOAP::Marshallable + + @@schema_type = 'Owner' + @@schema_ns = InterfaceNS + + attr_reader :id + attr_accessor :email, :name + + def initialize(email, name) + @email = email + @name = name + @id = "#{ @email }-#{ @name }" + end +end + +MappingRegistry.set( + ::RAA::Owner, + ::SOAP::SOAPStruct, + ::SOAP::Mapping::Registry::TypedStructFactory, + { :type => XSD::QName.new(InterfaceNS, "Owner") } +) + +class Info + include SOAP::Marshallable + + @@schema_type = 'Info' + @@schema_ns = InterfaceNS + + attr_accessor :category, :product, :owner, :updated, :created + + def initialize(category = nil, product = nil, owner = nil, updated = nil, created = nil) + @category = category + @product = product + @owner = owner + @updated = updated + @created = created + end + + def <=>(rhs) + @updated <=> rhs.updated + end + + def eql?(rhs) + @product.name == rhs.product.name + end +end + +MappingRegistry.set( + ::RAA::Info, + ::SOAP::SOAPStruct, + ::SOAP::Mapping::Registry::TypedStructFactory, + { :type => XSD::QName.new(InterfaceNS, "Info") } +) + +class StringArray < Array; end +MappingRegistry.set( + ::RAA::StringArray, + ::SOAP::SOAPArray, + ::SOAP::Mapping::Registry::TypedArrayFactory, + { :type => XSD::XSDString::Type } +) + +class InfoArray < Array; end +MappingRegistry.set( + ::RAA::InfoArray, + ::SOAP::SOAPArray, + ::SOAP::Mapping::Registry::TypedArrayFactory, + { :type => XSD::QName.new(InterfaceNS, 'Info') } +) + + +end diff --git a/sample/soap/raa/soap4r.rb b/sample/soap/raa/soap4r.rb new file mode 100644 index 0000000000..b93d1e7dbe --- /dev/null +++ b/sample/soap/raa/soap4r.rb @@ -0,0 +1,30 @@ +#!/usr/bin/env ruby + +require 'iRAA' +require 'soap/rpc/driver' + + +server = ARGV.shift || 'http://raa.ruby-lang.org/soap/1.0.2/' + +raa = SOAP::RPC::Driver.new(server, RAA::InterfaceNS) +raa.mapping_registry = RAA::MappingRegistry +RAA::Methods.each do |name, *params| + raa.add_method(name, params) +end +# raa.wiredump_dev = STDOUT + +p raa.getAllListings().sort + +p raa.getProductTree() + +p raa.getInfoFromCategory(RAA::Category.new("Library", "XML")) + +t = Time.at(Time.now.to_i - 24 * 3600) +p raa.getModifiedInfoSince(t) + +p raa.getModifiedInfoSince(DateTime.new(t.year, t.mon, t.mday, t.hour, t.min, t.sec)) + +o = raa.getInfoFromName("SOAP4R") +p o.class +p o.owner.name +p o diff --git a/sample/soap/sampleStruct/client.rb b/sample/soap/sampleStruct/client.rb new file mode 100644 index 0000000000..b55c7fdfc5 --- /dev/null +++ b/sample/soap/sampleStruct/client.rb @@ -0,0 +1,16 @@ +require 'soap/rpc/driver' + +require 'iSampleStruct' + +server = ARGV.shift || 'http://localhost:7000/' +# server = 'http://localhost:8808/server.cgi' + +drv = SOAP::RPC::Driver.new(server, SampleStructServiceNamespace) +drv.wiredump_dev = STDERR +drv.add_method('hi', 'sampleStruct') + +o1 = SampleStruct.new +puts "Sending struct: #{ o1.inspect }" +puts +o2 = drv.hi(o1) +puts "Received (wrapped): #{ o2.inspect }" diff --git a/sample/soap/sampleStruct/httpd.rb b/sample/soap/sampleStruct/httpd.rb new file mode 100644 index 0000000000..ee8ab09f50 --- /dev/null +++ b/sample/soap/sampleStruct/httpd.rb @@ -0,0 +1,15 @@ +#!/usr/bin/env ruby + +require 'webrick' +require 'getopts' + +getopts "", 'r:', 'p:8808' + +s = WEBrick::HTTPServer.new( + :BindAddress => "0.0.0.0", + :Port => $OPT_p.to_i, + :DocumentRoot => $OPT_r || ".", + :CGIPathEnv => ENV['PATH'] +) +trap(:INT){ s.shutdown } +s.start diff --git a/sample/soap/sampleStruct/iSampleStruct.rb b/sample/soap/sampleStruct/iSampleStruct.rb new file mode 100644 index 0000000000..399ea52eb8 --- /dev/null +++ b/sample/soap/sampleStruct/iSampleStruct.rb @@ -0,0 +1,22 @@ +require 'soap/mapping' + +SampleStructServiceNamespace = 'http://tempuri.org/sampleStructService' + +class SampleStruct; include SOAP::Marshallable + attr_accessor :sampleArray + attr_accessor :date + + def initialize + @sampleArray = SampleArray[ "cyclic", self ] + @date = DateTime.now + end + + def wrap( rhs ) + @sampleArray = SampleArray[ "wrap", rhs.dup ] + @date = DateTime.now + self + end +end + +class SampleArray < Array; include SOAP::Marshallable +end diff --git a/sample/soap/sampleStruct/sampleStruct.rb b/sample/soap/sampleStruct/sampleStruct.rb new file mode 100644 index 0000000000..394c1bff09 --- /dev/null +++ b/sample/soap/sampleStruct/sampleStruct.rb @@ -0,0 +1,13 @@ +require 'iSampleStruct' + +class SampleStructService + def hi(struct) + ack = SampleStruct.new + ack.wrap(struct) + ack + end +end + +if __FILE__ == $0 + p SampleStructService.new.hi(SampleStruct.new) +end diff --git a/sample/soap/sampleStruct/server.cgi b/sample/soap/sampleStruct/server.cgi new file mode 100644 index 0000000000..42751386a0 --- /dev/null +++ b/sample/soap/sampleStruct/server.cgi @@ -0,0 +1,14 @@ +#!/usr/local/bin/ruby + +require 'soap/rpc/cgistub' +require 'sampleStruct' + +class SampleStructServer < SOAP::RPC::CGIStub + def initialize(*arg) + super + servant = SampleStructService.new + add_servant(servant) + end +end + +status = SampleStructServer.new('SampleStructServer', SampleStructServiceNamespace).start diff --git a/sample/soap/sampleStruct/server.rb b/sample/soap/sampleStruct/server.rb new file mode 100644 index 0000000000..3caa31a052 --- /dev/null +++ b/sample/soap/sampleStruct/server.rb @@ -0,0 +1,16 @@ +#!/usr/bin/env ruby + +require 'soap/rpc/standaloneServer' +require 'sampleStruct' + +class SampleStructServer < SOAP::RPC::StandaloneServer + def initialize(*arg) + super + servant = SampleStructService.new + add_servant(servant) + end +end + +if $0 == __FILE__ + status = SampleStructServer.new('SampleStructServer', SampleStructServiceNamespace, '0.0.0.0', 7000).start +end diff --git a/sample/wsdl/amazon/AmazonSearch.rb b/sample/wsdl/amazon/AmazonSearch.rb new file mode 100644 index 0000000000..29397c2d56 --- /dev/null +++ b/sample/wsdl/amazon/AmazonSearch.rb @@ -0,0 +1,3701 @@ +# http://soap.amazon.com +class ProductLineArray < Array + # Contents type should be dumped here... + @@schema_type = "ProductLineArray" + @@schema_ns = "http://soap.amazon.com" +end + +# http://soap.amazon.com +class ProductLine + @@schema_type = "ProductLine" + @@schema_ns = "http://soap.amazon.com" + + def Mode + @mode + end + + def Mode=(value) + @mode = value + end + + def ProductInfo + @productInfo + end + + def ProductInfo=(value) + @productInfo = value + end + + def initialize(mode = nil, + productInfo = nil) + @mode = mode + @productInfo = productInfo + end +end + +# http://soap.amazon.com +class ProductInfo + @@schema_type = "ProductInfo" + @@schema_ns = "http://soap.amazon.com" + + def TotalResults + @totalResults + end + + def TotalResults=(value) + @totalResults = value + end + + def TotalPages + @totalPages + end + + def TotalPages=(value) + @totalPages = value + end + + def ListName + @listName + end + + def ListName=(value) + @listName = value + end + + def Details + @details + end + + def Details=(value) + @details = value + end + + def initialize(totalResults = nil, + totalPages = nil, + listName = nil, + details = nil) + @totalResults = totalResults + @totalPages = totalPages + @listName = listName + @details = details + end +end + +# http://soap.amazon.com +class DetailsArray < Array + # Contents type should be dumped here... + @@schema_type = "DetailsArray" + @@schema_ns = "http://soap.amazon.com" +end + +# http://soap.amazon.com +class Details + @@schema_type = "Details" + @@schema_ns = "http://soap.amazon.com" + + def Url + @url + end + + def Url=(value) + @url = value + end + + def Asin + @asin + end + + def Asin=(value) + @asin = value + end + + def ProductName + @productName + end + + def ProductName=(value) + @productName = value + end + + def Catalog + @catalog + end + + def Catalog=(value) + @catalog = value + end + + def KeyPhrases + @keyPhrases + end + + def KeyPhrases=(value) + @keyPhrases = value + end + + def Artists + @artists + end + + def Artists=(value) + @artists = value + end + + def Authors + @authors + end + + def Authors=(value) + @authors = value + end + + def Mpn + @mpn + end + + def Mpn=(value) + @mpn = value + end + + def Starring + @starring + end + + def Starring=(value) + @starring = value + end + + def Directors + @directors + end + + def Directors=(value) + @directors = value + end + + def TheatricalReleaseDate + @theatricalReleaseDate + end + + def TheatricalReleaseDate=(value) + @theatricalReleaseDate = value + end + + def ReleaseDate + @releaseDate + end + + def ReleaseDate=(value) + @releaseDate = value + end + + def Manufacturer + @manufacturer + end + + def Manufacturer=(value) + @manufacturer = value + end + + def Distributor + @distributor + end + + def Distributor=(value) + @distributor = value + end + + def ImageUrlSmall + @imageUrlSmall + end + + def ImageUrlSmall=(value) + @imageUrlSmall = value + end + + def ImageUrlMedium + @imageUrlMedium + end + + def ImageUrlMedium=(value) + @imageUrlMedium = value + end + + def ImageUrlLarge + @imageUrlLarge + end + + def ImageUrlLarge=(value) + @imageUrlLarge = value + end + + def ListPrice + @listPrice + end + + def ListPrice=(value) + @listPrice = value + end + + def OurPrice + @ourPrice + end + + def OurPrice=(value) + @ourPrice = value + end + + def UsedPrice + @usedPrice + end + + def UsedPrice=(value) + @usedPrice = value + end + + def RefurbishedPrice + @refurbishedPrice + end + + def RefurbishedPrice=(value) + @refurbishedPrice = value + end + + def CollectiblePrice + @collectiblePrice + end + + def CollectiblePrice=(value) + @collectiblePrice = value + end + + def ThirdPartyNewPrice + @thirdPartyNewPrice + end + + def ThirdPartyNewPrice=(value) + @thirdPartyNewPrice = value + end + + def NumberOfOfferings + @numberOfOfferings + end + + def NumberOfOfferings=(value) + @numberOfOfferings = value + end + + def ThirdPartyNewCount + @thirdPartyNewCount + end + + def ThirdPartyNewCount=(value) + @thirdPartyNewCount = value + end + + def UsedCount + @usedCount + end + + def UsedCount=(value) + @usedCount = value + end + + def CollectibleCount + @collectibleCount + end + + def CollectibleCount=(value) + @collectibleCount = value + end + + def RefurbishedCount + @refurbishedCount + end + + def RefurbishedCount=(value) + @refurbishedCount = value + end + + def ThirdPartyProductInfo + @thirdPartyProductInfo + end + + def ThirdPartyProductInfo=(value) + @thirdPartyProductInfo = value + end + + def SalesRank + @salesRank + end + + def SalesRank=(value) + @salesRank = value + end + + def BrowseList + @browseList + end + + def BrowseList=(value) + @browseList = value + end + + def Media + @media + end + + def Media=(value) + @media = value + end + + def ReadingLevel + @readingLevel + end + + def ReadingLevel=(value) + @readingLevel = value + end + + def Publisher + @publisher + end + + def Publisher=(value) + @publisher = value + end + + def NumMedia + @numMedia + end + + def NumMedia=(value) + @numMedia = value + end + + def Isbn + @isbn + end + + def Isbn=(value) + @isbn = value + end + + def Features + @features + end + + def Features=(value) + @features = value + end + + def MpaaRating + @mpaaRating + end + + def MpaaRating=(value) + @mpaaRating = value + end + + def EsrbRating + @esrbRating + end + + def EsrbRating=(value) + @esrbRating = value + end + + def AgeGroup + @ageGroup + end + + def AgeGroup=(value) + @ageGroup = value + end + + def Availability + @availability + end + + def Availability=(value) + @availability = value + end + + def Upc + @upc + end + + def Upc=(value) + @upc = value + end + + def Tracks + @tracks + end + + def Tracks=(value) + @tracks = value + end + + def Accessories + @accessories + end + + def Accessories=(value) + @accessories = value + end + + def Platforms + @platforms + end + + def Platforms=(value) + @platforms = value + end + + def Encoding + @encoding + end + + def Encoding=(value) + @encoding = value + end + + def Reviews + @reviews + end + + def Reviews=(value) + @reviews = value + end + + def SimilarProducts + @similarProducts + end + + def SimilarProducts=(value) + @similarProducts = value + end + + def Lists + @lists + end + + def Lists=(value) + @lists = value + end + + def Status + @status + end + + def Status=(value) + @status = value + end + + def initialize(url = nil, + asin = nil, + productName = nil, + catalog = nil, + keyPhrases = nil, + artists = nil, + authors = nil, + mpn = nil, + starring = nil, + directors = nil, + theatricalReleaseDate = nil, + releaseDate = nil, + manufacturer = nil, + distributor = nil, + imageUrlSmall = nil, + imageUrlMedium = nil, + imageUrlLarge = nil, + listPrice = nil, + ourPrice = nil, + usedPrice = nil, + refurbishedPrice = nil, + collectiblePrice = nil, + thirdPartyNewPrice = nil, + numberOfOfferings = nil, + thirdPartyNewCount = nil, + usedCount = nil, + collectibleCount = nil, + refurbishedCount = nil, + thirdPartyProductInfo = nil, + salesRank = nil, + browseList = nil, + media = nil, + readingLevel = nil, + publisher = nil, + numMedia = nil, + isbn = nil, + features = nil, + mpaaRating = nil, + esrbRating = nil, + ageGroup = nil, + availability = nil, + upc = nil, + tracks = nil, + accessories = nil, + platforms = nil, + encoding = nil, + reviews = nil, + similarProducts = nil, + lists = nil, + status = nil) + @url = url + @asin = asin + @productName = productName + @catalog = catalog + @keyPhrases = keyPhrases + @artists = artists + @authors = authors + @mpn = mpn + @starring = starring + @directors = directors + @theatricalReleaseDate = theatricalReleaseDate + @releaseDate = releaseDate + @manufacturer = manufacturer + @distributor = distributor + @imageUrlSmall = imageUrlSmall + @imageUrlMedium = imageUrlMedium + @imageUrlLarge = imageUrlLarge + @listPrice = listPrice + @ourPrice = ourPrice + @usedPrice = usedPrice + @refurbishedPrice = refurbishedPrice + @collectiblePrice = collectiblePrice + @thirdPartyNewPrice = thirdPartyNewPrice + @numberOfOfferings = numberOfOfferings + @thirdPartyNewCount = thirdPartyNewCount + @usedCount = usedCount + @collectibleCount = collectibleCount + @refurbishedCount = refurbishedCount + @thirdPartyProductInfo = thirdPartyProductInfo + @salesRank = salesRank + @browseList = browseList + @media = media + @readingLevel = readingLevel + @publisher = publisher + @numMedia = numMedia + @isbn = isbn + @features = features + @mpaaRating = mpaaRating + @esrbRating = esrbRating + @ageGroup = ageGroup + @availability = availability + @upc = upc + @tracks = tracks + @accessories = accessories + @platforms = platforms + @encoding = encoding + @reviews = reviews + @similarProducts = similarProducts + @lists = lists + @status = status + end +end + +# http://soap.amazon.com +class KeyPhraseArray < Array + # Contents type should be dumped here... + @@schema_type = "KeyPhraseArray" + @@schema_ns = "http://soap.amazon.com" +end + +# http://soap.amazon.com +class KeyPhrase + @@schema_type = "KeyPhrase" + @@schema_ns = "http://soap.amazon.com" + + def KeyPhrase + @keyPhrase + end + + def KeyPhrase=(value) + @keyPhrase = value + end + + def Type + @type + end + + def Type=(value) + @type = value + end + + def initialize(keyPhrase = nil, + type = nil) + @keyPhrase = keyPhrase + @type = type + end +end + +# http://soap.amazon.com +class ArtistArray < Array + # Contents type should be dumped here... + @@schema_type = "ArtistArray" + @@schema_ns = "http://soap.amazon.com" +end + +# http://soap.amazon.com +class AuthorArray < Array + # Contents type should be dumped here... + @@schema_type = "AuthorArray" + @@schema_ns = "http://soap.amazon.com" +end + +# http://soap.amazon.com +class StarringArray < Array + # Contents type should be dumped here... + @@schema_type = "StarringArray" + @@schema_ns = "http://soap.amazon.com" +end + +# http://soap.amazon.com +class DirectorArray < Array + # Contents type should be dumped here... + @@schema_type = "DirectorArray" + @@schema_ns = "http://soap.amazon.com" +end + +# http://soap.amazon.com +class BrowseNodeArray < Array + # Contents type should be dumped here... + @@schema_type = "BrowseNodeArray" + @@schema_ns = "http://soap.amazon.com" +end + +# http://soap.amazon.com +class BrowseNode + @@schema_type = "BrowseNode" + @@schema_ns = "http://soap.amazon.com" + + def BrowseId + @browseId + end + + def BrowseId=(value) + @browseId = value + end + + def BrowseName + @browseName + end + + def BrowseName=(value) + @browseName = value + end + + def initialize(browseId = nil, + browseName = nil) + @browseId = browseId + @browseName = browseName + end +end + +# http://soap.amazon.com +class FeaturesArray < Array + # Contents type should be dumped here... + @@schema_type = "FeaturesArray" + @@schema_ns = "http://soap.amazon.com" +end + +# http://soap.amazon.com +class TrackArray < Array + # Contents type should be dumped here... + @@schema_type = "TrackArray" + @@schema_ns = "http://soap.amazon.com" +end + +# http://soap.amazon.com +class Track + @@schema_type = "Track" + @@schema_ns = "http://soap.amazon.com" + + def TrackName + @trackName + end + + def TrackName=(value) + @trackName = value + end + + def ByArtist + @byArtist + end + + def ByArtist=(value) + @byArtist = value + end + + def initialize(trackName = nil, + byArtist = nil) + @trackName = trackName + @byArtist = byArtist + end +end + +# http://soap.amazon.com +class AccessoryArray < Array + # Contents type should be dumped here... + @@schema_type = "AccessoryArray" + @@schema_ns = "http://soap.amazon.com" +end + +# http://soap.amazon.com +class PlatformArray < Array + # Contents type should be dumped here... + @@schema_type = "PlatformArray" + @@schema_ns = "http://soap.amazon.com" +end + +# http://soap.amazon.com +class Reviews + @@schema_type = "Reviews" + @@schema_ns = "http://soap.amazon.com" + + def AvgCustomerRating + @avgCustomerRating + end + + def AvgCustomerRating=(value) + @avgCustomerRating = value + end + + def TotalCustomerReviews + @totalCustomerReviews + end + + def TotalCustomerReviews=(value) + @totalCustomerReviews = value + end + + def CustomerReviews + @customerReviews + end + + def CustomerReviews=(value) + @customerReviews = value + end + + def initialize(avgCustomerRating = nil, + totalCustomerReviews = nil, + customerReviews = nil) + @avgCustomerRating = avgCustomerRating + @totalCustomerReviews = totalCustomerReviews + @customerReviews = customerReviews + end +end + +# http://soap.amazon.com +class CustomerReviewArray < Array + # Contents type should be dumped here... + @@schema_type = "CustomerReviewArray" + @@schema_ns = "http://soap.amazon.com" +end + +# http://soap.amazon.com +class CustomerReview + @@schema_type = "CustomerReview" + @@schema_ns = "http://soap.amazon.com" + + def Rating + @rating + end + + def Rating=(value) + @rating = value + end + + def Summary + @summary + end + + def Summary=(value) + @summary = value + end + + def Comment + @comment + end + + def Comment=(value) + @comment = value + end + + def initialize(rating = nil, + summary = nil, + comment = nil) + @rating = rating + @summary = summary + @comment = comment + end +end + +# http://soap.amazon.com +class SimilarProductsArray < Array + # Contents type should be dumped here... + @@schema_type = "SimilarProductsArray" + @@schema_ns = "http://soap.amazon.com" +end + +# http://soap.amazon.com +class ListArray < Array + # Contents type should be dumped here... + @@schema_type = "ListArray" + @@schema_ns = "http://soap.amazon.com" +end + +# http://soap.amazon.com +class MarketplaceSearch + @@schema_type = "MarketplaceSearch" + @@schema_ns = "http://soap.amazon.com" + + def MarketplaceSearchDetails + @marketplaceSearchDetails + end + + def MarketplaceSearchDetails=(value) + @marketplaceSearchDetails = value + end + + def initialize(marketplaceSearchDetails = nil) + @marketplaceSearchDetails = marketplaceSearchDetails + end +end + +# http://soap.amazon.com +class SellerProfile + @@schema_type = "SellerProfile" + @@schema_ns = "http://soap.amazon.com" + + def SellerProfileDetails + @sellerProfileDetails + end + + def SellerProfileDetails=(value) + @sellerProfileDetails = value + end + + def initialize(sellerProfileDetails = nil) + @sellerProfileDetails = sellerProfileDetails + end +end + +# http://soap.amazon.com +class SellerSearch + @@schema_type = "SellerSearch" + @@schema_ns = "http://soap.amazon.com" + + def SellerSearchDetails + @sellerSearchDetails + end + + def SellerSearchDetails=(value) + @sellerSearchDetails = value + end + + def initialize(sellerSearchDetails = nil) + @sellerSearchDetails = sellerSearchDetails + end +end + +# http://soap.amazon.com +class MarketplaceSearchDetails + @@schema_type = "MarketplaceSearchDetails" + @@schema_ns = "http://soap.amazon.com" + + def NumberOfOpenListings + @numberOfOpenListings + end + + def NumberOfOpenListings=(value) + @numberOfOpenListings = value + end + + def ListingProductInfo + @listingProductInfo + end + + def ListingProductInfo=(value) + @listingProductInfo = value + end + + def initialize(numberOfOpenListings = nil, + listingProductInfo = nil) + @numberOfOpenListings = numberOfOpenListings + @listingProductInfo = listingProductInfo + end +end + +# http://soap.amazon.com +class MarketplaceSearchDetailsArray < Array + # Contents type should be dumped here... + @@schema_type = "MarketplaceSearchDetailsArray" + @@schema_ns = "http://soap.amazon.com" +end + +# http://soap.amazon.com +class SellerProfileDetails + @@schema_type = "SellerProfileDetails" + @@schema_ns = "http://soap.amazon.com" + + def SellerNickname + @sellerNickname + end + + def SellerNickname=(value) + @sellerNickname = value + end + + def OverallFeedbackRating + @overallFeedbackRating + end + + def OverallFeedbackRating=(value) + @overallFeedbackRating = value + end + + def NumberOfFeedback + @numberOfFeedback + end + + def NumberOfFeedback=(value) + @numberOfFeedback = value + end + + def NumberOfCanceledBids + @numberOfCanceledBids + end + + def NumberOfCanceledBids=(value) + @numberOfCanceledBids = value + end + + def NumberOfCanceledAuctions + @numberOfCanceledAuctions + end + + def NumberOfCanceledAuctions=(value) + @numberOfCanceledAuctions = value + end + + def StoreId + @storeId + end + + def StoreId=(value) + @storeId = value + end + + def StoreName + @storeName + end + + def StoreName=(value) + @storeName = value + end + + def SellerFeedback + @sellerFeedback + end + + def SellerFeedback=(value) + @sellerFeedback = value + end + + def initialize(sellerNickname = nil, + overallFeedbackRating = nil, + numberOfFeedback = nil, + numberOfCanceledBids = nil, + numberOfCanceledAuctions = nil, + storeId = nil, + storeName = nil, + sellerFeedback = nil) + @sellerNickname = sellerNickname + @overallFeedbackRating = overallFeedbackRating + @numberOfFeedback = numberOfFeedback + @numberOfCanceledBids = numberOfCanceledBids + @numberOfCanceledAuctions = numberOfCanceledAuctions + @storeId = storeId + @storeName = storeName + @sellerFeedback = sellerFeedback + end +end + +# http://soap.amazon.com +class SellerProfileDetailsArray < Array + # Contents type should be dumped here... + @@schema_type = "SellerProfileDetailsArray" + @@schema_ns = "http://soap.amazon.com" +end + +# http://soap.amazon.com +class SellerSearchDetails + @@schema_type = "SellerSearchDetails" + @@schema_ns = "http://soap.amazon.com" + + def SellerNickname + @sellerNickname + end + + def SellerNickname=(value) + @sellerNickname = value + end + + def StoreId + @storeId + end + + def StoreId=(value) + @storeId = value + end + + def StoreName + @storeName + end + + def StoreName=(value) + @storeName = value + end + + def NumberOfOpenListings + @numberOfOpenListings + end + + def NumberOfOpenListings=(value) + @numberOfOpenListings = value + end + + def ListingProductInfo + @listingProductInfo + end + + def ListingProductInfo=(value) + @listingProductInfo = value + end + + def initialize(sellerNickname = nil, + storeId = nil, + storeName = nil, + numberOfOpenListings = nil, + listingProductInfo = nil) + @sellerNickname = sellerNickname + @storeId = storeId + @storeName = storeName + @numberOfOpenListings = numberOfOpenListings + @listingProductInfo = listingProductInfo + end +end + +# http://soap.amazon.com +class SellerSearchDetailsArray < Array + # Contents type should be dumped here... + @@schema_type = "SellerSearchDetailsArray" + @@schema_ns = "http://soap.amazon.com" +end + +# http://soap.amazon.com +class ListingProductInfo + @@schema_type = "ListingProductInfo" + @@schema_ns = "http://soap.amazon.com" + + def ListingProductDetails + @listingProductDetails + end + + def ListingProductDetails=(value) + @listingProductDetails = value + end + + def initialize(listingProductDetails = nil) + @listingProductDetails = listingProductDetails + end +end + +# http://soap.amazon.com +class ListingProductDetailsArray < Array + # Contents type should be dumped here... + @@schema_type = "ListingProductDetailsArray" + @@schema_ns = "http://soap.amazon.com" +end + +# http://soap.amazon.com +class ListingProductDetails + @@schema_type = "ListingProductDetails" + @@schema_ns = "http://soap.amazon.com" + + def ExchangeId + @exchangeId + end + + def ExchangeId=(value) + @exchangeId = value + end + + def ListingId + @listingId + end + + def ListingId=(value) + @listingId = value + end + + def ExchangeTitle + @exchangeTitle + end + + def ExchangeTitle=(value) + @exchangeTitle = value + end + + def ExchangePrice + @exchangePrice + end + + def ExchangePrice=(value) + @exchangePrice = value + end + + def ExchangeAsin + @exchangeAsin + end + + def ExchangeAsin=(value) + @exchangeAsin = value + end + + def ExchangeEndDate + @exchangeEndDate + end + + def ExchangeEndDate=(value) + @exchangeEndDate = value + end + + def ExchangeTinyImage + @exchangeTinyImage + end + + def ExchangeTinyImage=(value) + @exchangeTinyImage = value + end + + def ExchangeSellerId + @exchangeSellerId + end + + def ExchangeSellerId=(value) + @exchangeSellerId = value + end + + def ExchangeSellerNickname + @exchangeSellerNickname + end + + def ExchangeSellerNickname=(value) + @exchangeSellerNickname = value + end + + def ExchangeStartDate + @exchangeStartDate + end + + def ExchangeStartDate=(value) + @exchangeStartDate = value + end + + def ExchangeStatus + @exchangeStatus + end + + def ExchangeStatus=(value) + @exchangeStatus = value + end + + def ExchangeQuantity + @exchangeQuantity + end + + def ExchangeQuantity=(value) + @exchangeQuantity = value + end + + def ExchangeQuantityAllocated + @exchangeQuantityAllocated + end + + def ExchangeQuantityAllocated=(value) + @exchangeQuantityAllocated = value + end + + def ExchangeFeaturedCategory + @exchangeFeaturedCategory + end + + def ExchangeFeaturedCategory=(value) + @exchangeFeaturedCategory = value + end + + def ExchangeCondition + @exchangeCondition + end + + def ExchangeCondition=(value) + @exchangeCondition = value + end + + def ExchangeConditionType + @exchangeConditionType + end + + def ExchangeConditionType=(value) + @exchangeConditionType = value + end + + def ExchangeAvailability + @exchangeAvailability + end + + def ExchangeAvailability=(value) + @exchangeAvailability = value + end + + def ExchangeOfferingType + @exchangeOfferingType + end + + def ExchangeOfferingType=(value) + @exchangeOfferingType = value + end + + def ExchangeSellerState + @exchangeSellerState + end + + def ExchangeSellerState=(value) + @exchangeSellerState = value + end + + def ExchangeSellerCountry + @exchangeSellerCountry + end + + def ExchangeSellerCountry=(value) + @exchangeSellerCountry = value + end + + def ExchangeSellerRating + @exchangeSellerRating + end + + def ExchangeSellerRating=(value) + @exchangeSellerRating = value + end + + def initialize(exchangeId = nil, + listingId = nil, + exchangeTitle = nil, + exchangePrice = nil, + exchangeAsin = nil, + exchangeEndDate = nil, + exchangeTinyImage = nil, + exchangeSellerId = nil, + exchangeSellerNickname = nil, + exchangeStartDate = nil, + exchangeStatus = nil, + exchangeQuantity = nil, + exchangeQuantityAllocated = nil, + exchangeFeaturedCategory = nil, + exchangeCondition = nil, + exchangeConditionType = nil, + exchangeAvailability = nil, + exchangeOfferingType = nil, + exchangeSellerState = nil, + exchangeSellerCountry = nil, + exchangeSellerRating = nil) + @exchangeId = exchangeId + @listingId = listingId + @exchangeTitle = exchangeTitle + @exchangePrice = exchangePrice + @exchangeAsin = exchangeAsin + @exchangeEndDate = exchangeEndDate + @exchangeTinyImage = exchangeTinyImage + @exchangeSellerId = exchangeSellerId + @exchangeSellerNickname = exchangeSellerNickname + @exchangeStartDate = exchangeStartDate + @exchangeStatus = exchangeStatus + @exchangeQuantity = exchangeQuantity + @exchangeQuantityAllocated = exchangeQuantityAllocated + @exchangeFeaturedCategory = exchangeFeaturedCategory + @exchangeCondition = exchangeCondition + @exchangeConditionType = exchangeConditionType + @exchangeAvailability = exchangeAvailability + @exchangeOfferingType = exchangeOfferingType + @exchangeSellerState = exchangeSellerState + @exchangeSellerCountry = exchangeSellerCountry + @exchangeSellerRating = exchangeSellerRating + end +end + +# http://soap.amazon.com +class SellerFeedback + @@schema_type = "SellerFeedback" + @@schema_ns = "http://soap.amazon.com" + + def Feedback + @feedback + end + + def Feedback=(value) + @feedback = value + end + + def initialize(feedback = nil) + @feedback = feedback + end +end + +# http://soap.amazon.com +class FeedbackArray < Array + # Contents type should be dumped here... + @@schema_type = "FeedbackArray" + @@schema_ns = "http://soap.amazon.com" +end + +# http://soap.amazon.com +class Feedback + @@schema_type = "Feedback" + @@schema_ns = "http://soap.amazon.com" + + def FeedbackRating + @feedbackRating + end + + def FeedbackRating=(value) + @feedbackRating = value + end + + def FeedbackComments + @feedbackComments + end + + def FeedbackComments=(value) + @feedbackComments = value + end + + def FeedbackDate + @feedbackDate + end + + def FeedbackDate=(value) + @feedbackDate = value + end + + def FeedbackRater + @feedbackRater + end + + def FeedbackRater=(value) + @feedbackRater = value + end + + def initialize(feedbackRating = nil, + feedbackComments = nil, + feedbackDate = nil, + feedbackRater = nil) + @feedbackRating = feedbackRating + @feedbackComments = feedbackComments + @feedbackDate = feedbackDate + @feedbackRater = feedbackRater + end +end + +# http://soap.amazon.com +class ThirdPartyProductInfo + @@schema_type = "ThirdPartyProductInfo" + @@schema_ns = "http://soap.amazon.com" + + def ThirdPartyProductDetails + @thirdPartyProductDetails + end + + def ThirdPartyProductDetails=(value) + @thirdPartyProductDetails = value + end + + def initialize(thirdPartyProductDetails = nil) + @thirdPartyProductDetails = thirdPartyProductDetails + end +end + +# http://soap.amazon.com +class ThirdPartyProductDetailsArray < Array + # Contents type should be dumped here... + @@schema_type = "ThirdPartyProductDetailsArray" + @@schema_ns = "http://soap.amazon.com" +end + +# http://soap.amazon.com +class ThirdPartyProductDetails + @@schema_type = "ThirdPartyProductDetails" + @@schema_ns = "http://soap.amazon.com" + + def OfferingType + @offeringType + end + + def OfferingType=(value) + @offeringType = value + end + + def SellerId + @sellerId + end + + def SellerId=(value) + @sellerId = value + end + + def SellerNickname + @sellerNickname + end + + def SellerNickname=(value) + @sellerNickname = value + end + + def ExchangeId + @exchangeId + end + + def ExchangeId=(value) + @exchangeId = value + end + + def OfferingPrice + @offeringPrice + end + + def OfferingPrice=(value) + @offeringPrice = value + end + + def Condition + @condition + end + + def Condition=(value) + @condition = value + end + + def ConditionType + @conditionType + end + + def ConditionType=(value) + @conditionType = value + end + + def ExchangeAvailability + @exchangeAvailability + end + + def ExchangeAvailability=(value) + @exchangeAvailability = value + end + + def SellerCountry + @sellerCountry + end + + def SellerCountry=(value) + @sellerCountry = value + end + + def SellerState + @sellerState + end + + def SellerState=(value) + @sellerState = value + end + + def ShipComments + @shipComments + end + + def ShipComments=(value) + @shipComments = value + end + + def SellerRating + @sellerRating + end + + def SellerRating=(value) + @sellerRating = value + end + + def initialize(offeringType = nil, + sellerId = nil, + sellerNickname = nil, + exchangeId = nil, + offeringPrice = nil, + condition = nil, + conditionType = nil, + exchangeAvailability = nil, + sellerCountry = nil, + sellerState = nil, + shipComments = nil, + sellerRating = nil) + @offeringType = offeringType + @sellerId = sellerId + @sellerNickname = sellerNickname + @exchangeId = exchangeId + @offeringPrice = offeringPrice + @condition = condition + @conditionType = conditionType + @exchangeAvailability = exchangeAvailability + @sellerCountry = sellerCountry + @sellerState = sellerState + @shipComments = shipComments + @sellerRating = sellerRating + end +end + +# http://soap.amazon.com +class KeywordRequest + @@schema_type = "KeywordRequest" + @@schema_ns = "http://soap.amazon.com" + + def keyword + @keyword + end + + def keyword=(value) + @keyword = value + end + + def page + @page + end + + def page=(value) + @page = value + end + + def mode + @mode + end + + def mode=(value) + @mode = value + end + + def tag + @tag + end + + def tag=(value) + @tag = value + end + + def type + @type + end + + def type=(value) + @type = value + end + + def devtag + @devtag + end + + def devtag=(value) + @devtag = value + end + + def sort + @sort + end + + def sort=(value) + @sort = value + end + + def variations + @variations + end + + def variations=(value) + @variations = value + end + + def locale + @locale + end + + def locale=(value) + @locale = value + end + + def initialize(keyword = nil, + page = nil, + mode = nil, + tag = nil, + type = nil, + devtag = nil, + sort = nil, + variations = nil, + locale = nil) + @keyword = keyword + @page = page + @mode = mode + @tag = tag + @type = type + @devtag = devtag + @sort = sort + @variations = variations + @locale = locale + end +end + +# http://soap.amazon.com +class PowerRequest + @@schema_type = "PowerRequest" + @@schema_ns = "http://soap.amazon.com" + + def power + @power + end + + def power=(value) + @power = value + end + + def page + @page + end + + def page=(value) + @page = value + end + + def mode + @mode + end + + def mode=(value) + @mode = value + end + + def tag + @tag + end + + def tag=(value) + @tag = value + end + + def type + @type + end + + def type=(value) + @type = value + end + + def devtag + @devtag + end + + def devtag=(value) + @devtag = value + end + + def sort + @sort + end + + def sort=(value) + @sort = value + end + + def variations + @variations + end + + def variations=(value) + @variations = value + end + + def locale + @locale + end + + def locale=(value) + @locale = value + end + + def initialize(power = nil, + page = nil, + mode = nil, + tag = nil, + type = nil, + devtag = nil, + sort = nil, + variations = nil, + locale = nil) + @power = power + @page = page + @mode = mode + @tag = tag + @type = type + @devtag = devtag + @sort = sort + @variations = variations + @locale = locale + end +end + +# http://soap.amazon.com +class BrowseNodeRequest + @@schema_type = "BrowseNodeRequest" + @@schema_ns = "http://soap.amazon.com" + + def browse_node + @browse_node + end + + def browse_node=(value) + @browse_node = value + end + + def page + @page + end + + def page=(value) + @page = value + end + + def mode + @mode + end + + def mode=(value) + @mode = value + end + + def tag + @tag + end + + def tag=(value) + @tag = value + end + + def type + @type + end + + def type=(value) + @type = value + end + + def devtag + @devtag + end + + def devtag=(value) + @devtag = value + end + + def sort + @sort + end + + def sort=(value) + @sort = value + end + + def locale + @locale + end + + def locale=(value) + @locale = value + end + + def initialize(browse_node = nil, + page = nil, + mode = nil, + tag = nil, + type = nil, + devtag = nil, + sort = nil, + locale = nil) + @browse_node = browse_node + @page = page + @mode = mode + @tag = tag + @type = type + @devtag = devtag + @sort = sort + @locale = locale + end +end + +# http://soap.amazon.com +class AsinRequest + @@schema_type = "AsinRequest" + @@schema_ns = "http://soap.amazon.com" + + def asin + @asin + end + + def asin=(value) + @asin = value + end + + def tag + @tag + end + + def tag=(value) + @tag = value + end + + def type + @type + end + + def type=(value) + @type = value + end + + def devtag + @devtag + end + + def devtag=(value) + @devtag = value + end + + def offer + @offer + end + + def offer=(value) + @offer = value + end + + def offerpage + @offerpage + end + + def offerpage=(value) + @offerpage = value + end + + def locale + @locale + end + + def locale=(value) + @locale = value + end + + def initialize(asin = nil, + tag = nil, + type = nil, + devtag = nil, + offer = nil, + offerpage = nil, + locale = nil) + @asin = asin + @tag = tag + @type = type + @devtag = devtag + @offer = offer + @offerpage = offerpage + @locale = locale + end +end + +# http://soap.amazon.com +class BlendedRequest + @@schema_type = "BlendedRequest" + @@schema_ns = "http://soap.amazon.com" + + def blended + @blended + end + + def blended=(value) + @blended = value + end + + def tag + @tag + end + + def tag=(value) + @tag = value + end + + def type + @type + end + + def type=(value) + @type = value + end + + def devtag + @devtag + end + + def devtag=(value) + @devtag = value + end + + def locale + @locale + end + + def locale=(value) + @locale = value + end + + def initialize(blended = nil, + tag = nil, + type = nil, + devtag = nil, + locale = nil) + @blended = blended + @tag = tag + @type = type + @devtag = devtag + @locale = locale + end +end + +# http://soap.amazon.com +class UpcRequest + @@schema_type = "UpcRequest" + @@schema_ns = "http://soap.amazon.com" + + def upc + @upc + end + + def upc=(value) + @upc = value + end + + def mode + @mode + end + + def mode=(value) + @mode = value + end + + def tag + @tag + end + + def tag=(value) + @tag = value + end + + def type + @type + end + + def type=(value) + @type = value + end + + def devtag + @devtag + end + + def devtag=(value) + @devtag = value + end + + def sort + @sort + end + + def sort=(value) + @sort = value + end + + def variations + @variations + end + + def variations=(value) + @variations = value + end + + def locale + @locale + end + + def locale=(value) + @locale = value + end + + def initialize(upc = nil, + mode = nil, + tag = nil, + type = nil, + devtag = nil, + sort = nil, + variations = nil, + locale = nil) + @upc = upc + @mode = mode + @tag = tag + @type = type + @devtag = devtag + @sort = sort + @variations = variations + @locale = locale + end +end + +# http://soap.amazon.com +class ArtistRequest + @@schema_type = "ArtistRequest" + @@schema_ns = "http://soap.amazon.com" + + def artist + @artist + end + + def artist=(value) + @artist = value + end + + def page + @page + end + + def page=(value) + @page = value + end + + def mode + @mode + end + + def mode=(value) + @mode = value + end + + def tag + @tag + end + + def tag=(value) + @tag = value + end + + def type + @type + end + + def type=(value) + @type = value + end + + def devtag + @devtag + end + + def devtag=(value) + @devtag = value + end + + def sort + @sort + end + + def sort=(value) + @sort = value + end + + def variations + @variations + end + + def variations=(value) + @variations = value + end + + def locale + @locale + end + + def locale=(value) + @locale = value + end + + def initialize(artist = nil, + page = nil, + mode = nil, + tag = nil, + type = nil, + devtag = nil, + sort = nil, + variations = nil, + locale = nil) + @artist = artist + @page = page + @mode = mode + @tag = tag + @type = type + @devtag = devtag + @sort = sort + @variations = variations + @locale = locale + end +end + +# http://soap.amazon.com +class AuthorRequest + @@schema_type = "AuthorRequest" + @@schema_ns = "http://soap.amazon.com" + + def author + @author + end + + def author=(value) + @author = value + end + + def page + @page + end + + def page=(value) + @page = value + end + + def mode + @mode + end + + def mode=(value) + @mode = value + end + + def tag + @tag + end + + def tag=(value) + @tag = value + end + + def type + @type + end + + def type=(value) + @type = value + end + + def devtag + @devtag + end + + def devtag=(value) + @devtag = value + end + + def sort + @sort + end + + def sort=(value) + @sort = value + end + + def variations + @variations + end + + def variations=(value) + @variations = value + end + + def locale + @locale + end + + def locale=(value) + @locale = value + end + + def initialize(author = nil, + page = nil, + mode = nil, + tag = nil, + type = nil, + devtag = nil, + sort = nil, + variations = nil, + locale = nil) + @author = author + @page = page + @mode = mode + @tag = tag + @type = type + @devtag = devtag + @sort = sort + @variations = variations + @locale = locale + end +end + +# http://soap.amazon.com +class ActorRequest + @@schema_type = "ActorRequest" + @@schema_ns = "http://soap.amazon.com" + + def actor + @actor + end + + def actor=(value) + @actor = value + end + + def page + @page + end + + def page=(value) + @page = value + end + + def mode + @mode + end + + def mode=(value) + @mode = value + end + + def tag + @tag + end + + def tag=(value) + @tag = value + end + + def type + @type + end + + def type=(value) + @type = value + end + + def devtag + @devtag + end + + def devtag=(value) + @devtag = value + end + + def sort + @sort + end + + def sort=(value) + @sort = value + end + + def variations + @variations + end + + def variations=(value) + @variations = value + end + + def locale + @locale + end + + def locale=(value) + @locale = value + end + + def initialize(actor = nil, + page = nil, + mode = nil, + tag = nil, + type = nil, + devtag = nil, + sort = nil, + variations = nil, + locale = nil) + @actor = actor + @page = page + @mode = mode + @tag = tag + @type = type + @devtag = devtag + @sort = sort + @variations = variations + @locale = locale + end +end + +# http://soap.amazon.com +class DirectorRequest + @@schema_type = "DirectorRequest" + @@schema_ns = "http://soap.amazon.com" + + def director + @director + end + + def director=(value) + @director = value + end + + def page + @page + end + + def page=(value) + @page = value + end + + def mode + @mode + end + + def mode=(value) + @mode = value + end + + def tag + @tag + end + + def tag=(value) + @tag = value + end + + def type + @type + end + + def type=(value) + @type = value + end + + def devtag + @devtag + end + + def devtag=(value) + @devtag = value + end + + def sort + @sort + end + + def sort=(value) + @sort = value + end + + def variations + @variations + end + + def variations=(value) + @variations = value + end + + def locale + @locale + end + + def locale=(value) + @locale = value + end + + def initialize(director = nil, + page = nil, + mode = nil, + tag = nil, + type = nil, + devtag = nil, + sort = nil, + variations = nil, + locale = nil) + @director = director + @page = page + @mode = mode + @tag = tag + @type = type + @devtag = devtag + @sort = sort + @variations = variations + @locale = locale + end +end + +# http://soap.amazon.com +class ExchangeRequest + @@schema_type = "ExchangeRequest" + @@schema_ns = "http://soap.amazon.com" + + def exchange_id + @exchange_id + end + + def exchange_id=(value) + @exchange_id = value + end + + def tag + @tag + end + + def tag=(value) + @tag = value + end + + def type + @type + end + + def type=(value) + @type = value + end + + def devtag + @devtag + end + + def devtag=(value) + @devtag = value + end + + def locale + @locale + end + + def locale=(value) + @locale = value + end + + def initialize(exchange_id = nil, + tag = nil, + type = nil, + devtag = nil, + locale = nil) + @exchange_id = exchange_id + @tag = tag + @type = type + @devtag = devtag + @locale = locale + end +end + +# http://soap.amazon.com +class ManufacturerRequest + @@schema_type = "ManufacturerRequest" + @@schema_ns = "http://soap.amazon.com" + + def manufacturer + @manufacturer + end + + def manufacturer=(value) + @manufacturer = value + end + + def page + @page + end + + def page=(value) + @page = value + end + + def mode + @mode + end + + def mode=(value) + @mode = value + end + + def tag + @tag + end + + def tag=(value) + @tag = value + end + + def type + @type + end + + def type=(value) + @type = value + end + + def devtag + @devtag + end + + def devtag=(value) + @devtag = value + end + + def sort + @sort + end + + def sort=(value) + @sort = value + end + + def variations + @variations + end + + def variations=(value) + @variations = value + end + + def locale + @locale + end + + def locale=(value) + @locale = value + end + + def initialize(manufacturer = nil, + page = nil, + mode = nil, + tag = nil, + type = nil, + devtag = nil, + sort = nil, + variations = nil, + locale = nil) + @manufacturer = manufacturer + @page = page + @mode = mode + @tag = tag + @type = type + @devtag = devtag + @sort = sort + @variations = variations + @locale = locale + end +end + +# http://soap.amazon.com +class ListManiaRequest + @@schema_type = "ListManiaRequest" + @@schema_ns = "http://soap.amazon.com" + + def lm_id + @lm_id + end + + def lm_id=(value) + @lm_id = value + end + + def page + @page + end + + def page=(value) + @page = value + end + + def tag + @tag + end + + def tag=(value) + @tag = value + end + + def type + @type + end + + def type=(value) + @type = value + end + + def devtag + @devtag + end + + def devtag=(value) + @devtag = value + end + + def locale + @locale + end + + def locale=(value) + @locale = value + end + + def initialize(lm_id = nil, + page = nil, + tag = nil, + type = nil, + devtag = nil, + locale = nil) + @lm_id = lm_id + @page = page + @tag = tag + @type = type + @devtag = devtag + @locale = locale + end +end + +# http://soap.amazon.com +class WishlistRequest + @@schema_type = "WishlistRequest" + @@schema_ns = "http://soap.amazon.com" + + def wishlist_id + @wishlist_id + end + + def wishlist_id=(value) + @wishlist_id = value + end + + def page + @page + end + + def page=(value) + @page = value + end + + def tag + @tag + end + + def tag=(value) + @tag = value + end + + def type + @type + end + + def type=(value) + @type = value + end + + def devtag + @devtag + end + + def devtag=(value) + @devtag = value + end + + def locale + @locale + end + + def locale=(value) + @locale = value + end + + def initialize(wishlist_id = nil, + page = nil, + tag = nil, + type = nil, + devtag = nil, + locale = nil) + @wishlist_id = wishlist_id + @page = page + @tag = tag + @type = type + @devtag = devtag + @locale = locale + end +end + +# http://soap.amazon.com +class MarketplaceRequest + @@schema_type = "MarketplaceRequest" + @@schema_ns = "http://soap.amazon.com" + + def marketplace_search + @marketplace_search + end + + def marketplace_search=(value) + @marketplace_search = value + end + + def tag + @tag + end + + def tag=(value) + @tag = value + end + + def type + @type + end + + def type=(value) + @type = value + end + + def devtag + @devtag + end + + def devtag=(value) + @devtag = value + end + + def page + @page + end + + def page=(value) + @page = value + end + + def keyword + @keyword + end + + def keyword=(value) + @keyword = value + end + + def keyword_search + @keyword_search + end + + def keyword_search=(value) + @keyword_search = value + end + + def browse_id + @browse_id + end + + def browse_id=(value) + @browse_id = value + end + + def zipcode + @zipcode + end + + def zipcode=(value) + @zipcode = value + end + + def area_id + @area_id + end + + def area_id=(value) + @area_id = value + end + + def geo + @geo + end + + def geo=(value) + @geo = value + end + + def sort + @sort + end + + def sort=(value) + @sort = value + end + + def listing_id + @listing_id + end + + def listing_id=(value) + @listing_id = value + end + + def locale + @locale + end + + def locale=(value) + @locale = value + end + + def index + @index + end + + def index=(value) + @index = value + end + + def initialize(marketplace_search = nil, + tag = nil, + type = nil, + devtag = nil, + page = nil, + keyword = nil, + keyword_search = nil, + browse_id = nil, + zipcode = nil, + area_id = nil, + geo = nil, + sort = nil, + listing_id = nil, + locale = nil, + index = nil) + @marketplace_search = marketplace_search + @tag = tag + @type = type + @devtag = devtag + @page = page + @keyword = keyword + @keyword_search = keyword_search + @browse_id = browse_id + @zipcode = zipcode + @area_id = area_id + @geo = geo + @sort = sort + @listing_id = listing_id + @locale = locale + @index = index + end +end + +# http://soap.amazon.com +class SellerProfileRequest + @@schema_type = "SellerProfileRequest" + @@schema_ns = "http://soap.amazon.com" + + def seller_id + @seller_id + end + + def seller_id=(value) + @seller_id = value + end + + def tag + @tag + end + + def tag=(value) + @tag = value + end + + def type + @type + end + + def type=(value) + @type = value + end + + def devtag + @devtag + end + + def devtag=(value) + @devtag = value + end + + def page + @page + end + + def page=(value) + @page = value + end + + def locale + @locale + end + + def locale=(value) + @locale = value + end + + def initialize(seller_id = nil, + tag = nil, + type = nil, + devtag = nil, + page = nil, + locale = nil) + @seller_id = seller_id + @tag = tag + @type = type + @devtag = devtag + @page = page + @locale = locale + end +end + +# http://soap.amazon.com +class SellerRequest + @@schema_type = "SellerRequest" + @@schema_ns = "http://soap.amazon.com" + + def seller_id + @seller_id + end + + def seller_id=(value) + @seller_id = value + end + + def tag + @tag + end + + def tag=(value) + @tag = value + end + + def type + @type + end + + def type=(value) + @type = value + end + + def devtag + @devtag + end + + def devtag=(value) + @devtag = value + end + + def offerstatus + @offerstatus + end + + def offerstatus=(value) + @offerstatus = value + end + + def page + @page + end + + def page=(value) + @page = value + end + + def seller_browse_id + @seller_browse_id + end + + def seller_browse_id=(value) + @seller_browse_id = value + end + + def keyword + @keyword + end + + def keyword=(value) + @keyword = value + end + + def locale + @locale + end + + def locale=(value) + @locale = value + end + + def index + @index + end + + def index=(value) + @index = value + end + + def initialize(seller_id = nil, + tag = nil, + type = nil, + devtag = nil, + offerstatus = nil, + page = nil, + seller_browse_id = nil, + keyword = nil, + locale = nil, + index = nil) + @seller_id = seller_id + @tag = tag + @type = type + @devtag = devtag + @offerstatus = offerstatus + @page = page + @seller_browse_id = seller_browse_id + @keyword = keyword + @locale = locale + @index = index + end +end + +# http://soap.amazon.com +class SimilarityRequest + @@schema_type = "SimilarityRequest" + @@schema_ns = "http://soap.amazon.com" + + def asin + @asin + end + + def asin=(value) + @asin = value + end + + def tag + @tag + end + + def tag=(value) + @tag = value + end + + def type + @type + end + + def type=(value) + @type = value + end + + def devtag + @devtag + end + + def devtag=(value) + @devtag = value + end + + def locale + @locale + end + + def locale=(value) + @locale = value + end + + def initialize(asin = nil, + tag = nil, + type = nil, + devtag = nil, + locale = nil) + @asin = asin + @tag = tag + @type = type + @devtag = devtag + @locale = locale + end +end + +# http://soap.amazon.com +class ItemIdArray < Array + # Contents type should be dumped here... + @@schema_type = "ItemIdArray" + @@schema_ns = "http://soap.amazon.com" +end + +# http://soap.amazon.com +class ItemArray < Array + # Contents type should be dumped here... + @@schema_type = "ItemArray" + @@schema_ns = "http://soap.amazon.com" +end + +# http://soap.amazon.com +class Item + @@schema_type = "Item" + @@schema_ns = "http://soap.amazon.com" + + def ItemId + @itemId + end + + def ItemId=(value) + @itemId = value + end + + def ProductName + @productName + end + + def ProductName=(value) + @productName = value + end + + def Catalog + @catalog + end + + def Catalog=(value) + @catalog = value + end + + def Asin + @asin + end + + def Asin=(value) + @asin = value + end + + def ExchangeId + @exchangeId + end + + def ExchangeId=(value) + @exchangeId = value + end + + def Quantity + @quantity + end + + def Quantity=(value) + @quantity = value + end + + def ListPrice + @listPrice + end + + def ListPrice=(value) + @listPrice = value + end + + def OurPrice + @ourPrice + end + + def OurPrice=(value) + @ourPrice = value + end + + def initialize(itemId = nil, + productName = nil, + catalog = nil, + asin = nil, + exchangeId = nil, + quantity = nil, + listPrice = nil, + ourPrice = nil) + @itemId = itemId + @productName = productName + @catalog = catalog + @asin = asin + @exchangeId = exchangeId + @quantity = quantity + @listPrice = listPrice + @ourPrice = ourPrice + end +end + +# http://soap.amazon.com +class ItemQuantityArray < Array + # Contents type should be dumped here... + @@schema_type = "ItemQuantityArray" + @@schema_ns = "http://soap.amazon.com" +end + +# http://soap.amazon.com +class ItemQuantity + @@schema_type = "ItemQuantity" + @@schema_ns = "http://soap.amazon.com" + + def ItemId + @itemId + end + + def ItemId=(value) + @itemId = value + end + + def Quantity + @quantity + end + + def Quantity=(value) + @quantity = value + end + + def initialize(itemId = nil, + quantity = nil) + @itemId = itemId + @quantity = quantity + end +end + +# http://soap.amazon.com +class AddItemArray < Array + # Contents type should be dumped here... + @@schema_type = "AddItemArray" + @@schema_ns = "http://soap.amazon.com" +end + +# http://soap.amazon.com +class AddItem + @@schema_type = "AddItem" + @@schema_ns = "http://soap.amazon.com" + + def Asin + @asin + end + + def Asin=(value) + @asin = value + end + + def ExchangeId + @exchangeId + end + + def ExchangeId=(value) + @exchangeId = value + end + + def Quantity + @quantity + end + + def Quantity=(value) + @quantity = value + end + + def initialize(asin = nil, + exchangeId = nil, + quantity = nil) + @asin = asin + @exchangeId = exchangeId + @quantity = quantity + end +end + +# http://soap.amazon.com +class ShoppingCart + @@schema_type = "ShoppingCart" + @@schema_ns = "http://soap.amazon.com" + + def CartId + @cartId + end + + def CartId=(value) + @cartId = value + end + + def HMAC + @hMAC + end + + def HMAC=(value) + @hMAC = value + end + + def PurchaseUrl + @purchaseUrl + end + + def PurchaseUrl=(value) + @purchaseUrl = value + end + + def Items + @items + end + + def Items=(value) + @items = value + end + + def initialize(cartId = nil, + hMAC = nil, + purchaseUrl = nil, + items = nil) + @cartId = cartId + @hMAC = hMAC + @purchaseUrl = purchaseUrl + @items = items + end +end + +# http://soap.amazon.com +class GetShoppingCartRequest + @@schema_type = "GetShoppingCartRequest" + @@schema_ns = "http://soap.amazon.com" + + def tag + @tag + end + + def tag=(value) + @tag = value + end + + def devtag + @devtag + end + + def devtag=(value) + @devtag = value + end + + def CartId + @cartId + end + + def CartId=(value) + @cartId = value + end + + def HMAC + @hMAC + end + + def HMAC=(value) + @hMAC = value + end + + def locale + @locale + end + + def locale=(value) + @locale = value + end + + def initialize(tag = nil, + devtag = nil, + cartId = nil, + hMAC = nil, + locale = nil) + @tag = tag + @devtag = devtag + @cartId = cartId + @hMAC = hMAC + @locale = locale + end +end + +# http://soap.amazon.com +class ClearShoppingCartRequest + @@schema_type = "ClearShoppingCartRequest" + @@schema_ns = "http://soap.amazon.com" + + def tag + @tag + end + + def tag=(value) + @tag = value + end + + def devtag + @devtag + end + + def devtag=(value) + @devtag = value + end + + def CartId + @cartId + end + + def CartId=(value) + @cartId = value + end + + def HMAC + @hMAC + end + + def HMAC=(value) + @hMAC = value + end + + def locale + @locale + end + + def locale=(value) + @locale = value + end + + def initialize(tag = nil, + devtag = nil, + cartId = nil, + hMAC = nil, + locale = nil) + @tag = tag + @devtag = devtag + @cartId = cartId + @hMAC = hMAC + @locale = locale + end +end + +# http://soap.amazon.com +class AddShoppingCartItemsRequest + @@schema_type = "AddShoppingCartItemsRequest" + @@schema_ns = "http://soap.amazon.com" + + def tag + @tag + end + + def tag=(value) + @tag = value + end + + def devtag + @devtag + end + + def devtag=(value) + @devtag = value + end + + def CartId + @cartId + end + + def CartId=(value) + @cartId = value + end + + def HMAC + @hMAC + end + + def HMAC=(value) + @hMAC = value + end + + def Items + @items + end + + def Items=(value) + @items = value + end + + def locale + @locale + end + + def locale=(value) + @locale = value + end + + def initialize(tag = nil, + devtag = nil, + cartId = nil, + hMAC = nil, + items = nil, + locale = nil) + @tag = tag + @devtag = devtag + @cartId = cartId + @hMAC = hMAC + @items = items + @locale = locale + end +end + +# http://soap.amazon.com +class RemoveShoppingCartItemsRequest + @@schema_type = "RemoveShoppingCartItemsRequest" + @@schema_ns = "http://soap.amazon.com" + + def tag + @tag + end + + def tag=(value) + @tag = value + end + + def devtag + @devtag + end + + def devtag=(value) + @devtag = value + end + + def CartId + @cartId + end + + def CartId=(value) + @cartId = value + end + + def HMAC + @hMAC + end + + def HMAC=(value) + @hMAC = value + end + + def Items + @items + end + + def Items=(value) + @items = value + end + + def locale + @locale + end + + def locale=(value) + @locale = value + end + + def initialize(tag = nil, + devtag = nil, + cartId = nil, + hMAC = nil, + items = nil, + locale = nil) + @tag = tag + @devtag = devtag + @cartId = cartId + @hMAC = hMAC + @items = items + @locale = locale + end +end + +# http://soap.amazon.com +class ModifyShoppingCartItemsRequest + @@schema_type = "ModifyShoppingCartItemsRequest" + @@schema_ns = "http://soap.amazon.com" + + def tag + @tag + end + + def tag=(value) + @tag = value + end + + def devtag + @devtag + end + + def devtag=(value) + @devtag = value + end + + def CartId + @cartId + end + + def CartId=(value) + @cartId = value + end + + def HMAC + @hMAC + end + + def HMAC=(value) + @hMAC = value + end + + def Items + @items + end + + def Items=(value) + @items = value + end + + def locale + @locale + end + + def locale=(value) + @locale = value + end + + def initialize(tag = nil, + devtag = nil, + cartId = nil, + hMAC = nil, + items = nil, + locale = nil) + @tag = tag + @devtag = devtag + @cartId = cartId + @hMAC = hMAC + @items = items + @locale = locale + end +end + diff --git a/sample/wsdl/amazon/AmazonSearchDriver.rb b/sample/wsdl/amazon/AmazonSearchDriver.rb new file mode 100644 index 0000000000..bf532b75a5 --- /dev/null +++ b/sample/wsdl/amazon/AmazonSearchDriver.rb @@ -0,0 +1,445 @@ +require 'AmazonSearch.rb' + +require 'soap/rpc/driver' + +class AmazonSearchPort < SOAP::RPC::Driver + MappingRegistry = ::SOAP::Mapping::Registry.new + + MappingRegistry.set( + KeywordRequest, + ::SOAP::SOAPStruct, + ::SOAP::Mapping::Registry::TypedStructFactory, + { :type => XSD::QName.new("http://soap.amazon.com", "KeywordRequest") } + ) + MappingRegistry.set( + ProductInfo, + ::SOAP::SOAPStruct, + ::SOAP::Mapping::Registry::TypedStructFactory, + { :type => XSD::QName.new("http://soap.amazon.com", "ProductInfo") } + ) + MappingRegistry.set( + DetailsArray, + ::SOAP::SOAPArray, + ::SOAP::Mapping::Registry::TypedArrayFactory, + { :type => XSD::QName.new("http://soap.amazon.com", "Details") } + ) + MappingRegistry.set( + PowerRequest, + ::SOAP::SOAPStruct, + ::SOAP::Mapping::Registry::TypedStructFactory, + { :type => XSD::QName.new("http://soap.amazon.com", "PowerRequest") } + ) + MappingRegistry.set( + BrowseNodeRequest, + ::SOAP::SOAPStruct, + ::SOAP::Mapping::Registry::TypedStructFactory, + { :type => XSD::QName.new("http://soap.amazon.com", "BrowseNodeRequest") } + ) + MappingRegistry.set( + AsinRequest, + ::SOAP::SOAPStruct, + ::SOAP::Mapping::Registry::TypedStructFactory, + { :type => XSD::QName.new("http://soap.amazon.com", "AsinRequest") } + ) + MappingRegistry.set( + BlendedRequest, + ::SOAP::SOAPStruct, + ::SOAP::Mapping::Registry::TypedStructFactory, + { :type => XSD::QName.new("http://soap.amazon.com", "BlendedRequest") } + ) + MappingRegistry.set( + ProductLineArray, + ::SOAP::SOAPArray, + ::SOAP::Mapping::Registry::TypedArrayFactory, + { :type => XSD::QName.new("http://soap.amazon.com", "ProductLine") } + ) + MappingRegistry.set( + UpcRequest, + ::SOAP::SOAPStruct, + ::SOAP::Mapping::Registry::TypedStructFactory, + { :type => XSD::QName.new("http://soap.amazon.com", "UpcRequest") } + ) + MappingRegistry.set( + AuthorRequest, + ::SOAP::SOAPStruct, + ::SOAP::Mapping::Registry::TypedStructFactory, + { :type => XSD::QName.new("http://soap.amazon.com", "AuthorRequest") } + ) + MappingRegistry.set( + ArtistRequest, + ::SOAP::SOAPStruct, + ::SOAP::Mapping::Registry::TypedStructFactory, + { :type => XSD::QName.new("http://soap.amazon.com", "ArtistRequest") } + ) + MappingRegistry.set( + ActorRequest, + ::SOAP::SOAPStruct, + ::SOAP::Mapping::Registry::TypedStructFactory, + { :type => XSD::QName.new("http://soap.amazon.com", "ActorRequest") } + ) + MappingRegistry.set( + ManufacturerRequest, + ::SOAP::SOAPStruct, + ::SOAP::Mapping::Registry::TypedStructFactory, + { :type => XSD::QName.new("http://soap.amazon.com", "ManufacturerRequest") } + ) + MappingRegistry.set( + DirectorRequest, + ::SOAP::SOAPStruct, + ::SOAP::Mapping::Registry::TypedStructFactory, + { :type => XSD::QName.new("http://soap.amazon.com", "DirectorRequest") } + ) + MappingRegistry.set( + ListManiaRequest, + ::SOAP::SOAPStruct, + ::SOAP::Mapping::Registry::TypedStructFactory, + { :type => XSD::QName.new("http://soap.amazon.com", "ListManiaRequest") } + ) + MappingRegistry.set( + WishlistRequest, + ::SOAP::SOAPStruct, + ::SOAP::Mapping::Registry::TypedStructFactory, + { :type => XSD::QName.new("http://soap.amazon.com", "WishlistRequest") } + ) + MappingRegistry.set( + ExchangeRequest, + ::SOAP::SOAPStruct, + ::SOAP::Mapping::Registry::TypedStructFactory, + { :type => XSD::QName.new("http://soap.amazon.com", "ExchangeRequest") } + ) + MappingRegistry.set( + ListingProductDetails, + ::SOAP::SOAPStruct, + ::SOAP::Mapping::Registry::TypedStructFactory, + { :type => XSD::QName.new("http://soap.amazon.com", "ListingProductDetails") } + ) + MappingRegistry.set( + MarketplaceRequest, + ::SOAP::SOAPStruct, + ::SOAP::Mapping::Registry::TypedStructFactory, + { :type => XSD::QName.new("http://soap.amazon.com", "MarketplaceRequest") } + ) + MappingRegistry.set( + MarketplaceSearch, + ::SOAP::SOAPStruct, + ::SOAP::Mapping::Registry::TypedStructFactory, + { :type => XSD::QName.new("http://soap.amazon.com", "MarketplaceSearch") } + ) + MappingRegistry.set( + MarketplaceSearchDetailsArray, + ::SOAP::SOAPArray, + ::SOAP::Mapping::Registry::TypedArrayFactory, + { :type => XSD::QName.new("http://soap.amazon.com", "MarketplaceSearchDetails") } + ) + MappingRegistry.set( + SellerProfileRequest, + ::SOAP::SOAPStruct, + ::SOAP::Mapping::Registry::TypedStructFactory, + { :type => XSD::QName.new("http://soap.amazon.com", "SellerProfileRequest") } + ) + MappingRegistry.set( + SellerProfile, + ::SOAP::SOAPStruct, + ::SOAP::Mapping::Registry::TypedStructFactory, + { :type => XSD::QName.new("http://soap.amazon.com", "SellerProfile") } + ) + MappingRegistry.set( + SellerProfileDetailsArray, + ::SOAP::SOAPArray, + ::SOAP::Mapping::Registry::TypedArrayFactory, + { :type => XSD::QName.new("http://soap.amazon.com", "SellerProfileDetails") } + ) + MappingRegistry.set( + SellerRequest, + ::SOAP::SOAPStruct, + ::SOAP::Mapping::Registry::TypedStructFactory, + { :type => XSD::QName.new("http://soap.amazon.com", "SellerRequest") } + ) + MappingRegistry.set( + SellerSearch, + ::SOAP::SOAPStruct, + ::SOAP::Mapping::Registry::TypedStructFactory, + { :type => XSD::QName.new("http://soap.amazon.com", "SellerSearch") } + ) + MappingRegistry.set( + SellerSearchDetailsArray, + ::SOAP::SOAPArray, + ::SOAP::Mapping::Registry::TypedArrayFactory, + { :type => XSD::QName.new("http://soap.amazon.com", "SellerSearchDetails") } + ) + MappingRegistry.set( + SimilarityRequest, + ::SOAP::SOAPStruct, + ::SOAP::Mapping::Registry::TypedStructFactory, + { :type => XSD::QName.new("http://soap.amazon.com", "SimilarityRequest") } + ) + MappingRegistry.set( + GetShoppingCartRequest, + ::SOAP::SOAPStruct, + ::SOAP::Mapping::Registry::TypedStructFactory, + { :type => XSD::QName.new("http://soap.amazon.com", "GetShoppingCartRequest") } + ) + MappingRegistry.set( + ShoppingCart, + ::SOAP::SOAPStruct, + ::SOAP::Mapping::Registry::TypedStructFactory, + { :type => XSD::QName.new("http://soap.amazon.com", "ShoppingCart") } + ) + MappingRegistry.set( + ItemArray, + ::SOAP::SOAPArray, + ::SOAP::Mapping::Registry::TypedArrayFactory, + { :type => XSD::QName.new("http://soap.amazon.com", "Item") } + ) + MappingRegistry.set( + ClearShoppingCartRequest, + ::SOAP::SOAPStruct, + ::SOAP::Mapping::Registry::TypedStructFactory, + { :type => XSD::QName.new("http://soap.amazon.com", "ClearShoppingCartRequest") } + ) + MappingRegistry.set( + AddShoppingCartItemsRequest, + ::SOAP::SOAPStruct, + ::SOAP::Mapping::Registry::TypedStructFactory, + { :type => XSD::QName.new("http://soap.amazon.com", "AddShoppingCartItemsRequest") } + ) + MappingRegistry.set( + AddItemArray, + ::SOAP::SOAPArray, + ::SOAP::Mapping::Registry::TypedArrayFactory, + { :type => XSD::QName.new("http://soap.amazon.com", "AddItem") } + ) + MappingRegistry.set( + RemoveShoppingCartItemsRequest, + ::SOAP::SOAPStruct, + ::SOAP::Mapping::Registry::TypedStructFactory, + { :type => XSD::QName.new("http://soap.amazon.com", "RemoveShoppingCartItemsRequest") } + ) + MappingRegistry.set( + ItemIdArray, + ::SOAP::SOAPArray, + ::SOAP::Mapping::Registry::TypedArrayFactory, + { :type => XSD::QName.new("http://www.w3.org/2001/XMLSchema", "string") } + ) + MappingRegistry.set( + ModifyShoppingCartItemsRequest, + ::SOAP::SOAPStruct, + ::SOAP::Mapping::Registry::TypedStructFactory, + { :type => XSD::QName.new("http://soap.amazon.com", "ModifyShoppingCartItemsRequest") } + ) + MappingRegistry.set( + ItemQuantityArray, + ::SOAP::SOAPArray, + ::SOAP::Mapping::Registry::TypedArrayFactory, + { :type => XSD::QName.new("http://soap.amazon.com", "ItemQuantity") } + ) + MappingRegistry.set( + Details, + ::SOAP::SOAPStruct, + ::SOAP::Mapping::Registry::TypedStructFactory, + { :type => XSD::QName.new("http://soap.amazon.com", "Details") } + ) + MappingRegistry.set( + ProductLine, + ::SOAP::SOAPStruct, + ::SOAP::Mapping::Registry::TypedStructFactory, + { :type => XSD::QName.new("http://soap.amazon.com", "ProductLine") } + ) + MappingRegistry.set( + MarketplaceSearchDetails, + ::SOAP::SOAPStruct, + ::SOAP::Mapping::Registry::TypedStructFactory, + { :type => XSD::QName.new("http://soap.amazon.com", "MarketplaceSearchDetails") } + ) + MappingRegistry.set( + SellerProfileDetails, + ::SOAP::SOAPStruct, + ::SOAP::Mapping::Registry::TypedStructFactory, + { :type => XSD::QName.new("http://soap.amazon.com", "SellerProfileDetails") } + ) + MappingRegistry.set( + SellerSearchDetails, + ::SOAP::SOAPStruct, + ::SOAP::Mapping::Registry::TypedStructFactory, + { :type => XSD::QName.new("http://soap.amazon.com", "SellerSearchDetails") } + ) + MappingRegistry.set( + Item, + ::SOAP::SOAPStruct, + ::SOAP::Mapping::Registry::TypedStructFactory, + { :type => XSD::QName.new("http://soap.amazon.com", "Item") } + ) + MappingRegistry.set( + AddItem, + ::SOAP::SOAPStruct, + ::SOAP::Mapping::Registry::TypedStructFactory, + { :type => XSD::QName.new("http://soap.amazon.com", "AddItem") } + ) + MappingRegistry.set( + ItemQuantity, + ::SOAP::SOAPStruct, + ::SOAP::Mapping::Registry::TypedStructFactory, + { :type => XSD::QName.new("http://soap.amazon.com", "ItemQuantity") } + ) + + Methods = [ + ["KeywordSearchRequest", "keywordSearchRequest", [ + ["in", "KeywordSearchRequest", + [::SOAP::SOAPStruct, "http://soap.amazon.com", "KeywordRequest"]], + ["retval", "return", + [::SOAP::SOAPStruct, "http://soap.amazon.com", "ProductInfo"]]], + "http://soap.amazon.com", "http://soap.amazon.com"], + ["PowerSearchRequest", "powerSearchRequest", [ + ["in", "PowerSearchRequest", + [::SOAP::SOAPStruct, "http://soap.amazon.com", "PowerRequest"]], + ["retval", "return", + [::SOAP::SOAPStruct, "http://soap.amazon.com", "ProductInfo"]]], + "http://soap.amazon.com", "http://soap.amazon.com"], + ["BrowseNodeSearchRequest", "browseNodeSearchRequest", [ + ["in", "BrowseNodeSearchRequest", + [::SOAP::SOAPStruct, "http://soap.amazon.com", "BrowseNodeRequest"]], + ["retval", "return", + [::SOAP::SOAPStruct, "http://soap.amazon.com", "ProductInfo"]]], + "http://soap.amazon.com", "http://soap.amazon.com"], + ["AsinSearchRequest", "asinSearchRequest", [ + ["in", "AsinSearchRequest", + [::SOAP::SOAPStruct, "http://soap.amazon.com", "AsinRequest"]], + ["retval", "return", + [::SOAP::SOAPStruct, "http://soap.amazon.com", "ProductInfo"]]], + "http://soap.amazon.com", "http://soap.amazon.com"], + ["BlendedSearchRequest", "blendedSearchRequest", [ + ["in", "BlendedSearchRequest", + [::SOAP::SOAPStruct, "http://soap.amazon.com", "BlendedRequest"]], + ["retval", "return", + [::SOAP::SOAPArray, "http://soap.amazon.com", "ProductLine"]]], + "http://soap.amazon.com", "http://soap.amazon.com"], + ["UpcSearchRequest", "upcSearchRequest", [ + ["in", "UpcSearchRequest", + [::SOAP::SOAPStruct, "http://soap.amazon.com", "UpcRequest"]], + ["retval", "return", + [::SOAP::SOAPStruct, "http://soap.amazon.com", "ProductInfo"]]], + "http://soap.amazon.com", "http://soap.amazon.com"], + ["AuthorSearchRequest", "authorSearchRequest", [ + ["in", "AuthorSearchRequest", + [::SOAP::SOAPStruct, "http://soap.amazon.com", "AuthorRequest"]], + ["retval", "return", + [::SOAP::SOAPStruct, "http://soap.amazon.com", "ProductInfo"]]], + "http://soap.amazon.com", "http://soap.amazon.com"], + ["ArtistSearchRequest", "artistSearchRequest", [ + ["in", "ArtistSearchRequest", + [::SOAP::SOAPStruct, "http://soap.amazon.com", "ArtistRequest"]], + ["retval", "return", + [::SOAP::SOAPStruct, "http://soap.amazon.com", "ProductInfo"]]], + "http://soap.amazon.com", "http://soap.amazon.com"], + ["ActorSearchRequest", "actorSearchRequest", [ + ["in", "ActorSearchRequest", + [::SOAP::SOAPStruct, "http://soap.amazon.com", "ActorRequest"]], + ["retval", "return", + [::SOAP::SOAPStruct, "http://soap.amazon.com", "ProductInfo"]]], + "http://soap.amazon.com", "http://soap.amazon.com"], + ["ManufacturerSearchRequest", "manufacturerSearchRequest", [ + ["in", "ManufacturerSearchRequest", + [::SOAP::SOAPStruct, "http://soap.amazon.com", "ManufacturerRequest"]], + ["retval", "return", + [::SOAP::SOAPStruct, "http://soap.amazon.com", "ProductInfo"]]], + "http://soap.amazon.com", "http://soap.amazon.com"], + ["DirectorSearchRequest", "directorSearchRequest", [ + ["in", "DirectorSearchRequest", + [::SOAP::SOAPStruct, "http://soap.amazon.com", "DirectorRequest"]], + ["retval", "return", + [::SOAP::SOAPStruct, "http://soap.amazon.com", "ProductInfo"]]], + "http://soap.amazon.com", "http://soap.amazon.com"], + ["ListManiaSearchRequest", "listManiaSearchRequest", [ + ["in", "ListManiaSearchRequest", + [::SOAP::SOAPStruct, "http://soap.amazon.com", "ListManiaRequest"]], + ["retval", "return", + [::SOAP::SOAPStruct, "http://soap.amazon.com", "ProductInfo"]]], + "http://soap.amazon.com", "http://soap.amazon.com"], + ["WishlistSearchRequest", "wishlistSearchRequest", [ + ["in", "WishlistSearchRequest", + [::SOAP::SOAPStruct, "http://soap.amazon.com", "WishlistRequest"]], + ["retval", "return", + [::SOAP::SOAPStruct, "http://soap.amazon.com", "ProductInfo"]]], + "http://soap.amazon.com", "http://soap.amazon.com"], + ["ExchangeSearchRequest", "exchangeSearchRequest", [ + ["in", "ExchangeSearchRequest", + [::SOAP::SOAPStruct, "http://soap.amazon.com", "ExchangeRequest"]], + ["retval", "return", + [::SOAP::SOAPStruct, "http://soap.amazon.com", "ListingProductDetails"]]], + "http://soap.amazon.com", "http://soap.amazon.com"], + ["MarketplaceSearchRequest", "marketplaceSearchRequest", [ + ["in", "MarketplaceSearchRequest", + [::SOAP::SOAPStruct, "http://soap.amazon.com", "MarketplaceRequest"]], + ["retval", "return", + [::SOAP::SOAPStruct, "http://soap.amazon.com", "MarketplaceSearch"]]], + "http://soap.amazon.com", "http://soap.amazon.com"], + ["SellerProfileSearchRequest", "sellerProfileSearchRequest", [ + ["in", "SellerProfileSearchRequest", + [::SOAP::SOAPStruct, "http://soap.amazon.com", "SellerProfileRequest"]], + ["retval", "return", + [::SOAP::SOAPStruct, "http://soap.amazon.com", "SellerProfile"]]], + "http://soap.amazon.com", "http://soap.amazon.com"], + ["SellerSearchRequest", "sellerSearchRequest", [ + ["in", "SellerSearchRequest", + [::SOAP::SOAPStruct, "http://soap.amazon.com", "SellerRequest"]], + ["retval", "return", + [::SOAP::SOAPStruct, "http://soap.amazon.com", "SellerSearch"]]], + "http://soap.amazon.com", "http://soap.amazon.com"], + ["SimilaritySearchRequest", "similaritySearchRequest", [ + ["in", "SimilaritySearchRequest", + [::SOAP::SOAPStruct, "http://soap.amazon.com", "SimilarityRequest"]], + ["retval", "return", + [::SOAP::SOAPStruct, "http://soap.amazon.com", "ProductInfo"]]], + "http://soap.amazon.com", "http://soap.amazon.com"], + ["GetShoppingCartRequest", "getShoppingCartRequest", [ + ["in", "GetShoppingCartRequest", + [::SOAP::SOAPStruct, "http://soap.amazon.com", "GetShoppingCartRequest"]], + ["retval", "ShoppingCart", + [::SOAP::SOAPStruct, "http://soap.amazon.com", "ShoppingCart"]]], + "http://soap.amazon.com", "http://soap.amazon.com"], + ["ClearShoppingCartRequest", "clearShoppingCartRequest", [ + ["in", "ClearShoppingCartRequest", + [::SOAP::SOAPStruct, "http://soap.amazon.com", "ClearShoppingCartRequest"]], + ["retval", "ShoppingCart", + [::SOAP::SOAPStruct, "http://soap.amazon.com", "ShoppingCart"]]], + "http://soap.amazon.com", "http://soap.amazon.com"], + ["AddShoppingCartItemsRequest", "addShoppingCartItemsRequest", [ + ["in", "AddShoppingCartItemsRequest", + [::SOAP::SOAPStruct, "http://soap.amazon.com", "AddShoppingCartItemsRequest"]], + ["retval", "ShoppingCart", + [::SOAP::SOAPStruct, "http://soap.amazon.com", "ShoppingCart"]]], + "http://soap.amazon.com", "http://soap.amazon.com"], + ["RemoveShoppingCartItemsRequest", "removeShoppingCartItemsRequest", [ + ["in", "RemoveShoppingCartItemsRequest", + [::SOAP::SOAPStruct, "http://soap.amazon.com", "RemoveShoppingCartItemsRequest"]], + ["retval", "ShoppingCart", + [::SOAP::SOAPStruct, "http://soap.amazon.com", "ShoppingCart"]]], + "http://soap.amazon.com", "http://soap.amazon.com"], + ["ModifyShoppingCartItemsRequest", "modifyShoppingCartItemsRequest", [ + ["in", "ModifyShoppingCartItemsRequest", + [::SOAP::SOAPStruct, "http://soap.amazon.com", "ModifyShoppingCartItemsRequest"]], + ["retval", "ShoppingCart", + [::SOAP::SOAPStruct, "http://soap.amazon.com", "ShoppingCart"]]], + "http://soap.amazon.com", "http://soap.amazon.com"] + ] + + DefaultEndpointUrl = "http://soap.amazon.com/onca/soap2" + + def initialize(endpoint_url = nil) + endpoint_url ||= DefaultEndpointUrl + super(endpoint_url, nil) + self.mapping_registry = MappingRegistry + init_methods + end + +private + + def init_methods + Methods.each do |name_as, name, params, soapaction, namespace| + qname = XSD::QName.new(namespace, name_as) + @proxy.add_method(qname, soapaction, name, params) + add_rpc_method_interface(name, params) + end + end +end + diff --git a/sample/wsdl/amazon/sampleClient.rb b/sample/wsdl/amazon/sampleClient.rb new file mode 100644 index 0000000000..71bafd7345 --- /dev/null +++ b/sample/wsdl/amazon/sampleClient.rb @@ -0,0 +1,37 @@ +#!/usr/bin/env ruby + +# This file is a sample based on AmazonSearchServiceClient.rb, which can be +# generated by WSDL file and wsdl2ruby.rb. +# +# $ wsdl2ruby.rb --type client --force \ +# --wsdl http://soap.amazon.com/schemas2/AmazonWebServices.wsdl +# +# See wsdlDriver.rb to use WSDL file directly (slow). +require 'AmazonSearchDriver.rb' + +endpoint_url = ARGV.shift +obj = AmazonSearchPort.new(endpoint_url) + +# Uncomment the below line to see SOAP wiredumps. +# obj.wiredump_dev = STDERR + +# SYNOPSIS +# KeywordSearchRequest(keywordSearchRequest) +# +# ARGS +# keywordSearchRequest KeywordRequest - {urn:PI/DevCentral/SoapService}KeywordRequest +# +# RETURNS +# return ProductInfo - {urn:PI/DevCentral/SoapService}ProductInfo +# +# RAISES +# N/A +# +keywordSearchRequest = KeywordRequest.new("Ruby Object", "1", "books", "webservices-20", "lite", "", "+salesrank") +obj.keywordSearchRequest(keywordSearchRequest).Details.each do |detail| + puts "== #{detail.ProductName}" + puts "Author: #{detail.Authors.join(", ")}" + puts "Release date: #{detail.ReleaseDate}" + puts "List price: #{detail.ListPrice}, our price: #{detail.OurPrice}" + puts +end diff --git a/sample/wsdl/amazon/wsdlDriver.rb b/sample/wsdl/amazon/wsdlDriver.rb new file mode 100644 index 0000000000..baf55a4968 --- /dev/null +++ b/sample/wsdl/amazon/wsdlDriver.rb @@ -0,0 +1,48 @@ +require 'soap/wsdlDriver' + +book = ARGV.shift || "Ruby" + +# AmazonSearch.rb is generated from WSDL. +# Run "wsdl2ruby.rb --wsdl http://soap.amazon.com/schemas2/AmazonWebServices.wsdl --classDef --force" +require 'AmazonSearch.rb' + +=begin +Or, define the class by yourself like this. + +class KeywordRequest + def initialize(keyword = nil, + page = nil, + mode = nil, + tag = nil, + type = nil, + devtag = nil, + sort = nil) + @keyword = keyword + @page = page + @mode = mode + @tag = tag + @type = type + @devtag = devtag + @sort = sort + end +end +=end + +# You must get 'developer's token" from http://associates.amazon.com/exec/panama/associates/ntg/browse/-/1067662 to use Amazon Web Services 2.0. +#devtag = File.open(File.expand_path("~/.amazon_key")).read.chomp + +AMAZON_WSDL = 'http://soap.amazon.com/schemas2/AmazonWebServices.wsdl' +amazon = SOAP::WSDLDriverFactory.new(AMAZON_WSDL).create_driver +p "WSDL loaded" +amazon.generate_explicit_type = true +#amazon.wiredump_dev = STDERR + +# Show sales rank. +req = KeywordRequest.new(book, "1", "books", "webservices-20", "lite", devtag, "+salesrank") +amazon.KeywordSearchRequest(req).Details.each do |detail| + puts "== #{detail.ProductName}" + puts "Author: #{detail.Authors.join(", ")}" + puts "Release date: #{detail.ReleaseDate}" + puts "List price: #{detail.ListPrice}, our price: #{detail.OurPrice}" + puts +end diff --git a/sample/wsdl/googleSearch/GoogleSearch.rb b/sample/wsdl/googleSearch/GoogleSearch.rb new file mode 100644 index 0000000000..e124208b91 --- /dev/null +++ b/sample/wsdl/googleSearch/GoogleSearch.rb @@ -0,0 +1,258 @@ +# urn:GoogleSearch +class GoogleSearchResult + @@schema_type = "GoogleSearchResult" + @@schema_ns = "urn:GoogleSearch" + + def documentFiltering + @documentFiltering + end + + def documentFiltering=(value) + @documentFiltering = value + end + + def searchComments + @searchComments + end + + def searchComments=(value) + @searchComments = value + end + + def estimatedTotalResultsCount + @estimatedTotalResultsCount + end + + def estimatedTotalResultsCount=(value) + @estimatedTotalResultsCount = value + end + + def estimateIsExact + @estimateIsExact + end + + def estimateIsExact=(value) + @estimateIsExact = value + end + + def resultElements + @resultElements + end + + def resultElements=(value) + @resultElements = value + end + + def searchQuery + @searchQuery + end + + def searchQuery=(value) + @searchQuery = value + end + + def startIndex + @startIndex + end + + def startIndex=(value) + @startIndex = value + end + + def endIndex + @endIndex + end + + def endIndex=(value) + @endIndex = value + end + + def searchTips + @searchTips + end + + def searchTips=(value) + @searchTips = value + end + + def directoryCategories + @directoryCategories + end + + def directoryCategories=(value) + @directoryCategories = value + end + + def searchTime + @searchTime + end + + def searchTime=(value) + @searchTime = value + end + + def initialize(documentFiltering = nil, + searchComments = nil, + estimatedTotalResultsCount = nil, + estimateIsExact = nil, + resultElements = nil, + searchQuery = nil, + startIndex = nil, + endIndex = nil, + searchTips = nil, + directoryCategories = nil, + searchTime = nil) + @documentFiltering = documentFiltering + @searchComments = searchComments + @estimatedTotalResultsCount = estimatedTotalResultsCount + @estimateIsExact = estimateIsExact + @resultElements = resultElements + @searchQuery = searchQuery + @startIndex = startIndex + @endIndex = endIndex + @searchTips = searchTips + @directoryCategories = directoryCategories + @searchTime = searchTime + end +end + +# urn:GoogleSearch +class ResultElement + @@schema_type = "ResultElement" + @@schema_ns = "urn:GoogleSearch" + + def summary + @summary + end + + def summary=(value) + @summary = value + end + + def URL + @uRL + end + + def URL=(value) + @uRL = value + end + + def snippet + @snippet + end + + def snippet=(value) + @snippet = value + end + + def title + @title + end + + def title=(value) + @title = value + end + + def cachedSize + @cachedSize + end + + def cachedSize=(value) + @cachedSize = value + end + + def relatedInformationPresent + @relatedInformationPresent + end + + def relatedInformationPresent=(value) + @relatedInformationPresent = value + end + + def hostName + @hostName + end + + def hostName=(value) + @hostName = value + end + + def directoryCategory + @directoryCategory + end + + def directoryCategory=(value) + @directoryCategory = value + end + + def directoryTitle + @directoryTitle + end + + def directoryTitle=(value) + @directoryTitle = value + end + + def initialize(summary = nil, + uRL = nil, + snippet = nil, + title = nil, + cachedSize = nil, + relatedInformationPresent = nil, + hostName = nil, + directoryCategory = nil, + directoryTitle = nil) + @summary = summary + @uRL = uRL + @snippet = snippet + @title = title + @cachedSize = cachedSize + @relatedInformationPresent = relatedInformationPresent + @hostName = hostName + @directoryCategory = directoryCategory + @directoryTitle = directoryTitle + end +end + +# urn:GoogleSearch +class ResultElementArray < Array + # Contents type should be dumped here... + @@schema_type = "ResultElementArray" + @@schema_ns = "urn:GoogleSearch" +end + +# urn:GoogleSearch +class DirectoryCategoryArray < Array + # Contents type should be dumped here... + @@schema_type = "DirectoryCategoryArray" + @@schema_ns = "urn:GoogleSearch" +end + +# urn:GoogleSearch +class DirectoryCategory + @@schema_type = "DirectoryCategory" + @@schema_ns = "urn:GoogleSearch" + + def fullViewableName + @fullViewableName + end + + def fullViewableName=(value) + @fullViewableName = value + end + + def specialEncoding + @specialEncoding + end + + def specialEncoding=(value) + @specialEncoding = value + end + + def initialize(fullViewableName = nil, + specialEncoding = nil) + @fullViewableName = fullViewableName + @specialEncoding = specialEncoding + end +end + diff --git a/sample/wsdl/googleSearch/GoogleSearchDriver.rb b/sample/wsdl/googleSearch/GoogleSearchDriver.rb new file mode 100644 index 0000000000..9901b89071 --- /dev/null +++ b/sample/wsdl/googleSearch/GoogleSearchDriver.rb @@ -0,0 +1,101 @@ +require 'GoogleSearch.rb' + +require 'soap/rpc/driver' + +class GoogleSearchPort < SOAP::RPC::Driver + MappingRegistry = ::SOAP::Mapping::Registry.new + + MappingRegistry.set( + GoogleSearchResult, + ::SOAP::SOAPStruct, + ::SOAP::Mapping::Registry::TypedStructFactory, + { :type => XSD::QName.new("urn:GoogleSearch", "GoogleSearchResult") } + ) + MappingRegistry.set( + ResultElementArray, + ::SOAP::SOAPArray, + ::SOAP::Mapping::Registry::TypedArrayFactory, + { :type => XSD::QName.new("urn:GoogleSearch", "ResultElement") } + ) + MappingRegistry.set( + DirectoryCategoryArray, + ::SOAP::SOAPArray, + ::SOAP::Mapping::Registry::TypedArrayFactory, + { :type => XSD::QName.new("urn:GoogleSearch", "DirectoryCategory") } + ) + MappingRegistry.set( + ResultElement, + ::SOAP::SOAPStruct, + ::SOAP::Mapping::Registry::TypedStructFactory, + { :type => XSD::QName.new("urn:GoogleSearch", "ResultElement") } + ) + MappingRegistry.set( + DirectoryCategory, + ::SOAP::SOAPStruct, + ::SOAP::Mapping::Registry::TypedStructFactory, + { :type => XSD::QName.new("urn:GoogleSearch", "DirectoryCategory") } + ) + + Methods = [ + ["doGetCachedPage", "doGetCachedPage", [ + ["in", "key", + [SOAP::SOAPString]], + ["in", "url", + [SOAP::SOAPString]], + ["retval", "return", + [SOAP::SOAPBase64]]], + "urn:GoogleSearchAction", "urn:GoogleSearch"], + ["doSpellingSuggestion", "doSpellingSuggestion", [ + ["in", "key", + [SOAP::SOAPString]], + ["in", "phrase", + [SOAP::SOAPString]], + ["retval", "return", + [SOAP::SOAPString]]], + "urn:GoogleSearchAction", "urn:GoogleSearch"], + ["doGoogleSearch", "doGoogleSearch", [ + ["in", "key", + [SOAP::SOAPString]], + ["in", "q", + [SOAP::SOAPString]], + ["in", "start", + [SOAP::SOAPInt]], + ["in", "maxResults", + [SOAP::SOAPInt]], + ["in", "filter", + [SOAP::SOAPBoolean]], + ["in", "restrict", + [SOAP::SOAPString]], + ["in", "safeSearch", + [SOAP::SOAPBoolean]], + ["in", "lr", + [SOAP::SOAPString]], + ["in", "ie", + [SOAP::SOAPString]], + ["in", "oe", + [SOAP::SOAPString]], + ["retval", "return", + [::SOAP::SOAPStruct, "urn:GoogleSearch", "GoogleSearchResult"]]], + "urn:GoogleSearchAction", "urn:GoogleSearch"] + ] + + DefaultEndpointUrl = "http://api.google.com/search/beta2" + + def initialize(endpoint_url = nil) + endpoint_url ||= DefaultEndpointUrl + super(endpoint_url, nil) + self.mapping_registry = MappingRegistry + init_methods + end + +private + + def init_methods + Methods.each do |name_as, name, params, soapaction, namespace| + qname = XSD::QName.new(namespace, name_as) + @proxy.add_method(qname, soapaction, name, params) + add_rpc_method_interface(name, params) + end + end +end + diff --git a/sample/wsdl/googleSearch/README b/sample/wsdl/googleSearch/README new file mode 100644 index 0000000000..461a5634dc --- /dev/null +++ b/sample/wsdl/googleSearch/README @@ -0,0 +1,6 @@ +wsdlDriver.rb: Do google search using WSDL. + +Whole files except wsdl.rb in this directory is created by wsdl2ruby.rb from +GoogleSearch.wsdl with options; + +% wsdl2ruby.rb --wsdl http://api.google.com/GoogleSearch.wsdl --classdef --client_skelton --servant_skelton --cgi_stub --standalone_server_stub --driver --force diff --git a/sample/wsdl/googleSearch/httpd.rb b/sample/wsdl/googleSearch/httpd.rb new file mode 100644 index 0000000000..ee8ab09f50 --- /dev/null +++ b/sample/wsdl/googleSearch/httpd.rb @@ -0,0 +1,15 @@ +#!/usr/bin/env ruby + +require 'webrick' +require 'getopts' + +getopts "", 'r:', 'p:8808' + +s = WEBrick::HTTPServer.new( + :BindAddress => "0.0.0.0", + :Port => $OPT_p.to_i, + :DocumentRoot => $OPT_r || ".", + :CGIPathEnv => ENV['PATH'] +) +trap(:INT){ s.shutdown } +s.start diff --git a/sample/wsdl/googleSearch/sampleClient.rb b/sample/wsdl/googleSearch/sampleClient.rb new file mode 100644 index 0000000000..38e7253ef5 --- /dev/null +++ b/sample/wsdl/googleSearch/sampleClient.rb @@ -0,0 +1,56 @@ +#!/usr/bin/env ruby + +# This file is a sample based on GoogleSearchClient.rb, which can be +# generated by WSDL file and wsdl2ruby.rb. +# +# $ wsdl2ruby.rb --type client --force \ +# --wsdl http://api.google.com/GoogleSearch.wsdl +# +# See wsdlDriver.rb to use WSDL file directly (slow). +require 'GoogleSearchDriver.rb' + +endpoint_url = ARGV.shift +obj = GoogleSearchPort.new(endpoint_url) + +# Uncomment the below line to see SOAP wiredumps. +# obj.wiredump_dev = STDERR + +# SYNOPSIS +# doGoogleSearch(key, q, start, maxResults, filter, restrict, safeSearch, lr, ie, oe) +# +# ARGS +# key - {http://www.w3.org/2001/XMLSchema}string +# q - {http://www.w3.org/2001/XMLSchema}string +# start - {http://www.w3.org/2001/XMLSchema}int +# maxResults - {http://www.w3.org/2001/XMLSchema}int +# filter - {http://www.w3.org/2001/XMLSchema}boolean +# restrict - {http://www.w3.org/2001/XMLSchema}string +# safeSearch - {http://www.w3.org/2001/XMLSchema}boolean +# lr - {http://www.w3.org/2001/XMLSchema}string +# ie - {http://www.w3.org/2001/XMLSchema}string +# oe - {http://www.w3.org/2001/XMLSchema}string +# +# RETURNS +# return GoogleSearchResult - {urn:GoogleSearch}GoogleSearchResult +# +# RAISES +# N/A +# +key = q = start = maxResults = filter = restrict = safeSearch = lr = ie = oe = nil +key = File.open(File.expand_path("~/.google_key")).read.chomp +q = "Ruby" +start = 0 +maxResults = 10 +filter = false +restrict = "" +safeSearch = false +lr = "" +ie = "utf-8" +oe = "utf-8" +result = obj.doGoogleSearch(key, q, start, maxResults, filter, restrict, safeSearch, lr, ie, oe) + +result.resultElements.each do |ele| + puts "== #{ele.title}: #{ele.URL}" + puts ele.snippet + puts +end diff --git a/sample/wsdl/googleSearch/sjissearch.sh b/sample/wsdl/googleSearch/sjissearch.sh new file mode 100644 index 0000000000..b8efb20647 --- /dev/null +++ b/sample/wsdl/googleSearch/sjissearch.sh @@ -0,0 +1,3 @@ +#!/bin/sh - + +ruby -Ks wsdlDriver.rb 'Ruby ‚È‚Ð' diff --git a/sample/wsdl/googleSearch/wsdlDriver.rb b/sample/wsdl/googleSearch/wsdlDriver.rb new file mode 100644 index 0000000000..e8b91abfe2 --- /dev/null +++ b/sample/wsdl/googleSearch/wsdlDriver.rb @@ -0,0 +1,23 @@ +#require 'uconv' +require 'soap/wsdlDriver' + +word = ARGV.shift +# You must get key from http://www.google.com/apis/ to use Google Web APIs. +key = File.open(File.expand_path("~/.google_key")).read.chomp + +GOOGLE_WSDL = 'http://api.google.com/GoogleSearch.wsdl' +# GOOGLE_WSDL = 'GoogleSearch.wsdl' + +def html2rd(str) + str.gsub(%r((.*?)), '((*\\1*))').strip +end + + +google = SOAP::WSDLDriverFactory.new(GOOGLE_WSDL).create_driver +#google.wiredump_dev = STDERR +result = google.doGoogleSearch( key, word, 0, 10, false, "", false, "", 'utf-8', 'utf-8' ) +result.resultElements.each do |ele| + puts "== #{html2rd(ele.title)}: #{ele.URL}" + puts html2rd(ele.snippet) + puts +end diff --git a/sample/wsdl/raa/raa.wsdl b/sample/wsdl/raa/raa.wsdl new file mode 100644 index 0000000000..78376893dd --- /dev/null +++ b/sample/wsdl/raa/raa.wsdl @@ -0,0 +1,264 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sample/wsdl/raa/soap4r.rb b/sample/wsdl/raa/soap4r.rb new file mode 100644 index 0000000000..a70b3b5b21 --- /dev/null +++ b/sample/wsdl/raa/soap4r.rb @@ -0,0 +1,31 @@ +#!/usr/bin/env ruby + +require 'soap/wsdlDriver' +wsdl = 'http://www.ruby-lang.org/xmlns/soap/interface/RAA/0.0.2/' +raa = SOAP::WSDLDriverFactory.new(wsdl).create_driver +raa.generate_explicit_type = true +p "WSDL loaded." + +class Category + def initialize(major, minor) + @major = major + @minor = minor + end +end + +p raa.getAllListings().sort + +p raa.getProductTree() + +p raa.getInfoFromCategory(Category.new("Library", "XML")) + +t = Time.at(Time.now.to_i - 24 * 3600) +p raa.getModifiedInfoSince(t) + +p raa.getModifiedInfoSince(DateTime.new(t.year, t.mon, t.mday, t.hour, t.min, t.sec)) + +o = raa.getInfoFromName("SOAP4R") +p o.type +p o.owner.name +p o + diff --git a/test/soap/calc/calc.rb b/test/soap/calc/calc.rb new file mode 100644 index 0000000000..6bc78803b3 --- /dev/null +++ b/test/soap/calc/calc.rb @@ -0,0 +1,17 @@ +module CalcService + def self.add(lhs, rhs) + lhs + rhs + end + + def self.sub(lhs, rhs) + lhs - rhs + end + + def self.multi(lhs, rhs) + lhs * rhs + end + + def self.div(lhs, rhs) + lhs / rhs + end +end diff --git a/test/soap/calc/calc2.rb b/test/soap/calc/calc2.rb new file mode 100644 index 0000000000..e9cf6bbca7 --- /dev/null +++ b/test/soap/calc/calc2.rb @@ -0,0 +1,29 @@ +class CalcService2 + def initialize(value = 0) + @value = value + end + + def set(value) + @value = value + end + + def get + @value + end + + def +(rhs) + @value + rhs + end + + def -(rhs) + @value - rhs + end + + def *(rhs) + @value * rhs + end + + def /(rhs) + @value / rhs + end +end diff --git a/test/soap/calc/server.cgi b/test/soap/calc/server.cgi new file mode 100644 index 0000000000..c4fa687550 --- /dev/null +++ b/test/soap/calc/server.cgi @@ -0,0 +1,15 @@ +#!/usr/bin/env ruby + +require 'soap/rpc/cgistub' + +class CalcServer < SOAP::RPC::CGIStub + def initialize(*arg) + super + + require 'calc' + servant = CalcService + add_servant(servant, 'http://tempuri.org/calcService') + end +end + +status = CalcServer.new('CalcServer', nil).start diff --git a/test/soap/calc/server.rb b/test/soap/calc/server.rb new file mode 100644 index 0000000000..12a3968b5a --- /dev/null +++ b/test/soap/calc/server.rb @@ -0,0 +1,17 @@ +#!/usr/bin/env ruby + +require 'soap/rpc/standaloneServer' +require 'calc' + +class CalcServer < SOAP::RPC::StandaloneServer + def initialize(*arg) + super + + servant = CalcService + add_servant(servant, 'http://tempuri.org/calcService') + end +end + +if $0 == __FILE__ + status = CalcServer.new('CalcServer', nil, '0.0.0.0', 7000).start +end diff --git a/test/soap/calc/server2.rb b/test/soap/calc/server2.rb new file mode 100644 index 0000000000..735721de64 --- /dev/null +++ b/test/soap/calc/server2.rb @@ -0,0 +1,20 @@ +#!/usr/bin/env ruby + +require 'soap/rpc/standaloneServer' +require 'calc2' + +class CalcServer2 < SOAP::RPC::StandaloneServer + def on_init + servant = CalcService2.new + add_method(servant, 'set', 'newValue') + add_method(servant, 'get') + add_method_as(servant, '+', 'add', 'lhs') + add_method_as(servant, '-', 'sub', 'lhs') + add_method_as(servant, '*', 'multi', 'lhs') + add_method_as(servant, '/', 'div', 'lhs') + end +end + +if $0 == __FILE__ + status = CalcServer2.new('CalcServer', 'http://tempuri.org/calcService', '0.0.0.0', 7000).start +end diff --git a/test/soap/calc/test_calc.rb b/test/soap/calc/test_calc.rb new file mode 100644 index 0000000000..191295bac0 --- /dev/null +++ b/test/soap/calc/test_calc.rb @@ -0,0 +1,41 @@ +require 'test/unit' +require 'soap/rpc/driver' + +dir = File.dirname(__FILE__) +$:.push(dir) +require 'server.rb' +$:.delete(dir) + +class TestCalc < Test::Unit::TestCase + def setup + @server = CalcServer.new(self.class.name, nil, '0.0.0.0', 7000) + @t = Thread.new { + @server.start + } + while @server.server.status != :Running + sleep 0.1 + end + @calc = SOAP::RPC::Driver.new('http://localhost:7000/', 'http://tempuri.org/calcService') + @calc.add_method('add', 'lhs', 'rhs') + @calc.add_method('sub', 'lhs', 'rhs') + @calc.add_method('multi', 'lhs', 'rhs') + @calc.add_method('div', 'lhs', 'rhs') + end + + def teardown + @server.server.shutdown + @t.kill + end + + def test_calc + assert_equal(3, @calc.add(1, 2)) + assert_equal(-1.1, @calc.sub(1.1, 2.2)) + assert_equal(2.42, @calc.multi(1.1, 2.2)) + assert_equal(2, @calc.div(5, 2)) + assert_equal(2.5, @calc.div(5.0, 2)) + assert_equal(1.0/0.0, @calc.div(1.1, 0)) + assert_raises(ZeroDivisionError) do + @calc.div(1, 0) + end + end +end diff --git a/test/soap/calc/test_calc2.rb b/test/soap/calc/test_calc2.rb new file mode 100644 index 0000000000..05a43b60ec --- /dev/null +++ b/test/soap/calc/test_calc2.rb @@ -0,0 +1,43 @@ +require 'test/unit' +require 'soap/rpc/driver' + +dir = File.dirname(__FILE__) +$:.push(dir) +require 'server2.rb' +$:.delete(dir) + +class TestCalc2 < Test::Unit::TestCase + def setup + @server = CalcServer2.new('CalcServer', 'http://tempuri.org/calcService', '0.0.0.0', 7000) + @t = Thread.new { + @server.start + } + while @server.server.status != :Running + sleep 0.1 + end + @var = SOAP::RPC::Driver.new('http://localhost:7000/', 'http://tempuri.org/calcService') + @var.add_method('set', 'newValue') + @var.add_method('get') + @var.add_method_as('+', 'add', 'rhs') + @var.add_method_as('-', 'sub', 'rhs') + @var.add_method_as('*', 'multi', 'rhs') + @var.add_method_as('/', 'div', 'rhs') + end + + def teardown + @server.server.shutdown + @t.kill + end + + def test_calc2 + assert_equal(1, @var.set(1)) + assert_equal(3, @var + 2) + assert_equal(-1.2, @var - 2.2) + assert_equal(2.2, @var * 2.2) + assert_equal(0, @var / 2) + assert_equal(0.5, @var / 2.0) + assert_raises(ZeroDivisionError) do + @var / 0 + end + end +end diff --git a/test/soap/calc/test_calc_cgi.rb b/test/soap/calc/test_calc_cgi.rb new file mode 100644 index 0000000000..682356e525 --- /dev/null +++ b/test/soap/calc/test_calc_cgi.rb @@ -0,0 +1,42 @@ +require 'test/unit' +require 'soap/rpc/driver' +require 'webrick' + +class TestCalcCGI < Test::Unit::TestCase + def setup + @server = WEBrick::HTTPServer.new( + :BindAddress => "0.0.0.0", + :Port => 8808, + :DocumentRoot => File.dirname(File.expand_path(__FILE__)), + :CGIPathEnv => ENV['PATH'] + ) + @t = Thread.new { + @server.start + } + while @server.status != :Running + sleep 0.1 + end + @calc = SOAP::RPC::Driver.new('http://localhost:8808/server.cgi', 'http://tempuri.org/calcService') + @calc.add_method('add', 'lhs', 'rhs') + @calc.add_method('sub', 'lhs', 'rhs') + @calc.add_method('multi', 'lhs', 'rhs') + @calc.add_method('div', 'lhs', 'rhs') + end + + def teardown + @server.shutdown + @t.kill + end + + def test_calc + assert_equal(3, @calc.add(1, 2)) + assert_equal(-1.1, @calc.sub(1.1, 2.2)) + assert_equal(2.42, @calc.multi(1.1, 2.2)) + assert_equal(2, @calc.div(5, 2)) + assert_equal(2.5, @calc.div(5.0, 2)) + assert_equal(1.0/0.0, @calc.div(1.1, 0)) + assert_raises(ZeroDivisionError) do + @calc.div(1, 0) + end + end +end diff --git a/test/soap/helloworld/hw_s.rb b/test/soap/helloworld/hw_s.rb new file mode 100644 index 0000000000..b917f72fc0 --- /dev/null +++ b/test/soap/helloworld/hw_s.rb @@ -0,0 +1,17 @@ +require 'soap/rpc/standaloneServer' + +class HelloWorldServer < SOAP::RPC::StandaloneServer + def on_init + @log.level = Logger::Severity::DEBUG + add_method(self, 'hello_world', 'from') + end + + def hello_world(from) + "Hello World, from #{ from }" + end +end + +if $0 == __FILE__ + server = HelloWorldServer.new('hws', 'urn:hws', '0.0.0.0', 2000) + server.start +end diff --git a/test/soap/helloworld/test_helloworld.rb b/test/soap/helloworld/test_helloworld.rb new file mode 100644 index 0000000000..ccf77f9c1c --- /dev/null +++ b/test/soap/helloworld/test_helloworld.rb @@ -0,0 +1,31 @@ +require 'test/unit' +require 'soap/rpc/driver' + +dir = File.dirname(__FILE__) +$:.push(dir) +require 'hw_s.rb' +$:.delete(dir) + +class TestHelloWorld < Test::Unit::TestCase + def setup + @server = HelloWorldServer.new('hws', 'urn:hws', '0.0.0.0', 2000) + @t = Thread.new { + @server.start + } + while @server.server.status != :Running + sleep 0.1 + end + @client = SOAP::RPC::Driver.new('http://localhost:2000/', 'urn:hws') + @client.add_method("hello_world", "from") + end + + def teardown + @server.server.shutdown + @t.kill + end + + def test_hello_world + assert_equal("Hello World, from NaHi", @client.hello_world("NaHi")) + assert_equal("Hello World, from <&>", @client.hello_world("<&>")) + end +end diff --git a/test/soap/marshal/cmarshal.rb b/test/soap/marshal/cmarshal.rb new file mode 100644 index 0000000000..d1f66b5b1e --- /dev/null +++ b/test/soap/marshal/cmarshal.rb @@ -0,0 +1,142 @@ +module CMarshal + class << self + public + def ruby + class << self + def dump(o) + Marshal.dump(o) + end + + def load(o) + Marshal.load(o) + end + end + end + + def amarshal + require 'amarshal' + class << self + def dump(o) + AMarshal.dump(o) + end + + def load(o) + AMarshal.load(o) + end + end + end + + def to_src + require 'to_src' + ToSrc.independent(false) + class << self + def dump(o) + ToSrc.reset + o.to_src + end + + def load(o) + eval(o) + end + end + end + + def to_source + require 'ToSource' + class << self + def dump(o) + o.to_source + end + + def load(o) + eval(o) + end + end + end + + class ClXmlSerialContainer + attr_accessor :var + end + + def clxmlserial + require 'cl/xmlserial' + ClXmlSerialContainer.instance_eval { include XmlSerialization } + class << self + def dump(o) + c = ClXmlSerialContainer.new + c.var = o + c.to_xml + end + + def load(o) + ClXmlSerialContainer.from_xml(o).var + end + end + end + + def soap4r + require 'soap/marshal' + class << self + def dump(o) + SOAP::Marshal.dump(o) + end + + def load(o) + SOAP::Marshal.load(o) + end + end + end + + def xmarshal + require 'xmarshal' + class << self + def dump(o) + XMarshal.dump(o) + end + + def load(o) + XMarshal.load(o) + end + end + end + + def xmlrpc + require 'xmlrpc/marshal' + class << self + def dump(o) + XMLRPC::Marshal.dump(o) + end + + def load(o) + XMLRPC::Marshal.load(o) + end + end + end + + def tmarshal + require 'tmarshal' + class << self + def dump(o) + TMarshal.dump(o) + end + + def load(o) + TMarshal.restore(o) + end + end + end + + def yaml + require 'yaml' + class << self + def dump(o) + o.to_yaml + end + + def load(o) + YAML.load(o) + end + end + end + end +end diff --git a/test/soap/marshal/test_digraph.rb b/test/soap/marshal/test_digraph.rb new file mode 100644 index 0000000000..93f555ffc1 --- /dev/null +++ b/test/soap/marshal/test_digraph.rb @@ -0,0 +1,45 @@ +require 'test/unit' +require 'soap/marshal' + +class Node; include SOAP::Marshallable + attr_reader :first, :second, :str + + def initialize(*init_next) + @first = init_next[0] + @second = init_next[1] + end +end + +class TestDigraph < Test::Unit::TestCase + def setup + @n9 = Node.new + @n81 = Node.new(@n9) + @n82 = Node.new(@n9) + @n7 = Node.new(@n81, @n82) + @n61 = Node.new(@n7) + @n62 = Node.new(@n7) + @n5 = Node.new(@n61, @n62) + @n41 = Node.new(@n5) + @n42 = Node.new(@n5) + @n3 = Node.new(@n41, @n42) + @n21 = Node.new(@n3) + @n22 = Node.new(@n3) + @n1 = Node.new(@n21, @n22) + end + + def test_marshal + f = File.open("digraph_marshalled_string.soap", "wb") + SOAP::Marshal.dump(@n1, f) + f.close + str = File.open("digraph_marshalled_string.soap").read + newnode = SOAP::Marshal.unmarshal(str) + assert_equal(newnode.first.first.__id__, newnode.second.first.__id__) + assert_equal(newnode.first.first.first.first.__id__, newnode.second.first.second.first.__id__) + end + + def teardown + if File.exist?("digraph_marshalled_string.soap") + File.unlink("digraph_marshalled_string.soap") + end + end +end diff --git a/test/soap/marshal/test_marshal.rb b/test/soap/marshal/test_marshal.rb new file mode 100644 index 0000000000..8a1b82946f --- /dev/null +++ b/test/soap/marshal/test_marshal.rb @@ -0,0 +1,301 @@ +require 'test/unit' + +dir = File.dirname(__FILE__) +$:.push(dir) +require 'cmarshal' +$:.delete(dir) + +CMarshal.soap4r + + +module MarshalTestLib + def encode(o) + #self.class::MarshalClass.dump(o) + CMarshal.dump(o) + end + + def decode(s) + #self.class::MarshalClass.load(s) + CMarshal.load(s) + end + + def marshaltest(o1) + #o1.instance_eval { remove_instance_variable '@v' if defined? @v } + str = encode(o1) + print str, "\n" if $DEBUG + o2 = decode(str) + o2 + end + + def marshal_equal(o1) + o2 = marshaltest(o1) + assert_equal(o1.class, o2.class, caller[0]) + iv1 = o1.instance_variables.sort + iv2 = o2.instance_variables.sort + assert_equal(iv1, iv2) + val1 = iv1.map {|var| o1.instance_eval {eval var}} + val2 = iv1.map {|var| o2.instance_eval {eval var}} + assert_equal(val1, val2, caller[0]) + if block_given? + assert_equal(yield(o1), yield(o2), caller[0]) + else + assert_equal(o1, o2, caller[0]) + end + end + + class MyObject; def initialize(v) @v = v end; attr_reader :v; end + def test_object + o1 = Object.new + o1.instance_eval { @iv = 1 } + marshal_equal(o1) {|o| o.instance_eval { @iv }} + end + + def test_object_subclass + marshal_equal(MyObject.new(2)) {|o| o.v} + end + + class MyArray < Array; def initialize(v, *args) super args; @v = v; end end + def test_array + marshal_equal([1,2,3]) + end + + def test_array_subclass + marshal_equal(MyArray.new(0, 1,2,3)) + end + + class MyException < Exception; def initialize(v, *args) super(*args); @v = v; end; attr_reader :v; end + def test_exception + marshal_equal(Exception.new('foo')) {|o| o.message} + end + + def test_exception_subclass + marshal_equal(MyException.new(20, "bar")) {|o| [o.message, o.v]} + end + + def test_false + marshal_equal(false) + end + + class MyHash < Hash; def initialize(v, *args) super(*args); @v = v; end end + def test_hash + marshal_equal({1=>2, 3=>4}) + end + + def test_hash_default + h = Hash.new(:default) + h[5] = 6 + marshal_equal(h) + end + + def test_hash_subclass + h = MyHash.new(7, 8) + h[4] = 5 + marshal_equal(h) + end + + def test_hash_default_proc + h = Hash.new {} + assert_raises(TypeError) { marshaltest(h) } + end + + def test_bignum + marshal_equal(-0x4000_0000_0000_0001) + marshal_equal(-0x4000_0001) + marshal_equal(0x4000_0000) + marshal_equal(0x4000_0000_0000_0000) + end + + def test_fixnum + marshal_equal(-0x4000_0000) + marshal_equal(-1) + marshal_equal(0) + marshal_equal(1) + marshal_equal(0x3fff_ffff) + end + + def test_float + marshal_equal(-1.0) + marshal_equal(0.0) + marshal_equal(1.0) + end + + def test_float_inf_nan + marshal_equal(1.0/0.0) + marshal_equal(-1.0/0.0) + marshal_equal(0.0/0.0) {|o| o.nan?} + marshal_equal(-0.0) {|o| 1.0/o} + end + + class MyRange < Range; def initialize(v, *args) super(*args); @v = v; end end + def test_range + marshal_equal(1..2) + marshal_equal(1...3) + end + + def test_range_subclass + STDERR.puts("test_range_subclass: known bug should be fixed.") + return + marshal_equal(MyRange.new(4,5,8, false)) + end + + class MyRegexp < Regexp; def initialize(v, *args) super(*args); @v = v; end end + def test_regexp + marshal_equal(/a/) + end + + def test_regexp_subclass + STDERR.puts("test_regexp_subclass: known bug should be fixed.") + return + marshal_equal(MyRegexp.new(10, "a")) + end + + class MyString < String; def initialize(v, *args) super(*args); @v = v; end end + def test_string + marshal_equal("abc") + end + + def test_string_subclass + marshal_equal(MyString.new(10, "a")) + end + + MyStruct = Struct.new("MyStruct", :a, :b) + class MySubStruct < MyStruct; def initialize(v, *args) super(*args); @v = v; end end + def test_struct + marshal_equal(MyStruct.new(1,2)) + end + + def test_struct_subclass + marshal_equal(MySubStruct.new(10,1,2)) + end + + def test_symbol + marshal_equal(:a) + marshal_equal(:a?) + marshal_equal(:a!) + marshal_equal(:a=) + marshal_equal(:|) + marshal_equal(:^) + marshal_equal(:&) + marshal_equal(:<=>) + marshal_equal(:==) + marshal_equal(:===) + marshal_equal(:=~) + marshal_equal(:>) + marshal_equal(:>=) + marshal_equal(:<) + marshal_equal(:<=) + marshal_equal(:<<) + marshal_equal(:>>) + marshal_equal(:+) + marshal_equal(:-) + marshal_equal(:*) + marshal_equal(:/) + marshal_equal(:%) + marshal_equal(:**) + marshal_equal(:~) + marshal_equal(:+@) + marshal_equal(:-@) + marshal_equal(:[]) + marshal_equal(:[]=) + marshal_equal(:`) #` + marshal_equal("a b".intern) + end + + class MyTime < Time; def initialize(v, *args) super(*args); @v = v; end end + def test_time + marshal_equal(Time.now) + end + + def test_time_subclass + STDERR.puts("test_time_subclass: known bug should be fixed.") + return + marshal_equal(MyTime.new(10)) + end + + def test_true + marshal_equal(true) + end + + def test_nil + marshal_equal(nil) + end + + def test_share + o = [:share] + o1 = [o, o] + o2 = marshaltest(o1) + assert_same(o2.first, o2.last) + end + + class CyclicRange < Range + def <=>(other); true; end + end + def test_range_cyclic + o1 = CyclicRange.allocate + o1.instance_eval { initialize(o1, o1) } + o2 = marshaltest(o1) + assert_same(o2, o2.begin) + assert_same(o2, o2.end) + end + + def test_singleton + o = Object.new + def o.m() end + assert_raises(TypeError) { marshaltest(o) } + o = Object.new + class << o + @v = 1 + end + assert_raises(TypeError) { marshaltest(o) } + assert_raises(TypeError) { marshaltest(ARGF) } + assert_raises(TypeError) { marshaltest(ENV) } + end + + module Mod1 end + module Mod2 end + def test_extend + o = Object.new + o.extend Module.new + assert_raises(TypeError) { marshaltest(o) } + + STDERR.puts("test_range_subclass: known bug should be fixed.") + return + o = Object.new + o.extend Mod1 + marshal_equal(o) { |obj| obj.kind_of? Mod1 } + o = Object.new + o.extend Mod1 + o.extend Mod2 + marshal_equal(o) {|obj| class << obj; ancestors end} + end + + def test_anonymous + c = Class.new + assert_raises(TypeError) { marshaltest(c) } + o = c.new + assert_raises(TypeError) { marshaltest(o) } + m = Module.new + assert_raises(TypeError) { marshaltest(m) } + end + + def test_string_empty + marshal_equal("") + end + + def test_string_crlf + marshal_equal("\r\n") + end + + def test_string_escape + marshal_equal("\0<;;>\1;;") + end + + MyStruct2 = Struct.new(:a, :b) + def test_struct_toplevel + marshal_equal(MyStruct2.new(1,2)) + end +end + +class TestMarshal < Test::Unit::TestCase + include MarshalTestLib +end diff --git a/test/soap/marshal/test_struct.rb b/test/soap/marshal/test_struct.rb new file mode 100644 index 0000000000..6596fb7a33 --- /dev/null +++ b/test/soap/marshal/test_struct.rb @@ -0,0 +1,38 @@ +require 'test/unit' +require 'soap/marshal' + +Foo1 = Struct.new("Foo1", :m) +Foo2 = Struct.new(:m) +class Foo3 + attr_accessor :m +end + +class TestStruct < Test::Unit::TestCase + def test_foo1 + org = Foo1.new + org.m = org + obj = convert(org) + assert_equal(Foo1, obj.class) + assert_equal(obj.m, obj) + end + + def test_foo2 + org = Foo2.new + org.m = org + obj = convert(org) + assert_equal(Foo2, obj.class) + assert_equal(obj.m, obj) + end + + def test_foo3 + org = Foo3.new + org.m = org + obj = convert(org) + assert_equal(Foo3, obj.class) + assert_equal(obj.m, obj) + end + + def convert(obj) + SOAP::Marshal.unmarshal(SOAP::Marshal.marshal(obj)) + end +end diff --git a/test/soap/test_basetype.rb b/test/soap/test_basetype.rb new file mode 100644 index 0000000000..80903633b1 --- /dev/null +++ b/test/soap/test_basetype.rb @@ -0,0 +1,951 @@ +require 'test/unit' +require 'soap/baseData' + +class TestSOAP < Test::Unit::TestCase + def setup + # Nothing to do. + end + + def teardown + # Nothing to do. + end + + def assert_parsed_result(klass, str) + o = klass.new(str) + assert_equal(str, o.to_s) + end + + def test_SOAPNil + o = SOAP::SOAPNil.new + assert_equal(XSD::Namespace, o.type.namespace) + assert_equal(XSD::NilLiteral, o.type.name) + assert_equal(nil, o.data) + assert_equal(true, o.is_nil) + + o = SOAP::SOAPNil.new(nil) + assert_equal(true, o.is_nil) + assert_equal(nil, o.data) + assert_equal("", o.to_s) + o = SOAP::SOAPNil.new('var') + assert_equal(false, o.is_nil) + assert_equal('var', o.data) + assert_equal('var', o.to_s) + end + + def test_SOAPString + o = SOAP::SOAPString.new + assert_equal(XSD::Namespace, o.type.namespace) + assert_equal(XSD::StringLiteral, o.type.name) + assert_equal(nil, o.data) + assert_equal(true, o.is_nil) + + str = "abc" + assert_equal(str, SOAP::SOAPString.new(str).data) + assert_equal(str, SOAP::SOAPString.new(str).to_s) + assert_raises(XSD::ValueSpaceError) do + SOAP::SOAPString.new("\0") + end + assert_raises(XSD::ValueSpaceError) do + p SOAP::SOAPString.new("\xC0\xC0").to_s + end + end + + def test_SOAPBoolean + o = SOAP::SOAPBoolean.new + assert_equal(XSD::Namespace, o.type.namespace) + assert_equal(XSD::BooleanLiteral, o.type.name) + assert_equal(nil, o.data) + assert_equal(true, o.is_nil) + + targets = [ + ["true", true], + ["1", true], + ["false", false], + ["0", false], + ] + targets.each do |data, expected| + assert_equal(expected, SOAP::SOAPBoolean.new(data).data) + assert_equal(expected.to_s, SOAP::SOAPBoolean.new(data).to_s) + end + + assert_raises(XSD::ValueSpaceError) do + SOAP::SOAPBoolean.new("nil").to_s + end + end + + def test_SOAPDecimal + o = SOAP::SOAPDecimal.new + assert_equal(XSD::Namespace, o.type.namespace) + assert_equal(XSD::DecimalLiteral, o.type.name) + assert_equal(nil, o.data) + assert_equal(true, o.is_nil) + + targets = [ + 0, + 1000000000, + -9999999999, + 12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890, + 12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890, + -1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789, + ] + targets.each do |dec| + assert_equal(dec.to_s, SOAP::SOAPDecimal.new(dec).data) + end + + targets = [ + "0", + "0.00000001", + "1000000000", + "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890", + "-12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123.45678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789", + ] + targets.each do |str| + assert_equal(str, SOAP::SOAPDecimal.new(str).to_s) + end + + targets = [ + ["-0", "0"], + ["+0", "0"], + ["0.0", "0"], + ["-0.0", "0"], + ["+0.0", "0"], + ["0.", "0"], + [".0", "0"], + [ + "+0.12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890", + "0.1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" + ], + [ + ".0000012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890", + "0.000001234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" + ], + [ + "-12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890.", + "-12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" + ], + ] + targets.each do |data, expected| + assert_equal(expected, SOAP::SOAPDecimal.new(data).to_s) + end + + targets = [ + "0.000000000000a", + "00a.0000000000001", + "+-5", + ] + targets.each do |d| + assert_raises(XSD::ValueSpaceError) do + SOAP::SOAPDecimal.new(d) + end + end + end + + def test_SOAPFloat + o = SOAP::SOAPFloat.new + assert_equal(XSD::Namespace, o.type.namespace) + assert_equal(XSD::FloatLiteral, o.type.name) + assert_equal(nil, o.data) + assert_equal(true, o.is_nil) + + targets = [ + 3.14159265358979, + 12.34e36, + 1.4e-45, + -1.4e-45, + ] + targets.each do |f| + assert_equal(f, SOAP::SOAPFloat.new(f).data) + end + + targets = [ + "3.141592654", + "1.234e+37", + "1.4e-45", + "-1.4e-45", + ] + targets.each do |f| + assert_equal(f, SOAP::SOAPFloat.new(f).to_s) + end + + targets = [ + [3, "3"], # should be 3.0? + [-2, "-2"], # ditto + [3.14159265358979, "3.141592654"], + [12.34e36, "1.234e+37"], + [1.4e-45, "1.4e-45"], + [-1.4e-45, "-1.4e-45"], + ["1.4e", "1.4"], + ["12.34E36", "1.234e+37"], + ["1.4E-45", "1.4e-45"], + ["-1.4E-45", "-1.4e-45"], + ["1.4E", "1.4"], + ] + targets.each do |f, str| + assert_equal(str, SOAP::SOAPFloat.new(f).to_s) + end + + assert_equal("0", SOAP::SOAPFloat.new(+0.0).to_s) + assert_equal("-0", SOAP::SOAPFloat.new(-0.0).to_s) + assert(SOAP::SOAPFloat.new(0.0/0.0).data.nan?) + assert_equal("INF", SOAP::SOAPFloat.new(1.0/0.0).to_s) + assert_equal(1, SOAP::SOAPFloat.new(1.0/0.0).data.infinite?) + assert_equal("-INF", SOAP::SOAPFloat.new(-1.0/0.0).to_s) + assert_equal(-1, SOAP::SOAPFloat.new(-1.0/0.0).data.infinite?) + + targets = [ + "0.000000000000a", + "00a.0000000000001", + "+-5", + "5_0", + ] + targets.each do |d| + assert_raises(XSD::ValueSpaceError) do + SOAP::SOAPFloat.new(d) + end + end + end + + def test_SOAPDouble + o = SOAP::SOAPDouble.new + assert_equal(XSD::Namespace, o.type.namespace) + assert_equal(XSD::DoubleLiteral, o.type.name) + assert_equal(nil, o.data) + assert_equal(true, o.is_nil) + + targets = [ + 3.14159265358979, + 12.34e36, + 1.4e-45, + -1.4e-45, + ] + targets.each do |f| + assert_equal(f, SOAP::SOAPDouble.new(f).data) + end + + targets = [ + "3.14159265358979", + "1.234e+37", + "1.4e-45", + "-1.4e-45", + ] + targets.each do |f| + assert_equal(f, SOAP::SOAPDouble.new(f).to_s) + end + + targets = [ + [3, "3"], # should be 3.0? + [-2, "-2"], # ditto. + [3.14159265358979, "3.14159265358979"], + [12.34e36, "1.234e+37"], + [1.4e-45, "1.4e-45"], + [-1.4e-45, "-1.4e-45"], + ["1.4e", "1.4"], + ["12.34E36", "1.234e+37"], + ["1.4E-45", "1.4e-45"], + ["-1.4E-45", "-1.4e-45"], + ["1.4E", "1.4"], + ] + targets.each do |f, str| + assert_equal(str, SOAP::SOAPDouble.new(f).to_s) + end + + assert_equal("0", SOAP::SOAPFloat.new(+0.0).to_s) + assert_equal("-0", SOAP::SOAPFloat.new(-0.0).to_s) + assert_equal("NaN", SOAP::SOAPDouble.new(0.0/0.0).to_s) + assert(SOAP::SOAPDouble.new(0.0/0.0).data.nan?) + assert_equal("INF", SOAP::SOAPDouble.new(1.0/0.0).to_s) + assert_equal(1, SOAP::SOAPDouble.new(1.0/0.0).data.infinite?) + assert_equal("-INF", SOAP::SOAPDouble.new(-1.0/0.0).to_s) + assert_equal(-1, SOAP::SOAPDouble.new(-1.0/0.0).data.infinite?) + + targets = [ + "0.000000000000a", + "00a.0000000000001", + "+-5", + ] + targets.each do |d| + assert_raises(XSD::ValueSpaceError) do + SOAP::SOAPDouble.new(d) + end + end + end + + def test_SOAPDuration + o = SOAP::SOAPDuration.new + assert_equal(XSD::Namespace, o.type.namespace) + assert_equal(XSD::DurationLiteral, o.type.name) + assert_equal(nil, o.data) + assert_equal(true, o.is_nil) + + targets = [ + "P1Y2M3DT4H5M6S", + "P1234Y5678M9012DT3456H7890M1234.5678S", + "P0DT3456H7890M1234.5678S", + "P1234Y5678M9012D", + "-P1234Y5678M9012DT3456H7890M1234.5678S", + "P5678M9012DT3456H7890M1234.5678S", + "-P1234Y9012DT3456H7890M1234.5678S", + "+P1234Y5678MT3456H7890M1234.5678S", + "P1234Y5678M9012DT7890M1234.5678S", + "-P1234Y5678M9012DT3456H1234.5678S", + "+P1234Y5678M9012DT3456H7890M", + "P123400000000000Y", + "-P567800000000000M", + "+P901200000000000D", + "P0DT345600000000000H", + "-P0DT789000000000000M", + "+P0DT123400000000000.000000000005678S", + "P1234YT1234.5678S", + "-P5678MT7890M", + "+P9012DT3456H", + ] + targets.each do |str| + assert_parsed_result(SOAP::SOAPDuration, str) + end + + targets = [ + ["P0Y0M0DT0H0M0S", + "P0D"], + ["-P0DT0S", + "-P0D"], + ["P01234Y5678M9012DT3456H7890M1234.5678S", + "P1234Y5678M9012DT3456H7890M1234.5678S"], + ["P1234Y005678M9012DT3456H7890M1234.5678S", + "P1234Y5678M9012DT3456H7890M1234.5678S"], + ["P1234Y5678M0009012DT3456H7890M1234.5678S", + "P1234Y5678M9012DT3456H7890M1234.5678S"], + ["P1234Y5678M9012DT00003456H7890M1234.5678S", + "P1234Y5678M9012DT3456H7890M1234.5678S"], + ["P1234Y5678M9012DT3456H000007890M1234.5678S", + "P1234Y5678M9012DT3456H7890M1234.5678S"], + ["P1234Y5678M9012DT3456H7890M0000001234.5678S", + "P1234Y5678M9012DT3456H7890M1234.5678S"], + ] + targets.each do |data, expected| + assert_equal(expected, SOAP::SOAPDuration.new(data).to_s) + end + end + + def test_SOAPDateTime + o = SOAP::SOAPDateTime.new + assert_equal(XSD::Namespace, o.type.namespace) + assert_equal(XSD::DateTimeLiteral, o.type.name) + assert_equal(nil, o.data) + assert_equal(true, o.is_nil) + + targets = [ + "2002-05-18T16:52:20Z", + "0001-01-01T00:00:00Z", + "9999-12-31T23:59:59Z", + "19999-12-31T23:59:59Z", + "2002-12-31T23:59:59.999Z", + "2002-12-31T23:59:59.001Z", + "2002-12-31T23:59:59.99999999999999999999Z", + "2002-12-31T23:59:59.00000000000000000001Z", + "2002-12-31T23:59:59+09:00", + "2002-12-31T23:59:59+00:01", + "2002-12-31T23:59:59-00:01", + "2002-12-31T23:59:59-23:59", + "2002-12-31T23:59:59.00000000000000000001+13:30", + "-2002-05-18T16:52:20Z", + "-4711-12-31T23:59:59Z", + "-4713-01-01T12:00:00Z", + "-19999-12-31T23:59:59Z", + "-2002-12-31T23:59:59+00:01", + "-0001-12-31T23:59:59.00000000000000000001+13:30", + ] + targets.each do |str| + assert_parsed_result(SOAP::SOAPDateTime, str) + end + + targets = [ + ["2002-12-31T23:59:59.00", + "2002-12-31T23:59:59Z"], + ["2002-12-31T23:59:59+00:00", + "2002-12-31T23:59:59Z"], + ["2002-12-31T23:59:59-00:00", + "2002-12-31T23:59:59Z"], + ["-2002-12-31T23:59:59.00", + "-2002-12-31T23:59:59Z"], + ["-2002-12-31T23:59:59+00:00", + "-2002-12-31T23:59:59Z"], + ["-2002-12-31T23:59:59-00:00", + "-2002-12-31T23:59:59Z"], + ] + targets.each do |data, expected| + assert_equal(expected, SOAP::SOAPDateTime.new(data).to_s) + end + + targets = [ + "1-05-18T16:52:20Z", + "05-18T16:52:20Z", + "2002-05T16:52:20Z", + "2002-05-18T16:52Z", + "", + ] + targets.each do |d| + assert_raises(XSD::ValueSpaceError, d.to_s) do + SOAP::SOAPDateTime.new(d) + end + end + end + + def test_SOAPTime + o = SOAP::SOAPTime.new + assert_equal(XSD::Namespace, o.type.namespace) + assert_equal(XSD::TimeLiteral, o.type.name) + assert_equal(nil, o.data) + assert_equal(true, o.is_nil) + + targets = [ + "16:52:20Z", + "00:00:00Z", + "23:59:59Z", + "23:59:59.999Z", + "23:59:59.001Z", + "23:59:59.99999999999999999999Z", + "23:59:59.00000000000000000001Z", + "23:59:59+09:00", + "23:59:59+00:01", + "23:59:59-00:01", + "23:59:59-23:59", + "23:59:59.00000000000000000001+13:30", + "23:59:59+00:01", + ] + targets.each do |str| + assert_parsed_result(SOAP::SOAPTime, str) + end + + targets = [ + ["23:59:59.00", + "23:59:59Z"], + ["23:59:59+00:00", + "23:59:59Z"], + ["23:59:59-00:00", + "23:59:59Z"], + ] + targets.each do |data, expected| + assert_equal(expected, SOAP::SOAPTime.new(data).to_s) + end + end + + def test_SOAPDate + o = SOAP::SOAPDate.new + assert_equal(XSD::Namespace, o.type.namespace) + assert_equal(XSD::DateLiteral, o.type.name) + assert_equal(nil, o.data) + assert_equal(true, o.is_nil) + + targets = [ + "2002-05-18Z", + "0001-01-01Z", + "9999-12-31Z", + "19999-12-31Z", + "2002-12-31+09:00", + "2002-12-31+00:01", + "2002-12-31-00:01", + "2002-12-31-23:59", + "2002-12-31+13:30", + "-2002-05-18Z", + "-19999-12-31Z", + "-2002-12-31+00:01", + "-0001-12-31+13:30", + ] + targets.each do |str| + assert_parsed_result(SOAP::SOAPDate, str) + end + + targets = [ + ["2002-12-31", + "2002-12-31Z"], + ["2002-12-31+00:00", + "2002-12-31Z"], + ["2002-12-31-00:00", + "2002-12-31Z"], + ["-2002-12-31", + "-2002-12-31Z"], + ["-2002-12-31+00:00", + "-2002-12-31Z"], + ["-2002-12-31-00:00", + "-2002-12-31Z"], + ] + targets.each do |data, expected| + assert_equal(expected, SOAP::SOAPDate.new(data).to_s) + end + end + + def test_SOAPGYearMonth + o = SOAP::SOAPGYearMonth.new + assert_equal(XSD::Namespace, o.type.namespace) + assert_equal(XSD::GYearMonthLiteral, o.type.name) + assert_equal(nil, o.data) + assert_equal(true, o.is_nil) + + targets = [ + "2002-05Z", + "0001-01Z", + "9999-12Z", + "19999-12Z", + "2002-12+09:00", + "2002-12+00:01", + "2002-12-00:01", + "2002-12-23:59", + "2002-12+13:30", + "-2002-05Z", + "-19999-12Z", + "-2002-12+00:01", + "-0001-12+13:30", + ] + targets.each do |str| + assert_parsed_result(SOAP::SOAPGYearMonth, str) + end + + targets = [ + ["2002-12", + "2002-12Z"], + ["2002-12+00:00", + "2002-12Z"], + ["2002-12-00:00", + "2002-12Z"], + ["-2002-12", + "-2002-12Z"], + ["-2002-12+00:00", + "-2002-12Z"], + ["-2002-12-00:00", + "-2002-12Z"], + ] + targets.each do |data, expected| + assert_equal(expected, SOAP::SOAPGYearMonth.new(data).to_s) + end + end + + def test_SOAPGYear + o = SOAP::SOAPGYear.new + assert_equal(XSD::Namespace, o.type.namespace) + assert_equal(XSD::GYearLiteral, o.type.name) + assert_equal(nil, o.data) + assert_equal(true, o.is_nil) + + targets = [ + "2002Z", + "0001Z", + "9999Z", + "19999Z", + "2002+09:00", + "2002+00:01", + "2002-00:01", + "2002-23:59", + "2002+13:30", + "-2002Z", + "-19999Z", + "-2002+00:01", + "-0001+13:30", + ] + targets.each do |str| + assert_parsed_result(SOAP::SOAPGYear, str) + end + + targets = [ + ["2002", + "2002Z"], + ["2002+00:00", + "2002Z"], + ["2002-00:00", + "2002Z"], + ["-2002", + "-2002Z"], + ["-2002+00:00", + "-2002Z"], + ["-2002-00:00", + "-2002Z"], + ] + targets.each do |data, expected| + assert_equal(expected, SOAP::SOAPGYear.new(data).to_s) + end + end + + def test_SOAPGMonthDay + o = SOAP::SOAPGMonthDay.new + assert_equal(XSD::Namespace, o.type.namespace) + assert_equal(XSD::GMonthDayLiteral, o.type.name) + assert_equal(nil, o.data) + assert_equal(true, o.is_nil) + + targets = [ + "05-18Z", + "01-01Z", + "12-31Z", + "12-31+09:00", + "12-31+00:01", + "12-31-00:01", + "12-31-23:59", + "12-31+13:30", + ] + targets.each do |str| + assert_parsed_result(SOAP::SOAPGMonthDay, str) + end + + targets = [ + ["12-31", + "12-31Z"], + ["12-31+00:00", + "12-31Z"], + ["12-31-00:00", + "12-31Z"], + ] + targets.each do |data, expected| + assert_equal(expected, SOAP::SOAPGMonthDay.new(data).to_s) + end + end + + def test_SOAPGDay + o = SOAP::SOAPGDay.new + assert_equal(XSD::Namespace, o.type.namespace) + assert_equal(XSD::GDayLiteral, o.type.name) + assert_equal(nil, o.data) + assert_equal(true, o.is_nil) + + targets = [ + "18Z", + "01Z", + "31Z", + "31+09:00", + "31+00:01", + "31-00:01", + "31-23:59", + "31+13:30", + ] + targets.each do |str| + assert_parsed_result(SOAP::SOAPGDay, str) + end + + targets = [ + ["31", + "31Z"], + ["31+00:00", + "31Z"], + ["31-00:00", + "31Z"], + ] + targets.each do |data, expected| + assert_equal(expected, SOAP::SOAPGDay.new(data).to_s) + end + end + + def test_SOAPGMonth + o = SOAP::SOAPGMonth.new + assert_equal(XSD::Namespace, o.type.namespace) + assert_equal(XSD::GMonthLiteral, o.type.name) + assert_equal(nil, o.data) + assert_equal(true, o.is_nil) + + targets = [ + "05Z", + "01Z", + "12Z", + "12+09:00", + "12+00:01", + "12-00:01", + "12-23:59", + "12+13:30", + ] + targets.each do |str| + assert_parsed_result(SOAP::SOAPGMonth, str) + end + + targets = [ + ["12", + "12Z"], + ["12+00:00", + "12Z"], + ["12-00:00", + "12Z"], + ] + targets.each do |data, expected| + assert_equal(expected, SOAP::SOAPGMonth.new(data).to_s) + end + end + + def test_SOAPHexBinary + o = SOAP::SOAPHexBinary.new + assert_equal(XSD::Namespace, o.type.namespace) + assert_equal(XSD::HexBinaryLiteral, o.type.name) + assert_equal(nil, o.data) + assert_equal(true, o.is_nil) + + targets = [ + "abcdef", + "\xe3\x81\xaa\xe3\x81\xb2", + "\0", + "", + ] + targets.each do |str| + assert_equal(str, SOAP::SOAPHexBinary.new(str).string) + assert_equal(str.unpack("H*")[0].tr('a-f', 'A-F'), + SOAP::SOAPHexBinary.new(str).data) + o = SOAP::SOAPHexBinary.new + o.set_encoded(str.unpack("H*")[0].tr('a-f', 'A-F')) + assert_equal(str, o.string) + o.set_encoded(str.unpack("H*")[0].tr('A-F', 'a-f')) + assert_equal(str, o.string) + end + + targets = [ + "0FG7", + "0fg7", + ] + targets.each do |d| + assert_raises(XSD::ValueSpaceError, d.to_s) do + o = SOAP::SOAPHexBinary.new + o.set_encoded(d) + p o.string + end + end + end + + def test_SOAPBase64Binary + o = SOAP::SOAPBase64.new + assert_equal(SOAP::EncodingNamespace, o.type.namespace) + assert_equal(SOAP::Base64Literal, o.type.name) + assert_equal(nil, o.data) + assert_equal(true, o.is_nil) + + targets = [ + "abcdef", + "\xe3\x81\xaa\xe3\x81\xb2", + "\0", + "", + ] + targets.each do |str| + assert_equal(str, SOAP::SOAPBase64.new(str).string) + assert_equal([str].pack("m").chomp, SOAP::SOAPBase64.new(str).data) + o = SOAP::SOAPBase64.new + o.set_encoded([str].pack("m").chomp) + assert_equal(str, o.string) + end + + targets = [ + "-", + "*", + ] + targets.each do |d| + assert_raises(XSD::ValueSpaceError, d.to_s) do + o = SOAP::SOAPBase64.new + o.set_encoded(d) + p o.string + end + end + end + + def test_SOAPAnyURI + o = SOAP::SOAPAnyURI.new + assert_equal(XSD::Namespace, o.type.namespace) + assert_equal(XSD::AnyURILiteral, o.type.name) + assert_equal(nil, o.data) + assert_equal(true, o.is_nil) + + # Too few tests here I know. Believe uri module. :) + targets = [ + "foo", + "http://foo", + "http://foo/bar/baz", + "http://foo/bar#baz", + "http://foo/bar%20%20?a+b", + "HTTP://FOO/BAR%20%20?A+B", + ] + targets.each do |str| + assert_parsed_result(SOAP::SOAPAnyURI, str) + end + end + + def test_SOAPQName + o = SOAP::SOAPQName.new + assert_equal(XSD::Namespace, o.type.namespace) + assert_equal(XSD::QNameLiteral, o.type.name) + assert_equal(nil, o.data) + assert_equal(true, o.is_nil) + + # More strict test is needed but current implementation allows all non-':' + # chars like ' ', C0 or C1... + targets = [ + "foo", + "foo:bar", + "a:b", + ] + targets.each do |str| + assert_parsed_result(SOAP::SOAPQName, str) + end + end + + + ### + ## Derived types + # + + def test_SOAPInteger + o = SOAP::SOAPInteger.new + assert_equal(XSD::Namespace, o.type.namespace) + assert_equal(XSD::IntegerLiteral, o.type.name) + assert_equal(nil, o.data) + assert_equal(true, o.is_nil) + + targets = [ + 0, + 1000000000, + -9999999999, + 12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890, + 12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890, + -1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789, + ] + targets.each do |int| + assert_equal(int, SOAP::SOAPInteger.new(int).data) + end + + targets = [ + "0", + "1000000000", + "-9999999999", + "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890", + "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890", + "-1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789", + ] + targets.each do |str| + assert_equal(str, SOAP::SOAPInteger.new(str).to_s) + end + + targets = [ + ["-0", "0"], + ["+0", "0"], + ["000123", "123"], + ["-000123", "-123"], + [ + "+12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890", + "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" + ], + ] + targets.each do |data, expected| + assert_equal(expected, SOAP::SOAPInteger.new(data).to_s) + end + + targets = [ + "0.0", + "-5.2", + "0.000000000000a", + "+-5", + "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890." + ] + targets.each do |d| + assert_raises(XSD::ValueSpaceError) do + SOAP::SOAPInteger.new(d) + end + end + end + + def test_SOAPLong + o = SOAP::SOAPLong.new + assert_equal(XSD::Namespace, o.type.namespace) + assert_equal(XSD::LongLiteral, o.type.name) + assert_equal(nil, o.data) + assert_equal(true, o.is_nil) + + targets = [ + 0, + 123, + -123, + 9223372036854775807, + -9223372036854775808, + ] + targets.each do |lng| + assert_equal(lng, SOAP::SOAPLong.new(lng).data) + end + + targets = [ + "0", + "123", + "-123", + "9223372036854775807", + "-9223372036854775808", + ] + targets.each do |str| + assert_equal(str, SOAP::SOAPLong.new(str).to_s) + end + + targets = [ + ["-0", "0"], + ["+0", "0"], + ["000123", "123"], + ["-000123", "-123"], + ] + targets.each do |data, expected| + assert_equal(expected, SOAP::SOAPLong.new(data).to_s) + end + + targets = [ + 9223372036854775808, + -9223372036854775809, + "0.0", + "-5.2", + "0.000000000000a", + "+-5", + ] + targets.each do |d| + assert_raises(XSD::ValueSpaceError) do + SOAP::SOAPLong.new(d) + end + end + end + + def test_SOAPInt + o = SOAP::SOAPInt.new + assert_equal(XSD::Namespace, o.type.namespace) + assert_equal(XSD::IntLiteral, o.type.name) + assert_equal(nil, o.data) + assert_equal(true, o.is_nil) + + targets = [ + 0, + 123, + -123, + 2147483647, + -2147483648, + ] + targets.each do |lng| + assert_equal(lng, SOAP::SOAPInt.new(lng).data) + end + + targets = [ + "0", + "123", + "-123", + "2147483647", + "-2147483648", + ] + targets.each do |str| + assert_equal(str, SOAP::SOAPInt.new(str).to_s) + end + + targets = [ + ["-0", "0"], + ["+0", "0"], + ["000123", "123"], + ["-000123", "-123"], + ] + targets.each do |data, expected| + assert_equal(expected, SOAP::SOAPInt.new(data).to_s) + end + + targets = [ + 2147483648, + -2147483649, + "0.0", + "-5.2", + "0.000000000000a", + "+-5", + ] + targets.each do |d| + assert_raises(XSD::ValueSpaceError) do + SOAP::SOAPInt.new(d) + end + end + end +end diff --git a/test/soap/test_soapelement.rb b/test/soap/test_soapelement.rb new file mode 100644 index 0000000000..b45ebd9bb0 --- /dev/null +++ b/test/soap/test_soapelement.rb @@ -0,0 +1,114 @@ +require 'test/unit' +require '../lib/soap/baseData' + +class TestSOAPElement < Test::Unit::TestCase + include SOAP + + def setup + # Nothing to do. + end + + def teardown + # Nothing to do. + end + + def d(elename = nil, text = nil) + elename ||= n(nil, nil) + if text + SOAPElement.new(elename, text) + else + SOAPElement.new(elename) # do not merge. + end + end + + def n(namespace, name) + XSD::QName.new(namespace, name) + end + + def test_initialize + elename = n(nil, nil) + obj = d(elename) + assert_equal(elename, obj.elename) + assert_equal(LiteralNamespace, obj.encodingstyle) + assert_equal({}, obj.extraattr) + assert_equal([], obj.precedents) + assert_equal(false, obj.qualified) + assert_equal(nil, obj.text) + assert(obj.members.empty?) + + obj = d("foo", "text") + assert_equal(n(nil, "foo"), obj.elename) + assert_equal("text", obj.text) + end + + def test_add + obj = d() + child = d("abc") + obj.add(child) + assert(obj.key?("abc")) + assert_same(child, obj["abc"]) + assert_same(child, obj.abc) + def obj.foo; 1; end + child = d("foo") + obj.add(child) + assert_equal(1, obj.foo) + assert_equal(child, obj.var_foo) + child = d("_?a?b_") + obj.add(child) + assert_equal(child, obj.var__ab_) + end + + def test_member + obj = d() + c1 = d("c1") + obj.add(c1) + c2 = d("c2") + obj.add(c2) + assert(obj.key?("c1")) + assert(obj.key?("c2")) + assert_equal(c1, obj["c1"]) + assert_equal(c2, obj["c2"]) + c22 = d("c22") + obj["c2"] = c22 + assert(obj.key?("c2")) + assert_equal(c22, obj["c2"]) + assert_equal(["c1", "c2"], obj.members.sort) + # + k_expect = ["c1", "c2"] + v_expect = [c1, c22] + obj.each do |k, v| + assert(k_expect.include?(k)) + assert(v_expect.include?(v)) + k_expect.delete(k) + v_expect.delete(v) + end + assert(k_expect.empty?) + assert(v_expect.empty?) + end + + def test_to_obj + obj = d("root") + ct1 = d("ct1", "t1") + obj.add(ct1) + c2 = d("c2") + ct2 = d("ct2", "t2") + c2.add(ct2) + obj.add(c2) + assert_equal({ "ct1" => "t1", "c2" => { "ct2" => "t2" }}, obj.to_obj) + # + assert_equal(nil, d().to_obj) + assert_equal("abc", d(nil, "abc").to_obj) + assert_equal(nil, d("abc", nil).to_obj) + end + + def test_from_obj + source = { "ct1" => "t1", "c2" => { "ct2" => "t2" }} + assert_equal(source, SOAPElement.from_obj(source).to_obj) + source = { "1" => nil } + assert_equal(source, SOAPElement.from_obj(source).to_obj) + source = {} + assert_equal(nil, SOAPElement.from_obj(source).to_obj) # not {} + source = nil + assert_equal(nil, SOAPElement.from_obj(source).to_obj) + end +end diff --git a/test/wsdl/emptycomplextype.wsdl b/test/wsdl/emptycomplextype.wsdl new file mode 100644 index 0000000000..a4504c95ba --- /dev/null +++ b/test/wsdl/emptycomplextype.wsdl @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + doc doc doc. + + + + + + diff --git a/test/wsdl/test_emptycomplextype.rb b/test/wsdl/test_emptycomplextype.rb new file mode 100644 index 0000000000..f44c72f840 --- /dev/null +++ b/test/wsdl/test_emptycomplextype.rb @@ -0,0 +1,14 @@ +require 'test/unit' +require 'wsdl/parser' + +class TestWSDL < Test::Unit::TestCase + def self.setup(filename) + @@filename = filename + end + + def test_wsdl + @wsdl = WSDL::Parser.new.parse(File.open(@@filename).read) + end +end + +TestWSDL.setup(File.join(File.dirname(__FILE__), 'emptycomplextype.wsdl')) diff --git a/test/xsd/test_xmlschemaparser.rb b/test/xsd/test_xmlschemaparser.rb new file mode 100644 index 0000000000..6e3839a0d2 --- /dev/null +++ b/test/xsd/test_xmlschemaparser.rb @@ -0,0 +1,14 @@ +require 'test/unit' +require 'wsdl/xmlSchema/parser' + +class TestXMLSchemaParser < Test::Unit::TestCase + def self.setup(filename) + @@filename = filename + end + + def test_wsdl + @wsdl = WSDL::XMLSchema::Parser.new.parse(File.open(@@filename).read) + end +end + +TestXMLSchemaParser.setup(File.join(File.dirname(__FILE__), 'xmlschema.xml')) diff --git a/test/xsd/test_xsd.rb b/test/xsd/test_xsd.rb new file mode 100644 index 0000000000..7fb5052f43 --- /dev/null +++ b/test/xsd/test_xsd.rb @@ -0,0 +1,976 @@ +require 'test/unit' +require 'xsd/datatypes' + +class TestXSD < Test::Unit::TestCase + def setup + # Nothing to do. + end + + def teardown + # Nothing to do. + end + + def assert_parsed_result(klass, str) + o = klass.new(str) + assert_equal(str, o.to_s) + end + + def test_NSDBase + o = XSD::NSDBase.new + assert_equal(nil, o.type) + end + + def test_XSDBase + o = XSD::XSDAnySimpleType.new + assert_equal(nil, o.data) + assert_equal(true, o.is_nil) + assert_equal('', o.to_s) + end + + def test_XSDNil + o = XSD::XSDNil.new + assert_equal(XSD::Namespace, o.type.namespace) + assert_equal(XSD::NilLiteral, o.type.name) + assert_equal(nil, o.data) + assert_equal(true, o.is_nil) + + o = XSD::XSDNil.new(nil) + assert_equal(true, o.is_nil) + assert_equal(nil, o.data) + assert_equal("", o.to_s) + o = XSD::XSDNil.new('var') + assert_equal(false, o.is_nil) + assert_equal('var', o.data) + assert_equal('var', o.to_s) + end + + def test_XSDString + o = XSD::XSDString.new + assert_equal(XSD::Namespace, o.type.namespace) + assert_equal(XSD::StringLiteral, o.type.name) + assert_equal(nil, o.data) + assert_equal(true, o.is_nil) + + str = "abc" + assert_equal(str, XSD::XSDString.new(str).data) + assert_equal(str, XSD::XSDString.new(str).to_s) + assert_raises(XSD::ValueSpaceError) do + XSD::XSDString.new("\0") + end + assert_raises(XSD::ValueSpaceError) do + p XSD::XSDString.new("\xC0\xC0").to_s + end + end + + def test_XSDBoolean + o = XSD::XSDBoolean.new + assert_equal(XSD::Namespace, o.type.namespace) + assert_equal(XSD::BooleanLiteral, o.type.name) + assert_equal(nil, o.data) + assert_equal(true, o.is_nil) + + targets = [ + ["true", true], + ["1", true], + ["false", false], + ["0", false], + ] + targets.each do |data, expected| + assert_equal(expected, XSD::XSDBoolean.new(data).data) + assert_equal(expected.to_s, XSD::XSDBoolean.new(data).to_s) + end + + assert_raises(XSD::ValueSpaceError) do + XSD::XSDBoolean.new("nil").to_s + end + end + + def test_XSDDecimal + o = XSD::XSDDecimal.new + assert_equal(XSD::Namespace, o.type.namespace) + assert_equal(XSD::DecimalLiteral, o.type.name) + assert_equal(nil, o.data) + assert_equal(true, o.is_nil) + + targets = [ + 0, + 1000000000, + -9999999999, + 12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890, + 12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890, + -1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789, + ] + targets.each do |dec| + assert_equal(dec.to_s, XSD::XSDDecimal.new(dec).data) + end + + targets = [ + "0", + "0.00000001", + "1000000000", + "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890", + "-12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123.45678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789", + ] + targets.each do |str| + assert_equal(str, XSD::XSDDecimal.new(str).to_s) + end + + targets = [ + ["-0", "0"], + ["+0", "0"], + ["0.0", "0"], + ["-0.0", "0"], + ["+0.0", "0"], + ["0.", "0"], + [".0", "0"], + [ + "+0.12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890", + "0.1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" + ], + [ + ".0000012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890", + "0.000001234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" + ], + [ + "-12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890.", + "-12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" + ], + ] + targets.each do |data, expected| + assert_equal(expected, XSD::XSDDecimal.new(data).to_s) + end + + targets = [ + "0.000000000000a", + "00a.0000000000001", + "+-5", + ] + targets.each do |d| + assert_raises(XSD::ValueSpaceError) do + XSD::XSDDecimal.new(d) + end + end + end + + def test_XSDFloat + o = XSD::XSDFloat.new + assert_equal(XSD::Namespace, o.type.namespace) + assert_equal(XSD::FloatLiteral, o.type.name) + assert_equal(nil, o.data) + assert_equal(true, o.is_nil) + + targets = [ + 3.14159265358979, + 12.34e36, + 1.4e-45, + -1.4e-45, + ] + targets.each do |f| + assert_equal(f, XSD::XSDFloat.new(f).data) + end + + targets = [ + "3.141592654", + "1.234e+37", + "1.4e-45", + "-1.4e-45", + ] + targets.each do |f| + assert_equal(f, XSD::XSDFloat.new(f).to_s) + end + + targets = [ + [3, "3"], # should be 3.0? + [-2, "-2"], # ditto + [3.14159265358979, "3.141592654"], + [12.34e36, "1.234e+37"], + [1.4e-45, "1.4e-45"], + [-1.4e-45, "-1.4e-45"], + ["1.4e", "1.4"], + ["12.34E36", "1.234e+37"], + ["1.4E-45", "1.4e-45"], + ["-1.4E-45", "-1.4e-45"], + ["1.4E", "1.4"], + ] + targets.each do |f, str| + assert_equal(str, XSD::XSDFloat.new(f).to_s) + end + + assert_equal("0", XSD::XSDFloat.new(+0.0).to_s) + assert_equal("-0", XSD::XSDFloat.new(-0.0).to_s) + assert(XSD::XSDFloat.new(0.0/0.0).data.nan?) + assert_equal("INF", XSD::XSDFloat.new(1.0/0.0).to_s) + assert_equal(1, XSD::XSDFloat.new(1.0/0.0).data.infinite?) + assert_equal("-INF", XSD::XSDFloat.new(-1.0/0.0).to_s) + assert_equal(-1, XSD::XSDFloat.new(-1.0/0.0).data.infinite?) + + targets = [ + "0.000000000000a", + "00a.0000000000001", + "+-5", + "5_0", + ] + targets.each do |d| + assert_raises(XSD::ValueSpaceError) do + XSD::XSDFloat.new(d) + end + end + end + + def test_XSDDouble + o = XSD::XSDDouble.new + assert_equal(XSD::Namespace, o.type.namespace) + assert_equal(XSD::DoubleLiteral, o.type.name) + assert_equal(nil, o.data) + assert_equal(true, o.is_nil) + + targets = [ + 3.14159265358979, + 12.34e36, + 1.4e-45, + -1.4e-45, + ] + targets.each do |f| + assert_equal(f, XSD::XSDDouble.new(f).data) + end + + targets = [ + "3.14159265358979", + "1.234e+37", + "1.4e-45", + "-1.4e-45", + ] + targets.each do |f| + assert_equal(f, XSD::XSDDouble.new(f).to_s) + end + + targets = [ + [3, "3"], # should be 3.0? + [-2, "-2"], # ditto. + [3.14159265358979, "3.14159265358979"], + [12.34e36, "1.234e+37"], + [1.4e-45, "1.4e-45"], + [-1.4e-45, "-1.4e-45"], + ["1.4e", "1.4"], + ["12.34E36", "1.234e+37"], + ["1.4E-45", "1.4e-45"], + ["-1.4E-45", "-1.4e-45"], + ["1.4E", "1.4"], + ] + targets.each do |f, str| + assert_equal(str, XSD::XSDDouble.new(f).to_s) + end + + assert_equal("0", XSD::XSDFloat.new(+0.0).to_s) + assert_equal("-0", XSD::XSDFloat.new(-0.0).to_s) + assert_equal("NaN", XSD::XSDDouble.new(0.0/0.0).to_s) + assert(XSD::XSDDouble.new(0.0/0.0).data.nan?) + assert_equal("INF", XSD::XSDDouble.new(1.0/0.0).to_s) + assert_equal(1, XSD::XSDDouble.new(1.0/0.0).data.infinite?) + assert_equal("-INF", XSD::XSDDouble.new(-1.0/0.0).to_s) + assert_equal(-1, XSD::XSDDouble.new(-1.0/0.0).data.infinite?) + + targets = [ + "0.000000000000a", + "00a.0000000000001", + "+-5", + ] + targets.each do |d| + assert_raises(XSD::ValueSpaceError) do + XSD::XSDDouble.new(d) + end + end + end + + def test_XSDDuration + o = XSD::XSDDuration.new + assert_equal(XSD::Namespace, o.type.namespace) + assert_equal(XSD::DurationLiteral, o.type.name) + assert_equal(nil, o.data) + assert_equal(true, o.is_nil) + + targets = [ + "P1Y2M3DT4H5M6S", + "P1234Y5678M9012DT3456H7890M1234.5678S", + "P0DT3456H7890M1234.5678S", + "P1234Y5678M9012D", + "-P1234Y5678M9012DT3456H7890M1234.5678S", + "P5678M9012DT3456H7890M1234.5678S", + "-P1234Y9012DT3456H7890M1234.5678S", + "+P1234Y5678MT3456H7890M1234.5678S", + "P1234Y5678M9012DT7890M1234.5678S", + "-P1234Y5678M9012DT3456H1234.5678S", + "+P1234Y5678M9012DT3456H7890M", + "P123400000000000Y", + "-P567800000000000M", + "+P901200000000000D", + "P0DT345600000000000H", + "-P0DT789000000000000M", + "+P0DT123400000000000.000000000005678S", + "P1234YT1234.5678S", + "-P5678MT7890M", + "+P9012DT3456H", + ] + targets.each do |str| + assert_parsed_result(XSD::XSDDuration, str) + end + + targets = [ + ["P0Y0M0DT0H0M0S", + "P0D"], + ["-P0DT0S", + "-P0D"], + ["P01234Y5678M9012DT3456H7890M1234.5678S", + "P1234Y5678M9012DT3456H7890M1234.5678S"], + ["P1234Y005678M9012DT3456H7890M1234.5678S", + "P1234Y5678M9012DT3456H7890M1234.5678S"], + ["P1234Y5678M0009012DT3456H7890M1234.5678S", + "P1234Y5678M9012DT3456H7890M1234.5678S"], + ["P1234Y5678M9012DT00003456H7890M1234.5678S", + "P1234Y5678M9012DT3456H7890M1234.5678S"], + ["P1234Y5678M9012DT3456H000007890M1234.5678S", + "P1234Y5678M9012DT3456H7890M1234.5678S"], + ["P1234Y5678M9012DT3456H7890M0000001234.5678S", + "P1234Y5678M9012DT3456H7890M1234.5678S"], + ] + targets.each do |data, expected| + assert_equal(expected, XSD::XSDDuration.new(data).to_s) + end + end + + def test_XSDDateTime + o = XSD::XSDDateTime.new + assert_equal(XSD::Namespace, o.type.namespace) + assert_equal(XSD::DateTimeLiteral, o.type.name) + assert_equal(nil, o.data) + assert_equal(true, o.is_nil) + + targets = [ + "2002-05-18T16:52:20Z", + "0001-01-01T00:00:00Z", + "9999-12-31T23:59:59Z", + "19999-12-31T23:59:59Z", + "2002-12-31T23:59:59.999Z", + "2002-12-31T23:59:59.001Z", + "2002-12-31T23:59:59.99999999999999999999Z", + "2002-12-31T23:59:59.00000000000000000001Z", + "2002-12-31T23:59:59+09:00", + "2002-12-31T23:59:59+00:01", + "2002-12-31T23:59:59-00:01", + "2002-12-31T23:59:59-23:59", + "2002-12-31T23:59:59.00000000000000000001+13:30", + "-2002-05-18T16:52:20Z", + "-4713-01-01T12:00:00Z", + "-2002-12-31T23:59:59+00:01", + "-0001-12-31T23:59:59.00000000000000000001+13:30", + ] + targets.each do |str| + assert_parsed_result(XSD::XSDDateTime, str) + end + + targets = [ + ["2002-12-31T23:59:59.00", + "2002-12-31T23:59:59Z"], + ["2002-12-31T23:59:59+00:00", + "2002-12-31T23:59:59Z"], + ["2002-12-31T23:59:59-00:00", + "2002-12-31T23:59:59Z"], + ["-2002-12-31T23:59:59.00", + "-2002-12-31T23:59:59Z"], + ["-2002-12-31T23:59:59+00:00", + "-2002-12-31T23:59:59Z"], + ["-2002-12-31T23:59:59-00:00", + "-2002-12-31T23:59:59Z"], + ] + targets.each do |data, expected| + assert_equal(expected, XSD::XSDDateTime.new(data).to_s) + end + + targets = [ + "0000-05-18T16:52:20Z", + "05-18T16:52:20Z", + "2002-05T16:52:20Z", + "2002-05-18T16:52Z", + "", + ] + targets.each do |d| + assert_raises(XSD::ValueSpaceError, d.to_s) do + XSD::XSDDateTime.new(d) + end + end + end + + def test_XSDTime + o = XSD::XSDTime.new + assert_equal(XSD::Namespace, o.type.namespace) + assert_equal(XSD::TimeLiteral, o.type.name) + assert_equal(nil, o.data) + assert_equal(true, o.is_nil) + + targets = [ + "16:52:20Z", + "00:00:00Z", + "23:59:59Z", + "23:59:59.999Z", + "23:59:59.001Z", + "23:59:59.99999999999999999999Z", + "23:59:59.00000000000000000001Z", + "23:59:59+09:00", + "23:59:59+00:01", + "23:59:59-00:01", + "23:59:59-23:59", + "23:59:59.00000000000000000001+13:30", + "23:59:59+00:01", + ] + targets.each do |str| + assert_parsed_result(XSD::XSDTime, str) + end + + targets = [ + ["23:59:59.00", + "23:59:59Z"], + ["23:59:59+00:00", + "23:59:59Z"], + ["23:59:59-00:00", + "23:59:59Z"], + ] + targets.each do |data, expected| + assert_equal(expected, XSD::XSDTime.new(data).to_s) + end + end + + def test_XSDDate + o = XSD::XSDDate.new + assert_equal(XSD::Namespace, o.type.namespace) + assert_equal(XSD::DateLiteral, o.type.name) + assert_equal(nil, o.data) + assert_equal(true, o.is_nil) + + targets = [ + "2002-05-18Z", + "0001-01-01Z", + "9999-12-31Z", + "19999-12-31Z", + "2002-12-31+09:00", + "2002-12-31+00:01", + "2002-12-31-00:01", + "2002-12-31-23:59", + "2002-12-31+13:30", + "-2002-05-18Z", + "-19999-12-31Z", + "-2002-12-31+00:01", + "-0001-12-31+13:30", + ] + targets.each do |str| + assert_parsed_result(XSD::XSDDate, str) + end + + targets = [ + ["2002-12-31", + "2002-12-31Z"], + ["2002-12-31+00:00", + "2002-12-31Z"], + ["2002-12-31-00:00", + "2002-12-31Z"], + ["-2002-12-31", + "-2002-12-31Z"], + ["-2002-12-31+00:00", + "-2002-12-31Z"], + ["-2002-12-31-00:00", + "-2002-12-31Z"], + ] + targets.each do |data, expected| + assert_equal(expected, XSD::XSDDate.new(data).to_s) + end + end +end + +class TestXSD2 < Test::Unit::TestCase + def setup + # Nothing to do. + end + + def teardown + # Nothing to do. + end + + def assert_parsed_result(klass, str) + o = klass.new(str) + assert_equal(str, o.to_s) + end + + def test_XSDGYearMonth + o = XSD::XSDGYearMonth.new + assert_equal(XSD::Namespace, o.type.namespace) + assert_equal(XSD::GYearMonthLiteral, o.type.name) + assert_equal(nil, o.data) + assert_equal(true, o.is_nil) + + targets = [ + "2002-05Z", + "0001-01Z", + "9999-12Z", + "19999-12Z", + "2002-12+09:00", + "2002-12+00:01", + "2002-12-00:01", + "2002-12-23:59", + "2002-12+13:30", + "-2002-05Z", + "-19999-12Z", + "-2002-12+00:01", + "-0001-12+13:30", + ] + targets.each do |str| + assert_parsed_result(XSD::XSDGYearMonth, str) + end + + targets = [ + ["2002-12", + "2002-12Z"], + ["2002-12+00:00", + "2002-12Z"], + ["2002-12-00:00", + "2002-12Z"], + ["-2002-12", + "-2002-12Z"], + ["-2002-12+00:00", + "-2002-12Z"], + ["-2002-12-00:00", + "-2002-12Z"], + ] + targets.each do |data, expected| + assert_equal(expected, XSD::XSDGYearMonth.new(data).to_s) + end + end + + def test_XSDGYear + o = XSD::XSDGYear.new + assert_equal(XSD::Namespace, o.type.namespace) + assert_equal(XSD::GYearLiteral, o.type.name) + assert_equal(nil, o.data) + assert_equal(true, o.is_nil) + + targets = [ + "2002Z", + "0001Z", + "9999Z", + "19999Z", + "2002+09:00", + "2002+00:01", + "2002-00:01", + "2002-23:59", + "2002+13:30", + "-2002Z", + "-19999Z", + "-2002+00:01", + "-0001+13:30", + ] + targets.each do |str| + assert_parsed_result(XSD::XSDGYear, str) + end + + targets = [ + ["2002", + "2002Z"], + ["2002+00:00", + "2002Z"], + ["2002-00:00", + "2002Z"], + ["-2002", + "-2002Z"], + ["-2002+00:00", + "-2002Z"], + ["-2002-00:00", + "-2002Z"], + ] + targets.each do |data, expected| + assert_equal(expected, XSD::XSDGYear.new(data).to_s) + end + end + + def test_XSDGMonthDay + o = XSD::XSDGMonthDay.new + assert_equal(XSD::Namespace, o.type.namespace) + assert_equal(XSD::GMonthDayLiteral, o.type.name) + assert_equal(nil, o.data) + assert_equal(true, o.is_nil) + + targets = [ + "05-18Z", + "01-01Z", + "12-31Z", + "12-31+09:00", + "12-31+00:01", + "12-31-00:01", + "12-31-23:59", + "12-31+13:30", + ] + targets.each do |str| + assert_parsed_result(XSD::XSDGMonthDay, str) + end + + targets = [ + ["12-31", + "12-31Z"], + ["12-31+00:00", + "12-31Z"], + ["12-31-00:00", + "12-31Z"], + ] + targets.each do |data, expected| + assert_equal(expected, XSD::XSDGMonthDay.new(data).to_s) + end + end + + def test_XSDGDay + o = XSD::XSDGDay.new + assert_equal(XSD::Namespace, o.type.namespace) + assert_equal(XSD::GDayLiteral, o.type.name) + assert_equal(nil, o.data) + assert_equal(true, o.is_nil) + + targets = [ + "18Z", + "01Z", + "31Z", + "31+09:00", + "31+00:01", + "31-00:01", + "31-23:59", + "31+13:30", + ] + targets.each do |str| + assert_parsed_result(XSD::XSDGDay, str) + end + + targets = [ + ["31", + "31Z"], + ["31+00:00", + "31Z"], + ["31-00:00", + "31Z"], + ] + targets.each do |data, expected| + assert_equal(expected, XSD::XSDGDay.new(data).to_s) + end + end + + def test_XSDGMonth + o = XSD::XSDGMonth.new + assert_equal(XSD::Namespace, o.type.namespace) + assert_equal(XSD::GMonthLiteral, o.type.name) + assert_equal(nil, o.data) + assert_equal(true, o.is_nil) + + targets = [ + "05Z", + "01Z", + "12Z", + "12+09:00", + "12+00:01", + "12-00:01", + "12-23:59", + "12+13:30", + ] + targets.each do |str| + assert_parsed_result(XSD::XSDGMonth, str) + end + + targets = [ + ["12", + "12Z"], + ["12+00:00", + "12Z"], + ["12-00:00", + "12Z"], + ] + targets.each do |data, expected| + assert_equal(expected, XSD::XSDGMonth.new(data).to_s) + end + end + + def test_XSDHexBinary + o = XSD::XSDHexBinary.new + assert_equal(XSD::Namespace, o.type.namespace) + assert_equal(XSD::HexBinaryLiteral, o.type.name) + assert_equal(nil, o.data) + assert_equal(true, o.is_nil) + + targets = [ + "abcdef", + "\xe3\x81\xaa\xe3\x81\xb2", + %Q(\0), + "", + ] + targets.each do |str| + assert_equal(str, XSD::XSDHexBinary.new(str).string) + assert_equal(str.unpack("H*")[0 ].tr('a-f', 'A-F'), + XSD::XSDHexBinary.new(str).data) + o = XSD::XSDHexBinary.new + o.set_encoded(str.unpack("H*")[0 ].tr('a-f', 'A-F')) + assert_equal(str, o.string) + o.set_encoded(str.unpack("H*")[0 ].tr('A-F', 'a-f')) + assert_equal(str, o.string) + end + + targets = [ + "0FG7", + "0fg7", + ] + targets.each do |d| + assert_raises(XSD::ValueSpaceError, d.to_s) do + o = XSD::XSDHexBinary.new + o.set_encoded(d) + p o.string + end + end + end + + def test_XSDBase64Binary + o = XSD::XSDBase64Binary.new + assert_equal(XSD::Namespace, o.type.namespace) + assert_equal(XSD::Base64BinaryLiteral, o.type.name) + assert_equal(nil, o.data) + assert_equal(true, o.is_nil) + + targets = [ + "abcdef", + "\xe3\x81\xaa\xe3\x81\xb2", + %Q(\0), + "", + ] + targets.each do |str| + assert_equal(str, XSD::XSDBase64Binary.new(str).string) + assert_equal([str ].pack("m").chomp, XSD::XSDBase64Binary.new(str).data) + o = XSD::XSDBase64Binary.new + o.set_encoded([str ].pack("m").chomp) + assert_equal(str, o.string) + end + + targets = [ + "-", + "*", + ] + targets.each do |d| + assert_raises(XSD::ValueSpaceError, d.to_s) do + o = XSD::XSDBase64Binary.new + o.set_encoded(d) + p o.string + end + end + end + + def test_XSDAnyURI + o = XSD::XSDAnyURI.new + assert_equal(XSD::Namespace, o.type.namespace) + assert_equal(XSD::AnyURILiteral, o.type.name) + assert_equal(nil, o.data) + assert_equal(true, o.is_nil) + + # Too few tests here I know. Believe uri module. :) + targets = [ + "foo", + "http://foo", + "http://foo/bar/baz", + "http://foo/bar#baz", + "http://foo/bar%20%20?a+b", + "HTTP://FOO/BAR%20%20?A+B", + ] + targets.each do |str| + assert_parsed_result(XSD::XSDAnyURI, str) + end + end + + def test_XSDQName + o = XSD::XSDQName.new + assert_equal(XSD::Namespace, o.type.namespace) + assert_equal(XSD::QNameLiteral, o.type.name) + assert_equal(nil, o.data) + assert_equal(true, o.is_nil) + + # More strict test is needed but current implementation allows all non-':' + # chars like ' ', C0 or C1... + targets = [ + "foo", + "foo:bar", + "a:b", + ] + targets.each do |str| + assert_parsed_result(XSD::XSDQName, str) + end + end + + + ### + ## Derived types + # + + def test_XSDInteger + o = XSD::XSDInteger.new + assert_equal(XSD::Namespace, o.type.namespace) + assert_equal(XSD::IntegerLiteral, o.type.name) + assert_equal(nil, o.data) + assert_equal(true, o.is_nil) + + targets = [ + 0, + 1000000000, + -9999999999, + 12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890, + 12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890, + -1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789, + ] + targets.each do |int| + assert_equal(int, XSD::XSDInteger.new(int).data) + end + + targets = [ + "0", + "1000000000", + "-9999999999", + "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890", + "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890", + "-1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789", + ] + targets.each do |str| + assert_equal(str, XSD::XSDInteger.new(str).to_s) + end + + targets = [ + ["-0", "0"], + ["+0", "0"], + ["000123", "123"], + ["-000123", "-123"], + [ + "+12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890", + "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" + ], + ] + targets.each do |data, expected| + assert_equal(expected, XSD::XSDInteger.new(data).to_s) + end + + targets = [ + "0.0", + "-5.2", + "0.000000000000a", + "+-5", + "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890." + ] + targets.each do |d| + assert_raises(XSD::ValueSpaceError) do + XSD::XSDInteger.new(d) + end + end + end + + def test_XSDLong + o = XSD::XSDLong.new + assert_equal(XSD::Namespace, o.type.namespace) + assert_equal(XSD::LongLiteral, o.type.name) + assert_equal(nil, o.data) + assert_equal(true, o.is_nil) + + targets = [ + 0, + 123, + -123, + 9223372036854775807, + -9223372036854775808, + ] + targets.each do |lng| + assert_equal(lng, XSD::XSDLong.new(lng).data) + end + + targets = [ + "0", + "123", + "-123", + "9223372036854775807", + "-9223372036854775808", + ] + targets.each do |str| + assert_equal(str, XSD::XSDLong.new(str).to_s) + end + + targets = [ + ["-0", "0"], + ["+0", "0"], + ["000123", "123"], + ["-000123", "-123"], + ] + targets.each do |data, expected| + assert_equal(expected, XSD::XSDLong.new(data).to_s) + end + + targets = [ + 9223372036854775808, + -9223372036854775809, + "0.0", + "-5.2", + "0.000000000000a", + "+-5", + ] + targets.each do |d| + assert_raises(XSD::ValueSpaceError) do + XSD::XSDLong.new(d) + end + end + end + + def test_XSDInt + o = XSD::XSDInt.new + assert_equal(XSD::Namespace, o.type.namespace) + assert_equal(XSD::IntLiteral, o.type.name) + assert_equal(nil, o.data) + assert_equal(true, o.is_nil) + + targets = [ + 0, + 123, + -123, + 2147483647, + -2147483648, + ] + targets.each do |lng| + assert_equal(lng, XSD::XSDInt.new(lng).data) + end + + targets = [ + "0", + "123", + "-123", + "2147483647", + "-2147483648", + ] + targets.each do |str| + assert_equal(str, XSD::XSDInt.new(str).to_s) + end + + targets = [ + ["-0", "0"], + ["+0", "0"], + ["000123", "123"], + ["-000123", "-123"], + ] + targets.each do |data, expected| + assert_equal(expected, XSD::XSDInt.new(data).to_s) + end + + targets = [ + 2147483648, + -2147483649, + "0.0", + "-5.2", + "0.000000000000a", + "+-5", + ] + targets.each do |d| + assert_raises(XSD::ValueSpaceError) do + XSD::XSDInt.new(d) + end + end + end +end diff --git a/test/xsd/xmlschema.xml b/test/xsd/xmlschema.xml new file mode 100644 index 0000000000..f532e2934e --- /dev/null +++ b/test/xsd/xmlschema.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file -- cgit v1.2.3