diff options
author | nahi <nahi@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2005-05-22 13:20:28 +0000 |
---|---|---|
committer | nahi <nahi@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2005-05-22 13:20:28 +0000 |
commit | 991d0c409cc6b1d916330a32a9624aef808176a4 (patch) | |
tree | 5e2cc150dc84ab3f6f64685ec7f54e6b2077eae7 /lib/wsdl | |
parent | 15b7d439885f4aa97e0f508ef485cadab4b23577 (diff) |
* lib/{soap,wsdl,xsd}, test/{soap,wsdl,xsd}: imported soap4r/1.5.4.
== SOAP client and server ==
=== for both client side and server side ===
* improved document/literal service support.
style(rpc,document)/use(encoding, literal) combination are all
supported. for the detail about combination, see
test/soap/test_style.rb.
* let WSDLEncodedRegistry#soap2obj map SOAP/OM to Ruby according to
WSDL as well as obj2soap. closes #70.
* let SOAP::Mapping::Object handle XML attribute for doc/lit service.
you can set/get XML attribute via accessor methods which as a name
'xmlattr_' prefixed (<foo name="bar"/> -> Foo#xmlattr_name).
=== client side ===
* WSDLDriver capitalized name operation bug fixed. from
1.5.3-ruby1.8.2, operation which has capitalized name (such as
KeywordSearchRequest in AWS) is defined as a method having
uncapitalized name. (converted with GenSupport.safemethodname
to handle operation name 'foo-bar'). it introduced serious
incompatibility; in the past, it was defined as a capitalized.
define capitalized method as well under that circumstance.
* added new factory interface 'WSDLDriverFactory#create_rpc_driver'
to create RPC::Driver, not WSDLDriver (RPC::Driver and WSDLDriver
are merged). 'WSDLDriverFactory#create_driver' still creates
WSDLDriver for compatibility but it warns that the method is
deprecated. please use create_rpc_driver instead of create_driver.
* allow to use an URI object as an endpoint_url even with net/http,
not http-access2.
=== server side ===
* added mod_ruby support to SOAP::CGIStub. rename a CGI script
server.cgi to server.rb and let mod_ruby's RubyHandler handles the
script. CGIStub detects if it's running under mod_ruby environment
or not.
* added fcgi support to SOAP::CGIStub. see the sample at
sample/soap/calc/server.fcgi. (almost same as server.cgi but has
fcgi handler at the bottom.)
* allow to return a SOAPFault object to respond customized SOAP fault.
* added the interface 'generate_explicit_type' for server side
(CGIStub, HTTPServer). call 'self.generate_explicit_type = true'
if you want to return simplified XML even if it's rpc/encoded
service.
== WSDL ==
=== WSDL definition ===
* improved XML Schema support such as extension, restriction,
simpleType, complexType + simpleContent, ref, length, import,
include.
* reduced "unknown element/attribute" warnings (warn only 1 time for
each QName).
* importing XSD file at schemaLocation with xsd:import.
=== code generation from WSDL ===
* generator crashed when there's '-' in defined element/attribute
name.
* added ApacheMap WSDL definition.
* sample/{soap,wsdl}: removed.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/ruby_1_8@8502 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'lib/wsdl')
34 files changed, 715 insertions, 376 deletions
diff --git a/lib/wsdl/definitions.rb b/lib/wsdl/definitions.rb index a7c71f2a93..5235037cfe 100644 --- a/lib/wsdl/definitions.rb +++ b/lib/wsdl/definitions.rb @@ -18,19 +18,16 @@ class Definitions < Info attr_reader :targetnamespace attr_reader :imports - # Overrides Info#root - def root - @root - end - - def root=(root) - @root = root - end + attr_accessor :location + attr_reader :importedschema def initialize super @name = nil @targetnamespace = nil + @location = nil + @importedschema = {} + @types = nil @imports = [] @messages = XSD::NamedElements.new @@ -53,6 +50,19 @@ class Definitions < Info end end + def collect_attributes + result = XSD::NamedElements.new + if @types + @types.schemas.each do |schema| + result.concat(schema.collect_attributes) + end + end + @imports.each do |import| + result.concat(import.content.collect_attributes) + end + result + end + def collect_elements result = XSD::NamedElements.new if @types diff --git a/lib/wsdl/import.rb b/lib/wsdl/import.rb index 706cb95fe2..faf60871a5 100644 --- a/lib/wsdl/import.rb +++ b/lib/wsdl/import.rb @@ -45,13 +45,23 @@ class Import < Info end @namespace when LocationAttrName - @location = value.source - @content = import(@location) - if @content.is_a?(Definitions) - @content.root = root - if @namespace - @content.targetnamespace = @namespace - end + @location = URI.parse(value.source) + if @location.relative? and !parent.location.nil? and + !parent.location.relative? + @location = parent.location + @location + end + if root.importedschema.key?(@location) + @content = root.importedschema[@location] + else + root.importedschema[@location] = nil # placeholder + @content = import(@location) + if @content.is_a?(Definitions) + @content.root = root + if @namespace + @content.targetnamespace = @namespace + end + end + root.importedschema[@location] = @content end @location else @@ -62,7 +72,7 @@ class Import < Info private def import(location) - Importer.import(location) + Importer.import(location, root) end end diff --git a/lib/wsdl/importer.rb b/lib/wsdl/importer.rb index 873be710b5..481bd81b25 100644 --- a/lib/wsdl/importer.rb +++ b/lib/wsdl/importer.rb @@ -1,69 +1,37 @@ # WSDL4R - WSDL importer library. -# Copyright (C) 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. +# Copyright (C) 2003, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. # This program is copyrighted free software by NAKAMURA, Hiroshi. You can # redistribute it and/or modify it under the same terms of Ruby's license; # either the dual license version in 2003, or any later version. -require 'wsdl/info' +require 'wsdl/xmlSchema/importer' require 'wsdl/parser' -require 'soap/soap' -require 'soap/property' module WSDL -class Importer - def self.import(location) - new.import(location) +class Importer < WSDL::XMLSchema::Importer + def self.import(location, originalroot = nil) + new.import(location, originalroot) end - def initialize - @web_client = nil - end +private - def import(location) - STDERR.puts("importing: #{location}") if $DEBUG - content = nil - if FileTest.exist?(location) - content = File.open(location).read - else - client = web_client.new(nil, "WSDL4R") - if opt = ::SOAP::Property.loadproperty(::SOAP::PropertyName) - client.proxy = opt["client.protocol.http.proxy"] - client.no_proxy = opt["client.protocol.http.no_proxy"] - end - client.proxy ||= ::SOAP::Env::HTTP_PROXY - client.no_proxy ||= ::SOAP::Env::NO_PROXY - content = client.get_content(location) - end - opt = {} + def parse(content, location, originalroot) + opt = { + :location => location, + :originalroot => originalroot + } begin WSDL::Parser.new(opt).parse(content) - rescue WSDL::Parser::ParseError => orgexcn - require 'wsdl/xmlSchema/parser' - WSDL::XMLSchema::Parser.new(opt).parse(content) + rescue WSDL::Parser::ParseError + super(content, location, originalroot) 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 diff --git a/lib/wsdl/info.rb b/lib/wsdl/info.rb index 657ff5863a..ffd90390a4 100644 --- a/lib/wsdl/info.rb +++ b/lib/wsdl/info.rb @@ -10,16 +10,22 @@ module WSDL class Info + attr_accessor :root attr_accessor :parent attr_accessor :id def initialize + @root = nil @parent = nil @id = nil end - def root - @parent.root + def inspect + if self.respond_to?(:name) + sprintf("#<%s:0x%x %s>", self.class.name, __id__, self.name) + else + sprintf("#<%s:0x%x>", self.class.name, __id__) + end end def parse_element(element); end # abstract diff --git a/lib/wsdl/operation.rb b/lib/wsdl/operation.rb index 3c1f66859f..727bb9a56c 100644 --- a/lib/wsdl/operation.rb +++ b/lib/wsdl/operation.rb @@ -46,21 +46,23 @@ class Operation < Info 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) + typename = input.find_message.name + NameInfo.new(@name, typename, 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) + typename = output.find_message.name + NameInfo.new(@name, typename, outputparts) end def inputparts sort_parts(input.find_message.parts) end + def inputname + XSD::QName.new(targetnamespace, input.name ? input.name.name : @name.name) + end + def outputparts sort_parts(output.find_message.parts) end diff --git a/lib/wsdl/operationBinding.rb b/lib/wsdl/operationBinding.rb index fb44eb9660..c2b8cd6591 100644 --- a/lib/wsdl/operationBinding.rb +++ b/lib/wsdl/operationBinding.rb @@ -37,7 +37,35 @@ class OperationBinding < Info end def find_operation - porttype.operations[@name] + porttype.operations[@name] or raise RuntimeError.new("#{@name} not found") + end + + def soapoperation_name + if @soapoperation + @soapoperation.input_info.op_name + else + find_operation.name + end + end + + def soapoperation_style + style = nil + if @soapoperation + style = @soapoperation.operation_style + elsif parent.soapbinding + style = parent.soapbinding.style + else + raise TypeError.new("operation style definition not found") + end + style || :document + end + + def soapaction + if @soapoperation + @soapoperation.soapaction + else + nil + end end def parse_element(element) diff --git a/lib/wsdl/param.rb b/lib/wsdl/param.rb index 581ecbd8d3..08ba07ee9b 100644 --- a/lib/wsdl/param.rb +++ b/lib/wsdl/param.rb @@ -33,7 +33,7 @@ class Param < Info end def find_message - root.message(@message) + root.message(@message) or raise RuntimeError.new("#{@message} not found") end def parse_element(element) @@ -61,6 +61,9 @@ class Param < Info def parse_attr(attr, value) case attr when MessageAttrName + if value.namespace.nil? + value = XSD::QName.new(targetnamespace, value.source) + end @message = value when NameAttrName @name = XSD::QName.new(targetnamespace, value.source) diff --git a/lib/wsdl/parser.rb b/lib/wsdl/parser.rb index 417ea20b47..f96e96ee2a 100644 --- a/lib/wsdl/parser.rb +++ b/lib/wsdl/parser.rb @@ -1,5 +1,5 @@ # WSDL4R - WSDL XML Instance parser library. -# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. +# Copyright (C) 2002, 2003, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. # This program is copyrighted free software by NAKAMURA, Hiroshi. You can # redistribute it and/or modify it under the same terms of Ruby's license; @@ -53,6 +53,9 @@ public @parser = XSD::XMLParser.create_parser(self, opt) @parsestack = nil @lastnode = nil + @ignored = {} + @location = opt[:location] + @originalroot = opt[:originalroot] end def parse(string_or_readable) @@ -96,7 +99,7 @@ public 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 }'.") + 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 @@ -106,20 +109,31 @@ private def decode_tag(ns, name, attrs, parent) o = nil - element = ns.parse(name) + elename = ns.parse(name) if !parent - if element == DefinitionsName - o = Definitions.parse_element(element) + if elename == DefinitionsName + o = Definitions.parse_element(elename) + o.location = @location else - raise UnknownElementError.new("Unknown element #{ element }.") + raise UnknownElementError.new("unknown element: #{elename}") end + o.root = @originalroot if @originalroot # o.root = o otherwise else - o = parent.parse_element(element) + if elename == XMLSchema::AnnotationName + # only the first annotation element is allowed for each xsd element. + o = XMLSchema::Annotation.new + else + o = parent.parse_element(elename) + end unless o - STDERR.puts("Unknown element #{ element }.") + unless @ignored.key?(elename) + warn("ignored element: #{elename}") + @ignored[elename] = elename + end o = Documentation.new # which accepts any element. end # node could be a pseudo element. pseudo element has its own parent. + o.root = parent.root o.parent = parent if o.parent.nil? end attrs.each do |key, value| @@ -127,7 +141,10 @@ private value_ele = ns.parse(value, true) value_ele.source = value # for recovery; value may not be a QName unless o.parse_attr(attr_ele, value_ele) - STDERR.puts("Unknown attr #{ attr_ele }.") + unless @ignored.key?(attr_ele) + warn("ignored attr: #{attr_ele}") + @ignored[attr_ele] = attr_ele + end end end o diff --git a/lib/wsdl/port.rb b/lib/wsdl/port.rb index 15ba86ad7c..883cc3232d 100644 --- a/lib/wsdl/port.rb +++ b/lib/wsdl/port.rb @@ -33,7 +33,7 @@ class Port < Info end def find_binding - root.binding(@binding) + root.binding(@binding) or raise RuntimeError.new("#{@binding} not found") end def inputoperation_map diff --git a/lib/wsdl/portType.rb b/lib/wsdl/portType.rb index 86893ba039..03e37690a8 100644 --- a/lib/wsdl/portType.rb +++ b/lib/wsdl/portType.rb @@ -28,7 +28,8 @@ class PortType < Info end def find_binding - root.bindings.find { |item| item.type == @name } + root.bindings.find { |item| item.type == @name } or + raise RuntimeError.new("#{@name} not found") end def locations diff --git a/lib/wsdl/soap/cgiStubCreator.rb b/lib/wsdl/soap/cgiStubCreator.rb index 68ecfaf0a4..0a76dc43a1 100644 --- a/lib/wsdl/soap/cgiStubCreator.rb +++ b/lib/wsdl/soap/cgiStubCreator.rb @@ -1,5 +1,5 @@ # WSDL4R - Creating CGI stub code from WSDL. -# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. +# Copyright (C) 2002, 2003, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. # This program is copyrighted free software by NAKAMURA, Hiroshi. You can # redistribute it and/or modify it under the same terms of Ruby's license; @@ -26,9 +26,7 @@ class CGIStubCreator end def dump(service_name) - STDERR.puts "!!! IMPORTANT !!!" - STDERR.puts "- CGI stub can only 1 port. Creating stub for the first port... Rests are ignored." - STDERR.puts "!!! IMPORTANT !!!" + warn("CGI stub can have only 1 port. Creating stub for the first port... Rests are ignored.") port = @definitions.service(service_name).ports[0] dump_porttype(port.porttype.name) end @@ -39,28 +37,28 @@ private class_name = create_class_name(name) methoddef, types = MethodDefCreator.new(@definitions).dump(name) mr_creator = MappingRegistryCreator.new(@definitions) - c1 = ::XSD::CodeGen::ClassDef.new(class_name) + c1 = XSD::CodeGen::ClassDef.new(class_name) c1.def_require("soap/rpc/cgistub") c1.def_require("soap/mapping/registry") c1.def_const("MappingRegistry", "::SOAP::Mapping::Registry.new") c1.def_code(mr_creator.dump(types)) c1.def_code <<-EOD Methods = [ -#{ methoddef.gsub(/^/, " ") } +#{methoddef.gsub(/^/, " ")} ] EOD - c2 = ::XSD::CodeGen::ClassDef.new(class_name + "App", + c2 = XSD::CodeGen::ClassDef.new(class_name + "App", "::SOAP::RPC::CGIStub") c2.def_method("initialize", "*arg") do <<-EOD super(*arg) servant = #{class_name}.new #{class_name}::Methods.each do |name_as, name, param_def, soapaction, namespace, style| - qname = XSD::QName.new(namespace, name_as) if style == :document - @router.add_document_method(servant, qname, soapaction, name, param_def) + @router.add_document_operation(servant, soapaction, name, param_def) else - @router.add_rpc_method(servant, qname, soapaction, name, param_def) + qname = XSD::QName.new(namespace, name_as) + @router.add_rpc_operation(servant, qname, soapaction, name, param_def) end end self.mapping_registry = #{class_name}::MappingRegistry diff --git a/lib/wsdl/soap/classDefCreator.rb b/lib/wsdl/soap/classDefCreator.rb index 13f7802b72..deda4f131f 100644 --- a/lib/wsdl/soap/classDefCreator.rb +++ b/lib/wsdl/soap/classDefCreator.rb @@ -1,5 +1,5 @@ # WSDL4R - Creating class definition from WSDL -# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. +# Copyright (C) 2002, 2003, 2004 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. # This program is copyrighted free software by NAKAMURA, Hiroshi. You can # redistribute it and/or modify it under the same terms of Ruby's license; @@ -22,13 +22,16 @@ class ClassDefCreator @elements = definitions.collect_elements @simpletypes = definitions.collect_simpletypes @complextypes = definitions.collect_complextypes - @faulttypes = definitions.collect_faulttypes if definitions.respond_to?(:collect_faulttypes) + @faulttypes = nil + if definitions.respond_to?(:collect_faulttypes) + @faulttypes = definitions.collect_faulttypes + end end - def dump(class_name = nil) - result = '' - if class_name - result = dump_classdef(class_name) + def dump(type = nil) + result = "require 'xsd/qname'\n" + if type + result = dump_classdef(type.name, type) else str = dump_element unless str.empty? @@ -53,117 +56,129 @@ private def dump_element @elements.collect { |ele| - ele.local_complextype ? dump_classdef(ele) : '' - }.join("\n") + if ele.local_complextype + dump_classdef(ele.name, ele.local_complextype) + elsif ele.local_simpletype + dump_simpletypedef(ele.name, ele.local_simpletype) + else + nil + end + }.compact.join("\n") end def dump_simpletype @simpletypes.collect { |type| - dump_simpletypedef(type) - }.join("\n") + dump_simpletypedef(type.name, type) + }.compact.join("\n") end def dump_complextype @complextypes.collect { |type| case type.compoundtype - when :TYPE_STRUCT - dump_classdef(type) + when :TYPE_STRUCT, :TYPE_EMPTY + dump_classdef(type.name, type) when :TYPE_ARRAY dump_arraydef(type) when :TYPE_SIMPLE - STDERR.puts("not implemented: ToDo") + dump_simpleclassdef(type) + when :TYPE_MAP + # mapped as a general Hash + nil else raise RuntimeError.new( - "Unknown kind of complexContent: #{type.compoundtype}") + "unknown kind of complexContent: #{type.compoundtype}") end - }.join("\n") + }.compact.join("\n") end - def dump_simpletypedef(simpletype) - qname = simpletype.name - if simpletype.restriction.enumeration.empty? - STDERR.puts("#{qname}: simpleType which is not enum type not supported.") - return '' + def dump_simpletypedef(qname, simpletype) + if !simpletype.restriction or simpletype.restriction.enumeration.empty? + return nil end c = XSD::CodeGen::ModuleDef.new(create_class_name(qname)) - c.comment = "#{ qname.namespace }" + c.comment = "#{qname}" + const = {} simpletype.restriction.enumeration.each do |value| - c.def_const(safeconstname(value), value.dump) + constname = safeconstname(value) + const[constname] ||= 0 + if (const[constname] += 1) > 1 + constname += "_#{const[constname]}" + end + c.def_const(constname, ndq(value)) end c.dump end - def dump_classdef(type_or_element) + def dump_simpleclassdef(type_or_element) qname = type_or_element.name + base = create_class_name(type_or_element.simplecontent.base) + c = XSD::CodeGen::ClassDef.new(create_class_name(qname), base) + c.comment = "#{qname}" + c.dump + end + + def dump_classdef(qname, typedef) if @faulttypes and @faulttypes.index(qname) c = XSD::CodeGen::ClassDef.new(create_class_name(qname), '::StandardError') else c = XSD::CodeGen::ClassDef.new(create_class_name(qname)) end - c.comment = "#{ qname.namespace }" - c.def_classvar('schema_type', qname.name.dump) - c.def_classvar('schema_ns', qname.namespace.dump) - schema_attribute = [] + c.comment = "#{qname}" + c.def_classvar('schema_type', ndq(qname.name)) + c.def_classvar('schema_ns', ndq(qname.namespace)) schema_element = [] init_lines = '' params = [] - type_or_element.each_element do |element| - next unless element.name - name = element.name.name + typedef.each_element do |element| if element.type == XSD::AnyTypeName type = nil - elsif basetype = basetype_class(element.type) - type = basetype.name - else + elsif klass = element_basetype(element) + type = klass.name + elsif element.type type = create_class_name(element.type) + else + type = nil # means anyType. + # do we define a class for local complexType from it's name? + # type = create_class_name(element.name) + # <element> + # <complexType> + # <seq...> + # </complexType> + # </element> end + name = name_element(element).name attrname = safemethodname?(name) ? name : safemethodname(name) varname = safevarname(name) c.def_attr(attrname, true, varname) - init_lines << "@#{ varname } = #{ varname }\n" + init_lines << "@#{varname} = #{varname}\n" if element.map_as_array? - params << "#{ varname } = []" - type << '[]' + params << "#{varname} = []" + type << '[]' if type else - params << "#{ varname } = nil" + params << "#{varname} = nil" end - schema_element << [name, type] + eleqname = (varname == name) ? nil : element.name + schema_element << [varname, eleqname, type] end - unless type_or_element.attributes.empty? - type_or_element.attributes.each do |attribute| - name = attribute.name.name - if basetype = basetype_class(attribute.type) - type = basetype_class(attribute.type).name - else - type = nil - end - varname = safevarname('attr_' + name) - c.def_method(varname) do <<-__EOD__ - @__soap_attribute[#{name.dump}] - __EOD__ - end - c.def_method(varname + '=', 'value') do <<-__EOD__ - @__soap_attribute[#{name.dump}] = value - __EOD__ - end - schema_attribute << [name, type] - end - init_lines << "@__soap_attribute = {}\n" + unless typedef.attributes.empty? + define_attribute(c, typedef.attributes) + init_lines << "@__xmlattr = {}\n" end - c.def_classvar('schema_attribute', - '{' + - schema_attribute.collect { |name, type| - name.dump + ' => ' + ndq(type) - }.join(', ') + - '}' - ) c.def_classvar('schema_element', - '{' + - schema_element.collect { |name, type| - name.dump + ' => ' + ndq(type) + '[' + + schema_element.collect { |varname, name, type| + '[' + + ( + if name + varname.dump + ', [' + ndq(type) + ', ' + dqname(name) + ']' + else + varname.dump + ', ' + ndq(type) + end + ) + + ']' }.join(', ') + - '}' + ']' ) c.def_method('initialize', *params) do init_lines @@ -171,20 +186,83 @@ private c.dump end + def element_basetype(ele) + if klass = basetype_class(ele.type) + klass + elsif ele.local_simpletype + basetype_class(ele.local_simpletype.base) + else + nil + end + end + + def attribute_basetype(attr) + if klass = basetype_class(attr.type) + klass + elsif attr.local_simpletype + basetype_class(attr.local_simpletype.base) + else + nil + end + end + def basetype_class(type) - if @simpletypes[type] - basetype_mapped_class(@simpletypes[type].base) + return nil if type.nil? + if simpletype = @simpletypes[type] + basetype_mapped_class(simpletype.base) else basetype_mapped_class(type) end end + def define_attribute(c, attributes) + schema_attribute = [] + attributes.each do |attribute| + name = name_attribute(attribute) + if klass = attribute_basetype(attribute) + type = klass.name + else + type = nil + end + methodname = safemethodname('xmlattr_' + name.name) + c.def_method(methodname) do <<-__EOD__ + (@__xmlattr ||= {})[#{dqname(name)}] + __EOD__ + end + c.def_method(methodname + '=', 'value') do <<-__EOD__ + (@__xmlattr ||= {})[#{dqname(name)}] = value + __EOD__ + end + schema_attribute << [name, type] + end + c.def_classvar('schema_attribute', + '{' + + schema_attribute.collect { |name, type| + dqname(name) + ' => ' + ndq(type) + }.join(', ') + + '}' + ) + end + + def name_element(element) + return element.name if element.name + return element.ref if element.ref + raise RuntimeError.new("cannot define name of #{element}") + end + + def name_attribute(attribute) + return attribute.name if attribute.name + return attribute.ref if attribute.ref + raise RuntimeError.new("cannot define name of #{attribute}") + end + def dump_arraydef(complextype) qname = complextype.name c = XSD::CodeGen::ClassDef.new(create_class_name(qname), '::Array') - c.comment = "#{ qname.namespace }" - c.def_classvar('schema_type', qname.name.dump) - c.def_classvar('schema_ns', qname.namespace.dump) + c.comment = "#{qname}" + type = complextype.child_type + c.def_classvar('schema_type', ndq(type.name)) + c.def_classvar('schema_ns', ndq(type.namespace)) c.dump end end diff --git a/lib/wsdl/soap/classDefCreatorSupport.rb b/lib/wsdl/soap/classDefCreatorSupport.rb index 706c00d4f6..8f335653c8 100644 --- a/lib/wsdl/soap/classDefCreatorSupport.rb +++ b/lib/wsdl/soap/classDefCreatorSupport.rb @@ -21,7 +21,7 @@ module ClassDefCreatorSupport def create_class_name(qname) if klass = basetype_mapped_class(qname) - ::SOAP::Mapping::DefaultRegistry.find_mapped_obj_class(klass.name) + ::SOAP::Mapping::DefaultRegistry.find_mapped_obj_class(klass).name else safeconstname(qname.name) end @@ -71,6 +71,10 @@ __EOD__ ':' + ele end + def dqname(qname) + qname.dump + end + private def dump_inout_type(param) diff --git a/lib/wsdl/soap/complexType.rb b/lib/wsdl/soap/complexType.rb index 1bed059f7e..bba50fd153 100644 --- a/lib/wsdl/soap/complexType.rb +++ b/lib/wsdl/soap/complexType.rb @@ -31,42 +31,44 @@ class ComplexType < Info else :TYPE_STRUCT end - elsif complexcontent and complexcontent.base == ::SOAP::ValueArrayName - :TYPE_ARRAY + elsif complexcontent + if complexcontent.base == ::SOAP::ValueArrayName + :TYPE_ARRAY + else + complexcontent.basetype.check_type + end elsif simplecontent :TYPE_SIMPLE elsif !attributes.empty? :TYPE_STRUCT - else - raise NotImplementedError.new("Unknown kind of complexType.") + else # empty complexType definition (seen in partner.wsdl of salesforce) + :TYPE_EMPTY end end def child_type(name = nil) - type = nil case compoundtype when :TYPE_STRUCT if ele = find_element(name) - type = ele.type + ele.type elsif ele = find_element_by_name(name.name) - type = ele.type + ele.type end when :TYPE_ARRAY - type = @contenttype ||= content_arytype + @contenttype ||= content_arytype when :TYPE_MAP item_ele = find_element_by_name("item") or raise RuntimeError.new("'item' element not found in Map definition.") content = item_ele.local_complextype or raise RuntimeError.new("No complexType definition for 'item'.") if ele = content.find_element(name) - type = ele.type + ele.type elsif ele = content.find_element_by_name(name.name) - type = ele.type + ele.type end else raise NotImplementedError.new("Unknown kind of complexType.") end - type end def child_defined_complextype(name) @@ -103,16 +105,21 @@ class ComplexType < Info return attribute.arytype end end - elsif content.elements.size == 1 and content.elements[0].maxoccurs != '1' + if check_array_content(complexcontent.content) + return complexcontent.content.elements[0].type + end + elsif check_array_content(content) return content.elements[0].type - else - raise RuntimeError.new("Assert: Unknown array definition.") end - nil + raise RuntimeError.new("Assert: Unknown array definition.") end private + def check_array_content(content) + content.elements.size == 1 and content.elements[0].maxoccurs != '1' + end + def content_arytype if arytype = find_arytype ns = arytype.namespace diff --git a/lib/wsdl/soap/definitions.rb b/lib/wsdl/soap/definitions.rb index 2f6e7e19f0..b014d5af6b 100644 --- a/lib/wsdl/soap/definitions.rb +++ b/lib/wsdl/soap/definitions.rb @@ -1,5 +1,5 @@ # WSDL4R - WSDL additional definitions for SOAP. -# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. +# Copyright (C) 2002-2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. # This program is copyrighted free software by NAKAMURA, Hiroshi. You can # redistribute it and/or modify it under the same terms of Ruby's license; @@ -77,13 +77,13 @@ class Definitions < Info def collect_faulttypes result = [] - collect_fault_messages.each do |message| - parts = message(message).parts - if parts.size != 1 - raise RuntimeError.new("Expecting fault message to have only 1 part.") + collect_fault_messages.each do |name| + faultparts = message(name).parts + if faultparts.size != 1 + raise RuntimeError.new("expecting fault message to have only 1 part") end - if result.index(parts[0].type).nil? - result << parts[0].type + if result.index(faultparts[0].type).nil? + result << faultparts[0].type end end result @@ -111,13 +111,13 @@ private if op_bind_rpc?(op_bind) operation = op_bind.find_operation if op_bind.input - type = XMLSchema::ComplexType.new(operation_input_name(operation)) + type = XMLSchema::ComplexType.new(op_bind.soapoperation_name) 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)) + type = XMLSchema::ComplexType.new(operation.outputname) message = messages[operation.output.message] type.sequence_elements = elements_from_message(message) types << type @@ -127,23 +127,20 @@ private 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 + op_bind.soapoperation_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) + if part.element + collect_elements[part.element] + elsif part.name.nil? or part.type.nil? + raise RuntimeError.new("part of a message must be an element or typed") + else + qname = XSD::QName.new(nil, part.name) + XMLSchema::Element.new(qname, part.type) + end } end end diff --git a/lib/wsdl/soap/driverCreator.rb b/lib/wsdl/soap/driverCreator.rb index b752ee336d..dd504210f1 100644 --- a/lib/wsdl/soap/driverCreator.rb +++ b/lib/wsdl/soap/driverCreator.rb @@ -1,5 +1,5 @@ # WSDL4R - Creating driver code from WSDL. -# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. +# Copyright (C) 2002, 2003, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. # This program is copyrighted free software by NAKAMURA, Hiroshi. You can # redistribute it and/or modify it under the same terms of Ruby's license; @@ -46,16 +46,17 @@ private methoddef, types = MethodDefCreator.new(@definitions).dump(name) mr_creator = MappingRegistryCreator.new(@definitions) binding = @definitions.bindings.find { |item| item.type == name } - addresses = @definitions.porttype(name).locations + return '' unless binding.soapbinding # not a SOAP binding + address = @definitions.porttype(name).locations[0] - c = ::XSD::CodeGen::ClassDef.new(class_name, "::SOAP::RPC::Driver") + c = XSD::CodeGen::ClassDef.new(class_name, "::SOAP::RPC::Driver") c.def_require("soap/rpc/driver") c.def_const("MappingRegistry", "::SOAP::Mapping::Registry.new") - c.def_const("DefaultEndpointUrl", addresses[0].dump) + c.def_const("DefaultEndpointUrl", ndq(address)) c.def_code(mr_creator.dump(types)) c.def_code <<-EOD Methods = [ -#{ methoddef.gsub(/^/, " ") } +#{methoddef.gsub(/^/, " ")} ] EOD c.def_method("initialize", "endpoint_url = nil") do @@ -69,14 +70,19 @@ Methods = [ c.def_privatemethod("init_methods") do <<-EOD Methods.each do |name_as, name, params, soapaction, namespace, style| - qname = ::XSD::QName.new(namespace, name_as) + qname = XSD::QName.new(namespace, name_as) if style == :document - @proxy.add_document_method(qname, soapaction, name, params) - add_document_method_interface(name, name_as) + @proxy.add_document_method(soapaction, name, params) + add_document_method_interface(name, params) else @proxy.add_rpc_method(qname, soapaction, name, params) add_rpc_method_interface(name, params) end + if name_as != name and name_as.capitalize == name.capitalize + ::SOAP::Mapping.define_singleton_method(self, name_as) do |*arg| + __send__(name, *arg) + end + end end EOD end diff --git a/lib/wsdl/soap/element.rb b/lib/wsdl/soap/element.rb index c39a00d25a..0fa6017c5b 100644 --- a/lib/wsdl/soap/element.rb +++ b/lib/wsdl/soap/element.rb @@ -21,12 +21,6 @@ class Element < Info def attributes @local_complextype.attributes end - - def each_element - @local_complextype.each_element do |element| - yield(element) - end - end end diff --git a/lib/wsdl/soap/fault.rb b/lib/wsdl/soap/fault.rb index 019c881f97..2862b659ab 100644 --- a/lib/wsdl/soap/fault.rb +++ b/lib/wsdl/soap/fault.rb @@ -27,6 +27,10 @@ class Fault < Info @namespace = nil end + def targetnamespace + parent.targetnamespace + end + def parse_element(element) nil end diff --git a/lib/wsdl/soap/header.rb b/lib/wsdl/soap/header.rb index 247531a76d..8d7c4e9d70 100644 --- a/lib/wsdl/soap/header.rb +++ b/lib/wsdl/soap/header.rb @@ -32,8 +32,12 @@ class Header < Info @headerfault = nil end + def targetnamespace + parent.targetnamespace + end + def find_message - root.message(@message) + root.message(@message) or raise RuntimeError.new("#{@message} not found") end def find_part @@ -42,7 +46,7 @@ class Header < Info return part end end - nil + raise RuntimeError.new("#{@part} not found") end def parse_element(element) @@ -59,7 +63,10 @@ class Header < Info def parse_attr(attr, value) case attr when MessageAttrName - @message = XSD::QName.new(targetnamespace, value.source) + if value.namespace.nil? + value = XSD::QName.new(targetnamespace, value.source) + end + @message = value when PartAttrName @part = value.source when UseAttrName diff --git a/lib/wsdl/soap/mappingRegistryCreator.rb b/lib/wsdl/soap/mappingRegistryCreator.rb index d3b28f47e0..8669339ce4 100644 --- a/lib/wsdl/soap/mappingRegistryCreator.rb +++ b/lib/wsdl/soap/mappingRegistryCreator.rb @@ -1,5 +1,5 @@ # WSDL4R - Creating MappingRegistry code from WSDL. -# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. +# Copyright (C) 2002, 2003, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. # This program is copyrighted free software by NAKAMURA, Hiroshi. You can # redistribute it and/or modify it under the same terms of Ruby's license; @@ -38,7 +38,7 @@ class MappingRegistryCreator end end end - end + end return map end @@ -51,8 +51,10 @@ private dump_struct_typemap(definedtype) when :TYPE_ARRAY dump_array_typemap(definedtype) + when :TYPE_MAP, :TYPE_EMPTY + nil else - raise NotImplementedError.new("Must not reach here.") + raise NotImplementedError.new("must not reach here") end end end @@ -61,10 +63,10 @@ private ele = definedtype.name return <<__EOD__ MappingRegistry.set( - #{ create_class_name(ele) }, + #{create_class_name(ele)}, ::SOAP::SOAPStruct, ::SOAP::Mapping::Registry::TypedStructFactory, - { :type => ::XSD::QName.new("#{ ele.namespace }", "#{ ele.name }") } + { :type => #{dqname(ele)} } ) __EOD__ end @@ -76,10 +78,10 @@ __EOD__ @types << type return <<__EOD__ MappingRegistry.set( - #{ create_class_name(ele) }, + #{create_class_name(ele)}, ::SOAP::SOAPArray, ::SOAP::Mapping::Registry::TypedArrayFactory, - { :type => ::XSD::QName.new("#{ type.namespace }", "#{ type.name }") } + { :type => #{dqname(type)} } ) __EOD__ end diff --git a/lib/wsdl/soap/methodDefCreator.rb b/lib/wsdl/soap/methodDefCreator.rb index 59b8ee4253..f256b42451 100644 --- a/lib/wsdl/soap/methodDefCreator.rb +++ b/lib/wsdl/soap/methodDefCreator.rb @@ -1,5 +1,5 @@ # WSDL4R - Creating driver code from WSDL. -# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. +# Copyright (C) 2002, 2003, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. # This program is copyrighted free software by NAKAMURA, Hiroshi. You can # redistribute it and/or modify it under the same terms of Ruby's license; @@ -8,6 +8,7 @@ require 'wsdl/info' require 'wsdl/soap/classDefCreatorSupport' +require 'soap/rpc/element' module WSDL @@ -24,75 +25,80 @@ class MethodDefCreator @simpletypes = @definitions.collect_simpletypes @complextypes = @definitions.collect_complextypes @elements = @definitions.collect_elements - @types = nil + @types = [] end def dump(porttype) - @types = [] + @types.clear result = "" operations = @definitions.porttype(porttype).operations binding = @definitions.porttype_binding(porttype) operations.each do |operation| op_bind = binding.operations[operation.name] + next unless op_bind # no binding is defined + next unless op_bind.soapoperation # not a SOAP operation binding result << ",\n" unless result.empty? result << dump_method(operation, op_bind).chomp end return result, @types end -private - - def dump_method(operation, binding) - name = safemethodname(operation.name.name) - name_as = operation.name.name - stylestr = binding.soapoperation.operation_style.id2name - if binding.soapoperation.operation_style == :rpc - soapaction = binding.soapoperation.soapaction - namespace = binding.input.soapbody.namespace - params = collect_rpcparameter(operation) - else - soapaction = namespace = nil - params = collect_documentparameter(operation) - end - paramstr = param2str(params) - if paramstr.empty? - paramstr = '[]' - else - paramstr = "[\n" << paramstr.gsub(/^/, ' ') << "\n ]" - end - return <<__EOD__ -[#{ dq(name_as) }, #{ dq(name) }, - #{ paramstr }, - #{ ndq(soapaction) }, #{ ndq(namespace) }, #{ sym(stylestr) } -] -__EOD__ - end - def collect_rpcparameter(operation) result = operation.inputparts.collect { |part| collect_type(part.type) - param_set('in', rpcdefinedtype(part), part.name) + param_set(::SOAP::RPC::SOAPMethod::IN, rpcdefinedtype(part), part.name) } outparts = operation.outputparts if outparts.size > 0 retval = outparts[0] collect_type(retval.type) - result << param_set('retval', rpcdefinedtype(retval), retval.name) + result << param_set(::SOAP::RPC::SOAPMethod::RETVAL, + rpcdefinedtype(retval), retval.name) cdr(outparts).each { |part| collect_type(part.type) - result << param_set('out', rpcdefinedtype(part), part.name) + result << param_set(::SOAP::RPC::SOAPMethod::OUT, rpcdefinedtype(part), + part.name) } end result end def collect_documentparameter(operation) - input = operation.inputparts[0] - output = operation.outputparts[0] - [ - param_set('input', documentdefinedtype(input), input.name), - param_set('output', documentdefinedtype(output), output.name) - ] + param = [] + operation.inputparts.each do |input| + param << param_set(::SOAP::RPC::SOAPMethod::IN, + documentdefinedtype(input), input.name) + end + operation.outputparts.each do |output| + param << param_set(::SOAP::RPC::SOAPMethod::OUT, + documentdefinedtype(output), output.name) + end + param + end + +private + + def dump_method(operation, binding) + name = safemethodname(operation.name.name) + name_as = operation.name.name + style = binding.soapoperation_style + namespace = binding.input.soapbody.namespace + if style == :rpc + paramstr = param2str(collect_rpcparameter(operation)) + else + paramstr = param2str(collect_documentparameter(operation)) + end + if paramstr.empty? + paramstr = '[]' + else + paramstr = "[\n" << paramstr.gsub(/^/, ' ') << "\n ]" + end + return <<__EOD__ +[#{dq(name_as)}, #{dq(name)}, + #{paramstr}, + #{ndq(binding.soapaction)}, #{ndq(namespace)}, #{sym(style.id2name)} +] +__EOD__ end def rpcdefinedtype(part) @@ -101,33 +107,40 @@ __EOD__ elsif definedtype = @simpletypes[part.type] ['::' + basetype_mapped_class(definedtype.base).name] elsif definedtype = @elements[part.element] - ['::SOAP::SOAPStruct', part.element.namespace, part.element.name] + #['::SOAP::SOAPStruct', part.element.namespace, part.element.name] + ['nil', part.element.namespace, part.element.name] elsif definedtype = @complextypes[part.type] case definedtype.compoundtype - when :TYPE_STRUCT - ['::SOAP::SOAPStruct', part.type.namespace, part.type.name] + when :TYPE_STRUCT, :TYPE_EMPTY # ToDo: empty should be treated as void. + type = create_class_name(part.type) + [type, part.type.namespace, part.type.name] + when :TYPE_MAP + [Hash.name, part.type.namespace, part.type.name] when :TYPE_ARRAY arytype = definedtype.find_arytype || XSD::AnyTypeName ns = arytype.namespace name = arytype.name.sub(/\[(?:,)*\]$/, '') - ['::SOAP::SOAPArray', ns, name] + type = create_class_name(XSD::QName.new(ns, name)) + [type + '[]', ns, name] else - raise NotImplementedError.new("Must not reach here.") + raise NotImplementedError.new("must not reach here") end else - raise RuntimeError.new("Part: #{part.name} cannot be resolved.") + raise RuntimeError.new("part: #{part.name} cannot be resolved") end end def documentdefinedtype(part) - if definedtype = @simpletypes[part.type] + if mapped = basetype_mapped_class(part.type) + ['::' + mapped.name, nil, part.name] + elsif definedtype = @simpletypes[part.type] ['::' + basetype_mapped_class(definedtype.base).name, nil, part.name] elsif definedtype = @elements[part.element] ['::SOAP::SOAPElement', part.element.namespace, part.element.name] elsif definedtype = @complextypes[part.type] ['::SOAP::SOAPElement', part.type.namespace, part.type.name] else - raise RuntimeError.new("Part: #{part.name} cannot be resolved.") + raise RuntimeError.new("part: #{part.name} cannot be resolved") end end @@ -138,6 +151,7 @@ __EOD__ def collect_type(type) # ignore inline type definition. return if type.nil? + return if @types.include?(type) @types << type return unless @complextypes[type] @complextypes[type].each_element do |element| @@ -147,15 +161,15 @@ __EOD__ def param2str(params) params.collect { |param| - "[#{ dq(param[0]) }, #{ dq(param[2]) }, #{ type2str(param[1]) }]" + "[#{dq(param[0])}, #{dq(param[2])}, #{type2str(param[1])}]" }.join(",\n") end def type2str(type) if type.size == 1 - "[#{ type[0] }]" + "[#{dq(type[0])}]" else - "[#{ type[0] }, #{ ndq(type[1]) }, #{ dq(type[2]) }]" + "[#{dq(type[0])}, #{ndq(type[1])}, #{dq(type[2])}]" end end diff --git a/lib/wsdl/soap/operation.rb b/lib/wsdl/soap/operation.rb index 51bb2e9403..502d34a07d 100644 --- a/lib/wsdl/soap/operation.rb +++ b/lib/wsdl/soap/operation.rb @@ -101,8 +101,7 @@ private "EncodingStyle '#{ soapbody.encodingstyle }' not supported.") end if soapbody.namespace - op_name = op_name.dup - op_name.namespace = soapbody.namespace + op_name = XSD::QName.new(soapbody.namespace, op_name.name) end if soapbody.parts target = soapbody.parts.split(/\s+/) @@ -114,8 +113,7 @@ private end faultpart = nil - soapaction = parent.soapoperation.soapaction - OperationInfo.new(operation_style, op_name, optype_name, headerparts, bodyparts, faultpart, soapaction) + OperationInfo.new(operation_style, op_name, optype_name, headerparts, bodyparts, faultpart, parent.soapaction) end end diff --git a/lib/wsdl/soap/servantSkeltonCreator.rb b/lib/wsdl/soap/servantSkeltonCreator.rb index 12761ab5b4..88294ffed8 100644 --- a/lib/wsdl/soap/servantSkeltonCreator.rb +++ b/lib/wsdl/soap/servantSkeltonCreator.rb @@ -1,5 +1,5 @@ # WSDL4R - Creating servant skelton code from WSDL. -# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. +# Copyright (C) 2002, 2003, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. # This program is copyrighted free software by NAKAMURA, Hiroshi. You can # redistribute it and/or modify it under the same terms of Ruby's license; @@ -17,7 +17,7 @@ module SOAP class ServantSkeltonCreator include ClassDefCreatorSupport - include ::XSD::CodeGen::GenSupport + include XSD::CodeGen::GenSupport attr_reader :definitions @@ -42,7 +42,7 @@ private def dump_porttype(name) class_name = create_class_name(name) - c = ::XSD::CodeGen::ClassDef.new(class_name) + c = XSD::CodeGen::ClassDef.new(class_name) operations = @definitions.porttype(name).operations operations.each do |operation| name = safemethodname(operation.name.name) @@ -50,7 +50,7 @@ private params = input.find_message.parts.collect { |part| safevarname(part.name) } - m = ::XSD::CodeGen::MethodDef.new(name, params) do <<-EOD + m = XSD::CodeGen::MethodDef.new(name, params) do <<-EOD p [#{params.join(", ")}] raise NotImplementedError.new EOD diff --git a/lib/wsdl/soap/standaloneServerStubCreator.rb b/lib/wsdl/soap/standaloneServerStubCreator.rb index 779139a5f4..243ac12216 100644 --- a/lib/wsdl/soap/standaloneServerStubCreator.rb +++ b/lib/wsdl/soap/standaloneServerStubCreator.rb @@ -1,5 +1,5 @@ # WSDL4R - Creating standalone server stub code from WSDL. -# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. +# Copyright (C) 2002, 2003, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. # This program is copyrighted free software by NAKAMURA, Hiroshi. You can # redistribute it and/or modify it under the same terms of Ruby's license; @@ -26,10 +26,8 @@ class StandaloneServerStubCreator end def dump(service_name) - STDERR.puts "!!! IMPORTANT !!!" - STDERR.puts "- Standalone stub can have only 1 port for now. So creating stub for the first port and rests are ignored." - STDERR.puts "- Standalone server stub ignores port location defined in WSDL. Location is http://localhost:10080/ by default. Generated client from WSDL must be configured to point this endpoint by hand." - STDERR.puts "!!! IMPORTANT !!!" + warn("- Standalone stub can have only 1 port for now. So creating stub for the first port and rests are ignored.") + warn("- Standalone server stub ignores port location defined in WSDL. Location is http://localhost:10080/ by default. Generated client from WSDL must be configured to point this endpoint manually.") port = @definitions.service(service_name).ports[0] dump_porttype(port.porttype.name) end @@ -41,28 +39,28 @@ private methoddef, types = MethodDefCreator.new(@definitions).dump(name) mr_creator = MappingRegistryCreator.new(@definitions) - c1 = ::XSD::CodeGen::ClassDef.new(class_name) + c1 = XSD::CodeGen::ClassDef.new(class_name) c1.def_require("soap/rpc/standaloneServer") c1.def_require("soap/mapping/registry") c1.def_const("MappingRegistry", "::SOAP::Mapping::Registry.new") c1.def_code(mr_creator.dump(types)) c1.def_code <<-EOD Methods = [ -#{ methoddef.gsub(/^/, " ") } +#{methoddef.gsub(/^/, " ")} ] EOD - c2 = ::XSD::CodeGen::ClassDef.new(class_name + "App", + c2 = XSD::CodeGen::ClassDef.new(class_name + "App", "::SOAP::RPC::StandaloneServer") c2.def_method("initialize", "*arg") do <<-EOD super(*arg) servant = #{class_name}.new #{class_name}::Methods.each do |name_as, name, param_def, soapaction, namespace, style| - qname = XSD::QName.new(namespace, name_as) if style == :document - @soaplet.app_scope_router.add_document_method(servant, qname, soapaction, name, param_def) + @router.add_document_operation(servant, soapaction, name, param_def) else - @soaplet.app_scope_router.add_rpc_method(servant, qname, soapaction, name, param_def) + qname = XSD::QName.new(namespace, name_as) + @router.add_rpc_operation(servant, qname, soapaction, name, param_def) end end self.mapping_registry = #{class_name}::MappingRegistry diff --git a/lib/wsdl/xmlSchema/attribute.rb b/lib/wsdl/xmlSchema/attribute.rb index 6861fc171e..cfd4c68422 100644 --- a/lib/wsdl/xmlSchema/attribute.rb +++ b/lib/wsdl/xmlSchema/attribute.rb @@ -1,5 +1,5 @@ # WSDL4R - XMLSchema attribute definition for WSDL. -# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. +# Copyright (C) 2002, 2003, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. # This program is copyrighted free software by NAKAMURA, Hiroshi. You can # redistribute it and/or modify it under the same terms of Ruby's license; @@ -14,34 +14,74 @@ 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 + class << self + if RUBY_VERSION > "1.7.0" + def attr_reader_ref(symbol) + name = symbol.to_s + self.__send__(:define_method, name, proc { + instance_variable_get("@#{name}") || + (refelement ? refelement.__send__(name) : nil) + }) + end + else + def attr_reader_ref(symbol) + name = symbol.to_s + module_eval <<-EOS + def #{name} + @#{name} || (refelement ? refelement.#{name} : nil) + end + EOS + end + end + end + + attr_writer :use + attr_writer :form + attr_writer :name + attr_writer :type + attr_writer :local_simpletype + attr_writer :default + attr_writer :fixed + + attr_reader_ref :use + attr_reader_ref :form + attr_reader_ref :name + attr_reader_ref :type + attr_reader_ref :local_simpletype + attr_reader_ref :default + attr_reader_ref :fixed + attr_accessor :ref attr_accessor :arytype def initialize super - @ref = nil @use = nil @form = nil @name = nil @type = nil + @local_simpletype = nil @default = nil @fixed = nil + @ref = nil + @refelement = nil @arytype = nil end + def refelement + @refelement ||= root.collect_attributes[@ref] + end + def targetnamespace parent.targetnamespace end def parse_element(element) - nil + case element + when SimpleTypeName + @local_simpletype = SimpleType.new + @local_simpletype + end end def parse_attr(attr, value) diff --git a/lib/wsdl/xmlSchema/complexContent.rb b/lib/wsdl/xmlSchema/complexContent.rb index 66ad9e251d..eddb52f5ef 100644 --- a/lib/wsdl/xmlSchema/complexContent.rb +++ b/lib/wsdl/xmlSchema/complexContent.rb @@ -26,12 +26,17 @@ class ComplexContent < Info @derivetype = nil @content = nil @attributes = XSD::NamedElements.new + @basetype = nil end def targetnamespace parent.targetnamespace end + def basetype + @basetype ||= root.collect_complextypes[@base] + end + def parse_element(element) case element when RestrictionName, ExtensionName diff --git a/lib/wsdl/xmlSchema/data.rb b/lib/wsdl/xmlSchema/data.rb index 10bc343adb..23ab1adf0b 100644 --- a/lib/wsdl/xmlSchema/data.rb +++ b/lib/wsdl/xmlSchema/data.rb @@ -1,5 +1,5 @@ # WSDL4R - XMLSchema data definitions. -# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. +# Copyright (C) 2002, 2003, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. # This program is copyrighted free software by NAKAMURA, Hiroshi. You can # redistribute it and/or modify it under the same terms of Ruby's license; @@ -7,10 +7,13 @@ require 'xsd/datatypes' +require 'wsdl/xmlSchema/annotation' require 'wsdl/xmlSchema/schema' require 'wsdl/xmlSchema/import' +require 'wsdl/xmlSchema/include' require 'wsdl/xmlSchema/simpleType' require 'wsdl/xmlSchema/simpleRestriction' +require 'wsdl/xmlSchema/simpleExtension' require 'wsdl/xmlSchema/complexType' require 'wsdl/xmlSchema/complexContent' require 'wsdl/xmlSchema/simpleContent' @@ -22,12 +25,15 @@ require 'wsdl/xmlSchema/sequence' require 'wsdl/xmlSchema/attribute' require 'wsdl/xmlSchema/unique' require 'wsdl/xmlSchema/enumeration' +require 'wsdl/xmlSchema/length' +require 'wsdl/xmlSchema/pattern' module WSDL module XMLSchema AllName = XSD::QName.new(XSD::Namespace, 'all') +AnnotationName = XSD::QName.new(XSD::Namespace, 'annotation') AnyName = XSD::QName.new(XSD::Namespace, 'any') AttributeName = XSD::QName.new(XSD::Namespace, 'attribute') ChoiceName = XSD::QName.new(XSD::Namespace, 'choice') @@ -37,6 +43,9 @@ ElementName = XSD::QName.new(XSD::Namespace, 'element') EnumerationName = XSD::QName.new(XSD::Namespace, 'enumeration') ExtensionName = XSD::QName.new(XSD::Namespace, 'extension') ImportName = XSD::QName.new(XSD::Namespace, 'import') +IncludeName = XSD::QName.new(XSD::Namespace, 'include') +LengthName = XSD::QName.new(XSD::Namespace, 'length') +PatternName = XSD::QName.new(XSD::Namespace, 'pattern') RestrictionName = XSD::QName.new(XSD::Namespace, 'restriction') SequenceName = XSD::QName.new(XSD::Namespace, 'sequence') SchemaName = XSD::QName.new(XSD::Namespace, 'schema') diff --git a/lib/wsdl/xmlSchema/element.rb b/lib/wsdl/xmlSchema/element.rb index cc9d4e9ed8..584afe9dc6 100644 --- a/lib/wsdl/xmlSchema/element.rb +++ b/lib/wsdl/xmlSchema/element.rb @@ -1,5 +1,5 @@ # WSDL4R - XMLSchema element definition for WSDL. -# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. +# Copyright (C) 2002, 2003, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. # This program is copyrighted free software by NAKAMURA, Hiroshi. You can # redistribute it and/or modify it under the same terms of Ruby's license; @@ -14,23 +14,62 @@ 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) + class << self + if RUBY_VERSION > "1.7.0" + def attr_reader_ref(symbol) + name = symbol.to_s + self.__send__(:define_method, name, proc { + instance_variable_get("@#{name}") || + (refelement ? refelement.__send__(name) : nil) + }) + end + else + def attr_reader_ref(symbol) + name = symbol.to_s + module_eval <<-EOS + def #{name} + @#{name} || (refelement ? refelement.#{name} : nil) + end + EOS + end + end + end + + attr_writer :name # required + attr_writer :type + attr_writer :local_simpletype + attr_writer :local_complextype + attr_writer :constraint + attr_writer :maxoccurs + attr_writer :minoccurs + attr_writer :nillable + + attr_reader_ref :name + attr_reader_ref :type + attr_reader_ref :local_simpletype + attr_reader_ref :local_complextype + attr_reader_ref :constraint + attr_reader_ref :maxoccurs + attr_reader_ref :minoccurs + attr_reader_ref :nillable + + attr_accessor :ref + + def initialize(name = nil, type = nil) super() @name = name @type = type - @local_complextype = nil + @local_simpletype = @local_complextype = nil @constraint = nil @maxoccurs = '1' @minoccurs = '1' @nillable = nil + @ref = nil + @refelement = nil + end + + def refelement + @refelement ||= root.collect_elements[@ref] end def targetnamespace @@ -44,6 +83,9 @@ class Element < Info def parse_element(element) case element + when SimpleTypeName + @local_simpletype = SimpleType.new + @local_simpletype when ComplexTypeName @type = nil @local_complextype = ComplexType.new @@ -62,19 +104,19 @@ class Element < Info @name = XSD::QName.new(targetnamespace, value.source) when TypeAttrName @type = value + when RefAttrName + @ref = value when MaxOccursAttrName if parent.is_a?(All) if value.source != '1' - raise Parser::AttrConstraintError.new( - "Cannot parse #{ value } for #{ attr }.") + raise Parser::AttrConstraintError.new("cannot parse #{value} for #{attr}") end end @maxoccurs = value.source when MinOccursAttrName if parent.is_a?(All) unless ['0', '1'].include?(value.source) - raise Parser::AttrConstraintError.new( - "Cannot parse #{ value } for #{ attr }.") + raise Parser::AttrConstraintError.new("cannot parse #{value} for #{attr}") end end @minoccurs = value.source diff --git a/lib/wsdl/xmlSchema/import.rb b/lib/wsdl/xmlSchema/import.rb index e65641330d..d3487af934 100644 --- a/lib/wsdl/xmlSchema/import.rb +++ b/lib/wsdl/xmlSchema/import.rb @@ -1,5 +1,5 @@ # WSDL4R - XMLSchema import definition. -# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. +# Copyright (C) 2002, 2003, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. # This program is copyrighted free software by NAKAMURA, Hiroshi. You can # redistribute it and/or modify it under the same terms of Ruby's license; @@ -7,6 +7,7 @@ require 'wsdl/info' +require 'wsdl/xmlSchema/importer' module WSDL @@ -16,11 +17,13 @@ module XMLSchema class Import < Info attr_reader :namespace attr_reader :schemalocation + attr_reader :content def initialize super @namespace = nil @schemalocation = nil + @content = nil end def parse_element(element) @@ -32,11 +35,29 @@ class Import < Info when NamespaceAttrName @namespace = value.source when SchemaLocationAttrName - @schemalocation = value.source + @schemalocation = URI.parse(value.source) + if @schemalocation.relative? and !parent.location.nil? and + !parent.location.relative? + @schemalocation = parent.location + @schemalocation + end + if root.importedschema.key?(@schemalocation) + @content = root.importedschema[@schemalocation] + else + root.importedschema[@schemalocation] = nil # placeholder + @content = import(@schemalocation) + root.importedschema[@schemalocation] = @content + end + @schemalocation else nil end end + +private + + def import(location) + Importer.import(location, root) + end end diff --git a/lib/wsdl/xmlSchema/parser.rb b/lib/wsdl/xmlSchema/parser.rb index a7f1c29fd4..057d9d9b70 100644 --- a/lib/wsdl/xmlSchema/parser.rb +++ b/lib/wsdl/xmlSchema/parser.rb @@ -1,5 +1,5 @@ # WSDL4R - WSDL XML Instance parser library. -# Copyright (C) 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. +# Copyright (C) 2002, 2003, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. # This program is copyrighted free software by NAKAMURA, Hiroshi. You can # redistribute it and/or modify it under the same terms of Ruby's license; @@ -51,6 +51,9 @@ public @parser = XSD::XMLParser.create_parser(self, opt) @parsestack = nil @lastnode = nil + @ignored = {} + @location = opt[:location] + @originalroot = opt[:originalroot] end def parse(string_or_readable) @@ -94,7 +97,7 @@ public 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 }'.") + 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 @@ -104,20 +107,31 @@ private def decode_tag(ns, name, attrs, parent) o = nil - element = ns.parse(name) + elename = ns.parse(name) if !parent - if element == SchemaName - o = Schema.parse_element(element) + if elename == SchemaName + o = Schema.parse_element(elename) + o.location = @location else - raise UnknownElementError.new("Unknown element #{ element }.") + raise UnknownElementError.new("unknown element: #{elename}") end + o.root = @originalroot if @originalroot # o.root = o otherwise else - o = parent.parse_element(element) + if elename == AnnotationName + # only the first annotation element is allowed for each element. + o = Annotation.new + else + o = parent.parse_element(elename) + end unless o - STDERR.puts("Unknown element #{ element }.") + unless @ignored.key?(elename) + warn("ignored element: #{elename} of #{parent.class}") + @ignored[elename] = elename + end o = Documentation.new # which accepts any element. end # node could be a pseudo element. pseudo element has its own parent. + o.root = parent.root o.parent = parent if o.parent.nil? end attrs.each do |key, value| @@ -127,9 +141,12 @@ private if attr_ele == IdAttrName o.id = value_ele else - unless o.parse_attr(attr_ele, value_ele) - STDERR.puts("Unknown attr #{ attr_ele }.") - end + unless o.parse_attr(attr_ele, value_ele) + unless @ignored.key?(attr_ele) + warn("ignored attr: #{attr_ele}") + @ignored[attr_ele] = attr_ele + end + end end end o diff --git a/lib/wsdl/xmlSchema/schema.rb b/lib/wsdl/xmlSchema/schema.rb index ddd231bd97..43447f9fbf 100644 --- a/lib/wsdl/xmlSchema/schema.rb +++ b/lib/wsdl/xmlSchema/schema.rb @@ -24,6 +24,8 @@ class Schema < Info attr_accessor :attributeformdefault attr_accessor :elementformdefault + attr_reader :importedschema + def initialize super @targetnamespace = nil @@ -33,6 +35,17 @@ class Schema < Info @attributes = XSD::NamedElements.new @imports = [] @elementformdefault = "qualified" + @importedschema = {} + @location = nil + @root = self + end + + def location + @location || (root.nil? ? nil : root.location) + end + + def location=(location) + @location = location end def parse_element(element) @@ -41,6 +54,10 @@ class Schema < Info o = Import.new @imports << o o + when IncludeName + o = Include.new + @imports << o + o when ComplexTypeName o = ComplexType.new @complextypes << o @@ -55,6 +72,7 @@ class Schema < Info o when AttributeName o = Attribute.new + @attributes << o o else nil @@ -74,21 +92,39 @@ class Schema < Info end end + def collect_attributes + result = XSD::NamedElements.new + result.concat(@attributes) + @imports.each do |import| + result.concat(import.content.collect_attributes) if import.content + end + result + end + def collect_elements result = XSD::NamedElements.new result.concat(@elements) + @imports.each do |import| + result.concat(import.content.collect_elements) if import.content + end result end def collect_complextypes result = XSD::NamedElements.new result.concat(@complextypes) + @imports.each do |import| + result.concat(import.content.collect_complextypes) if import.content + end result end def collect_simpletypes result = XSD::NamedElements.new result.concat(@simpletypes) + @imports.each do |import| + result.concat(import.content.collect_simpletypes) if import.content + end result end diff --git a/lib/wsdl/xmlSchema/simpleContent.rb b/lib/wsdl/xmlSchema/simpleContent.rb index 0d83678a01..e1f35c88b8 100644 --- a/lib/wsdl/xmlSchema/simpleContent.rb +++ b/lib/wsdl/xmlSchema/simpleContent.rb @@ -1,5 +1,5 @@ # WSDL4R - XMLSchema simpleContent definition for WSDL. -# Copyright (C) 2004 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. +# Copyright (C) 2004, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. # This program is copyrighted free software by NAKAMURA, Hiroshi. You can # redistribute it and/or modify it under the same terms of Ruby's license; @@ -15,17 +15,21 @@ module XMLSchema class SimpleContent < Info - attr_accessor :base - attr_reader :derivetype - attr_reader :content - attr_reader :attributes + attr_reader :restriction + attr_reader :extension + + def check_lexical_format(value) + check(value) + end def initialize super - @base = nil - @derivetype = nil - @content = nil - @attributes = XSD::NamedElements.new + @restriction = nil + @extension = nil + end + + def base + content.base end def targetnamespace @@ -34,28 +38,24 @@ class SimpleContent < Info def parse_element(element) case element - when RestrictionName, ExtensionName - @derivetype = element.name - self - when AttributeName - if @derivetype.nil? - raise Parser::ElementConstraintError.new("base attr not found.") - end - o = Attribute.new - @attributes << o - o + when RestrictionName + @restriction = SimpleRestriction.new + @restriction + when ExtensionName + @extension = SimpleExtension.new + @extension end end - def parse_attr(attr, value) - if @derivetype.nil? - return nil - end - case attr - when BaseAttrName - @base = value - else - nil +private + + def content + @restriction || @extension + end + + def check(value) + unless content.valid?(value) + raise XSD::ValueSpaceError.new("#{@name}: cannot accept '#{value}'") end end end diff --git a/lib/wsdl/xmlSchema/simpleRestriction.rb b/lib/wsdl/xmlSchema/simpleRestriction.rb index 6986e74423..e8bf3ebfa5 100644 --- a/lib/wsdl/xmlSchema/simpleRestriction.rb +++ b/lib/wsdl/xmlSchema/simpleRestriction.rb @@ -1,4 +1,4 @@ -# WSDL4R - XMLSchema simpleType definition for WSDL. +# WSDL4R - XMLSchema simpleContent restriction definition for WSDL. # Copyright (C) 2004 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. # This program is copyrighted free software by NAKAMURA, Hiroshi. You can @@ -17,21 +17,32 @@ module XMLSchema class SimpleRestriction < Info attr_reader :base attr_reader :enumeration + attr_accessor :length + attr_accessor :pattern def initialize super @base = nil @enumeration = [] # NamedElements? + @length = nil + @pattern = nil end def valid?(value) - @enumeration.include?(value) + return false unless check_restriction(value) + return false unless check_length(value) + return false unless check_pattern(value) + true end def parse_element(element) case element when EnumerationName Enumeration.new # just a parsing handler + when LengthName + Length.new # just a parsing handler + when PatternName + Pattern.new # just a parsing handler end end @@ -41,6 +52,20 @@ class SimpleRestriction < Info @base = value end end + +private + + def check_restriction(value) + @enumeration.empty? or @enumeration.include?(value) + end + + def check_length(value) + @length.nil? or value.size == @length + end + + def check_pattern(value) + @pattern.nil? or @pattern =~ value + end end diff --git a/lib/wsdl/xmlSchema/simpleType.rb b/lib/wsdl/xmlSchema/simpleType.rb index d9f76f345c..e808c318c4 100644 --- a/lib/wsdl/xmlSchema/simpleType.rb +++ b/lib/wsdl/xmlSchema/simpleType.rb @@ -1,5 +1,5 @@ # WSDL4R - XMLSchema simpleType definition for WSDL. -# Copyright (C) 2004 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. +# Copyright (C) 2004, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>. # This program is copyrighted free software by NAKAMURA, Hiroshi. You can # redistribute it and/or modify it under the same terms of Ruby's license; @@ -16,15 +16,11 @@ module XMLSchema class SimpleType < Info attr_accessor :name - attr_reader :derivetype attr_reader :restriction def check_lexical_format(value) if @restriction check_restriction(value) - elsif @extension - raise NotImplementedError - # ToDo else raise ArgumentError.new("incomplete simpleType") end @@ -33,8 +29,6 @@ class SimpleType < Info def base if @restriction @restriction.base - elsif @extension - @extension.base else raise ArgumentError.new("incomplete simpleType") end @@ -43,7 +37,6 @@ class SimpleType < Info def initialize(name = nil) super() @name = name - @derivetype = nil @restriction = nil end @@ -55,7 +48,6 @@ class SimpleType < Info case element when RestrictionName @restriction = SimpleRestriction.new - @derivetype = element.name @restriction end end @@ -71,7 +63,7 @@ private def check_restriction(value) unless @restriction.valid?(value) - raise ::XSD::ValueSpaceError.new("#{@name}: cannot accept '#{value}'.") + raise XSD::ValueSpaceError.new("#{@name}: cannot accept '#{value}'") end end end |