1 | # SOAP4R - Ruby type mapping utility.
|
---|
2 | # Copyright (C) 2000, 2001, 2003-2005 NAKAMURA Hiroshi <[email protected]>.
|
---|
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 'xsd/codegen/gensupport'
|
---|
10 |
|
---|
11 |
|
---|
12 | module SOAP
|
---|
13 |
|
---|
14 |
|
---|
15 | module Mapping
|
---|
16 | RubyTypeNamespace = 'http://www.ruby-lang.org/xmlns/ruby/type/1.6'
|
---|
17 | RubyTypeInstanceNamespace =
|
---|
18 | 'http://www.ruby-lang.org/xmlns/ruby/type-instance'
|
---|
19 | RubyCustomTypeNamespace = 'http://www.ruby-lang.org/xmlns/ruby/type/custom'
|
---|
20 | ApacheSOAPTypeNamespace = 'http://xml.apache.org/xml-soap'
|
---|
21 |
|
---|
22 |
|
---|
23 | # TraverseSupport breaks following thread variables.
|
---|
24 | # Thread.current[:SOAPMarshalDataKey]
|
---|
25 | module TraverseSupport
|
---|
26 | def mark_marshalled_obj(obj, soap_obj)
|
---|
27 | raise if obj.nil?
|
---|
28 | Thread.current[:SOAPMarshalDataKey][obj.__id__] = soap_obj
|
---|
29 | end
|
---|
30 |
|
---|
31 | def mark_unmarshalled_obj(node, obj)
|
---|
32 | return if obj.nil?
|
---|
33 | # node.id is not Object#id but SOAPReference#id
|
---|
34 | Thread.current[:SOAPMarshalDataKey][node.id] = obj
|
---|
35 | end
|
---|
36 | end
|
---|
37 |
|
---|
38 |
|
---|
39 | EMPTY_OPT = {}
|
---|
40 | def self.obj2soap(obj, registry = nil, type = nil, opt = EMPTY_OPT)
|
---|
41 | registry ||= Mapping::DefaultRegistry
|
---|
42 | soap_obj = nil
|
---|
43 | protect_threadvars(:SOAPMarshalDataKey, :SOAPExternalCES, :SOAPMarshalNoReference) do
|
---|
44 | Thread.current[:SOAPMarshalDataKey] = {}
|
---|
45 | Thread.current[:SOAPExternalCES] = opt[:external_ces] || $KCODE
|
---|
46 | Thread.current[:SOAPMarshalNoReference] = opt[:no_reference]
|
---|
47 | soap_obj = _obj2soap(obj, registry, type)
|
---|
48 | end
|
---|
49 | soap_obj
|
---|
50 | end
|
---|
51 |
|
---|
52 | def self.soap2obj(node, registry = nil, klass = nil, opt = EMPTY_OPT)
|
---|
53 | registry ||= Mapping::DefaultRegistry
|
---|
54 | obj = nil
|
---|
55 | protect_threadvars(:SOAPMarshalDataKey, :SOAPExternalCES, :SOAPMarshalNoReference) do
|
---|
56 | Thread.current[:SOAPMarshalDataKey] = {}
|
---|
57 | Thread.current[:SOAPExternalCES] = opt[:external_ces] || $KCODE
|
---|
58 | Thread.current[:SOAPMarshalNoReference] = opt[:no_reference]
|
---|
59 | obj = _soap2obj(node, registry, klass)
|
---|
60 | end
|
---|
61 | obj
|
---|
62 | end
|
---|
63 |
|
---|
64 | def self.ary2soap(ary, type_ns = XSD::Namespace, typename = XSD::AnyTypeLiteral, registry = nil, opt = EMPTY_OPT)
|
---|
65 | registry ||= Mapping::DefaultRegistry
|
---|
66 | type = XSD::QName.new(type_ns, typename)
|
---|
67 | soap_ary = SOAPArray.new(ValueArrayName, 1, type)
|
---|
68 | protect_threadvars(:SOAPMarshalDataKey, :SOAPExternalCES, :SOAPMarshalNoReference) do
|
---|
69 | Thread.current[:SOAPMarshalDataKey] = {}
|
---|
70 | Thread.current[:SOAPExternalCES] = opt[:external_ces] || $KCODE
|
---|
71 | Thread.current[:SOAPMarshalNoReference] = opt[:no_reference]
|
---|
72 | ary.each do |ele|
|
---|
73 | soap_ary.add(_obj2soap(ele, registry, type))
|
---|
74 | end
|
---|
75 | end
|
---|
76 | soap_ary
|
---|
77 | end
|
---|
78 |
|
---|
79 | def self.ary2md(ary, rank, type_ns = XSD::Namespace, typename = XSD::AnyTypeLiteral, registry = nil, opt = EMPTY_OPT)
|
---|
80 | registry ||= Mapping::DefaultRegistry
|
---|
81 | type = XSD::QName.new(type_ns, typename)
|
---|
82 | md_ary = SOAPArray.new(ValueArrayName, rank, type)
|
---|
83 | protect_threadvars(:SOAPMarshalDataKey, :SOAPExternalCES, :SOAPMarshalNoReference) do
|
---|
84 | Thread.current[:SOAPMarshalDataKey] = {}
|
---|
85 | Thread.current[:SOAPExternalCES] = opt[:external_ces] || $KCODE
|
---|
86 | Thread.current[:SOAPMarshalNoReference] = opt[:no_reference]
|
---|
87 | add_md_ary(md_ary, ary, [], registry)
|
---|
88 | end
|
---|
89 | md_ary
|
---|
90 | end
|
---|
91 |
|
---|
92 | def self.fault2exception(fault, registry = nil)
|
---|
93 | registry ||= Mapping::DefaultRegistry
|
---|
94 | detail = if fault.detail
|
---|
95 | soap2obj(fault.detail, registry) || ""
|
---|
96 | else
|
---|
97 | ""
|
---|
98 | end
|
---|
99 | if detail.is_a?(Mapping::SOAPException)
|
---|
100 | begin
|
---|
101 | e = detail.to_e
|
---|
102 | remote_backtrace = e.backtrace
|
---|
103 | e.set_backtrace(nil)
|
---|
104 | raise e # ruby sets current caller as local backtrace of e => e2.
|
---|
105 | rescue Exception => e
|
---|
106 | e.set_backtrace(remote_backtrace + e.backtrace[1..-1])
|
---|
107 | raise
|
---|
108 | end
|
---|
109 | else
|
---|
110 | fault.detail = detail
|
---|
111 | fault.set_backtrace(
|
---|
112 | if detail.is_a?(Array)
|
---|
113 | detail
|
---|
114 | else
|
---|
115 | [detail.to_s]
|
---|
116 | end
|
---|
117 | )
|
---|
118 | raise
|
---|
119 | end
|
---|
120 | end
|
---|
121 |
|
---|
122 | def self._obj2soap(obj, registry, type = nil)
|
---|
123 | if referent = Thread.current[:SOAPMarshalDataKey][obj.__id__] and
|
---|
124 | !Thread.current[:SOAPMarshalNoReference]
|
---|
125 | SOAPReference.new(referent)
|
---|
126 | elsif registry
|
---|
127 | registry.obj2soap(obj, type)
|
---|
128 | else
|
---|
129 | raise MappingError.new("no mapping registry given")
|
---|
130 | end
|
---|
131 | end
|
---|
132 |
|
---|
133 | def self._soap2obj(node, registry, klass = nil)
|
---|
134 | if node.nil?
|
---|
135 | return nil
|
---|
136 | elsif node.is_a?(SOAPReference)
|
---|
137 | target = node.__getobj__
|
---|
138 | # target.id is not Object#id but SOAPReference#id
|
---|
139 | if referent = Thread.current[:SOAPMarshalDataKey][target.id] and
|
---|
140 | !Thread.current[:SOAPMarshalNoReference]
|
---|
141 | return referent
|
---|
142 | else
|
---|
143 | return _soap2obj(target, registry, klass)
|
---|
144 | end
|
---|
145 | end
|
---|
146 | return registry.soap2obj(node, klass)
|
---|
147 | end
|
---|
148 |
|
---|
149 | if Object.respond_to?(:allocate)
|
---|
150 | # ruby/1.7 or later.
|
---|
151 | def self.create_empty_object(klass)
|
---|
152 | klass.allocate
|
---|
153 | end
|
---|
154 | else
|
---|
155 | MARSHAL_TAG = {
|
---|
156 | String => ['"', 1],
|
---|
157 | Regexp => ['/', 2],
|
---|
158 | Array => ['[', 1],
|
---|
159 | Hash => ['{', 1]
|
---|
160 | }
|
---|
161 | def self.create_empty_object(klass)
|
---|
162 | if klass <= Struct
|
---|
163 | name = klass.name
|
---|
164 | return ::Marshal.load(sprintf("\004\006S:%c%s\000", name.length + 5, name))
|
---|
165 | end
|
---|
166 | if MARSHAL_TAG.has_key?(klass)
|
---|
167 | tag, terminate = MARSHAL_TAG[klass]
|
---|
168 | return ::Marshal.load(sprintf("\004\006%s%s", tag, "\000" * terminate))
|
---|
169 | end
|
---|
170 | MARSHAL_TAG.each do |k, v|
|
---|
171 | if klass < k
|
---|
172 | name = klass.name
|
---|
173 | tag, terminate = v
|
---|
174 | return ::Marshal.load(sprintf("\004\006C:%c%s%s%s", name.length + 5, name, tag, "\000" * terminate))
|
---|
175 | end
|
---|
176 | end
|
---|
177 | name = klass.name
|
---|
178 | ::Marshal.load(sprintf("\004\006o:%c%s\000", name.length + 5, name))
|
---|
179 | end
|
---|
180 | end
|
---|
181 |
|
---|
182 | # Allow only (Letter | '_') (Letter | Digit | '-' | '_')* here.
|
---|
183 | # Caution: '.' is not allowed here.
|
---|
184 | # To follow XML spec., it should be NCName.
|
---|
185 | # (denied chars) => .[0-F][0-F]
|
---|
186 | # ex. a.b => a.2eb
|
---|
187 | #
|
---|
188 | def self.name2elename(name)
|
---|
189 | name.gsub(/([^a-zA-Z0-9:_\-]+)/n) {
|
---|
190 | '.' << $1.unpack('H2' * $1.size).join('.')
|
---|
191 | }.gsub(/::/n, '..')
|
---|
192 | end
|
---|
193 |
|
---|
194 | def self.elename2name(name)
|
---|
195 | name.gsub(/\.\./n, '::').gsub(/((?:\.[0-9a-fA-F]{2})+)/n) {
|
---|
196 | [$1.delete('.')].pack('H*')
|
---|
197 | }
|
---|
198 | end
|
---|
199 |
|
---|
200 | def self.const_from_name(name, lenient = false)
|
---|
201 | const = ::Object
|
---|
202 | name.sub(/\A::/, '').split('::').each do |const_str|
|
---|
203 | if XSD::CodeGen::GenSupport.safeconstname?(const_str)
|
---|
204 | if const.const_defined?(const_str)
|
---|
205 | const = const.const_get(const_str)
|
---|
206 | next
|
---|
207 | end
|
---|
208 | elsif lenient
|
---|
209 | const_str = XSD::CodeGen::GenSupport.safeconstname(const_str)
|
---|
210 | if const.const_defined?(const_str)
|
---|
211 | const = const.const_get(const_str)
|
---|
212 | next
|
---|
213 | end
|
---|
214 | end
|
---|
215 | return nil
|
---|
216 | end
|
---|
217 | const
|
---|
218 | end
|
---|
219 |
|
---|
220 | def self.class_from_name(name, lenient = false)
|
---|
221 | const = const_from_name(name, lenient)
|
---|
222 | if const.is_a?(::Class)
|
---|
223 | const
|
---|
224 | else
|
---|
225 | nil
|
---|
226 | end
|
---|
227 | end
|
---|
228 |
|
---|
229 | def self.module_from_name(name, lenient = false)
|
---|
230 | const = const_from_name(name, lenient)
|
---|
231 | if const.is_a?(::Module)
|
---|
232 | const
|
---|
233 | else
|
---|
234 | nil
|
---|
235 | end
|
---|
236 | end
|
---|
237 |
|
---|
238 | def self.class2qname(klass)
|
---|
239 | name = schema_type_definition(klass)
|
---|
240 | namespace = schema_ns_definition(klass)
|
---|
241 | XSD::QName.new(namespace, name)
|
---|
242 | end
|
---|
243 |
|
---|
244 | def self.class2element(klass)
|
---|
245 | type = Mapping.class2qname(klass)
|
---|
246 | type.name ||= Mapping.name2elename(klass.name)
|
---|
247 | type.namespace ||= RubyCustomTypeNamespace
|
---|
248 | type
|
---|
249 | end
|
---|
250 |
|
---|
251 | def self.obj2element(obj)
|
---|
252 | name = namespace = nil
|
---|
253 | ivars = obj.instance_variables
|
---|
254 | if ivars.include?('@schema_type')
|
---|
255 | name = obj.instance_variable_get('@schema_type')
|
---|
256 | end
|
---|
257 | if ivars.include?('@schema_ns')
|
---|
258 | namespace = obj.instance_variable_get('@schema_ns')
|
---|
259 | end
|
---|
260 | if !name or !namespace
|
---|
261 | class2qname(obj.class)
|
---|
262 | else
|
---|
263 | XSD::QName.new(namespace, name)
|
---|
264 | end
|
---|
265 | end
|
---|
266 |
|
---|
267 | def self.define_singleton_method(obj, name, &block)
|
---|
268 | sclass = (class << obj; self; end)
|
---|
269 | sclass.class_eval {
|
---|
270 | define_method(name, &block)
|
---|
271 | }
|
---|
272 | end
|
---|
273 |
|
---|
274 | def self.get_attribute(obj, attr_name)
|
---|
275 | if obj.is_a?(::Hash)
|
---|
276 | obj[attr_name] || obj[attr_name.intern]
|
---|
277 | else
|
---|
278 | name = XSD::CodeGen::GenSupport.safevarname(attr_name)
|
---|
279 | if obj.instance_variables.include?('@' + name)
|
---|
280 | obj.instance_variable_get('@' + name)
|
---|
281 | elsif ((obj.is_a?(::Struct) or obj.is_a?(Marshallable)) and
|
---|
282 | obj.respond_to?(name))
|
---|
283 | obj.__send__(name)
|
---|
284 | end
|
---|
285 | end
|
---|
286 | end
|
---|
287 |
|
---|
288 | def self.set_attributes(obj, values)
|
---|
289 | if obj.is_a?(::SOAP::Mapping::Object)
|
---|
290 | values.each do |attr_name, value|
|
---|
291 | obj.__add_xmlele_value(attr_name, value)
|
---|
292 | end
|
---|
293 | else
|
---|
294 | values.each do |attr_name, value|
|
---|
295 | name = XSD::CodeGen::GenSupport.safevarname(attr_name)
|
---|
296 | setter = name + "="
|
---|
297 | if obj.respond_to?(setter)
|
---|
298 | obj.__send__(setter, value)
|
---|
299 | else
|
---|
300 | obj.instance_variable_set('@' + name, value)
|
---|
301 | begin
|
---|
302 | define_attr_accessor(obj, name,
|
---|
303 | proc { instance_variable_get('@' + name) },
|
---|
304 | proc { |value| instance_variable_set('@' + name, value) })
|
---|
305 | rescue TypeError
|
---|
306 | # singleton class may not exist (e.g. Float)
|
---|
307 | end
|
---|
308 | end
|
---|
309 | end
|
---|
310 | end
|
---|
311 | end
|
---|
312 |
|
---|
313 | def self.define_attr_accessor(obj, name, getterproc, setterproc = nil)
|
---|
314 | define_singleton_method(obj, name, &getterproc)
|
---|
315 | define_singleton_method(obj, name + '=', &setterproc) if setterproc
|
---|
316 | end
|
---|
317 |
|
---|
318 | def self.schema_type_definition(klass)
|
---|
319 | class_schema_variable(:schema_type, klass)
|
---|
320 | end
|
---|
321 |
|
---|
322 | def self.schema_ns_definition(klass)
|
---|
323 | class_schema_variable(:schema_ns, klass)
|
---|
324 | end
|
---|
325 |
|
---|
326 | def self.schema_element_definition(klass)
|
---|
327 | schema_element = class_schema_variable(:schema_element, klass) or return nil
|
---|
328 | schema_ns = schema_ns_definition(klass)
|
---|
329 | elements = []
|
---|
330 | as_array = []
|
---|
331 | schema_element.each do |varname, definition|
|
---|
332 | class_name, name = definition
|
---|
333 | if /\[\]$/ =~ class_name
|
---|
334 | class_name = class_name.sub(/\[\]$/, '')
|
---|
335 | as_array << (name ? name.name : varname)
|
---|
336 | end
|
---|
337 | elements << [name || XSD::QName.new(schema_ns, varname), class_name]
|
---|
338 | end
|
---|
339 | [elements, as_array]
|
---|
340 | end
|
---|
341 |
|
---|
342 | def self.schema_attribute_definition(klass)
|
---|
343 | class_schema_variable(:schema_attribute, klass)
|
---|
344 | end
|
---|
345 |
|
---|
346 | class << Mapping
|
---|
347 | private
|
---|
348 |
|
---|
349 | def class_schema_variable(sym, klass)
|
---|
350 | var = "@@#{sym}"
|
---|
351 | klass.class_variables.include?(var) ? klass.class_eval(var) : nil
|
---|
352 | end
|
---|
353 |
|
---|
354 | def protect_threadvars(*symbols)
|
---|
355 | backup = {}
|
---|
356 | begin
|
---|
357 | symbols.each do |sym|
|
---|
358 | backup[sym] = Thread.current[sym]
|
---|
359 | end
|
---|
360 | yield
|
---|
361 | ensure
|
---|
362 | symbols.each do |sym|
|
---|
363 | Thread.current[sym] = backup[sym]
|
---|
364 | end
|
---|
365 | end
|
---|
366 | end
|
---|
367 |
|
---|
368 | def add_md_ary(md_ary, ary, indices, registry)
|
---|
369 | for idx in 0..(ary.size - 1)
|
---|
370 | if ary[idx].is_a?(Array)
|
---|
371 | add_md_ary(md_ary, ary[idx], indices + [idx], registry)
|
---|
372 | else
|
---|
373 | md_ary[*(indices + [idx])] = _obj2soap(ary[idx], registry)
|
---|
374 | end
|
---|
375 | end
|
---|
376 | end
|
---|
377 | end
|
---|
378 | end
|
---|
379 |
|
---|
380 |
|
---|
381 | end
|
---|