rubyosa 0.3.0.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -36,877 +36,919 @@ begin require 'rubygems'; rescue LoadError; end
36
36
 
37
37
  # If libxml-ruby is not present, switch to REXML.
38
38
  USE_LIBXML = begin
39
- require 'xml/libxml'
39
+ require 'xml/libxml'
40
40
 
41
- # libxml-ruby bug workaround.
42
- class XML::Node
43
- alias_method :old_cmp, :==
44
- def ==(x)
45
- (x != nil and old_cmp(x))
46
- end
41
+ # libxml-ruby bug workaround.
42
+ class XML::Node
43
+ alias_method :old_cmp, :==
44
+ def ==(x)
45
+ (x != nil and old_cmp(x))
47
46
  end
48
- true
47
+ end
48
+ true
49
49
  rescue LoadError
50
- require 'rexml/document'
51
-
52
- # REXML -> libxml-ruby compatibility layer.
53
- class REXML::Element
54
- alias_method :old_find, :find
55
- def find(path=nil, &block)
56
- if path.nil? and block
57
- old_find { |*x| block.call(*x) }
58
- else
59
- list = []
60
- ::REXML::XPath.each(self, path) { |e| list << e }
61
- list
62
- end
63
- end
64
- def [](attr)
65
- attributes[attr]
66
- end
67
- def find_first(path)
68
- ::REXML::XPath.first(self, path)
69
- end
50
+ require 'rexml/document'
51
+
52
+ # REXML -> libxml-ruby compatibility layer.
53
+ class REXML::Element
54
+ alias_method :old_find, :find
55
+ def find(path=nil, &block)
56
+ if path.nil? and block
57
+ old_find { |*x| block.call(*x) }
58
+ else
59
+ list = []
60
+ ::REXML::XPath.each(self, path) { |e| list << e }
61
+ list
62
+ end
70
63
  end
71
- false
64
+ def [](attr)
65
+ attributes[attr]
66
+ end
67
+ def find_first(path)
68
+ ::REXML::XPath.first(self, path)
69
+ end
70
+ end
71
+ false
72
72
  end
73
73
 
74
74
  class String
75
- def to_4cc
76
- OSA.__four_char_code__(Iconv.iconv('MACROMAN', 'UTF-8', self).to_s)
77
- end
75
+ def to_4cc
76
+ OSA.__four_char_code__(Iconv.iconv('MACROMAN', 'UTF-8', self).to_s)
77
+ end
78
78
  end
79
79
 
80
80
  class OSA::Enumerator
81
- attr_reader :code, :name, :group_code
82
-
83
- def initialize(const, name, code, group_code)
84
- @const, @name, @code, @group_code = const, name, code, group_code
85
- self.class.instances[code] = self
86
- end
81
+ attr_reader :code, :name, :group_code
82
+
83
+ def initialize(const, name, code, group_code)
84
+ @const, @name, @code, @group_code = const, name, code, group_code
85
+ self.class.instances[code] = self
86
+ end
87
87
 
88
- def self.enum_for_code(code)
89
- instances[code]
90
- end
88
+ def self.enum_for_code(code)
89
+ instances[code]
90
+ end
91
91
 
92
- def to_s
93
- @name
94
- end
92
+ def to_s
93
+ @name
94
+ end
95
95
 
96
- def inspect
97
- "<#{@const}>"
98
- end
96
+ def inspect
97
+ "<#{@const}>"
98
+ end
99
99
 
100
- #######
101
- private
102
- #######
100
+ #######
101
+ private
102
+ #######
103
103
 
104
- def self.instances
105
- (@@instances rescue @@instances = {})
106
- end
104
+ def self.instances
105
+ (@@instances rescue @@instances = {})
106
+ end
107
107
  end
108
108
 
109
109
  class OSA::Element
110
- REAL_NAME = CODE = nil
111
- def to_rbobj
112
- unless __type__ == 'null'
113
- val = OSA.convert_to_ruby(self)
114
- val == nil ? self : val
115
- end
116
- end
117
-
118
- def self.from_rbobj(requested_type, value, enum_group_codes)
119
- obj = OSA.convert_to_osa(requested_type, value, enum_group_codes)
120
- obj.is_a?(OSA::Element) ? obj : self.__new__(*obj)
110
+ REAL_NAME = CODE = nil
111
+ def to_rbobj
112
+ unless __type__ == 'null'
113
+ val = OSA.convert_to_ruby(self)
114
+ val == 'msng' ? nil : val == nil ? self : val
121
115
  end
116
+ end
117
+
118
+ def self.from_rbobj(requested_type, value, enum_group_codes)
119
+ obj = OSA.convert_to_osa(requested_type, value, enum_group_codes)
120
+ obj.is_a?(OSA::Element) ? obj : self.__new__(*obj)
121
+ end
122
122
  end
123
123
 
124
124
  class OSA::ElementList
125
- include Enumerable
126
- def each
127
- self.size.times { |i| yield(self[i]) }
128
- end
125
+ include Enumerable
126
+ def each
127
+ self.size.times { |i| yield(self[i]) }
128
+ end
129
129
  end
130
130
 
131
131
  class OSA::ElementRecord
132
- def self.from_hash(hash)
133
- value = {}
134
- hash.each do |code, val|
135
- key = OSA.sym_to_code(code)
136
- if key.nil?
137
- raise ArgumentError, "invalid key `#{code}'" if code.to_s.length != 4
138
- key = code
139
- end
140
- value[key] = OSA::Element.from_rbobj(nil, val, nil)
141
- end
142
- OSA::ElementRecord.__new__(value)
132
+ def self.from_hash(hash)
133
+ value = {}
134
+ hash.each do |code, val|
135
+ key = OSA.sym_to_code(code)
136
+ if key.nil?
137
+ raise ArgumentError, "invalid key `#{code}'" if code.to_s.length != 4
138
+ key = code
139
+ end
140
+ value[key] = OSA::Element.from_rbobj(nil, val, nil)
143
141
  end
142
+ OSA::ElementRecord.__new__(value)
143
+ end
144
144
 
145
- def to_hash
146
- h = {}
147
- self.to_a.each do |code, val|
148
- key = (OSA.code_to_sym(code) or code)
149
- h[key] = val.to_rbobj
150
- end
151
- return h
152
- end
145
+ def to_hash
146
+ h = {}
147
+ self.to_a.each do |code, val|
148
+ key = (OSA.code_to_sym(code) or code)
149
+ h[key] = val.to_rbobj
150
+ end
151
+ return h
152
+ end
153
153
  end
154
154
 
155
155
  module OSA::ObjectSpecifier
156
- def get
157
- new_obj = @app.__send_event__('core', 'getd', [['----', self]], true).to_rbobj
158
- if !new_obj.is_a?(self.class) and new_obj.is_a?(OSA::Element) and self.respond_to?(:properties) and (klass = self.properties[:class])
159
- klass.__duplicate__(new_obj)
160
- else
161
- new_obj
162
- end
156
+ def get
157
+ new_obj = @app.__send_event__('core', 'getd', [['----', self]], true).to_rbobj
158
+ if !new_obj.is_a?(self.class) and new_obj.is_a?(OSA::Element) and self.respond_to?(:properties) and (klass = self.properties[:class])
159
+ klass.__duplicate__(new_obj)
160
+ else
161
+ new_obj
163
162
  end
163
+ end
164
164
  end
165
165
 
166
166
  class OSA::ObjectSpecifierList
167
- include Enumerable
168
-
169
- def initialize(app, desired_class, container)
170
- @app, @desired_class, @container = app, desired_class, container
171
- end
167
+ include Enumerable
168
+
169
+ def initialize(app, desired_class, container)
170
+ @app, @desired_class, @container = app, desired_class, container
171
+ end
172
172
 
173
- def length
174
- @app.__send_event__(
175
- 'core', 'cnte',
176
- [['----', @container], ['kocl', OSA::Element.__new__('type', @desired_class::CODE.to_4cc)]],
177
- true).to_rbobj
178
- end
179
- alias_method :size, :length
173
+ def length
174
+ @app.__send_event__(
175
+ 'core', 'cnte',
176
+ [['----', @container], ['kocl', OSA::Element.__new__('type', @desired_class::CODE.to_4cc)]],
177
+ true).to_rbobj
178
+ end
179
+ alias_method :size, :length
180
180
 
181
- def empty?
182
- length == 0
183
- end
181
+ def empty?
182
+ length == 0
183
+ end
184
184
 
185
- def [](idx)
186
- idx += 1 # AE starts counting at 1.
187
- o = obj_spec_with_key(OSA::Element.__new__('long', [idx].pack('l')))
188
- o.instance_variable_set(:@app, @app)
189
- o.extend OSA::ObjectSpecifier
190
- end
185
+ def [](idx)
186
+ idx += 1 # AE starts counting at 1.
187
+ o = obj_spec_with_key(OSA::Element.__new__('long', [idx].pack('l')))
188
+ o.instance_variable_set(:@app, @app)
189
+ o.extend OSA::ObjectSpecifier
190
+ end
191
191
 
