# soap/baseData.rb: SOAP4R - Base type library # Copyright (C) 2000, 2001, 2003 NAKAMURA, Hiroshi . # 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 '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 ### ## Marker of SOAP/DM types. # module SOAPType; end ### ## Mix-in module for SOAP base type instances. # module SOAPBasetype include SOAPType 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 SOAPType 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 self.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() if value.nil? @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?(SOAPType) 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