Welcome to the "trac"-ing site of soap4r!
[soap4r] [httpclient] [openpgp4u] [pkcs1] [logger] [csv] [vtr]

root/trunk/lib/soap/generator.rb

Revision 2011, 7.5 kB (checked in by nahi, 10 months ago)
  • merged from 1_5 branch.
    • enabled Sriver#generate_explicit_type for literal services.
    • test added.
    • removed generated files.
  • Property svn:eol-style set to native
  • Property svn:keywords set to author date id revision
Line 
1 # SOAP4R - SOAP XML Instance Generator library.
2 # Copyright (C) 2000-2007  NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
3
4 # This program is copyrighted free software by NAKAMURA, Hiroshi.  You can
5 # redistribute it and/or modify it under the same terms of Ruby's license;
6 # either the dual license version in 2003, or any later version.
7
8
9 require 'soap/soap'
10 require 'soap/ns'
11 require 'soap/baseData'
12 require 'soap/encodingstyle/handler'
13 require 'xsd/codegen/gensupport'
14
15
16 module SOAP
17
18
19 ###
20 ## CAUTION: MT-unsafe
21 #
22 class Generator
23   include SOAP
24   include XSD::CodeGen::GenSupport
25
26   class FormatEncodeError < Error; end
27
28 public
29
30   attr_accessor :charset
31   attr_accessor :default_encodingstyle
32   attr_accessor :generate_explicit_type
33   attr_accessor :use_numeric_character_reference
34   attr_accessor :use_default_namespace
35
36   def initialize(opt = {})
37     @reftarget = nil
38     @handlers = {}
39     @charset = opt[:charset] || XSD::Charset.xml_encoding_label
40     @default_encodingstyle = opt[:default_encodingstyle] || EncodingNamespace
41     @generate_explicit_type =
42       opt.key?(:generate_explicit_type) ? opt[:generate_explicit_type] : true
43     @use_default_namespace = opt[:use_default_namespace]
44     @attributeformdefault = opt[:attributeformdefault]
45     @use_numeric_character_reference = opt[:use_numeric_character_reference]
46     @indentstr = opt[:no_indent] ? '' : '  '
47     @buf = @indent = @curr = nil
48     @default_ns = opt[:default_ns]
49     @default_ns_tag = opt[:default_ns_tag]
50   end
51
52   def generate(obj, io = nil)
53     @buf = io || ''
54     @indent = ''
55     @encode_char_regexp = get_encode_char_regexp()
56
57     prologue
58     @handlers.each do |uri, handler|
59       handler.encode_prologue
60     end
61
62     ns = SOAP::NS.new
63     if @default_ns
64       @default_ns.each_ns do |default_ns, default_tag|
65         Generator.assign_ns(obj.extraattr, ns, default_ns, default_tag)
66       end
67     end
68     if @default_ns_tag
69       @default_ns_tag.each_ns do |default_ns, default_tag|
70         ns.known_tag[default_ns] = default_tag
71       end
72     end
73     @buf << xmldecl
74     encode_data(ns, obj, nil)
75
76     @handlers.each do |uri, handler|
77       handler.encode_epilogue
78     end
79     epilogue
80
81     @buf
82   end
83
84   def encode_data(ns, obj, parent)
85     if obj.respond_to?(:to_xmlpart)
86       formatted = trim_eol(obj.to_xmlpart)
87       formatted = trim_indent(formatted)
88       formatted = formatted.gsub(/^/, @indent).sub(/\n+\z/, '')
89       @buf << "\n#{formatted}"
90       return
91     elsif obj.is_a?(SOAPEnvelopeElement)
92       encode_element(ns, obj, parent)
93       return
94     end
95     if @reftarget && !obj.precedents.empty?
96       add_reftarget(obj.elename.name, obj)
97       ref = SOAPReference.new(obj)
98       ref.elename = ref.elename.dup_name(obj.elename.name)
99       obj.precedents.clear      # Avoid cyclic delay.
100       obj.encodingstyle = parent.encodingstyle
101       # SOAPReference is encoded here.
102       obj = ref
103     end
104     encodingstyle = obj.encodingstyle
105     # Children's encodingstyle is derived from its parent.
106     encodingstyle ||= parent.encodingstyle if parent
107     obj.encodingstyle = encodingstyle
108     handler = find_handler(encodingstyle || @default_encodingstyle)
109     unless handler
110       raise FormatEncodeError.new("Unknown encodingStyle: #{ encodingstyle }.")
111     end
112     if !obj.elename.name
113       raise FormatEncodeError.new("Element name not defined: #{ obj }.")
114     end
115     handler.encode_data(self, ns, obj, parent)
116     handler.encode_data_end(self, ns, obj, parent)
117   end
118
119   def add_reftarget(name, node)
120     unless @reftarget
121       raise FormatEncodeError.new("Reftarget is not defined.")
122     end
123     @reftarget.add(name, node)
124   end
125
126   def encode_child(ns, child, parent)
127     indent_backup, @indent = @indent, @indent + @indentstr
128     encode_data(ns.clone_ns, child, parent)
129     @indent = indent_backup
130   end
131
132   def encode_element(ns, obj, parent)
133     attrs = obj.extraattr
134     if obj.is_a?(SOAPBody)
135       @reftarget = obj
136       obj.encode(self, ns, attrs) do |child|
137         indent_backup, @indent = @indent, @indent + @indentstr
138         encode_data(ns.clone_ns, child, obj)
139         @indent = indent_backup
140       end
141       @reftarget = nil
142     else
143       if obj.is_a?(SOAPEnvelope)
144         Generator.assign_ns(attrs, ns, XSD::InstanceNamespace)
145         Generator.assign_ns(attrs, ns, XSD::Namespace)
146       end
147       obj.encode(self, ns, attrs) do |child|
148         indent_backup, @indent = @indent, @indent + @indentstr
149         encode_data(ns.clone_ns, child, obj)
150         @indent = indent_backup
151       end
152     end
153   end
154
155   def encode_name(ns, data, attrs)
156     if element_local?(data)
157       data.elename.name
158     else
159       if @use_default_namespace
160         Generator.assign_ns(attrs, ns, data.elename.namespace, '')
161       else
162         Generator.assign_ns(attrs, ns, data.elename.namespace)
163       end
164       ns.name(data.elename)
165     end
166   end
167
168   def encode_name_end(ns, data)
169     if element_local?(data)
170       data.elename.name
171     else
172       ns.name(data.elename)
173     end
174   end
175
176   def encode_tag(elename, attrs = nil)
177     if attrs.nil? or attrs.empty?
178       @buf << "\n#{ @indent }<#{ elename }>"
179       return
180     end
181     ary = []
182     attrs.each do |key, value|
183       ary << %Q[#{ key }="#{ get_encoded(value.to_s) }"]
184     end
185     case ary.size
186     when 0
187       @buf << "\n#{ @indent }<#{ elename }>"
188     when 1
189       @buf << %Q[\n#{ @indent }<#{ elename } #{ ary[0] }>]
190     else
191       @buf << "\n#{ @indent }<#{ elename } " <<
192         ary.join("\n#{ @indent }#{ @indentstr * 2 }") <<
193         '>'
194     end
195   end
196
197   def encode_tag_end(elename, cr = nil)
198     if cr
199       @buf << "\n#{ @indent }</#{ elename }>"
200     else
201       @buf << "</#{ elename }>"
202     end
203   end
204
205   def encode_rawstring(str)
206     @buf << str
207   end
208
209   def encode_string(str)
210     @buf << get_encoded(str)
211   end
212
213   def element_local?(element)
214     element.elename.namespace.nil?
215   end
216
217   def self.assign_ns(attrs, ns, namespace, tag = nil)
218     if namespace.nil?
219       raise FormatEncodeError.new("empty namespace")
220     end
221     override_default_ns = (tag == '' and namespace != ns.default_namespace)
222     if override_default_ns or !ns.assigned?(namespace)
223       assign_ns!(attrs, ns, namespace, tag)
224     end
225   end
226
227   def self.assign_ns!(attrs, ns, namespace, tag = nil)
228     tag = ns.assign(namespace, tag)
229     if tag == ''
230       attr = 'xmlns'
231     else
232       attr = "xmlns:#{tag}"
233     end
234     attrs[attr] = namespace
235   end
236
237 private
238
239   def prologue
240   end
241
242   def epilogue
243   end
244
245   ENCODE_CHAR_REGEXP = {}
246
247   EncodeMap = {
248     '&' => '&amp;',
249     '<' => '&lt;',
250     '>' => '&gt;',
251     '"' => '&quot;',
252     '\'' => '&apos;',
253     "\r" => '&#xd;'
254   }
255
256   def get_encoded(str)
257     if @use_numeric_character_reference and !XSD::Charset.is_us_ascii(str)
258       str.gsub!(@encode_char_regexp) { |c| EncodeMap[c] }
259       str.unpack("U*").collect { |c|
260         if c == 0x9 or c == 0xa or c == 0xd or (c >= 0x20 and c <= 0x7f)
261           c.chr
262         else
263           sprintf("&#x%x;", c)
264         end
265       }.join
266     else
267       str.gsub(@encode_char_regexp) { |c| EncodeMap[c] }
268     end
269   end
270
271   def get_encode_char_regexp
272     ENCODE_CHAR_REGEXP[XSD::Charset.encoding] ||=
273       Regexp.new("[#{EncodeMap.keys.join}]", nil, XSD::Charset.encoding)
274   end
275
276   def find_handler(encodingstyle)
277     unless @handlers.key?(encodingstyle)
278       factory = SOAP::EncodingStyle::Handler.handler(encodingstyle)
279       if factory
280         handler = factory.new(@charset)
281         handler.generate_explicit_type = @generate_explicit_type
282         handler.encode_prologue
283         @handlers[encodingstyle] = handler
284       end
285     end
286     @handlers[encodingstyle]
287   end
288
289   def xmldecl
290     if @charset
291       %Q[<?xml version="1.0" encoding="#{ @charset }" ?>]
292     else
293       %Q[<?xml version="1.0" ?>]
294     end
295   end
296 end
297
298
299 end
Note: See TracBrowser for help on using the browser.