192
- def each
193
- self.length.times { |i| yield(self[i]) }
194
- end
192
+ def first
193
+ self[0]
194
+ end
195
195
 
196
- def get
197
- o = obj_spec_with_key(OSA::Element.__new__('abso', 'all '.to_4cc))
198
- o.instance_variable_set(:@app, @app)
199
- o.extend OSA::ObjectSpecifier
200
- o.get
201
- end
202
- alias_method :to_a, :get
196
+ def last
197
+ self[-1]
198
+ end
203
199
 
204
- def ==(other)
205
- other.kind_of?(self.class) \
206
- and other.length == self.length \
207
- and (0..other.length).all? { |i| other[i] == self[i] }
208
- end
200
+ def each
201
+ self.length.times { |i| yield(self[i]) }
202
+ end
209
203
 
210
- def inspect
211
- super.scan(/^([^ ]+)/).to_s << " desired_class=#{@desired_class}>"
212
- end
204
+ def get
205
+ o = obj_spec_with_key(OSA::Element.__new__('abso', 'all '.to_4cc))
206
+ o.instance_variable_set(:@app, @app)
207
+ o.extend OSA::ObjectSpecifier
208
+ o.get
209
+ end
210
+ alias_method :to_a, :get
213
211
 
214
- #######
215
- private
216
- #######
212
+ def ==(other)
213
+ other.kind_of?(self.class) \
214
+ and other.length == self.length \
215
+ and (0..other.length).all? { |i| other[i] == self[i] }
216
+ end
217
+
218
+ def inspect
219
+ super.scan(/^([^ ]+)/).to_s << " desired_class=#{@desired_class}>"
220
+ end
217
221
 
218
- def obj_spec_with_key(element)
219
- @desired_class.__new_object_specifier__(@desired_class::CODE, @container,
220
- 'indx', element)
222
+ def every(sym)
223
+ if @desired_class.method_defined?(sym) and code = OSA.sym_to_code(sym)
224
+ o = obj_spec_with_key(OSA::Element.__new__('abso', 'all '.to_4cc))
225
+ pklass = @app.classes[code]
226
+ if pklass.nil? or !OSA.lazy_events
227
+ @app.__send_event__('core', 'getd',
228
+ [['----', OSA::Element.__new_object_specifier__('prop', o,
229
+ 'prop', OSA::Element.__new__('type', code.to_4cc))]],
230
+ true).to_rbobj
231
+ else
232
+ OSA::ObjectSpecifierList.new(@app, pklass, o)
233
+ end
234
+ else
235
+ raise ArgumentError, "desired class `#{@desired_class}' does not have a attribute named `#{sym.to_s}'"
221
236
  end
237
+ end
238
+
239
+ #######
240
+ private
241
+ #######
242
+
243
+ def obj_spec_with_key(element)
244
+ @desired_class.__new_object_specifier__(@desired_class::CODE, @container,
245
+ 'indx', element)
246
+ end
222
247
  end
223
248
 
224
249
  module OSA::EventDispatcher
225
250
 
226
- SCRIPTING_ADDITIONS_DIR = [
227
- '/System/Library/ScriptingAdditions',
228
- '/Library/ScriptingAdditions'
229
- ]
230
- if home = ENV['HOME']
231
- SCRIPTING_ADDITIONS_DIR << File.join(home, '/Library/ScriptingAdditions')
232
- end
233
-
234
- def merge(args)
235
- args = { :name => args } if args.is_a?(String)
236
- by_name = args[:name]
237
- begin
238
- name, target, sdef = OSA.__scripting_info__(args)
239
- rescue RuntimeError => excp
240
- # If an sdef bundle can't be find by name, let's be clever and look in the ScriptingAdditions locations.
241
- if by_name
242
- args = SCRIPTING_ADDITIONS_DIR.each do |dir|
243
- path = ['.app', '.osax'].map { |e| File.join(dir, by_name + e) }.find { |p| File.exists?(p) }
244
- if path
245
- break { :path => path }
246
- end
247
- end
248
- if args.is_a?(Hash)
249
- by_name = nil
250
- retry
251
- end
252
- end
253
- raise excp
251
+ SCRIPTING_ADDITIONS_DIR = [
252
+ '/System/Library/ScriptingAdditions',
253
+ '/Library/ScriptingAdditions'
254
+ ]
255
+ if home = ENV['HOME']
256
+ SCRIPTING_ADDITIONS_DIR << File.join(home, '/Library/ScriptingAdditions')
257
+ end
258
+
259
+ def merge(args)
260
+ args = { :name => args } if args.is_a?(String)
261
+ by_name = args[:name]
262
+ begin
263
+ name, target, sdef = OSA.__scripting_info__(args)
264
+ rescue RuntimeError => excp
265
+ # If an sdef bundle can't be find by name, let's be clever and look in the ScriptingAdditions locations.
266
+ if by_name
267
+ args = SCRIPTING_ADDITIONS_DIR.each do |dir|
268
+ path = ['.app', '.osax'].map { |e| File.join(dir, by_name + e) }.find { |p| File.exists?(p) }
269
+ if path
270
+ break { :path => path }
271
+ end
272
+ end
273
+ if args.is_a?(Hash)
274
+ by_name = nil
275
+ retry
254
276
  end
255
- app_module_name = self.class.name.scan(/^OSA::(.+)::.+$/).flatten.first
256
- app_module = OSA.const_get(app_module_name)
257
- OSA.__load_sdef__(sdef, target, app_module, true, self.class)
258
- (self.__send_event__('ascr', 'gdut', [], true) rescue nil) # Don't ask me why...
259
- return self
277
+ end
278
+ raise excp
260
279
  end
280
+ app_module_name = self.class.name.scan(/^OSA::(.+)::.+$/).flatten.first
281
+ app_module = OSA.const_get(app_module_name)
282
+ OSA.__load_sdef__(sdef, target, app_module, true, self.class)
283
+ (self.__send_event__('ascr', 'gdut', [], true) rescue nil) # Don't ask me why...
284
+ return self
285
+ end
261
286
  end
262
287
 
263
288
  module OSA
264
- def self.app_with_name(name)
265
- STDERR.puts "OSA.app_with_name() has been deprecated and its usage is now discouraged. Please use OSA.app('name') instead."
266
- self.__app__(*OSA.__scripting_info__(:name => name))
267
- end
289
+ def self.app_with_name(name)
290
+ STDERR.puts "OSA.app_with_name() has been deprecated and its usage is now discouraged. Please use OSA.app('name') instead."
291
+ self.__app__(*OSA.__scripting_info__(:name => name))
292
+ end
268
293
 
269
- def self.app_with_path(path)
270
- STDERR.puts "OSA.app_by_path() has been deprecated and its usage is now discouraged. Please use OSA.app(:path => 'path') instead."
271
- self.__app__(*OSA.__scripting_info__(:path => path))
272
- end
294
+ def self.app_with_path(path)
295
+ STDERR.puts "OSA.app_by_path() has been deprecated and its usage is now discouraged. Please use OSA.app(:path => 'path') instead."
296
+ self.__app__(*OSA.__scripting_info__(:path => path))
297
+ end
273
298
 
274
- def self.app_by_bundle_id(bundle_id)
275
- STDERR.puts "OSA.app_by_bundle_id() has been deprecated and its usage is now discouraged. Please use OSA.app(:bundle_id => 'bundle_id') instead."
276
- self.__app__(*OSA.__scripting_info__(:bundle_id => bundle_id))
277
- end
299
+ def self.app_by_bundle_id(bundle_id)
300
+ STDERR.puts "OSA.app_by_bundle_id() has been deprecated and its usage is now discouraged. Please use OSA.app(:bundle_id => 'bundle_id') instead."
301
+ self.__app__(*OSA.__scripting_info__(:bundle_id => bundle_id))
302
+ end
278
303
 
279
- def self.app_by_signature(signature)
280
- STDERR.puts "OSA.app_by_signature() has been deprecated and its usage is now discouraged. Please use OSA.app(:signature => 'signature') instead."
281
- self.__app__(*OSA.__scripting_info__(:signature => signature))
282
- end
304
+ def self.app_by_signature(signature)
305
+ STDERR.puts "OSA.app_by_signature() has been deprecated and its usage is now discouraged. Please use OSA.app(:signature => 'signature') instead."
306
+ self.__app__(*OSA.__scripting_info__(:signature => signature))
307
+ end
283
308
 
