rb-scpt 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGES +497 -0
  3. data/doc/aem-manual/01_introduction.html +60 -0
  4. data/doc/aem-manual/02_apioverview.html +107 -0
  5. data/doc/aem-manual/03_packingandunpackingdata.html +135 -0
  6. data/doc/aem-manual/04_references.html +409 -0
  7. data/doc/aem-manual/05_targetingapplications.html +164 -0
  8. data/doc/aem-manual/06_buildingandsendingevents.html +229 -0
  9. data/doc/aem-manual/07_findapp.html +63 -0
  10. data/doc/aem-manual/08_examples.html +94 -0
  11. data/doc/aem-manual/aemreferenceinheritance.gif +0 -0
  12. data/doc/aem-manual/index.html +56 -0
  13. data/doc/appscript-manual/01_introduction.html +94 -0
  14. data/doc/appscript-manual/02_aboutappscripting.html +247 -0
  15. data/doc/appscript-manual/03_quicktutorial.html +167 -0
  16. data/doc/appscript-manual/04_gettinghelp.html +188 -0
  17. data/doc/appscript-manual/05_keywordconversion.html +106 -0
  18. data/doc/appscript-manual/06_classesandenums.html +192 -0
  19. data/doc/appscript-manual/07_applicationobjects.html +211 -0
  20. data/doc/appscript-manual/08_realvsgenericreferences.html +96 -0
  21. data/doc/appscript-manual/09_referenceforms.html +241 -0
  22. data/doc/appscript-manual/10_referenceexamples.html +154 -0
  23. data/doc/appscript-manual/11_applicationcommands.html +245 -0
  24. data/doc/appscript-manual/12_commandexamples.html +138 -0
  25. data/doc/appscript-manual/13_performanceissues.html +142 -0
  26. data/doc/appscript-manual/14_notes.html +80 -0
  27. data/doc/appscript-manual/application_architecture.gif +0 -0
  28. data/doc/appscript-manual/application_architecture2.gif +0 -0
  29. data/doc/appscript-manual/finder_to_textedit_event.gif +0 -0
  30. data/doc/appscript-manual/index.html +62 -0
  31. data/doc/appscript-manual/relationships_example.gif +0 -0
  32. data/doc/appscript-manual/ruby_to_itunes_event.gif +0 -0
  33. data/doc/full.css +106 -0
  34. data/doc/index.html +45 -0
  35. data/doc/mactypes-manual/01_introduction.html +54 -0
  36. data/doc/mactypes-manual/02_aliasclass.html +124 -0
  37. data/doc/mactypes-manual/03_fileurlclass.html +126 -0
  38. data/doc/mactypes-manual/04_unitsclass.html +100 -0
  39. data/doc/mactypes-manual/index.html +53 -0
  40. data/doc/osax-manual/01_introduction.html +67 -0
  41. data/doc/osax-manual/02_interface.html +147 -0
  42. data/doc/osax-manual/03_examples.html +73 -0
  43. data/doc/osax-manual/04_notes.html +61 -0
  44. data/doc/osax-manual/index.html +53 -0
  45. data/doc/rb-appscript-logo.png +0 -0
  46. data/extconf.rb +65 -0
  47. data/rb-scpt.gemspec +14 -0
  48. data/sample/AB_export_vcard.rb +31 -0
  49. data/sample/AB_list_people_with_emails.rb +13 -0
  50. data/sample/Add_iCal_event.rb +21 -0
  51. data/sample/Create_daily_iCal_todos.rb +75 -0
  52. data/sample/Export_Address_Book_phone_numbers.rb +59 -0
  53. data/sample/Hello_world.rb +21 -0
  54. data/sample/List_iTunes_playlist_names.rb +11 -0
  55. data/sample/Make_Mail_message.rb +33 -0
  56. data/sample/Open_file_in_TextEdit.rb +13 -0
  57. data/sample/Organize_Mail_messages.rb +61 -0
  58. data/sample/Print_folder_tree.rb +16 -0
  59. data/sample/Select_all_HTML_files.rb +14 -0
  60. data/sample/Set_iChat_status.rb +24 -0
  61. data/sample/Simple_Finder_GUI_Scripting.rb +18 -0
  62. data/sample/Stagger_Finder_windows.rb +25 -0
  63. data/sample/TextEdit_demo.rb +130 -0
  64. data/sample/iTunes_top40_to_html.rb +71 -0
  65. data/src/SendThreadSafe.c +380 -0
  66. data/src/SendThreadSafe.h +139 -0
  67. data/src/lib/_aem/aemreference.rb +1022 -0
  68. data/src/lib/_aem/codecs.rb +662 -0
  69. data/src/lib/_aem/connect.rb +205 -0
  70. data/src/lib/_aem/encodingsupport.rb +77 -0
  71. data/src/lib/_aem/findapp.rb +85 -0
  72. data/src/lib/_aem/mactypes.rb +251 -0
  73. data/src/lib/_aem/send.rb +279 -0
  74. data/src/lib/_aem/typewrappers.rb +59 -0
  75. data/src/lib/_appscript/defaultterminology.rb +277 -0
  76. data/src/lib/_appscript/referencerenderer.rb +245 -0
  77. data/src/lib/_appscript/reservedkeywords.rb +116 -0
  78. data/src/lib/_appscript/safeobject.rb +249 -0
  79. data/src/lib/_appscript/terminology.rb +471 -0
  80. data/src/lib/aem.rb +253 -0
  81. data/src/lib/appscript.rb +1075 -0
  82. data/src/lib/kae.rb +1489 -0
  83. data/src/lib/osax.rb +659 -0
  84. data/src/rbae.c +979 -0
  85. data/test/README +3 -0
  86. data/test/test_aemreference.rb +118 -0
  87. data/test/test_appscriptcommands.rb +152 -0
  88. data/test/test_appscriptreference.rb +106 -0
  89. data/test/test_codecs.rb +186 -0
  90. data/test/test_findapp.rb +26 -0
  91. data/test/test_mactypes.rb +79 -0
  92. data/test/test_osax.rb +54 -0
  93. data/test/testall.sh +10 -0
  94. metadata +145 -0
