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.
- data/AUTHORS +3 -0
- data/bin/rdoc-osa +137 -78
- data/sample/Photoshop/new_doc.rb +13 -0
- data/sample/Photoshop/new_doc_with_text.rb +34 -0
- data/sample/iTunes/name_that_tune.rb +97 -0
- data/sample/iTunes/tag_genre_lastfm.rb +16 -4
- data/src/lib/rbosa.rb +808 -749
- data/src/lib/rbosa_properties.rb +7 -2
- data/src/rbosa.c +11 -1
- metadata +5 -2
data/src/lib/rbosa.rb
CHANGED
@@ -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
|
-
|
39
|
+
require 'xml/libxml'
|
40
40
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
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
|
-
|
47
|
+
end
|
48
|
+
true
|
49
49
|
rescue LoadError
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
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
|
-
|
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
|
-
|
76
|
-
|
77
|
-
|
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
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
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
|
-
|
89
|
-
|
90
|
-
|
88
|
+
def self.enum_for_code(code)
|
89
|
+
instances[code]
|
90
|
+
end
|
91
91
|
|
92
|
-
|
93
|
-
|
94
|
-
|
92
|
+
def to_s
|
93
|
+
@name
|
94
|
+
end
|
95
95
|
|
96
|
-
|
97
|
-
|
98
|
-
|
96
|
+
def inspect
|
97
|
+
"<#{@const}>"
|
98
|
+
end
|
99
99
|
|
100
|
-
|
101
|
-
|
102
|
-
|
100
|
+
#######
|
101
|
+
private
|
102
|
+
#######
|
103
103
|
|
104
|
-
|
105
|
-
|
106
|
-
|
104
|
+
def self.instances
|
105
|
+
(@@instances rescue @@instances = {})
|
106
|
+
end
|
107
107
|
end
|
108
108
|
|
109
109
|
class OSA::Element
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
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
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
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
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
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
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
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
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
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
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
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
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
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
|
-
|
182
|
-
|
183
|
-
|
181
|
+
def empty?
|
182
|
+
length == 0
|
183
|
+
end
|
184
184
|
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
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
|
-
|
193
|
-
|
194
|
-
|
192
|
+
def first
|
193
|
+
self[0]
|
194
|
+
end
|
195
195
|
|
196
|
-
|
197
|
-
|
198
|
-
|
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
|
-
|
205
|
-
|
206
|
-
|
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
|
-
|
211
|
-
|
212
|
-
|
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
|
-
|
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
|
-
|
219
|
-
|
220
|
-
|
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
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
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
|
-
|
256
|
-
|
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
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
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
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
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
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
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
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
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
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
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
|
-
|
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 =
|
304
|
-
|
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
|
-
|
308
|
-
|
332
|
+
@conversions_to_ruby = {}
|
333
|
+
@conversions_to_osa = {}
|
309
334
|
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
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
|
-
|
319
|
-
|
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
|
-
|
323
|
-
|
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
|
-
|
327
|
-
|
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
|
-
|
331
|
-
|
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
|
-
|
335
|
-
|
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
|
-
|
344
|
-
|
345
|
-
|
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
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
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
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
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
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
455
|
+
def self.__app__(name, target, sdef)
|
456
|
+
@apps ||= {}
|
457
|
+
app = @apps[target]
|
458
|
+
return app if app
|
428
459
|
|
429
|
-
|
430
|
-
|
431
|
-
|
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
|
-
|
434
|
-
|
464
|
+
@apps[target] = __load_sdef__(sdef, target, app_module)
|
465
|
+
end
|
435
466
|
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
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
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
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
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
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
|
-
|
464
|
-
|
494
|
+
enum = OSA::Enumerator.new(enum_const, name, enum_code, enum_group_code)
|
495
|
+
enum_module.const_set(enum_name, enum)
|
465
496
|
|
466
|
-
|
467
|
-
|
497
|
+
documentation << DocItem.new(enum_name, englishify_sentence(element['description']))
|
498
|
+
end
|
468
499
|
|
469
|
-
|
470
|
-
|
500
|
+
app_module.const_set(enum_module_name, enum_module) unless app_module.const_defined?(enum_module_name)
|
501
|
+
end
|
471
502
|
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
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
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
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
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
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
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
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
|
-
|
610
|
-
|
611
|
-
|
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
|
-
|
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
|
-
|
619
|
-
|
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
|
-
|
637
|
-
|
638
|
-
|
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
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
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
|
-
|
666
|
-
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
|
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
|
-
|
674
|
-
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
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
|
-
|
689
|
-
|
690
|
-
|
691
|
-
|
692
|
-
|
693
|
-
|
694
|
-
|
695
|
-
|
696
|
-
|
697
|
-
|
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
|
-
|
700
|
-
|
701
|
-
|
702
|
-
|
703
|
-
|
704
|
-
|
705
|
-
|
706
|
-
|
707
|
-
|
708
|
-
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
|
718
|
-
|
719
|
-
|
720
|
-
|
721
|
-
|
722
|
-
|
723
|
-
|
724
|
-
|
725
|
-
|
726
|
-
|
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
|
-
|
747
|
-
|
748
|
-
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
|
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
|
-
|
755
|
-
|
756
|
-
|
757
|
-
|
758
|
-
|
759
|
-
|
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
|
-
|
763
|
-
|
764
|
-
|
765
|
-
|
766
|
-
|
767
|
-
|
768
|
-
|
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
|
-
|
771
|
-
|
772
|
-
|
773
|
-
|
774
|
-
|
775
|
-
|
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
|
-
|
780
|
-
|
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
|
-
|
784
|
-
|
785
|
-
|
786
|
-
|
787
|
-
|
788
|
-
|
789
|
-
|
790
|
-
|
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
|
-
|
793
|
-
|
794
|
-
|
795
|
-
|
796
|
-
|
797
|
-
|
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
|
-
|
800
|
-
|
801
|
-
|
802
|
-
|
803
|
-
|
804
|
-
|
805
|
-
|
806
|
-
|
807
|
-
|
808
|
-
|
809
|
-
|
810
|
-
|
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
|
-
|
855
|
+
klass.class_eval { include OSA::EventDispatcher } if real_name == 'application'
|
814
856
|
|
815
|
-
|
816
|
-
|
817
|
-
|
818
|
-
|
819
|
-
|
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
|
-
|
822
|
-
|
863
|
+
repository[key] = klass
|
864
|
+
end
|
823
865
|
|
824
|
-
|
825
|
-
|
826
|
-
|
827
|
-
|
828
|
-
|
829
|
-
|
830
|
-
|
831
|
-
|
832
|
-
|
833
|
-
|
834
|
-
|
835
|
-
|
836
|
-
|
837
|
-
|
838
|
-
|
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
|
-
|
843
|
-
|
844
|
-
|
845
|
-
|
846
|
-
|
847
|
-
|
848
|
-
|
849
|
-
|
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
|
-
|
855
|
-
|
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
|
-
|
859
|
-
|
860
|
-
|
900
|
+
def self.escape_string(string)
|
901
|
+
string.gsub(/[\$\=\s\-\.\/]/, '_').gsub(/&/, 'and')
|
902
|
+
end
|
861
903
|
|
862
|
-
|
863
|
-
|
864
|
-
|
865
|
-
|
866
|
-
|
867
|
-
|
868
|
-
|
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
|
-
|
874
|
-
|
875
|
-
|
876
|
-
|
877
|
-
|
878
|
-
|
879
|
-
|
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
|
-
|
884
|
-
|
885
|
-
|
886
|
-
|
887
|
-
|
888
|
-
|
889
|
-
|
890
|
-
|
891
|
-
|
892
|
-
|
893
|
-
|
894
|
-
|
895
|
-
|
896
|
-
|
897
|
-
|
898
|
-
|
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
|
-
|
903
|
-
|
904
|
-
|
905
|
-
|
906
|
-
|
907
|
-
|
908
|
-
|
909
|
-
|
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
|
-
|
954
|
-
|
955
|
-
|
956
|
-
|
957
|
-
|
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
|
-
|
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'
|