284
- def self.app(*args)
285
- if args.size == 2
286
- if args.first.is_a?(String) and args.last.is_a?(Hash)
287
- hash = args.last
288
- if hash.has_key?(:name)
289
- warn "Given Hash argument already has a :name key, ignoring the first String argument `#{args.first}'"
290
- else
291
- hash[:name] = args.first
292
- end
293
- args = hash
294
- else
295
- raise ArgumentError, "when called with 2 arguments, the first is supposed to be a String and the second a Hash"
296
- end
309
+ def self.app(*args)
310
+ if args.size == 2
311
+ if args.first.is_a?(String) and args.last.is_a?(Hash)
312
+ hash = args.last
313
+ if hash.has_key?(:name)
314
+ warn "Given Hash argument already has a :name key, ignoring the first String argument `#{args.first}'"
297
315
  else
298
- if args.size != 1
299
- raise ArgumentError, "wrong number of arguments (#{args.size} for at least 1)"
300
- end
301
- args = args.first
316
+ hash[:name] = args.first
302
317
  end
303
- args = { :name => args } if args.is_a?(String)
304
- self.__app__(*OSA.__scripting_info__(args))
318
+ args = hash
319
+ else
320
+ raise ArgumentError, "when called with 2 arguments, the first is supposed to be a String and the second a Hash"
321
+ end
322
+ else
323
+ if args.size != 1
324
+ raise ArgumentError, "wrong number of arguments (#{args.size} for at least 1)"
325
+ end
326
+ args = args.first
305
327
  end
328
+ args = { :name => args } if args.is_a?(String)
329
+ self.__app__(*OSA.__scripting_info__(args))
330
+ end
306
331
 
307
- @conversions_to_ruby = {}
308
- @conversions_to_osa = {}
332
+ @conversions_to_ruby = {}
333
+ @conversions_to_osa = {}
309
334
 
310
- def self.add_conversion(hash, types, block, max_arity, replace=false)
311
- raise "Conversion block has to accept either #{(1..max_arity).to_a.join(', ')} arguments" unless (1..max_arity) === block.arity
312
- types.each do |type|
313
- next if !replace and hash.has_key?(type)
314
- hash[type] = block
315
- end
335
+ def self.add_conversion(hash, types, block, max_arity, replace=false)
336
+ raise "Conversion block has to accept either #{(1..max_arity).to_a.join(', ')} arguments" unless (1..max_arity) === block.arity
337
+ types.each do |type|
338
+ next if !replace and hash.has_key?(type)
339
+ hash[type] = block
316
340
  end
341
+ end
342
+
343
+ def self.replace_conversion_to_ruby(*types, &block)
344
+ add_conversion(@conversions_to_ruby, types, block, 3, true)
345
+ end
346
+
347
+ def self.add_conversion_to_ruby(*types, &block)
348
+ add_conversion(@conversions_to_ruby, types, block, 3)
349
+ end
317
350
 
318
- def self.replace_conversion_to_ruby(*types, &block)
319
- add_conversion(@conversions_to_ruby, types, block, 3, true)
351
+ def self.replace_conversion_to_osa(*types, &block)
352
+ add_conversion(@conversions_to_osa, types, block, 2, true)
353
+ end
354
+
355
+ def self.add_conversion_to_osa(*types, &block)
356
+ add_conversion(@conversions_to_osa, types, block, 2)
357
+ end
358
+
359
+ def self.convert_to_ruby(osa_object)
360
+ osa_type = osa_object.__type__
361
+ osa_data = osa_object.__data__(osa_type) if osa_type and osa_type != 'null'
362
+ if conversion = @conversions_to_ruby[osa_type]
363
+ args = [osa_data, osa_type, osa_object]
364
+ conversion.call(*args[0..(conversion.arity - 1)])
320
365
  end
321
-
322
- def self.add_conversion_to_ruby(*types, &block)
323
- add_conversion(@conversions_to_ruby, types, block, 3)
366
+ end
367
+
368
+ def self.__convert_to_osa__(requested_type, value, enum_group_codes=nil)
369
+ return value if value.is_a?(OSA::Element)
370
+ if conversion = @conversions_to_osa[requested_type]
371
+ args = [value, requested_type]
372
+ conversion.call(*args[0..(conversion.arity - 1)])
373
+ elsif enum_group_codes and enum_group_codes.include?(requested_type)
374
+ if value.is_a?(Array)
375
+ ary = value.map { |x| OSA::Element.__new__('enum', x.code.to_4cc) }
376
+ ElementList.__new__(ary)
377
+ else
378
+ ['enum', value.code.to_4cc]
379
+ end
380
+ elsif md = /^list_of_(.+)$/.match(requested_type)
381
+ ary = value.to_a.map do |elem|
382
+ obj = convert_to_osa(md[1], elem, enum_group_codes)
383
+ obj.is_a?(OSA::Element) ? obj : OSA::Element.__new__(*obj)
384
+ end
385
+ ElementList.__new__(ary)
386
+ else
387
+ STDERR.puts "unrecognized type #{requested_type}" if $VERBOSE
388
+ ['null', nil]
324
389
  end
390
+ end
325
391
 
326
- def self.replace_conversion_to_osa(*types, &block)
327
- add_conversion(@conversions_to_osa, types, block, 2, true)
392
+ def self.convert_to_osa(requested_type, value, enum_group_codes=nil)
393
+ ary = __convert_to_osa__(requested_type, value, enum_group_codes)
394
+ if ary == ['null', nil]
395
+ new_type = case value
396
+ when String then 'text'
397
+ when Array then 'list'
398
+ when Hash then 'record'
399
+ when Integer then 'integer'
400
+ when Float then 'double'
401
+ end
402
+ if new_type
403
+ ary = __convert_to_osa__(new_type, value, enum_group_codes)
404
+ end
328
405
  end
329
-
330
- def self.add_conversion_to_osa(*types, &block)
331
- add_conversion(@conversions_to_osa, types, block, 2)
406
+ ary
407
+ end
408
+
409
+ def self.set_params(hash)
410
+ previous_values = {}
411
+ hash.each do |key, val|
412
+ unless OSA.respond_to?(key)
413
+ raise ArgumentError, "Invalid key value (no parameter named #{key} was found)"
414
+ end
415
+ ivar_key = '@' + key.to_s
416
+ previous_val = self.instance_variable_get(ivar_key)
417
+ previous_values[ivar_key] = previous_val;
418
+ self.instance_variable_set(ivar_key, hash[key])
332
419
  end
333
-
334
- def self.convert_to_ruby(osa_object)
335
- osa_type = osa_object.__type__
336
- osa_data = osa_object.__data__(osa_type) if osa_type and osa_type != 'null'
337
- if conversion = @conversions_to_ruby[osa_type]
338
- args = [osa_data, osa_type, osa_object]
339
- conversion.call(*args[0..(conversion.arity - 1)])
340
- end
420
+ if block_given?
421
+ yield
422
+ previous_values.each { |key, val| self.instance_variable_set(key, val) }
341
423
  end
424
+ nil
425
+ end
342
426
 
343
- def self.__convert_to_osa__(requested_type, value, enum_group_codes=nil)
344
- return value if value.is_a?(OSA::Element)
345
- if conversion = @conversions_to_osa[requested_type]
346
- args = [value, requested_type]
347
- conversion.call(*args[0..(conversion.arity - 1)])
348
- elsif enum_group_codes and enum_group_codes.include?(requested_type)
349
- ['enum', value.code.to_4cc]
350
- elsif md = /^list_of_(.+)$/.match(requested_type)
351
- ary = value.to_a.map do |elem|
352
- obj = convert_to_osa(md[1], elem, enum_group_codes)
353
- obj.is_a?(OSA::Element) ? obj : OSA::Element.__new__(*obj)
354
- end
355
- ElementList.__new__(ary)
356
- else
357
- STDERR.puts "unrecognized type #{requested_type}" if $VERBOSE
358
- ['null', nil]
359
- end
360
- end
427
+ #######
428
+ private
429
+ #######
361
430
 
362
- def self.convert_to_osa(requested_type, value, enum_group_codes=nil)
363
- ary = __convert_to_osa__(requested_type, value, enum_group_codes)
364
- if ary == ['null', nil]
365
- new_type = case value
366
- when String then 'text'
367
- when Array then 'list'
368
- when Hash then 'record'
369
- when Integer then 'integer'
370
- end
371
- if new_type
372
- ary = __convert_to_osa__(new_type, value, enum_group_codes)
373
- end
374
- end
375
- ary
376
- end
377
-
378
- def self.set_params(hash)
379
- previous_values = {}
380
- hash.each do |key, val|
381
- unless OSA.respond_to?(key)
382
- raise ArgumentError, "Invalid key value (no parameter named #{key} was found)"
383
- end
384
- ivar_key = '@' + key.to_s
385
- previous_val = self.instance_variable_get(ivar_key)
386
- previous_values[ivar_key] = previous_val;
387
- self.instance_variable_set(ivar_key, hash[key])
388
- end
389
- if block_given?
390
- yield
391
- previous_values.each { |key, val| self.instance_variable_set(key, val) }
392
- end
393
- nil
431
+ class DocItem
432
+ attr_reader :name, :description
433
+ def initialize(name, description, optional=false)
434
+ @name = name
435
+ @description = description
436
+ @optional = optional
394
437
  end