@@ -0,0 +1,662 @@
1
+ #
2
+ # rb-appscript
3
+ #
4
+ # codecs -- convert native Ruby objects to AEDescs, and vice-versa
5
+ #
6
+
7
+ require "date"
8
+
9
+ require "ae"
10
+ require "kae"
11
+ require "_aem/typewrappers"
12
+ require "_aem/aemreference"
13
+ require "_aem/mactypes"
14
+ require "_aem/encodingsupport"
15
+
16
+ # Note that AE strings (typeChar, typeUnicodeText, etc.) are unpacked as UTF8-encoded Ruby strings, and UTF8-encoded Ruby strings are packed as typeUnicodeText. Using UTF8 on the Ruby side avoids data loss; using typeUnicodeText on the AEM side provides compatibility with all [reasonably well designed] applications. To change this behaviour (e.g. to support legacy apps that demand typeChar and break on typeUnicodeText), subclass Codecs and override pack and/or unpack methods to provide alternative packing/unpacking of string values. Users can also pack data manually using AE::AEDesc.new(type, data).
17
+
18
+
19
+ ######################################################################
20
+ # UNIT TYPE CODECS
21
+ ######################################################################
22
+
23
+
24
+ class UnitTypeCodecs
25
+ # Provides pack and unpack methods for converting between MacTypes::Units instances
26
+ # and AE unit types. Each Codecs instance is allocated its own UnitTypeCodecs instance,
27
+ #
28
+
29
+ DefaultUnitTypes = [
30
+ [:centimeters, KAE::TypeCentimeters],
31
+ [:meters, KAE::TypeMeters],
32
+ [:kilometers, KAE::TypeKilometers],
33
+ [:inches, KAE::TypeInches],
34
+ [:feet, KAE::TypeFeet],
35
+ [:yards, KAE::TypeYards],
36
+ [:miles, KAE::TypeMiles],
37
+
38
+ [:square_meters, KAE::TypeSquareMeters],
39
+ [:square_kilometers, KAE::TypeSquareKilometers],
40
+ [:square_feet, KAE::TypeSquareFeet],
41
+ [:square_yards, KAE::TypeSquareYards],
42
+ [:square_miles, KAE::TypeSquareMiles],
43
+
44
+ [:cubic_centimeters, KAE::TypeCubicCentimeter],
45
+ [:cubic_meters, KAE::TypeCubicMeters],
46
+ [:cubic_inches, KAE::TypeCubicInches],
47
+ [:cubic_feet, KAE::TypeCubicFeet],
48
+ [:cubic_yards, KAE::TypeCubicYards],
49
+
50
+ [:liters, KAE::TypeLiters],
51
+ [:quarts, KAE::TypeQuarts],
52
+ [:gallons, KAE::TypeGallons],
53
+
54
+ [:grams, KAE::TypeGrams],
55
+ [:kilograms, KAE::TypeKilograms],
56
+ [:ounces, KAE::TypeOunces],
57
+ [:pounds, KAE::TypePounds],
58
+
59
+ [:degrees_Celsius, KAE::TypeDegreesC],
60
+ [:degrees_Fahrenheit, KAE::TypeDegreesF],
61
+ [:degrees_Kelvin, KAE::TypeDegreesK],
62
+ ]
63
+
64
+ DefaultPacker = proc { |units, code| AE::AEDesc.new(code, [units.value].pack('d')) }
65
+ DefaultUnpacker = proc { |desc, name| MacTypes::Units.new(desc.data.unpack('d')[0], name) }
66
+
67
+ def initialize
68
+ @type_by_name = {}
69
+ @type_by_code = {}
70
+ add_types(DefaultUnitTypes)
71
+ end
72
+
73
+ def add_types(type_defs)
74
+ # type_defs is a list of lists, where each sublist is of form:
75
+ # [typename, typecode, packproc, unpackproc]
76
+ # or:
77
+ # [typename, typecode]
78
+ # If optional packproc and unpackproc are omitted, default pack/unpack procs
79
+ # are used instead; these pack/unpack AEDesc data as a double precision float.
80
+ type_defs.each do |name, code, packer, unpacker|
81
+ @type_by_name[name] = [code, (packer or DefaultPacker)]
82
+ @type_by_code[code] = [name, (unpacker or DefaultUnpacker)]
83
+ end
84
+ end
85
+
86
+ def pack(val)
87
+ if val.is_a?(MacTypes::Units)
88
+ code, packer = @type_by_name.fetch(val.type) { |v| raise IndexError, "Unknown unit type: #{v.inspect}" }
89
+ return [true, packer.call(val, code)]
90
+ else
91
+ return [false, val]
92
+ end
93
+ end
94
+
95
+ def unpack(desc)
96
+ name, unpacker = @type_by_code.fetch(desc.type) { |d| return [false, d] }
97
+ return [true, unpacker.call(desc, name)]
98
+ end
99
+ end
100
+
101
+
102
+ ######################################################################
103
+ # CODECS
104
+ ######################################################################
105
+ # Endianness support
106
+
107
+ module BigEndianConverters
108
+
109
+ def four_char_code(code)
110
+ return code
111
+ end
112
+
113
+ end
114
+
115
+
116
+ module SmallEndianConverters
117
+
118
+ def four_char_code(code)
119
+ return code.reverse
120
+ end
121
+
122
+ end
123
+
124
+
125
+ ######################################################################
126
+
127
+ module DisableObjectSpecifierCaching
128
+ def unpack_object_specifier(desc)
129
+ return fully_unpack_object_specifier(desc)
130
+ end
131
+ end
132
+
133
+ #######
134
+
135
+ class Codecs
136
+ # Provides pack and unpack methods for converting data between Ruby and AE types.
137
+ #
138
+ # May be subclassed to extend/alter its behaviour (e.g. the appscript layer does this).
139
+ # Conversions that are most likely to be modified (e.g. for packing and and unpacking
140
+ # references, records, types and enums) are exposed as overrideable hook methods.
141
+
142
+ extend([1].pack('s') == "\001\000" ? SmallEndianConverters : BigEndianConverters)
143
+
144
+ def initialize
145
+ @unit_type_codecs = UnitTypeCodecs.new
146
+ # Note: while typeUnicodeText is deprecated (see AEDataModel.h), it's still the
147
+ # most commonly used Unicode type so is used here for compatibility's sake.
148
+ # typeUTF8Text was initially tried, but existing applications had problems with it; i.e.
149
+ # some apps make unsafe assumptions on what to expect based on AS's behaviour.
150
+ # Once AppleScript is using typeUTF8Text/typeUTF16ExternalRepresentation
151
+ # and existing applications don't choke, this code can be similarly upgraded.
152
+ @pack_text_as_type = KAE::TypeUnicodeText
153
+ # on Ruby 1.9+, set String encoding to UTF-8
154
+ @encoding_support = AEMEncodingSupport.encoding_support
155
+ @unpack_dates_as_datetime = false
156
+ end
157
+
158
+ ######################################################################
159
+ # Compatibility options
160
+
161
+ def add_unit_types(type_defs)
162
+ # register custom unit type definitions with this Codecs instance
163
+ # e.g. Adobe apps define additional unit types (ciceros, pixels, etc.)
164
+ @unit_type_codecs.add_types(type_defs)
165
+ end
166
+
167
+ def dont_cache_unpacked_specifiers
168
+ # When unpacking object specifiers, unlike AppleScript, appscript caches
169
+ # the original AEDesc for efficiency, allowing the resulting reference to
170
+ # be re-packed much more quickly. Occasionally this causes compatibility
171
+ # problems with applications that returned subtly malformed specifiers.
172
+ # To force a Codecs object to fully unpack and repack object specifiers,
173
+ # call its dont_cache_unpacked_specifiers method.
174
+ extend(DisableObjectSpecifierCaching)
175
+ end
176
+
177
+ def pack_strings_as_type(code)
178
+ # Some older (pre-OS X) applications may require text to be passed as
179
+ # typeChar or typeIntlText rather than the usual typeUnicodeText. To force
180
+ # an AEM::Codecs object to pack strings as one of these older types, call
181
+ # its pack_strings_as_type method, specifying the type you want used instead.
182
+ if not(code.is_a?(String) and code.length == 4)
183
+ raise ArgumentError, "Code must be a four-character string: #{code.inspect}"
184
+ end
185
+ @pack_text_as_type = code
186
+ end
187
+
188
+ def use_ascii_8bit
189
+ # By default on Ruby 1.9+, Codecs#pack creates String instances with UTF-8
190
+ # encoding and #unpack ensures all strings are UTF-8 encoded before packing
191
+ # them into AEDescs of typeUTF8Text. To force the old-style behaviour where
192
+ # strings are treated as byte strings containing UTF-8 data, call:
193
+ #
194
+ # some_application.AS_app_data.use_ascii_8bit_strings
195
+ #
196
+ # This will cause Strings to use the binary ASCII-8BIT encoding; as in Ruby 1.8,
197
+ # the user is responsible for ensuring that strings contain UTF-8 data.
198
+ @encoding_support = AEMEncodingSupport::DisableStringEncodings
199
+ end
200
+
201
+ def use_datetime
202
+ # By default dates are unpacked as Time instances, which have limited range.
203
+ # Call this method to unpack dates as DateTime instances instead.
204
+ @unpack_dates_as_datetime = true
205
+ end
206
+
207
+ ######################################################################
208
+ # Subclasses could override these to provide their own reference roots if needed
209
+
210
+ App = AEMReference::App
211
+ Con = AEMReference::Con
212
+ Its = AEMReference::Its
213
+
214
+ ######################################################################
215
+ # Pack
216
+
217
+ SInt32Bounds = (-2**31)..(2**31-1)
218
+ SInt64Bounds = (-2**63)..(2**63-1)
219
+ UInt64Bounds = (2**63)..(2**64-1)
220
+
221
+ NullDesc = AE::AEDesc.new(KAE::TypeNull, '')
222
+ TrueDesc = AE::AEDesc.new(KAE::TypeTrue, '')
223
+ FalseDesc = AE::AEDesc.new(KAE::TypeFalse, '')
224
+
225
+ ##
226
+
227
+ def pack_unknown(val) # clients may override this to provide additional packers
228
+ raise TypeError, "Can't pack data into an AEDesc (unsupported type): #{val.inspect}"
229
+ end
230
+
231
+
232
+ def pack(val) # clients may override this to replace existing packers
233
+ case val
234
+ when AEMReference::Query then val.AEM_pack_self(self)
235
+
236
+ when Fixnum, Bignum then
237
+ if SInt32Bounds === val
238
+ AE::AEDesc.new(KAE::TypeSInt32, [val].pack('l'))
239
+ elsif SInt64Bounds === val
240
+ AE::AEDesc.new(KAE::TypeSInt64, [val].pack('q'))
241
+ elsif UInt64Bounds === val
242
+ pack_uint64(val)
243
+ else
244
+ AE::AEDesc.new(KAE::TypeFloat, [val.to_f].pack('d'))
245
+ end
246
+
247
+ when String then
248
+ @encoding_support.pack_string(val, @pack_text_as_type)
249
+
250
+ when TrueClass then TrueDesc
251
+ when FalseClass then FalseDesc
252
+
253
+ when Float then AE::AEDesc.new(KAE::TypeFloat, [val].pack('d'))
254
+
255
+ when Time
256
+ AE::AEDesc.new(KAE::TypeLongDateTime,
257
+ [AE.convert_unix_seconds_to_long_date_time(val.to_i)].pack('q'))
258
+
259
+ when DateTime, Date then
260
+ AE::AEDesc.new(KAE::TypeLongDateTime,
261
+ [AE.convert_string_to_long_date_time(val.strftime('%F %T'))].pack('q'))
262
+
263
+ when Array then pack_array(val)
264
+ when Hash then pack_hash(val)
265
+
266
+ when MacTypes::FileBase then val.desc
267
+
268
+ when TypeWrappers::AEType then
269
+ AE::AEDesc.new(KAE::TypeType, Codecs.four_char_code(val.code))
270
+ when TypeWrappers::AEEnum then
271
+ AE::AEDesc.new(KAE::TypeEnumerated, Codecs.four_char_code(val.code))
272
+ when TypeWrappers::AEProp then
273
+ AE::AEDesc.new(KAE::TypeProperty, Codecs.four_char_code(val.code))
274
+ when TypeWrappers::AEKey then
275
+ AE::AEDesc.new(KAE::TypeKeyword, Codecs.four_char_code(val.code))
276
+
277
+ when AE::AEDesc then val
278
+
279
+ when NilClass then NullDesc
280
+ else
281
+ did_pack, desc = @unit_type_codecs.pack(val)
282
+ if did_pack
283
+ desc
284
+ else
285
+ pack_unknown(val)
286
+ end
287
+ end
288
+ end
289
+
290
+ #######
291
+
292
+ def pack_uint64(val)
293
+ # On 10.5+, clients could override this method to do a non-lossy conversion,
294
+ # (assuming target app knows how to handle new UInt64 type):
295
+ #
296
+ # def pack_uint64(val)
297
+ # AE::AEDesc.new(KAE::TypeUInt64, [val.to_f].pack('Q'))
298
+ # end
299
+ AE::AEDesc.new(KAE::TypeFloat, [val.to_f].pack('d')) # pack as 64-bit float for compatibility (lossy conversion)
300
+ end
301
+
302
+ def pack_array(val)
303
+ lst = AE::AEDesc.new_list(false)
304
+ val.each do |item|
305
+ lst.put_item(0, pack(item))
306
+ end
307
+ return lst
308
+ end
309
+
310
+ def pack_hash(val)
311
+ record = AE::AEDesc.new_list(true)
312
+ usrf = nil
313
+ val.each do | key, value |
314
+ if key.is_a?(TypeWrappers::AETypeBase)
315
+ if key.code == KAE::PClass # AS packs records that contain a 'class' property by coercing the packed record to that type at the end
316
+ begin
317
+ record = record.coerce(value.code)
318
+ rescue
319
+ record.put_param(key.code, pack(value))
320
+ end
321
+ else
322
+ record.put_param(key.code, pack(value))
323
+ end
324
+ else
325
+ if usrf == nil
326
+ usrf = AE::AEDesc.new_list(false)
327
+ end
328
+ usrf.put_item(0, pack(key))
329
+ usrf.put_item(0, pack(value))
330
+ end
331
+ end
332
+ if usrf
333
+ record.put_param(KAE::KeyASUserRecordFields, usrf)
334
+ end
335
+ return record
336
+ end
337
+
338
+ ######################################################################
339
+ # Unpack
340
+
341
+ def unpack_unknown(desc) # clients may override this to provide additional unpackers
342
+ if desc.is_record? # if it's a record-like structure with an unknown/unsupported type then unpack it as a hash, including the original type info as a 'class' property
343
+ rec = desc.coerce(KAE::TypeAERecord)
344
+ rec.put_param(KAE::PClass, pack(TypeWrappers::AEType.new(desc.type)))
345
+ unpack(rec)
346
+ else # else return unchanged
347
+ desc
348
+ end
349
+ end
350
+
351
+
352
+ def unpack(desc) # clients may override this to replace existing unpackers
353
+ return case desc.type
354
+
355
+ when KAE::TypeObjectSpecifier then unpack_object_specifier(desc)
356
+
357
+ when KAE::TypeSInt32 then desc.data.unpack('l')[0]
358
+ when KAE::TypeIEEE64BitFloatingPoint then desc.data.unpack('d')[0]
359
+
360
+ when
361
+ KAE::TypeUnicodeText,
362
+ KAE::TypeChar,
363
+ KAE::TypeIntlText,
364
+ KAE::TypeUTF16ExternalRepresentation,
365
+ KAE::TypeStyledText
366
+ @encoding_support.unpack_string(desc)
367
+
368
+ when KAE::TypeFalse then false
369
+ when KAE::TypeTrue then true
370
+
371
+ when KAE::TypeLongDateTime then
372
+ t = desc.data.unpack('q')[0]
373
+ if @unpack_dates_as_datetime
374
+ DateTime.strptime(AE.convert_long_date_time_to_string(t), '%F %T')
375
+ else
376
+ Time.at(AE.convert_long_date_time_to_unix_seconds(t))
377
+ end
378
+
379
+ when KAE::TypeAEList then unpack_aelist(desc)
380
+ when KAE::TypeAERecord then unpack_aerecord(desc)
381
+
382
+ when KAE::TypeAlias then MacTypes::Alias.desc(desc)
383
+ when
384
+ KAE::TypeFileURL,
385
+ KAE::TypeFSRef,
386
+ KAE::TypeFSS
387
+ MacTypes::FileURL.desc(desc)
388
+
389
+ when KAE::TypeType then unpack_type(desc)
390
+ when KAE::TypeEnumerated then unpack_enumerated(desc)
391
+ when KAE::TypeProperty then unpack_property(desc)
392
+ when KAE::TypeKeyword then unpack_keyword(desc)
393
+
394
+ when KAE::TypeSInt16 then desc.data.unpack('s')[0]
395
+ when KAE::TypeUInt32 then desc.data.unpack('L')[0]
396
+ when KAE::TypeSInt64 then desc.data.unpack('q')[0]
397
+
398
+ when KAE::TypeNull then nil
399
+
400
+ when KAE::TypeUTF8Text then desc.data
401
+
402
+ when KAE::TypeInsertionLoc then unpack_insertion_loc(desc)
403
+ when KAE::TypeCurrentContainer then unpack_current_container(desc)
404
+ when KAE::TypeObjectBeingExamined then unpack_object_being_examined(desc)
405
+ when KAE::TypeCompDescriptor then unpack_comp_descriptor(desc)
406
+ when KAE::TypeLogicalDescriptor then unpack_logical_descriptor(desc)
407
+
408
+ when KAE::TypeIEEE32BitFloatingPoint then desc.data.unpack('f')[0]
409
+ when KAE::Type128BitFloatingPoint then
410
+ desc.coerce(KAE::TypeIEEE64BitFloatingPoint).data.unpack('d')[0]
411
+
412
+ when KAE::TypeQDPoint then desc.data.unpack('ss').reverse
413
+ when KAE::TypeQDRectangle then
414
+ x1, y1, x2, y2 = desc.data.unpack('ssss')
415
+ [y1, x1, y2, x2]
416
+ when KAE::TypeRGBColor then desc.data.unpack('SSS')
417
+
418
+ when KAE::TypeVersion
419
+ begin
420
+ unpack(desc.coerce(KAE::TypeUnicodeText)) # supported in 10.4+
421
+ rescue
422
+ vers, lo = desc.data.unpack('CC')
423
+ subvers, patch = lo.divmod(16)
424
+ "#{vers}.#{subvers}.#{patch}"
425
+ end
426
+ when KAE::TypeBoolean then desc.data[0,1] != "\000"
427
+
428
+ when KAE::TypeUInt16 then desc.data.unpack('S')[0] # 10.5+
429
+ when KAE::TypeUInt64 then desc.data.unpack('Q')[0] # 10.5+
430
+ else
431
+ did_unpack, val = @unit_type_codecs.unpack(desc)
432
+ if did_unpack
433
+ val
434
+ else
435
+ unpack_unknown(desc)
436
+ end
437
+ end
438
+ end
439
+
440
+ #######
441
+
442
+ def unpack_aelist(desc)
443
+ lst = []
444
+ desc.length().times do |i|
445
+ lst.push(unpack(desc.get_item(i + 1, KAE::TypeWildCard)[1]))
446
+ end
447
+ return lst
448
+ end
449
+
450
+ def unpack_aerecord(desc)
451
+ dct = {}
452
+ desc.length().times do |i|
453
+ key, value = desc.get_item(i + 1, KAE::TypeWildCard)
454
+ if key == KAE::KeyASUserRecordFields
455
+ lst = unpack_aelist(value)
456
+ (lst.length / 2).times do |j|
457
+ dct[lst[j * 2]] = lst[j * 2 + 1]
458
+ end
459
+ else
460
+ dct[TypeWrappers::AEType.new(key)] = unpack(value)
461
+ end
462
+ end
463
+ return dct
464
+ end
465
+
466
+ #######
467
+
468
+ def unpack_type(desc)
469
+ return TypeWrappers::AEType.new(Codecs.four_char_code(desc.data))
470
+ end
471
+
472
+ def unpack_enumerated(desc)
473
+ return TypeWrappers::AEEnum.new(Codecs.four_char_code(desc.data))
474
+ end
475
+
476
+ def unpack_property(desc)
477
+ return TypeWrappers::AEProp.new(Codecs.four_char_code(desc.data))
478
+ end
479
+
480
+ def unpack_keyword(desc)
481
+ return TypeWrappers::AEKey.new(Codecs.four_char_code(desc.data))
482
+ end
483
+
484
+ #######
485
+ # Lookup tables for converting enumerator, ordinal codes to aem reference method names.
486
+ # Used by unpack_object_specifier, fully_unpack_object_specifier to construct aem references.
487
+
488
+ AbsoluteOrdinals = {
489
+ Codecs.four_char_code(KAE::KAEFirst) => 'first',
490
+ Codecs.four_char_code(KAE::KAELast) => 'last',
491
+ Codecs.four_char_code(KAE::KAEMiddle) => 'middle',
492
+ Codecs.four_char_code(KAE::KAEAny) => 'any',
493
+ }
494
+
495
+ AllAbsoluteOrdinal = Codecs.four_char_code(KAE::KAEAll)
496
+
497
+ RelativePositionEnums = {
498
+ Codecs.four_char_code(KAE::KAEPrevious) => 'previous',
499
+ Codecs.four_char_code(KAE::KAENext) => 'next',
500
+ }
501
+
502
+ InsertionLocEnums = {
503
+ Codecs.four_char_code(KAE::KAEBefore) => 'before',
504
+ Codecs.four_char_code(KAE::KAEAfter) => 'after',
505
+ Codecs.four_char_code(KAE::KAEBeginning) => 'beginning',
506
+ Codecs.four_char_code(KAE::KAEEnd) => 'end',
507
+ }
508
+
509
+ ComparisonEnums = {
510
+ Codecs.four_char_code(KAE::KAEGreaterThan) => 'gt',
511
+ Codecs.four_char_code(KAE::KAEGreaterThanEquals) => 'ge',
512
+ Codecs.four_char_code(KAE::KAEEquals) => 'eq',
513
+ Codecs.four_char_code(KAE::KAELessThan) => 'lt',
514
+ Codecs.four_char_code(KAE::KAELessThanEquals) => 'le',
515
+ Codecs.four_char_code(KAE::KAEBeginsWith) => 'begins_with',
516
+ Codecs.four_char_code(KAE::KAEEndsWith) => 'ends_with',
517
+ Codecs.four_char_code(KAE::KAEContains) => 'contains',
518
+ }
519
+
520
+ LogicalEnums = {
521
+ Codecs.four_char_code(KAE::KAEAND) => 'and',
522
+ Codecs.four_char_code(KAE::KAEOR) => 'or',
523
+ Codecs.four_char_code(KAE::KAENOT) => 'not',
524
+ }
525
+
526
+ #######
527
+
528
+ def fully_unpack_object_specifier(desc)
529
+ # Recursively unpack an object specifier and all of its container descs.
530
+ # (Note: Codecs#unpack_object_specifier and AEMReference::DeferredSpecifier#_real_ref will call this when needed.)
531
+ case desc.type
532
+ when KAE::TypeObjectSpecifier
533
+ want = Codecs.four_char_code(desc.get_param(KAE::KeyAEDesiredClass, KAE::TypeType).data)
534
+ key_form = Codecs.four_char_code(desc.get_param(KAE::KeyAEKeyForm, KAE::TypeEnumeration).data)
535
+ key = desc.get_param(KAE::KeyAEKeyData, KAE::TypeWildCard)
536
+ ref = fully_unpack_object_specifier(desc.get_param(KAE::KeyAEContainer, KAE::TypeWildCard))
537
+ case key_form
538
+ when KAE::FormPropertyID
539
+ return ref.property(Codecs.four_char_code(key.data))
540
+ when KAE::FormUserPropertyID
541
+ return ref.userproperty(unpack(key))
542
+ when KAE::FormRelativePosition
543
+ return ref.send(RelativePositionEnums[key.data], want)
544
+ else
545
+ ref = ref.elements(want)
546
+ case key_form
547
+ when KAE::FormAbsolutePosition
548
+ if key.type == KAE::TypeAbsoluteOrdinal
549
+ if key.data == AllAbsoluteOrdinal
550
+ return ref
551
+ else
552
+ return ref.send(AbsoluteOrdinals[key.data])
553
+ end
554
+ else
555
+ return ref.by_index(unpack(key))
556
+ end
557
+ when KAE::FormName
558
+ return ref.by_name(unpack(key))
559
+ when KAE::FormUniqueID
560
+ return ref.by_id(unpack(key))
561
+ when KAE::FormRange
562
+ return ref.by_range(
563
+ unpack(key.get_param(KAE::KeyAERangeStart, KAE::TypeWildCard)),
564
+ unpack(key.get_param(KAE::KeyAERangeStop, KAE::TypeWildCard)))
565
+ when KAE::FormTest
566
+ return ref.by_filter(unpack(key))
567
+ end
568
+ end
569
+ raise TypeError
570
+ when KAE::TypeNull then return self.class::App
571
+ when KAE::TypeCurrentContainer then return self.class::Con
572
+ when KAE::TypeObjectBeingExamined then return self.class::Its
573
+ else
574
+ return unpack(desc)
575
+ end
576
+ end
577
+
578
+ ##
579
+
580
+ def unpack_object_specifier(desc)
581
+ # Shallow-unpack an object specifier, retaining the container AEDesc as-is.
582
+ # (i.e. Defers full unpacking of [most] object specifiers for efficiency.)
583
+ key_form = Codecs.four_char_code(desc.get_param(KAE::KeyAEKeyForm, KAE::TypeEnumeration).data)
584
+ if [KAE::FormPropertyID, KAE::FormAbsolutePosition, KAE::FormName, KAE::FormUniqueID].include?(key_form)
585
+ want = Codecs.four_char_code(desc.get_param(KAE::KeyAEDesiredClass, KAE::TypeType).data)
586
+ key = desc.get_param(KAE::KeyAEKeyData, KAE::TypeWildCard)
587
+ container = AEMReference::DeferredSpecifier.new(desc.get_param(KAE::KeyAEContainer, KAE::TypeWildCard), self)
588
+ case key_form
589
+ when KAE::FormPropertyID
590
+ ref = AEMReference::Property.new(want, container, Codecs.four_char_code(key.data))
591
+ when KAE::FormAbsolutePosition
592
+ if key.type == KAE::TypeAbsoluteOrdinal
593
+ if key.data == AllAbsoluteOrdinal
594
+ ref = AEMReference::AllElements.new(want, container)
595
+ else
596
+ ref = fully_unpack_object_specifier(desc) # do a full unpack of rarely returned reference forms
597
+ end
598
+ else
599
+ ref = AEMReference::ElementByIndex.new(want, AEMReference::UnkeyedElements.new(want, container), unpack(key))
600
+ end
601
+ when KAE::FormName
602
+ ref = AEMReference::ElementByName.new(want, AEMReference::UnkeyedElements.new(want, container), unpack(key))
603
+ when KAE::FormUniqueID
604
+ ref = AEMReference::ElementByID.new(want, AEMReference::UnkeyedElements.new(want, container), unpack(key))
605
+ end
606
+ else
607
+ ref = fully_unpack_object_specifier(desc) # do a full unpack of more complex, rarely returned reference forms
608
+ end
609
+ ref.AEM_set_desc(desc) # retain existing AEDesc for efficiency
610
+ return ref
611
+ end
612
+
613
+
614
+ def unpack_insertion_loc(desc)
615
+ return unpack_object_specifier(desc.get_param(KAE::KeyAEObject, KAE::TypeWildCard)).send(InsertionLocEnums[desc.get_param(KAE::KeyAEPosition, KAE::TypeEnumeration).data])
616
+ end
617
+
618
+ ##
619
+
620
+ def unpack_current_container(desc)
621
+ return Con
622
+ end
623
+
624
+ def unpack_object_being_examined(desc)
625
+ return Its
626
+ end
627
+
628
+ ##
629
+
630
+ def unpack_contains_comp_descriptor(op1, op2)
631
+ # KAEContains is also used to construct 'is_in' tests, where test value is first operand and
632
+ # reference being tested is second operand, so need to make sure first operand is an its-based ref;
633
+ # if not, rearrange accordingly.
634
+ # Since type-checking is involved, this extra hook is provided so that appscript's AppData subclass can override this method to add its own type checking
635
+ if op1.is_a?(AEMReference::Query) and op1.AEM_root == AEMReference::Its
636
+ return op1.contains(op2)
637
+ else
638
+ return op2.is_in(op1)
639
+ end
640
+ end
641
+
642
+ def unpack_comp_descriptor(desc)
643
+ operator = ComparisonEnums[desc.get_param(KAE::KeyAECompOperator, KAE::TypeEnumeration).data]
644
+ op1 = unpack(desc.get_param(KAE::KeyAEObject1, KAE::TypeWildCard))
645
+ op2 = unpack(desc.get_param(KAE::KeyAEObject2, KAE::TypeWildCard))
646
+ if operator == 'contains'
647
+ return unpack_contains_comp_descriptor(op1, op2)
648
+ else
649
+ return op1.send(operator, op2)
650
+ end
651
+ end
652
+
653
+ def unpack_logical_descriptor(desc)
654
+ operator = LogicalEnums[desc.get_param(KAE::KeyAELogicalOperator, KAE::TypeEnumeration).data]
655
+ operands = unpack(desc.get_param(KAE::KeyAELogicalTerms, KAE::TypeAEList))
656
+ return operands[0].send(operator, *operands[1, operands.length])
657
+ end
658
+
659
+ end
660
+
661
+ DefaultCodecs = Codecs.new
662
+