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

root/branches/1_5/lib/wsdl/soap/classDefCreator.rb

Revision 1995, 12.8 kB (checked in by nahi, 1 year ago)
  • avoid name crash in generated classdef for simpleType enumeration. closes #442.
  • Property svn:eol-style set to native
  • Property svn:keywords set to author date id revision
Line 
1 # WSDL4R - Creating class definition from WSDL
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 'wsdl/data'
10 require 'wsdl/soap/classDefCreatorSupport'
11 require 'xsd/codegen'
12 require 'set'
13
14
15 module WSDL
16 module SOAP
17
18
19 class ClassDefCreator
20   include ClassDefCreatorSupport
21   include XSD::CodeGen
22
23   def initialize(definitions, name_creator, modulepath = nil)
24     @definitions = definitions
25     @name_creator = name_creator
26     @modulepath = modulepath
27     @elements = definitions.collect_elements
28     @elements.uniq!
29     @attributes = definitions.collect_attributes
30     @attributes.uniq!
31     @simpletypes = definitions.collect_simpletypes
32     @simpletypes.uniq!
33     @complextypes = definitions.collect_complextypes
34     @complextypes.uniq!
35     @modelgroups = definitions.collect_modelgroups
36     @modelgroups.uniq!
37     @faulttypes = nil
38     if definitions.respond_to?(:collect_faulttypes)
39       @faulttypes = definitions.collect_faulttypes
40     end
41     @defined_const = {}
42   end
43
44   def dump(type = nil)
45     result = "require 'xsd/qname'\n"
46     # cannot use @modulepath because of multiple classes
47     if @modulepath
48       result << "\n"
49       result << modulepath_split(@modulepath).collect { |ele| "module #{ele}" }.join("; ")
50       result << "\n\n"
51     end
52     str = dump_group(type)
53     unless str.empty?
54       result << "\n" unless result.empty?
55       result << str
56     end
57     str = dump_complextype(type)
58     unless str.empty?
59       result << "\n" unless result.empty?
60       result << str
61     end
62     str = dump_simpletype(type)
63     unless str.empty?
64       result << "\n" unless result.empty?
65       result << str
66     end
67     str = dump_element(type)
68     unless str.empty?
69       result << "\n" unless result.empty?
70       result << str
71     end
72     str = dump_attribute(type)
73     unless str.empty?
74       result << "\n" unless result.empty?
75       result << str
76     end
77     if @modulepath
78       result << "\n\n"
79       result << modulepath_split(@modulepath).collect { |ele| "end" }.join("; ")
80       result << "\n"
81     end
82     result
83   end
84
85 private
86
87   def dump_element(target = nil)
88     @elements.collect { |ele|
89       next if @complextypes[ele.name]
90       next if target and target != ele.name
91       c = create_elementdef(@modulepath, ele)
92       c ? c.dump : nil
93     }.compact.join("\n")
94   end
95
96   def dump_attribute(target = nil)
97     @attributes.collect { |attribute|
98       next if target and target != attribute.name
99       if attribute.local_simpletype
100         c = create_simpletypedef(@modulepath, attribute.name, attribute.local_simpletype)
101       end
102       c ? c.dump : nil
103     }.compact.join("\n")
104   end
105
106   def dump_simpletype(target = nil)
107     @simpletypes.collect { |type|
108       next if target and target != type.name
109       c = create_simpletypedef(@modulepath, type.name, type)
110       c ? c.dump : nil
111     }.compact.join("\n")
112   end
113
114   def dump_complextype(target = nil)
115     definitions = sort_dependency(@complextypes).collect { |type|
116       next if target and target != type.name
117       c = create_complextypedef(@modulepath, type.name, type)
118       c ? c.dump : nil
119     }.compact.join("\n")
120   end
121
122   def dump_group(target = nil)
123     definitions = @modelgroups.collect { |group|
124       # TODO: not dumped for now but may be useful in the future
125     }.compact.join("\n")
126   end
127
128   def create_elementdef(mpath, ele)
129     qualified = (ele.elementform == 'qualified')
130     if ele.local_complextype
131       create_complextypedef(mpath, ele.name, ele.local_complextype, qualified)
132     elsif ele.local_simpletype
133       create_simpletypedef(mpath, ele.name, ele.local_simpletype, qualified)
134     elsif ele.empty?
135       create_simpleclassdef(mpath, ele.name, nil)
136     else
137       # ignores type only element
138       nil
139     end
140   end
141
142   def create_simpletypedef(mpath, qname, simpletype, qualified = false)
143     if simpletype.restriction
144       create_simpletypedef_restriction(mpath, qname, simpletype, qualified)
145     elsif simpletype.list
146       create_simpletypedef_list(mpath, qname, simpletype, qualified)
147     elsif simpletype.union
148       create_simpletypedef_union(mpath, qname, simpletype, qualified)
149     else
150       raise RuntimeError.new("unknown kind of simpletype: #{simpletype}")
151     end
152   end
153
154   def create_simpletypedef_restriction(mpath, qname, typedef, qualified)
155     restriction = typedef.restriction
156     unless restriction.enumeration?
157       # not supported.  minlength?
158       return nil
159     end
160     classname = mapped_class_basename(qname, mpath)
161     c = ClassDef.new(classname, '::String')
162     c.comment = "#{qname}"
163     define_classenum_restriction(c, classname, restriction.enumeration)
164     c
165   end
166
167   def create_simpletypedef_list(mpath, qname, typedef, qualified)
168     list = typedef.list
169     classname = mapped_class_basename(qname, mpath)
170     c = ClassDef.new(classname, '::Array')
171     c.comment = "#{qname}"
172     if simpletype = list.local_simpletype
173       if simpletype.restriction.nil?
174         raise RuntimeError.new(
175           "unknown kind of simpletype: #{simpletype}")
176       end
177       define_stringenum_restriction(c, simpletype.restriction.enumeration)
178       c.comment << "\n  contains list of #{classname}::*"
179     elsif list.itemtype
180       c.comment << "\n  contains list of #{mapped_class_basename(list.itemtype, mpath)}::*"
181     else
182       raise RuntimeError.new("unknown kind of list: #{list}")
183     end
184     c
185   end
186
187   def create_simpletypedef_union(mpath, qname, typedef, qualified)
188     union = typedef.union
189     classname = mapped_class_basename(qname, mpath)
190     c = ClassDef.new(classname, '::String')
191     c.comment = "#{qname}"
192     if union.member_types
193       # fixme
194       c.comment << "\n any of #{union.member_types}"
195     end
196     c
197   end
198
199   def define_stringenum_restriction(c, enumeration)
200     const = {}
201     enumeration.each do |value|
202       constname = safeconstname(value)
203       const[constname] ||= 0
204       if (const[constname] += 1) > 1
205         constname += "_#{const[constname]}"
206       end
207       c.def_const(constname, ndq(value))
208     end
209   end
210
211   def define_classenum_restriction(c, classname, enumeration)
212     const = {}
213     enumeration.each do |value|
214       constname = safeconstname(value)
215       const[constname] ||= 0
216       if (const[constname] += 1) > 1
217         constname += "_#{const[constname]}"
218       end
219       c.def_const(constname, "new(#{ndq(value)})")
220     end
221   end
222
223   def create_simpleclassdef(mpath, qname, type_or_element)
224     classname = mapped_class_basename(qname, mpath)
225     c = ClassDef.new(classname, '::String')
226     c.comment = "#{qname}"
227     init_lines = []
228     if type_or_element and !type_or_element.attributes.empty?
229       define_attribute(c, type_or_element.attributes)
230       init_lines << "@__xmlattr = {}"
231     end
232     c.def_method('initialize', '*arg') do
233       "super\n" + init_lines.join("\n")
234     end
235     c
236   end
237
238   def create_complextypedef(mpath, qname, type, qualified = false)
239     case type.compoundtype
240     when :TYPE_STRUCT, :TYPE_EMPTY
241       create_structdef(mpath, qname, type, qualified)
242     when :TYPE_ARRAY
243       create_arraydef(mpath, qname, type)
244     when :TYPE_SIMPLE
245       create_simpleclassdef(mpath, qname, type)
246     when :TYPE_MAP
247       # mapped as a general Hash
248       nil
249     else
250       raise RuntimeError.new(
251         "unknown kind of complexContent: #{type.compoundtype}")
252     end
253   end
254
255   def create_structdef(mpath, qname, typedef, qualified = false)
256     classname = mapped_class_basename(qname, mpath)
257     baseclassname = nil
258     if typedef.complexcontent
259       if base = typedef.complexcontent.base
260         # :TYPE_ARRAY must not be derived (#424)
261         basedef = @complextypes[base]
262         if basedef and basedef.compoundtype != :TYPE_ARRAY
263           # baseclass should be a toplevel complexType
264           baseclassname = mapped_class_basename(base, @modulepath)
265         end
266       end
267     end
268     if @faulttypes and @faulttypes.index(qname)
269       c = ClassDef.new(classname, '::StandardError')
270     else
271       c = ClassDef.new(classname, baseclassname)
272     end
273     c.comment = "#{qname}"
274     c.comment << "\nabstract" if typedef.abstract
275     parentmodule = mapped_class_name(qname, mpath)
276     init_lines, init_params =
277       parse_elements(c, typedef.elements, qname.namespace, parentmodule)
278     unless typedef.attributes.empty?
279       define_attribute(c, typedef.attributes)
280       init_lines << "@__xmlattr = {}"
281     end
282     c.def_method('initialize', *init_params) do
283       init_lines.join("\n")
284     end
285     c
286   end
287
288   def parse_elements(c, elements, base_namespace, mpath, as_array = false)
289     init_lines = []
290     init_params = []
291     any = false
292     elements.each do |element|
293       case element
294       when XMLSchema::Any
295         # only 1 <any/> is allowed for now.
296         raise RuntimeError.new("duplicated 'any'") if any
297         any = true
298         attrname = '__xmlele_any'
299         c.def_attr(attrname, false, attrname)
300         c.def_method('set_any', 'elements') do
301           '@__xmlele_any = elements'
302         end
303         init_lines << "@__xmlele_any = nil"
304       when XMLSchema::Element
305         next if element.ref == SchemaName
306         name = name_element(element).name
307         typebase = @modulepath
308         if element.anonymous_type?
309           inner = create_elementdef(mpath, element)
310           unless as_array
311             inner.comment = "inner class for member: #{name}\n" + inner.comment
312           end
313           c.innermodule << inner
314           typebase = mpath
315         end
316         unless as_array
317           attrname = safemethodname(name)
318           varname = safevarname(name)
319           c.def_attr(attrname, true, varname)
320           init_lines << "@#{varname} = #{varname}"
321           if element.map_as_array?
322             init_params << "#{varname} = []"
323           else
324             init_params << "#{varname} = nil"
325           end
326           c.comment << "\n  #{attrname} - #{create_type_name(typebase, element) || '(any)'}"
327         end
328       when WSDL::XMLSchema::Sequence
329         child_init_lines, child_init_params =
330           parse_elements(c, element.elements, base_namespace, mpath, as_array)
331         init_lines.concat(child_init_lines)
332         init_params.concat(child_init_params)
333       when WSDL::XMLSchema::Choice
334         child_init_lines, child_init_params =
335           parse_elements(c, element.elements, base_namespace, mpath, as_array)
336         init_lines.concat(child_init_lines)
337         init_params.concat(child_init_params)
338       when WSDL::XMLSchema::Group
339         if element.content.nil?
340           warn("no group definition found: #{element}")
341           next
342         end
343         child_init_lines, child_init_params =
344           parse_elements(c, element.content.elements, base_namespace, mpath, as_array)
345         init_lines.concat(child_init_lines)
346         init_params.concat(child_init_params)
347       else
348         raise RuntimeError.new("unknown type: #{element}")
349       end
350     end
351     [init_lines, init_params]
352   end
353
354   def define_attribute(c, attributes)
355     const = {}
356     unless attributes.empty?
357       c.def_method("__xmlattr") do <<-__EOD__
358           @__xmlattr ||= {}
359         __EOD__
360       end
361     end
362     attributes.each do |attribute|
363       name = name_attribute(attribute)
364       methodname = safemethodname('xmlattr_' + name.name)
365       constname = 'Attr' + safeconstname(name.name)
366       const[constname] ||= 0
367       if (const[constname] += 1) > 1
368         constname += "_#{const[constname]}"
369       end
370       c.def_const(constname, dqname(name))
371       c.def_method(methodname) do <<-__EOD__
372           __xmlattr[#{constname}]
373         __EOD__
374       end
375       c.def_method(methodname + '=', 'value') do <<-__EOD__
376           __xmlattr[#{constname}] = value
377         __EOD__
378       end
379       c.comment << "\n  #{methodname} - #{attribute_basetype(attribute) || '(any)'}"
380     end
381   end
382
383   def create_arraydef(mpath, qname, typedef)
384     classname = mapped_class_basename(qname, mpath)
385     c = ClassDef.new(classname, '::Array')
386     c.comment = "#{qname}"
387     parentmodule = mapped_class_name(qname, mpath)
388     parse_elements(c, typedef.elements, qname.namespace, parentmodule, true)
389     c
390   end
391
392   def sort_dependency(types)
393     dep = {}
394     root = []
395     types.each do |type|
396       if type.complexcontent and (base = type.complexcontent.base)
397         dep[base] ||= []
398         dep[base] << type
399       else
400         root << type
401       end
402     end
403     sorted = []
404     root.each do |type|
405       sorted.concat(collect_dependency(type, dep))
406     end
407     sorted.concat(dep.values.flatten)
408     sorted
409   end
410
411   # removes collected key from dep
412   def collect_dependency(type, dep)
413     result = [type]
414     return result unless dep.key?(type.name)
415     dep[type.name].each do |deptype|
416       result.concat(collect_dependency(deptype, dep))
417     end
418     dep.delete(type.name)
419     result
420   end
421
422   def modulepath_split(modulepath)
423     if modulepath.is_a?(::Array)
424       modulepath
425     else
426       modulepath.to_s.split('::')
427     end
428   end
429 end
430
431
432 end
433 end
Note: See TracBrowser for help on using the browser.