395
-
396
- #######
397
- private
398
- #######
399
-
400
- class DocItem
401
- attr_reader :name, :description
402
- def initialize(name, description, optional=false)
403
- @name = name
404
- @description = description
405
- @optional = optional
406
- end
407
- def optional?
408
- @optional
409
- end
438
+ def optional?
439
+ @optional
410
440
  end
441
+ end
411
442
 
412
- class DocMethod < DocItem
413
- attr_reader :result, :args
414
- def initialize(name, description, result, args)
415
- super(name, description)
416
- @result = result
417
- @args = args
418
- end
419
- def inspect
420
- "<Method #{name} (#{description})>"
421
- end
443
+ class DocMethod < DocItem
444
+ attr_reader :result, :args
445
+ def initialize(name, description, result, args)
446
+ super(name, description)
447
+ @result = result
448
+ @args = args
449
+ end
450
+ def inspect
451
+ "<Method #{name} (#{description})>"
422
452
  end
453
+ end
423
454
 
424
- def self.__app__(name, target, sdef)
425
- @apps ||= {}
426
- app = @apps[target]
427
- return app if app
455
+ def self.__app__(name, target, sdef)
456
+ @apps ||= {}
457
+ app = @apps[target]
458
+ return app if app
428
459
 
429
- # Creates a module for this app, we will define the scripting interface within it.
430
- app_module = Module.new
431
- self.const_set(rubyfy_constant_string(name), app_module)
460
+ # Creates a module for this app, we will define the scripting interface within it.
461
+ app_module = Module.new
462
+ self.const_set(rubyfy_constant_string(name), app_module)
432
463
 
433
- @apps[target] = __load_sdef__(sdef, target, app_module)
434
- end
464
+ @apps[target] = __load_sdef__(sdef, target, app_module)
465
+ end
435
466
 
436
- def self.__load_sdef__(sdef, target, app_module, merge_only=false, app_class=nil)
437
- # Load the sdef.
438
- doc = if USE_LIBXML
439
- parser = XML::Parser.new
440
- parser.string = sdef
441
- parser.parse
442
- else
443
- REXML::Document.new(sdef)
444
- end
467
+ def self.__load_sdef__(sdef, target, app_module, merge_only=false, app_class=nil)
468
+ # Load the sdef.
469
+ doc = if USE_LIBXML
470
+ parser = XML::Parser.new
471
+ parser.string = sdef
472
+ parser.parse
473
+ else
474
+ REXML::Document.new(sdef)
475
+ end
445
476
 
446
- # Retrieves and creates enumerations.
447
- enum_group_codes = {}
448
- doc.find('/dictionary/suite/enumeration').each do |element|
449
- enum_group_code = element['code']
450
- enum_module_name = rubyfy_constant_string(element['name'], true)
451
- enum_module = Module.new
452
- enum_group_codes[enum_group_code] = enum_module
453
-
454
- documentation = []
455
- enum_module.const_set(:DESCRIPTION, documentation)
477
+ # Retrieves and creates enumerations.
478
+ enum_group_codes = {}
479
+ doc.find('/dictionary/suite/enumeration').each do |element|
480
+ enum_group_code = element['code']
481
+ enum_module_name = rubyfy_constant_string(element['name'], true)
482
+ enum_module = Module.new
483
+ enum_group_codes[enum_group_code] = enum_module
484
+
485
+ documentation = []
486
+ enum_module.const_set(:DESCRIPTION, documentation)
456
487
 
457
- element.find('enumerator').each do |element|
458
- name = element['name']
459
- enum_name = rubyfy_constant_string(name, true)
460
- enum_code = element['code']
461
- enum_const = app_module.name + '::' + enum_module_name + '::' + enum_name
488
+ element.find('enumerator').each do |element|
489
+ name = element['name']
490
+ enum_name = rubyfy_constant_string(name, true)
491
+ enum_code = element['code']
492
+ enum_const = app_module.name + '::' + enum_module_name + '::' + enum_name
462
493
 
463
- enum = OSA::Enumerator.new(enum_const, name, enum_code, enum_group_code)
464
- enum_module.const_set(enum_name, enum)
494
+ enum = OSA::Enumerator.new(enum_const, name, enum_code, enum_group_code)
495
+ enum_module.const_set(enum_name, enum)
465
496
 
466
- documentation << DocItem.new(enum_name, englishify_sentence(element['description']))
467
- end
497
+ documentation << DocItem.new(enum_name, englishify_sentence(element['description']))
498
+ end
468
499
 
469
- app_module.const_set(enum_module_name, enum_module) unless app_module.const_defined?(enum_module_name)
470
- end
500
+ app_module.const_set(enum_module_name, enum_module) unless app_module.const_defined?(enum_module_name)
501
+ end
471
502
 
472
- # Retrieves and creates classes.
473
- classes = {}
474
- class_elements = {}
475
- doc.find('/dictionary/suite/class').each do |element|
476
- key = (element['id'] or element['name'])
477
- (class_elements[key] ||= []) << element
503
+ # Retrieves and creates classes.
504
+ classes = {}
505
+ class_elements = {}
506
+ doc.find('/dictionary/suite/class').each do |element|
507
+ key = (element['id'] or element['name'])
508
+ (class_elements[key] ||= []) << element
509
+ end
510
+ class_elements.values.flatten.each do |element|
511
+ klass = add_class_from_xml_element(element, class_elements, classes, app_module)
512
+ methods_doc = []
513
+ description = englishify_sentence(element['description'])
514
+ if klass.const_defined?(:DESCRIPTION)
515
+ klass.const_set(:DESCRIPTION, description) if klass.const_get(:DESCRIPTION).nil?
516
+ else
517
+ klass.const_set(:DESCRIPTION, description)
518
+ end
519
+ if klass.const_defined?(:METHODS_DESCRIPTION)
520
+ methods_doc = klass.const_get(:METHODS_DESCRIPTION)
521
+ else
522
+ methods_doc = []
523
+ klass.const_set(:METHODS_DESCRIPTION, methods_doc)
524
+ end
525
+
526
+ # Creates properties.
527
+ # Add basic properties that might be missing to the Item class (if any).
528
+ props = {}
529
+ element.find('property').each do |x|
530
+ props[x['name']] = [x['code'], type_of_parameter(x), x['access'], x['description']]
531
+ end
532
+ if klass.name[-6..-1] == '::Item'
533
+ unless props.has_key?('id')
534
+ props['id'] = ['ID ', 'integer', 'r', 'the unique ID of the item']
478
535
  end
479
- class_elements.values.flatten.each do |element|
480
- klass = add_class_from_xml_element(element, class_elements, classes, app_module)
481
- methods_doc = []
482
- description = englishify_sentence(element['description'])
483
- if klass.const_defined?(:DESCRIPTION)
484
- klass.const_set(:DESCRIPTION, description) if klass.const_get(:DESCRIPTION).nil?
485
- else
486
- klass.const_set(:DESCRIPTION, description)
487
- end
488
- if klass.const_defined?(:METHODS_DESCRIPTION)
489
- methods_doc = klass.const_get(:METHODS_DESCRIPTION)
490
- else
491
- methods_doc = []
492
- klass.const_set(:METHODS_DESCRIPTION, methods_doc)
493
- end
536
+ end
537
+ props.each do |name, pary|
538
+ code, type, access, description = pary
539
+ setter = (access == nil or access.include?('w'))
494
540
 
