1#-----------------------------
2# olegen.rb
3# $Revision: 25189 $
4#-----------------------------
5
6require 'win32ole'
7
8class WIN32COMGen
9  def initialize(typelib)
10    @typelib = typelib
11    @reciever = ""
12  end
13  attr_reader :typelib
14
15  def ole_classes(typelib)
16    begin
17      @ole = WIN32OLE.new(typelib)
18      [@ole.ole_obj_help]
19    rescue
20      WIN32OLE_TYPE.ole_classes(typelib)
21    end
22  end
23
24  def generate_args(method)
25    args = []
26    if method.size_opt_params >= 0
27      size_required_params = method.size_params - method.size_opt_params
28    else
29      size_required_params = method.size_params - 1
30    end
31    size_required_params.times do |i|
32      if method.params[i] && method.params[i].optional?
33        args.push "arg#{i}=nil"
34      else
35        args.push "arg#{i}"
36      end
37    end
38    if method.size_opt_params >= 0
39      method.size_opt_params.times do |i|
40        args.push "arg#{i + size_required_params}=nil"
41      end
42    else
43      args.push "*arg"
44    end
45    args.join(", ")
46  end
47
48  def generate_argtype(typedetails)
49    ts = ''
50    typedetails.each do |t|
51      case t
52      when 'CARRAY', 'VOID', 'UINT', 'RESULT', 'DECIMAL', 'I8', 'UI8'
53#	  raise "Sorry type\"" + t + "\" not supported"
54      ts << "\"??? NOT SUPPORTED TYPE:`#{t}'\""
55      when 'USERDEFINED', 'Unknown Type 9'
56        ts << 'VT_DISPATCH'
57        break;
58      when 'SAFEARRAY'
59        ts << 'VT_ARRAY|'
60      when 'PTR'
61        ts << 'VT_BYREF|'
62      when 'INT'
63        ts << 'VT_I4'
64      else
65        if String === t
66          ts << 'VT_' + t
67        end
68      end
69    end
70    if ts.empty?
71      ts = 'VT_VARIANT'
72    elsif ts[-1] == ?|
73	ts += 'VT_VARIANT'
74    end
75    ts
76  end
77
78  def generate_argtypes(method, proptypes)
79    types = method.params.collect{|param|
80      generate_argtype(param.ole_type_detail)
81    }.join(", ")
82    if proptypes
83      types += ", " if types.size > 0
84      types += generate_argtype(proptypes)
85    end
86    types
87  end
88
89  def generate_method_body(method, disptype, types=nil)
90    "    ret = #{@reciever}#{disptype}(#{method.dispid}, [" +
91    generate_args(method).gsub("=nil", "") +
92    "], [" +
93    generate_argtypes(method, types) +
94    "])\n" +
95    "    @lastargs = WIN32OLE::ARGV\n" +
96    "    ret"
97  end
98
99  def generate_method_help(method, type = nil)
100    str = "  # "
101    if type
102      str += type
103    else
104      str += method.return_type
105    end
106    str += " #{method.name}"
107    if method.event?
108      str += " EVENT"
109      str += " in #{method.event_interface}"
110    end
111    if method.helpstring && method.helpstring != ""
112      str += "\n  # "
113      str += method.helpstring
114    end
115    args_help = generate_method_args_help(method)
116    if args_help
117      str += "\n"
118      str += args_help
119    end
120    str
121  end
122
123  def generate_method_args_help(method)
124    args = []
125    method.params.each_with_index {|param, i|
126      h = "  #   #{param.ole_type} arg#{i} --- #{param.name}"
127      inout = []
128      inout.push "IN" if param.input?
129      inout.push "OUT" if param.output?
130      h += " [#{inout.join('/')}]"
131      h += " ( = #{param.default})" if param.default
132      args.push h
133    }
134    if args.size > 0
135      args.join("\n")
136    else
137      nil
138    end
139  end
140
141  def generate_method(method, disptype, io = STDOUT, types = nil)
142    io.puts "\n"
143    io.puts  generate_method_help(method)
144    if method.invoke_kind == 'PROPERTYPUT'
145      io.print "  def #{method.name}=("
146    else
147      io.print "  def #{method.name}("
148    end
149    io.print generate_args(method)
150    io.puts ")"
151    io.puts generate_method_body(method, disptype, types)
152    io.puts "  end"
153  end
154
155  def generate_propputref_methods(klass, io = STDOUT)
156    klass.ole_methods.select {|method|
157      method.invoke_kind == 'PROPERTYPUTREF' && method.visible?
158    }.each do |method|
159      generate_method(method, io)
160    end
161  end
162
163  def generate_properties_with_args(klass, io = STDOUT)
164    klass.ole_methods.select {|method|
165      method.invoke_kind == 'PROPERTYGET' &&
166      method.visible? &&
167      method.size_params > 0
168    }.each do |method|
169      types = method.return_type_detail
170      io.puts "\n"
171      io.puts  generate_method_help(method, types[0])
172      io.puts  "  def #{method.name}"
173      if klass.ole_type == "Class"
174        io.print "    OLEProperty.new(@dispatch, #{method.dispid}, ["
175      else
176        io.print "    OLEProperty.new(self, #{method.dispid}, ["
177      end
178      io.print generate_argtypes(method, nil)
179      io.print "], ["
180      io.print generate_argtypes(method, types)
181      io.puts "])"
182      io.puts  "  end"
183    end
184  end
185
186  def generate_propput_methods(klass, io = STDOUT)
187    klass.ole_methods.select {|method|
188      method.invoke_kind == 'PROPERTYPUT' && method.visible? &&
189      method.size_params == 1
190    }.each do |method|
191      ms = klass.ole_methods.select {|m|
192        m.invoke_kind == 'PROPERTYGET' &&
193        m.dispid == method.dispid
194      }
195      types = []
196      if ms.size == 1
197        types = ms[0].return_type_detail
198      end
199      generate_method(method, '_setproperty', io, types)
200    end
201  end
202
203  def generate_propget_methods(klass, io = STDOUT)
204    klass.ole_methods.select {|method|
205      method.invoke_kind == 'PROPERTYGET' && method.visible? &&
206      method.size_params == 0
207    }.each do |method|
208      generate_method(method, '_getproperty', io)
209    end
210  end
211
212  def generate_func_methods(klass, io = STDOUT)
213    klass.ole_methods.select {|method|
214      method.invoke_kind == "FUNC" && method.visible?
215    }.each do |method|
216      generate_method(method, '_invoke', io)
217    end
218  end
219
220  def generate_methods(klass, io = STDOUT)
221    generate_propget_methods(klass, io)
222    generate_propput_methods(klass, io)
223    generate_properties_with_args(klass, io)
224    generate_func_methods(klass, io)
225#   generate_propputref_methods(klass, io)
226  end
227
228  def generate_constants(klass, io = STDOUT)
229    klass.variables.select {|v|
230      v.visible? && v.variable_kind == 'CONSTANT'
231    }.each do |v|
232      io.print "  "
233      io.print v.name.sub(/^./){$&.upcase}
234      io.print " = "
235      io.puts  v.value
236    end
237  end
238
239  def class_name(klass)
240    klass_name = klass.name
241    if klass.ole_type == "Class" &&
242       klass.guid &&
243       klass.progid
244       klass_name = klass.progid.gsub(/\./, '_')
245    end
246    if /^[A-Z]/ !~ klass_name || Module.constants.include?(klass_name)
247      klass_name = 'OLE' + klass_name
248    end
249    klass_name
250  end
251
252  def define_initialize(klass)
253    <<STR
254
255  def initialize(obj = nil)
256    @clsid = "#{klass.guid}"
257    @progid = "#{klass.progid}"
258    if obj.nil?
259      @dispatch = WIN32OLE.new @progid
260    else
261      @dispatch = obj
262    end
263  end
264STR
265  end
266
267  def define_include
268    "  include WIN32OLE::VARIANT"
269  end
270
271  def define_instance_variables
272    "  attr_reader :lastargs"
273  end
274
275  def define_method_missing
276    <<STR
277
278  def method_missing(cmd, *arg)
279    @dispatch.method_missing(cmd, *arg)
280  end
281STR
282  end
283
284  def define_class(klass, io = STDOUT)
285    io.puts "class #{class_name(klass)} # #{klass.name}"
286    io.puts define_include
287    io.puts define_instance_variables
288    io.puts "  attr_reader :dispatch"
289    io.puts "  attr_reader :clsid"
290    io.puts "  attr_reader :progid"
291    io.puts define_initialize(klass)
292    io.puts define_method_missing
293  end
294
295  def define_module(klass, io = STDOUT)
296    io.puts "module #{class_name(klass)}"
297    io.puts define_include
298    io.puts define_instance_variables
299  end
300
301  def generate_class(klass, io = STDOUT)
302    io.puts "\n# #{klass.helpstring}"
303    if klass.ole_type == "Class" &&
304       klass.guid &&
305       klass.progid
306      @reciever = "@dispatch."
307      define_class(klass, io)
308    else
309      @reciever = ""
310      define_module(klass, io)
311    end
312    generate_constants(klass, io)
313    generate_methods(klass, io)
314    io.puts "end"
315  end
316
317  def generate(io = STDOUT)
318    io.puts "require 'win32ole'"
319    io.puts "require 'win32ole/property'"
320
321    ole_classes(typelib).select{|klass|
322      klass.visible? &&
323      (klass.ole_type == "Class" ||
324       klass.ole_type == "Interface" ||
325       klass.ole_type == "Dispatch" ||
326       klass.ole_type == "Enum")
327    }.each do |klass|
328      generate_class(klass, io)
329    end
330    begin
331      @ole.quit if @ole
332    rescue
333    end
334  end
335end
336
337require 'win32ole'
338if __FILE__ == $0
339  if ARGV.size == 0
340    $stderr.puts "usage: #{$0} Type Library [...]"
341    exit 1
342  end
343  ARGV.each do |typelib|
344    comgen = WIN32COMGen.new(typelib)
345    comgen.generate
346  end
347end
348