=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