495
- # Creates properties.
496
- # Add basic properties that might be missing to the Item class (if any).
497
- props = {}
498
- element.find('property').each do |x|
499
- props[x['name']] = [x['code'], type_of_parameter(x), x['access'], x['description']]
500
- end
501
- if klass.name[-6..-1] == '::Item'
502
- unless props.has_key?('id')
503
- props['id'] = ['ID ', 'integer', 'r', 'the unique ID of the item']
504
- end
505
- end
506
- props.each do |name, pary|
507
- code, type, access, description = pary
508
- setter = (access == nil or access.include?('w'))
509
-
510
- if type == 'reference'
511
- pklass = OSA::Element
512
- else
513
- pklass = classes[type]
514
- if pklass.nil?
515
- pklass_elements = class_elements[type]
516
- unless pklass_elements.nil?
517
- pklass = add_class_from_xml_element(pklass_elements.first, class_elements, classes, app_module)
518
- end
519
- end
520
- end
521
-
522
- # Implicit 'get' if the property class is primitive (not defined in the sdef),
523
- # otherwise just return an object specifier.
524
- method_name = rubyfy_method(name, klass, type)
525
- method_proc = if pklass.nil?
526
- proc do
527
- @app.__send_event__('core', 'getd',
528
- [['----', Element.__new_object_specifier__('prop', @app == self ? Element.__new__('null', nil) : self,
529
- 'prop', Element.__new__('type', code.to_4cc))]],
530
- true).to_rbobj
531
- end
532
- else
533
- proc do
534
- o = pklass.__new_object_specifier__('prop', @app == self ? Element.__new__('null', nil) : self,
535
- 'prop', Element.__new__('type', code.to_4cc))
536
- unless OSA.lazy_events?
537
- @app.__send_event__('core', 'getd', [['----', o]], true).to_rbobj
538
- else
539
- o.instance_variable_set(:@app, @app)
540
- o.extend(OSA::ObjectSpecifier)
541
- end
542
- end
543
- end
544
-
545
- klass.class_eval { define_method(method_name, method_proc) }
546
- ptypedoc = if pklass.nil?
547
- type_doc(type, enum_group_codes, app_module)
548
- else
549
- "a #{pklass} object"
550
- end
551
- if description
552
- description[0] = description[0].chr.downcase
553
- description = '-- ' << description
554
- end
555
- methods_doc << DocMethod.new(method_name, englishify_sentence("Gets the #{name} property #{description}"), DocItem.new('result', englishify_sentence("the property value, as #{ptypedoc}")), nil)
556
-
557
- # For the setter, always send an event.
558
- if setter
559
- method_name = rubyfy_method(name, klass, type, true)
560
- method_proc = proc do |val|
561
- @app.__send_event__('core', 'setd',
562
- [['----', Element.__new_object_specifier__('prop', @app == self ? Element.__new__('null', nil) : self,
563
- 'prop', Element.__new__('type', code.to_4cc))],
564
- ['data', val.is_a?(OSA::Element) ? val : Element.from_rbobj(type, val, enum_group_codes.keys)]],
565
- true)
566
- return nil
567
- end
568
- klass.class_eval { define_method(method_name, method_proc) }
569
- methods_doc << DocMethod.new(method_name, englishify_sentence("Sets the #{name} property #{description}"), nil, [DocItem.new('val', englishify_sentence("the value to be set, as #{ptypedoc}"))])
570
- end
571
-
572
- OSA.add_property(name.intern, code)
541
+ if type == 'reference'
542
+ pklass = OSA::Element
543
+ else
544
+ pklass = classes[type]
545
+ if pklass.nil?
546
+ pklass_elements = class_elements[type]
547
+ unless pklass_elements.nil?
548
+ pklass = add_class_from_xml_element(pklass_elements.first, class_elements, classes, app_module)
573
549
  end
550
+ end
551
+ end
574
552
 
575
- # Creates elements.
576
- element.find('element').each do |eelement|
577
- type = eelement['type']
578
-
579
- eklass = classes[type]
580
- if eklass.nil?
581
- eklass_elements = class_elements[type]
582
- unless eklass_elements.nil?
583
- eklass = add_class_from_xml_element(eklass_elements.first, class_elements, classes, app_module)
584
- end
585
- end
586
-
587
- if eklass.nil?
588
- STDERR.puts "Cannot find class '#{type}', skipping element '#{eelement}'" if $DEBUG
589
- next
590
- end
591
-
592
- method_name = rubyfy_method(eklass::PLURAL, klass)
593
- method_proc = proc do
594
- unless OSA.lazy_events?
595
- @app.__send_event__('core', 'getd',
596
- [['----', Element.__new_object_specifier__(
597
- eklass::CODE.to_4cc, @app == self ? Element.__new__('null', nil) : self,
598
- 'indx', Element.__new__('abso', 'all '.to_4cc))]],
599
- true).to_rbobj
600
- else
601
- ObjectSpecifierList.new(@app, eklass, @app == self ? Element.__new__('null', nil) : self)
602
- end
603
- end
604
- klass.class_eval { define_method(method_name, method_proc) }
605
- methods_doc << DocMethod.new(method_name, englishify_sentence("Gets the #{eklass::PLURAL} associated with this object"), DocItem.new('result', englishify_sentence("an Array of #{eklass} objects")), nil)
553
+ # Implicit 'get' if the property class is primitive (not defined in the sdef),
554
+ # otherwise just return an object specifier.
555
+ method_name = rubyfy_method(name, klass, type)
556
+ method_proc = if pklass.nil?
557
+ proc do
558
+ @app.__send_event__('core', 'getd',
559
+ [['----', Element.__new_object_specifier__('prop', @app == self ? Element.__new__('null', nil) : self,
560
+ 'prop', Element.__new__('type', code.to_4cc))]],
561
+ true).to_rbobj
562
+ end
563
+ else
564
+ proc do
565
+ o = pklass.__new_object_specifier__('prop', @app == self ? Element.__new__('null', nil) : self,
566
+ 'prop', Element.__new__('type', code.to_4cc))
567
+ unless OSA.lazy_events?
568
+ @app.__send_event__('core', 'getd', [['----', o]], true).to_rbobj
569
+ else
570
+ o.instance_variable_set(:@app, @app)
571
+ o.extend(OSA::ObjectSpecifier)
606
572
  end
573
+ end
607
574
  end
608
575
 
609
- unless merge_only
610
- # Having an 'application' class is required.
611
- app_class = classes['application']
612
- raise "No application class defined." if app_class.nil?
613
- all_classes_but_app = classes.values.reject { |x| x.ancestors.include?(OSA::EventDispatcher) }
576
+ klass.class_eval { define_method(method_name, method_proc) }
577
+ ptypedoc = if pklass.nil?
578
+ type_doc(type, enum_group_codes, app_module)
614
579
  else
615
- all_classes_but_app = classes.values
580
+ "a #{pklass} object"
616
581
  end
582
+ if description
583
+ description[0] = description[0].chr.downcase
584
+ description = '-- ' << description
585
+ end
586
+ methods_doc << DocMethod.new(method_name, englishify_sentence("Gets the #{name} property #{description}"), DocItem.new('result', englishify_sentence("the property value, as #{ptypedoc}")), nil)
587
+
588
+ # For the setter, always send an event.
589
+ if setter
590
+ method_name = rubyfy_method(name, klass, type, true)
591
+ method_proc = proc do |val|
592
+ @app.__send_event__('core', 'setd',
593
+ [['----', Element.__new_object_specifier__('prop', @app == self ? Element.__new__('null', nil) : self,
594
+ 'prop', Element.__new__('type', code.to_4cc))],
595
+ ['data', val.is_a?(OSA::Element) ? val : Element.from_rbobj(type, val, enum_group_codes.keys)]],
596
+ true)
597
+ return nil
598
+ end
599
+ klass.class_eval { define_method(method_name, method_proc) }
600
+ methods_doc << DocMethod.new(method_name, englishify_sentence("Sets the #{name} property #{description}"), nil, [DocItem.new('val', englishify_sentence("the value to be set, as #{ptypedoc}"))])
601
+ end
617
602
 
618
- # Maps commands to the right classes.
619
- doc.find('/dictionary/suite/command').each do |element|
620
- name = element['name']
621
- next if /NOT AVAILABLE/.match(name) # Finder's sdef (Tiger) names some commands with this 'tag'.
622
- description = element['description']
623
- direct_parameter = element.find_first('direct-parameter')
624
- result = element.find_first('result')
625
- has_result = result != nil
626
-
627
- code = element['code']
628
- begin
629
- code = Iconv.iconv('MACROMAN', 'UTF-8', code).to_s
630
- rescue Iconv::IllegalSequence
631
- # We can't do more...
632
- STDERR.puts "unrecognized command code encoding '#{code}', skipping..." if $DEBUG
633
- next
634
- end
603
+ OSA.add_property(name.intern, code)
604
+ end
635
605
 
636
- classes_to_define = []
637
- forget_direct_parameter = true
638
- direct_parameter_optional = false
606
+ # Creates elements.
607
+ element.find('element').each do |eelement|
608
+ type = eelement['type']
609
+
610
+ eklass = classes[type]
611
+ if eklass.nil?
612
+ eklass_elements = class_elements[type]
613
+ unless eklass_elements.nil?
614
+ eklass = add_class_from_xml_element(eklass_elements.first, class_elements, classes, app_module)
615
+ end
616
+ end
639
617
 
640
- if direct_parameter.nil?
641
- # No direct parameter, this is for the application class.
642
- classes_to_define << app_class
643
- else
644
- # We have a direct parameter:
645
- # - map it to the right class if it's a class defined in our scripting dictionary
646
- # - map it to all classes if it's a 'reference' and to the application class if it's optional
647
- # - otherwise, just map it to the application class.
648
- type = type_of_parameter(direct_parameter)
649
- direct_parameter_optional = parameter_optional?(direct_parameter)
650
-
651
- if type == 'reference'
652
- classes_to_define = all_classes_but_app
653
- classes_to_define << app_class if direct_parameter_optional
654
- else
655
- klass = classes[type]
656
- if klass.nil?
657
- forget_direct_parameter = false
658
- classes_to_define << app_class
659
- else
660
- classes_to_define << klass
661
- end
662
- end
663
- end
618
+ if eklass.nil?
619
+ STDERR.puts "Cannot find class '#{type}', skipping element '#{eelement}'" if $DEBUG
620
+ next
621
+ end
664
622
 
665
- # Reject classes which are already represented by an ancestor.
666
- if classes_to_define.length > 1
667
- classes_to_define.uniq!
668
- classes_to_define.reject! do |x|
669
- classes_to_define.any? { |y| x != y and x.ancestors.include?(y) }
670
- end
671
- end
623
+ method_name = rubyfy_method(eklass::PLURAL, klass)
624
+ method_proc = proc do
625
+ unless OSA.lazy_events?
626
+ @app.__send_event__('core', 'getd',
627
+ [['----', Element.__new_object_specifier__(
628
+ eklass::CODE.to_4cc, @app == self ? Element.__new__('null', nil) : self,
629
+ 'indx', Element.__new__('abso', 'all '.to_4cc))]],
630
+ true).to_rbobj
631
+ else
632
+ ObjectSpecifierList.new(@app, eklass, @app == self ? Element.__new__('null', nil) : self)
633
+ end
634
+ end
635
+ klass.class_eval { define_method(method_name, method_proc) }
636
+ methods_doc << DocMethod.new(method_name, englishify_sentence("Gets the #{eklass::PLURAL} associated with this object"), DocItem.new('result', englishify_sentence("an Array of #{eklass} objects")), nil)
637
+ end
638
+ end
672
639
 
673
- params = []
674
- params_doc = []
675
- unless direct_parameter.nil?
676
- pdesc = direct_parameter['description']
677
- params << [
678
- 'direct',
679
- '----',
680
- direct_parameter_optional,
681
- type_of_parameter(direct_parameter)
682
- ]
683
- unless forget_direct_parameter
684
- params_doc << DocItem.new('direct', englishify_sentence(pdesc))
685
- end
686
- end
640
+ unless merge_only
641
+ # Having an 'application' class is required.
642
+ app_class = classes['application']
643
+ raise "No application class defined." if app_class.nil?
644
+ all_classes_but_app = classes.values.reject { |x| x.ancestors.include?(OSA::EventDispatcher) }
645
+ else
646
+ all_classes_but_app = classes.values
647
+ end
687
648
 
688
- element.find('parameter').to_a.each do |element|
689
- poptional = parameter_optional?(element)
690
- params << [
691
- rubyfy_string(element['name']),
692
- element['code'],
693
- poptional,
694
- type_of_parameter(element)
695
- ]
696
- params_doc << DocItem.new(rubyfy_string(element['name'], true), englishify_sentence(element['description']), poptional)
697
- end
649
+ # Maps commands to the right classes.
650
+ doc.find('/dictionary/suite/command').each do |element|
651
+ name = element['name']
652
+ next if /NOT AVAILABLE/.match(name) # Finder's sdef (Tiger) names some commands with this 'tag'.
653
+ description = element['description']
654
+ direct_parameter = element.find_first('direct-parameter')
655
+ result = element.find_first('result')
656
+ has_result = result != nil
657
+
658
+ code = element['code']
659
+ begin
660
+ code = Iconv.iconv('MACROMAN', 'UTF-8', code).to_s
661
+ rescue Iconv::IllegalSequence
662
+ # We can't do more...
663
+ STDERR.puts "unrecognized command code encoding '#{code}', skipping..." if $DEBUG
664
+ next
665
+ end
698
666
 
699
- method_proc = proc do |*args_ary|
700
- args = []
701
- min_argc = i = 0
702
- already_has_optional_args = false # Once an argument is optional, all following arguments should be optional.
703
- optional_hash = nil
704
- params.each do |pname, pcode, optional, ptype|
705
- self_direct = (pcode == '----' and forget_direct_parameter)
706
- if already_has_optional_args or (optional and !self_direct)
707
- already_has_optional_args = true
708
- else
709
- if args_ary.size < i
710
- raise ArgumentError, "wrong number of arguments (#{args_ary.size} for #{i})"
711
- end
712
- end
713
- val = if self_direct
714
- self.is_a?(OSA::EventDispatcher) ? [] : ['----', self]
715
- else
716
- arg = args_ary[i]
717
- min_argc += 1 unless already_has_optional_args
718
- i += 1
719
- if arg.is_a?(Hash) and already_has_optional_args and i >= args_ary.size and min_argc + 1 == i
720
- optional_hash = arg
721
- end
722
- if optional_hash
723
- arg = optional_hash.delete(pname.intern)
724
- end
725
- if arg.nil?
726
- if already_has_optional_args
727
- []
728
- end
729
- else
730
- [pcode, arg.is_a?(OSA::Element) ? arg : OSA::Element.from_rbobj(ptype, arg, enum_group_codes.keys)]
731
- end
732
- end
733
- args << val
734
- end
735
- if args_ary.size > params.size or args_ary.size < min_argc
736
- raise ArgumentError, "wrong number of arguments (#{args_ary.size} for #{min_argc})"
737
- end
738
- if optional_hash and !optional_hash.empty?
739
- raise ArgumentError, "inappropriate optional argument(s): #{optional_hash.keys.join(', ')}"
740
- end
741
- wait_reply = (OSA.wait_reply != nil ? OSA.wait_reply : (has_result or @app.remote?))
742
- ret = @app.__send_event__(code[0..3], code[4..-1], args, wait_reply)
743
- wait_reply ? ret.to_rbobj : ret
744
- end
667
+ classes_to_define = []
668
+ forget_direct_parameter = true
669
+ direct_parameter_optional = false
670
+
671
+ if direct_parameter.nil?
672
+ # No direct parameter, this is for the application class.
673
+ classes_to_define << app_class
674
+ else
675
+ # We have a direct parameter:
676
+ # - map it to the right class if it's a class defined in our scripting dictionary
677
+ # - map it to all classes if it's a 'reference' and to the application class if it's optional
678
+ # - otherwise, just map it to the application class.
679
+ type = type_of_parameter(direct_parameter)
680
+ direct_parameter_optional = parameter_optional?(direct_parameter)
681
+
682
+ if type == 'reference'
683
+ classes_to_define = all_classes_but_app
684
+ classes_to_define << app_class if direct_parameter_optional
685
+ else
686
+ klass = classes[type]
687
+ if klass.nil?
688
+ forget_direct_parameter = false
689
+ classes_to_define << app_class
690
+ else
691
+ classes_to_define << klass
692
+ end
693
+ end
694
+ end
745
695
 
746
- unless has_result
747
- result_type = result_doc = nil
748
- else
749
- result_type = type_of_parameter(result)
750
- result_klass = classes[result_type]
751
- result_doc = DocItem.new('result', englishify_sentence(result['description']))
752
- end
696
+ # Reject classes which are already represented by an ancestor.
697
+ if classes_to_define.length > 1
698
+ classes_to_define.uniq!
699
+ classes_to_define.reject! do |x|
700
+ classes_to_define.any? { |y| x != y and x.ancestors.include?(y) }
701
+ end
702
+ end
753
703
 
754
- classes_to_define.each do |klass|
755
- method_name = rubyfy_method(name, klass, result_type)
756
- klass.class_eval { define_method(method_name, method_proc) }
757
- methods_doc = klass.const_get(:METHODS_DESCRIPTION)
758
- methods_doc << DocMethod.new(method_name, englishify_sentence(description), result_doc, params_doc)
759
- end
704
+ params = []
705
+ params_doc = []
706
+ unless direct_parameter.nil?
707
+ pdesc = direct_parameter['description']
708
+ params << [
709
+ 'direct',
710
+ '----',
711
+ direct_parameter_optional,
712
+ type_of_parameter(direct_parameter)
713
+ ]
714
+ unless forget_direct_parameter
715
+ params_doc << DocItem.new('direct', englishify_sentence(pdesc))
760
716
  end
717
+ end
718
+
719
+ element.find('parameter').to_a.each do |element|
720
+ poptional = parameter_optional?(element)
721
+ params << [
722
+ rubyfy_string(element['name']),
723
+ element['code'],
724
+ poptional,
725
+ type_of_parameter(element)
726
+ ]
727
+ params_doc << DocItem.new(rubyfy_string(element['name'], true), englishify_sentence(element['description']), poptional)
728
+ end
761
729
 
762
- unless merge_only
763
- # Returns an application instance, that's all folks!
764
- hash = {}
765
- classes.each_value { |klass| hash[klass::CODE] = klass }
766
- app_class.class_eval do
767
- attr_reader :sdef
768
- define_method(:remote?) { @is_remote == true }
730
+ method_proc = proc do |*args_ary|
731
+ args = []
732
+ min_argc = i = 0
733
+ already_has_optional_args = false # Once an argument is optional, all following arguments should be optional.
734
+ optional_hash = nil
735
+ params.each do |pname, pcode, optional, ptype|
736
+ self_direct = (pcode == '----' and forget_direct_parameter)
737
+ if already_has_optional_args or (optional and !self_direct)
738
+ already_has_optional_args = true
739
+ else
740
+ if args_ary.size < i
741
+ raise ArgumentError, "wrong number of arguments (#{args_ary.size} for #{i})"
769
742
  end
770
- is_remote = target.length > 4
771
- app = is_remote ? app_class.__new__('aprl', target) : app_class.__new__('sign', target.to_4cc)
772
- app.instance_variable_set(:@is_remote, is_remote)
773
- app.instance_variable_set(:@sdef, sdef)
774
- app.instance_variable_set(:@classes, hash)
775
- app.extend OSA::EventDispatcher
743
+ end
744
+ val = if self_direct
745
+ self.is_a?(OSA::EventDispatcher) ? [] : ['----', self]
746
+ else
747
+ arg = args_ary[i]
748
+ min_argc += 1 unless already_has_optional_args
749
+ i += 1
750
+ if arg.is_a?(Hash) and already_has_optional_args and i >= args_ary.size and min_argc + 1 == i
751
+ optional_hash = arg
752
+ end
753
+ if optional_hash
754
+ arg = optional_hash.delete(pname.intern)
755
+ end
756
+ if arg.nil?
757
+ if already_has_optional_args
758
+ []
759
+ end
760
+ else
761
+ [pcode, arg.is_a?(OSA::Element) ? arg : OSA::Element.from_rbobj(ptype, arg, enum_group_codes.keys)]
762
+ end
763
+ end
764
+ args << val
765
+ end
766
+ if args_ary.size > params.size or args_ary.size < min_argc
767
+ raise ArgumentError, "wrong number of arguments (#{args_ary.size} for #{min_argc})"
768
+ end
769
+ if optional_hash and !optional_hash.empty?
770
+ raise ArgumentError, "inappropriate optional argument(s): #{optional_hash.keys.join(', ')}"
771
+ end
772
+ wait_reply = (OSA.wait_reply != nil ? OSA.wait_reply : (has_result or @app.remote?))
773
+ ret = @app.__send_event__(code[0..3], code[4..-1], args, wait_reply)
774
+ wait_reply ? ret.to_rbobj : ret
775
+ end
776
+
777
+ unless has_result
778
+ result_type = result_doc = nil
779
+ else
780
+ result_type = type_of_parameter(result)
781
+ result_klass = classes[result_type]
782
+ result_doc = DocItem.new('result', englishify_sentence(result['description']))
783
+ end
784
+
785
+ classes_to_define.each do |klass|
786
+ method_name = rubyfy_method(name, klass, result_type)
787
+ klass.class_eval { define_method(method_name, method_proc) }
788
+ methods_doc = klass.const_get(:METHODS_DESCRIPTION)
789
+ methods_doc << DocMethod.new(method_name, englishify_sentence(description), result_doc, params_doc)
790
+ end
791
+
792
+ # Merge some additional commands, if necessary.
793
+ unless app_class.method_defined?(:activate)
794
+ app_class.class_eval do
795
+ define_method(:activate) do
796
+ __send_event__('misc', 'actv', [], true)
797
+ nil
798
+ end
776
799
  end
800
+ methods_doc = app_class.const_get(:METHODS_DESCRIPTION)
801
+ methods_doc << DocMethod.new('activate', 'Activate the application.', nil, [])
802
+ end
777
803
  end
778
804
 
779
- def self.parameter_optional?(element)
780
- element['optional'] == 'yes'
805
+ unless merge_only
806
+ # Returns an application instance, that's all folks!
807
+ hash = {}
808
+ classes.each_value { |klass| hash[klass::CODE] = klass }
809
+ app_class.class_eval do
810
+ attr_reader :sdef, :classes
811
+ define_method(:remote?) { @is_remote == true }
812
+ end
813
+ is_remote = target.length > 4
814
+ app = is_remote ? app_class.__new__('aprl', target) : app_class.__new__('sign', target.to_4cc)
815
+ app.instance_variable_set(:@is_remote, is_remote)
816
+ app.instance_variable_set(:@sdef, sdef)
817
+ app.instance_variable_set(:@classes, hash)
818
+ app.extend OSA::EventDispatcher
781
819
  end
820
+ end
821
+
822
+ def self.parameter_optional?(element)
823
+ element['optional'] == 'yes'
824
+ end
782
825
 
783
- def self.add_class_from_xml_element(element, class_elements, repository, app_module)
784
- real_name = element['name']
785
- key = (element['id'] or real_name)
786
- klass = repository[key]
787
- if klass.nil?
788
- code = element['code']
789
- inherits = element['inherits']
790
- plural = element['plural']
826
+ def self.add_class_from_xml_element(element, class_elements, repository, app_module)
827
+ real_name = element['name']
828
+ key = (element['id'] or real_name)
829
+ klass = repository[key]
830
+ if klass.nil?
831
+ code = element['code']
832
+ inherits = element['inherits']
833
+ plural = element['plural']
791
834
 
792
- if real_name == inherits
793
- # Inheriting from itself is a common idiom when adding methods
794
- # to a class that has already been defined, probably to avoid
795
- # mentioning the subclass name more than once.
796
- inherits = nil
797
- end
835
+ if real_name == inherits
836
+ # Inheriting from itself is a common idiom when adding methods
837
+ # to a class that has already been defined, probably to avoid
838
+ # mentioning the subclass name more than once.
839
+ inherits = nil
840
+ end
798
841
 
799
- if inherits.nil?
800
- klass = Class.new(OSA::Element)
801
- else
802
- super_elements = class_elements[inherits]
803
- if super_elements.nil?
804
- STDERR.puts "sdef bug: class '#{real_name}' inherits from '#{inherits}' which is not defined - fall back inheriting from OSA::Element" if $DEBUG
805
- klass = OSA::Element
806
- else
807
- super_class = add_class_from_xml_element(super_elements.first, class_elements,
808
- repository, app_module)
809
- klass = Class.new(super_class)
810
- end
811
- end
842
+ if inherits.nil?
843
+ klass = Class.new(OSA::Element)
844
+ else
845
+ super_elements = class_elements[inherits]
846
+ super_class = if super_elements.nil?
847
+ STDERR.puts "sdef bug: class '#{real_name}' inherits from '#{inherits}' which is not defined - fall back inheriting from OSA::Element" if $DEBUG
848
+ OSA::Element
849
+ else
850
+ add_class_from_xml_element(super_elements.first, class_elements, repository, app_module)
851
+ end
852
+ klass = Class.new(super_class)
853
+ end
812
854
 
813
- klass.class_eval { include OSA::EventDispatcher } if real_name == 'application'
855
+ klass.class_eval { include OSA::EventDispatcher } if real_name == 'application'
814
856
 
815
- klass.const_set(:REAL_NAME, real_name) unless klass.const_defined?(:REAL_NAME)
816
- klass.const_set(:PLURAL, plural == nil ? real_name + 's' : plural) unless klass.const_defined?(:PLURAL)
817
- klass.const_set(:CODE, code) unless klass.const_defined?(:CODE)
818
-
819
- app_module.const_set(rubyfy_constant_string(real_name), klass)
857
+ klass.const_set(:REAL_NAME, real_name) unless klass.const_defined?(:REAL_NAME)
858
+ klass.const_set(:PLURAL, plural == nil ? real_name + 's' : plural) unless klass.const_defined?(:PLURAL)
859
+ klass.const_set(:CODE, code) unless klass.const_defined?(:CODE)
860
+
861
+ app_module.const_set(rubyfy_constant_string(real_name), klass)
820
862
 
821
- repository[key] = klass
822
- end
863
+ repository[key] = klass
864
+ end
823
865
 
824
- return klass
825
- end
826
-
827
- def self.type_doc(type, enum_group_codes, app_module)
828
- if mod = enum_group_codes[type]
829
- mod.to_s
830
- elsif md = /^list_of_(.+)$/.match(type)
831
- "list of #{type_doc(md[1], enum_group_codes, app_module)}"
832
- else
833
- up_type = type.upcase
834
- begin
835
- app_module.const_get(up_type).to_s
836
- rescue
837
- type
838
- end
839
- end
866
+ return klass
867
+ end
868
+
869
+ def self.type_doc(type, enum_group_codes, app_module)
870
+ if mod = enum_group_codes[type]
871
+ mod.to_s
872
+ elsif md = /^list_of_(.+)$/.match(type)
873
+ "list of #{type_doc(md[1], enum_group_codes, app_module)}"
874
+ else
875
+ up_type = type.upcase
876
+ begin
877
+ app_module.const_get(up_type).to_s
878
+ rescue
879
+ type
880
+ end
840
881
  end
882
+ end
841
883
 
842
- def self.type_of_parameter(element)
843
- type = element['type']
844
- if type.nil?
845
- etype = element.find_first('type')
846
- if etype
847
- type = etype['type']
848
- if type.nil? and (etype2 = etype.find_first('type')) != nil
849
- type = etype2['type']
850
- end
851
- type = "list_of_#{type}" if etype['list'] == 'yes'
852
- end
884
+ def self.type_of_parameter(element)
885
+ type = element['type']
886
+ if type.nil?
887
+ etype = element.find_first('type')
888
+ if etype
889
+ type = etype['type']
890
+ if type.nil? and (etype2 = etype.find_first('type')) != nil
891
+ type = etype2['type']
853
892
  end
854
- raise "Parameter #{element} has no type." if type.nil?
855
- return type
893
+ type = "list_of_#{type}" if etype['list'] == 'yes'
894
+ end
856
895
  end
896
+ raise "Parameter #{element} has no type." if type.nil?
897
+ return type
898
+ end
857
899
 
858
- def self.escape_string(string)
859
- string.gsub(/[\$\=\s\-\.\/]/, '_').gsub(/&/, 'and')
860
- end
900
+ def self.escape_string(string)
901
+ string.gsub(/[\$\=\s\-\.\/]/, '_').gsub(/&/, 'and')
902
+ end
861
903
 
862
- def self.rubyfy_constant_string(string, upcase=false)
863
- string = string.gsub(/[^\w\s]/, '')
864
- first = string[0]
865
- if (?a..?z).include?(first)
866
- string[0] = first.chr.upcase
867
- elsif !(?A..?Z).include?(first)
868
- string.insert(0, 'C')
869
- end
870
- escape_string(upcase ? string.upcase : string.gsub(/\s(.)/) { |s| s[1].chr.upcase })
904
+ def self.rubyfy_constant_string(string, upcase=false)
905
+ string = string.gsub(/[^\w\s]/, '')
906
+ first = string[0]
907
+ if (?a..?z).include?(first)
908
+ string[0] = first.chr.upcase
909
+ elsif !(?A..?Z).include?(first)
910
+ string.insert(0, 'C')
871
911
  end
912
+ escape_string(upcase ? string.upcase : string.gsub(/\s(.)/) { |s| s[1].chr.upcase })
913
+ end
872
914
 
873
- RUBY_RESERVED_KEYWORDS = ['for', 'in', 'class']
874
- def self.rubyfy_string(string, handle_ruby_reserved_keywords=false)
875
- # Prefix with '_' parameter names to avoid possible collisions with reserved Ruby keywords (for, etc...).
876
- if handle_ruby_reserved_keywords and RUBY_RESERVED_KEYWORDS.include?(string)
877
- '_' + string
878
- else
879
- escape_string(string).downcase
880
- end
915
+ RUBY_RESERVED_KEYWORDS = ['for', 'in', 'class']
916
+ def self.rubyfy_string(string, handle_ruby_reserved_keywords=false)
917
+ # Prefix with '_' parameter names to avoid possible collisions with reserved Ruby keywords (for, etc...).
918
+ if handle_ruby_reserved_keywords and RUBY_RESERVED_KEYWORDS.include?(string)
919
+ '_' + string
920
+ else
921
+ escape_string(string).downcase
881
922
  end
882
-
883
- def self.rubyfy_method(string, klass, return_type=nil, setter=false)
884
- base = rubyfy_string(string)
885
- s, i = base.dup, 1
886
- loop do
887
- if setter
888
- # Suffix setters with '='.
889
- s << '='
890
- elsif return_type == 'boolean'
891
- # Suffix predicates with '?'.
892
- s << '?'
893
- end
894
- break unless klass.method_defined?(s)
895
- # Suffix with an integer if the class already has a method with such a name.
896
- i += 1
897
- s = base + i.to_s
898
- end
899
- return s
923
+ end
924
+
925
+ def self.rubyfy_method(string, klass, return_type=nil, setter=false)
926
+ base = rubyfy_string(string)
927
+ s, i = base.dup, 1
928
+ loop do
929
+ if setter
930
+ # Suffix setters with '='.
931
+ s << '='
932
+ elsif return_type == 'boolean'
933
+ # Suffix predicates with '?'.
934
+ s << '?'
935
+ end
936
+ break unless klass.method_defined?(s)
937
+ # Suffix with an integer if the class already has a method with such a name.
938
+ i += 1
939
+ s = base + i.to_s
900
940
  end
941
+ return s
942
+ end
901
943
 
902
- def self.englishify_sentence(string)
903
- return '' if string.nil?
904
- string[0] = string[0].chr.upcase
905
- string.strip!
906
- last = string[-1].chr
907
- string << '.' if last != '.' and last != '?' and last != '!'
908
- return string
909
- end
944
+ def self.englishify_sentence(string)
945
+ return '' if string.nil? or string.empty?
946
+ string[0] = string[0].chr.upcase
947
+ string.strip!
948
+ last = string[-1].chr
949
+ string << '.' if last != '.' and last != '?' and last != '!'
950
+ return string
951
+ end
910
952
  end
911
953
 
912
954
  # String, for unicode stuff force utf8 type if specified.
@@ -918,11 +960,12 @@ OSA.add_conversion_to_osa('Unicode text') { |value| [OSA.utf8_strings ? 'utf8' :
918
960
  # Signed/unsigned integer.
919
961
  OSA.add_conversion_to_ruby('shor', 'long') { |value| value.unpack('l').first }
920
962
  OSA.add_conversion_to_ruby('comp') { |value| value.unpack('q').first }
921
- OSA.add_conversion_to_ruby('magn', 'doub') { |value| value.unpack('d').first }
922
963
  OSA.add_conversion_to_osa('integer', 'double integer') { |value| ['magn', [value].pack('l')] }
923
964
 
924
965
  # Float
925
966
  OSA.add_conversion_to_ruby('sing') { |value| value.unpack('f').first }
967
+ OSA.add_conversion_to_ruby('magn', 'doub') { |value| value.unpack('d').first }
968
+ OSA.add_conversion_to_osa('double') { |value| ['doub', [value].pack('d')] }
926
969
 
927
970
  # Boolean.
928
971
  OSA.add_conversion_to_ruby('bool') { |value| value.unpack('c').first != 0 }
@@ -936,6 +979,15 @@ OSA.add_conversion_to_ruby('ldt ') { |value|
936
979
  }
937
980
 
938
981
  # Array.
982
+ OSA.add_conversion_to_osa('list') do |value|
983
+ # The `list_of_XXX' types are not handled here.
984
+ if value.is_a?(Array)
985
+ elements = value.map { |x| OSA::Element.from_rbobj(nil, x, nil) }
986
+ OSA::ElementList.__new__(elements)
987
+ else
988
+ value
989
+ end
990
+ end
939
991
  OSA.add_conversion_to_ruby('list') { |value, type, object|
940
992
  object.is_a?(OSA::ElementList) ? object.to_a.map { |x| x.to_rbobj } : object
941
993
  }
@@ -950,11 +1002,11 @@ end
950
1002
  # Hash.
951
1003
  OSA.add_conversion_to_ruby('reco') { |value, type, object| object.is_a?(OSA::ElementRecord) ? object.to_hash : value }
952
1004
  OSA.add_conversion_to_osa('record') do |value|
953
- if value.is_a?(Hash)
954
- OSA::ElementRecord.from_hash(value)
955
- else
956
- value
957
- end
1005
+ if value.is_a?(Hash)
1006
+ OSA::ElementRecord.from_hash(value)
1007
+ else
1008
+ value
1009
+ end
958
1010
  end
959
1011
 
960
1012
  # Enumerator.
@@ -965,7 +1017,7 @@ OSA.add_conversion_to_osa('type class', 'type') { |value| value.is_a?(Class) and
965
1017
  OSA.add_conversion_to_ruby('type') do |value, type, object|
966
1018
  if value == 'msng'
967
1019
  # Missing values.
968
- nil
1020
+ value
969
1021
  else
970
1022
  hash = object.instance_variable_get(:@app).instance_variable_get(:@classes)
971
1023
  hash[value] or value
@@ -984,4 +1036,11 @@ OSA.add_conversion_to_ruby('TIFF') { |value, type, object| value }
984
1036
  OSA.add_conversion_to_osa('Image') { |value| ['imaA', value.to_s] }
985
1037
  OSA.add_conversion_to_osa('TIFF picture') { |value| ['TIFF', value.to_s] }
986
1038
 
1039
+ # RGB color.
1040
+ OSA.add_conversion_to_ruby('cRGB') { |value| value.unpack('S3') }
1041
+ OSA.add_conversion_to_osa('color') do |values|
1042
+ ary = values.map { |i| OSA::Element.__new__('long', [i].pack('l')) }
1043
+ OSA::ElementList.__new__(ary)
1044
+ end
1045
+
987
1046
  require 'rbosa_properties'