rb-scpt 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGES +497 -0
- data/doc/aem-manual/01_introduction.html +60 -0
- data/doc/aem-manual/02_apioverview.html +107 -0
- data/doc/aem-manual/03_packingandunpackingdata.html +135 -0
- data/doc/aem-manual/04_references.html +409 -0
- data/doc/aem-manual/05_targetingapplications.html +164 -0
- data/doc/aem-manual/06_buildingandsendingevents.html +229 -0
- data/doc/aem-manual/07_findapp.html +63 -0
- data/doc/aem-manual/08_examples.html +94 -0
- data/doc/aem-manual/aemreferenceinheritance.gif +0 -0
- data/doc/aem-manual/index.html +56 -0
- data/doc/appscript-manual/01_introduction.html +94 -0
- data/doc/appscript-manual/02_aboutappscripting.html +247 -0
- data/doc/appscript-manual/03_quicktutorial.html +167 -0
- data/doc/appscript-manual/04_gettinghelp.html +188 -0
- data/doc/appscript-manual/05_keywordconversion.html +106 -0
- data/doc/appscript-manual/06_classesandenums.html +192 -0
- data/doc/appscript-manual/07_applicationobjects.html +211 -0
- data/doc/appscript-manual/08_realvsgenericreferences.html +96 -0
- data/doc/appscript-manual/09_referenceforms.html +241 -0
- data/doc/appscript-manual/10_referenceexamples.html +154 -0
- data/doc/appscript-manual/11_applicationcommands.html +245 -0
- data/doc/appscript-manual/12_commandexamples.html +138 -0
- data/doc/appscript-manual/13_performanceissues.html +142 -0
- data/doc/appscript-manual/14_notes.html +80 -0
- data/doc/appscript-manual/application_architecture.gif +0 -0
- data/doc/appscript-manual/application_architecture2.gif +0 -0
- data/doc/appscript-manual/finder_to_textedit_event.gif +0 -0
- data/doc/appscript-manual/index.html +62 -0
- data/doc/appscript-manual/relationships_example.gif +0 -0
- data/doc/appscript-manual/ruby_to_itunes_event.gif +0 -0
- data/doc/full.css +106 -0
- data/doc/index.html +45 -0
- data/doc/mactypes-manual/01_introduction.html +54 -0
- data/doc/mactypes-manual/02_aliasclass.html +124 -0
- data/doc/mactypes-manual/03_fileurlclass.html +126 -0
- data/doc/mactypes-manual/04_unitsclass.html +100 -0
- data/doc/mactypes-manual/index.html +53 -0
- data/doc/osax-manual/01_introduction.html +67 -0
- data/doc/osax-manual/02_interface.html +147 -0
- data/doc/osax-manual/03_examples.html +73 -0
- data/doc/osax-manual/04_notes.html +61 -0
- data/doc/osax-manual/index.html +53 -0
- data/doc/rb-appscript-logo.png +0 -0
- data/extconf.rb +65 -0
- data/rb-scpt.gemspec +14 -0
- data/sample/AB_export_vcard.rb +31 -0
- data/sample/AB_list_people_with_emails.rb +13 -0
- data/sample/Add_iCal_event.rb +21 -0
- data/sample/Create_daily_iCal_todos.rb +75 -0
- data/sample/Export_Address_Book_phone_numbers.rb +59 -0
- data/sample/Hello_world.rb +21 -0
- data/sample/List_iTunes_playlist_names.rb +11 -0
- data/sample/Make_Mail_message.rb +33 -0
- data/sample/Open_file_in_TextEdit.rb +13 -0
- data/sample/Organize_Mail_messages.rb +61 -0
- data/sample/Print_folder_tree.rb +16 -0
- data/sample/Select_all_HTML_files.rb +14 -0
- data/sample/Set_iChat_status.rb +24 -0
- data/sample/Simple_Finder_GUI_Scripting.rb +18 -0
- data/sample/Stagger_Finder_windows.rb +25 -0
- data/sample/TextEdit_demo.rb +130 -0
- data/sample/iTunes_top40_to_html.rb +71 -0
- data/src/SendThreadSafe.c +380 -0
- data/src/SendThreadSafe.h +139 -0
- data/src/lib/_aem/aemreference.rb +1022 -0
- data/src/lib/_aem/codecs.rb +662 -0
- data/src/lib/_aem/connect.rb +205 -0
- data/src/lib/_aem/encodingsupport.rb +77 -0
- data/src/lib/_aem/findapp.rb +85 -0
- data/src/lib/_aem/mactypes.rb +251 -0
- data/src/lib/_aem/send.rb +279 -0
- data/src/lib/_aem/typewrappers.rb +59 -0
- data/src/lib/_appscript/defaultterminology.rb +277 -0
- data/src/lib/_appscript/referencerenderer.rb +245 -0
- data/src/lib/_appscript/reservedkeywords.rb +116 -0
- data/src/lib/_appscript/safeobject.rb +249 -0
- data/src/lib/_appscript/terminology.rb +471 -0
- data/src/lib/aem.rb +253 -0
- data/src/lib/appscript.rb +1075 -0
- data/src/lib/kae.rb +1489 -0
- data/src/lib/osax.rb +659 -0
- data/src/rbae.c +979 -0
- data/test/README +3 -0
- data/test/test_aemreference.rb +118 -0
- data/test/test_appscriptcommands.rb +152 -0
- data/test/test_appscriptreference.rb +106 -0
- data/test/test_codecs.rb +186 -0
- data/test/test_findapp.rb +26 -0
- data/test/test_mactypes.rb +79 -0
- data/test/test_osax.rb +54 -0
- data/test/testall.sh +10 -0
- metadata +145 -0
@@ -0,0 +1,471 @@
|
|
1
|
+
#
|
2
|
+
# rb-appscript
|
3
|
+
#
|
4
|
+
# terminology -- retrieve and convert an application's terminology into lookup tables
|
5
|
+
#
|
6
|
+
|
7
|
+
######################################################################
|
8
|
+
# TERMINOLOGY PARSER
|
9
|
+
######################################################################
|
10
|
+
|
11
|
+
module TerminologyParser
|
12
|
+
|
13
|
+
require "ae"
|
14
|
+
require "kae"
|
15
|
+
require "_appscript/reservedkeywords" # names of all existing methods on ASReference::Application
|
16
|
+
|
17
|
+
class BigEndianParser
|
18
|
+
@@_name_cache = {}
|
19
|
+
LegalFirst = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_'
|
20
|
+
LegalRest = LegalFirst + '0123456789'
|
21
|
+
@@_reserved_keywords = {} # ersatz set
|
22
|
+
ReservedKeywords.each { |name| @@_reserved_keywords[name] = nil }
|
23
|
+
|
24
|
+
def initialize
|
25
|
+
# terminology tables; order is significant where synonym definitions occur
|
26
|
+
@commands = {}
|
27
|
+
@properties = []
|
28
|
+
@elements = []
|
29
|
+
@classes = []
|
30
|
+
@enumerators = []
|
31
|
+
# use ersatz sets to record previously found definitions, and avoid adding duplicates to lists
|
32
|
+
# (i.e. 'name+code not in <set>' is quicker than using 'name+code not in <list>')
|
33
|
+
@_found_properties = {} # set
|
34
|
+
@_found_elements = {} # set
|
35
|
+
@_found_classes = {} # set
|
36
|
+
@_found_enumerators = {} # set
|
37
|
+
# ideally, aetes should define both singular and plural names for each class, but
|
38
|
+
# some define only one or the other so we need to fill in any missing ones afterwards
|
39
|
+
@_spare_class_names = {} # names by code
|
40
|
+
@_found_class_codes = {} # set
|
41
|
+
@_found_element_codes = {} # set
|
42
|
+
end
|
43
|
+
|
44
|
+
def _integer
|
45
|
+
# Read a 2-byte integer.
|
46
|
+
@_ptr += 2
|
47
|
+
return @_str[@_ptr - 2, 2].unpack('S')[0]
|
48
|
+
end
|
49
|
+
|
50
|
+
def _word
|
51
|
+
# Read a 4-byte string (really a long, but represented as an 4-character 8-bit string for readability).
|
52
|
+
@_ptr += 4
|
53
|
+
return @_str[@_ptr - 4, 4] # big-endian
|
54
|
+
end
|
55
|
+
|
56
|
+
# Ruby 1.9 has changed the way that a character ordinal is obtained
|
57
|
+
maj, min, rev = RUBY_VERSION.split('.')
|
58
|
+
if (maj == '1' and min.to_i < 9) # is Ruby 1.8
|
59
|
+
def _length
|
60
|
+
return @_str[@_ptr]
|
61
|
+
end
|
62
|
+
else
|
63
|
+
def _length
|
64
|
+
return @_str[@_ptr].ord
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def _name
|
69
|
+
# Read a MacRoman-encoded Pascal keyword string.
|
70
|
+
count = _length
|
71
|
+
@_ptr += 1 + count
|
72
|
+
s = @_str[@_ptr - count, count]
|
73
|
+
if not @@_name_cache.has_key?(s)
|
74
|
+
legal = LegalFirst
|
75
|
+
res = ''
|
76
|
+
s.split(//).each do |c|
|
77
|
+
if legal[c]
|
78
|
+
res += c
|
79
|
+
else
|
80
|
+
case c
|
81
|
+
when ' ', '-', '/'
|
82
|
+
res += '_'
|
83
|
+
when '&'
|
84
|
+
res += 'and'
|
85
|
+
else
|
86
|
+
if res == ''
|
87
|
+
res = '_'
|
88
|
+
end
|
89
|
+
res += "0x#{c.unpack('HXh')}"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
legal = LegalRest
|
93
|
+
end
|
94
|
+
if res[0, 3] == 'AS_' or @@_reserved_keywords.has_key?(res) or res[0, 1] == '_'
|
95
|
+
res += '_'
|
96
|
+
end
|
97
|
+
@@_name_cache[s] = res
|
98
|
+
end
|
99
|
+
return @@_name_cache[s]
|
100
|
+
end
|
101
|
+
|
102
|
+
##
|
103
|
+
|
104
|
+
def parse_command
|
105
|
+
name = _name
|
106
|
+
@_ptr += 1 + _length # description string
|
107
|
+
@_ptr += @_ptr & 1 # align
|
108
|
+
code = _word + _word # event class + event id
|
109
|
+
# skip result
|
110
|
+
@_ptr += 4 # datatype word
|
111
|
+
@_ptr += 1 + _length # description string
|
112
|
+
@_ptr += @_ptr & 1 # align
|
113
|
+
@_ptr += 2 # flags integer
|
114
|
+
# skip direct parameter
|
115
|
+
@_ptr += 4 # datatype word
|
116
|
+
@_ptr += 1 + _length # description string
|
117
|
+
@_ptr += @_ptr & 1 # align
|
118
|
+
@_ptr += 2 # flags integer
|
119
|
+
#
|
120
|
+
current_command_args = []
|
121
|
+
# Note: overlapping command definitions (e.g. InDesign) should be processed as follows:
|
122
|
+
# - If their names and codes are the same, only the last definition is used; other definitions are ignored and will not compile.
|
123
|
+
# - If their names are the same but their codes are different, only the first definition is used; other definitions are ignored and will not compile.
|
124
|
+
# - If a dictionary-defined command has the same name but different code to a built-in definition, escape its name so it doesn't conflict with the default built-in definition.
|
125
|
+
if not @commands.has_key?(name) or @commands[name][1] == code
|
126
|
+
@commands[name] = [name, code, current_command_args]
|
127
|
+
end
|
128
|
+
# add labelled parameters
|
129
|
+
_integer.times do
|
130
|
+
parameter_name = _name
|
131
|
+
@_ptr += @_ptr & 1 # align
|
132
|
+
parameter_code = _word
|
133
|
+
@_ptr += 4 # datatype word
|
134
|
+
@_ptr += 1 + _length # description string
|
135
|
+
@_ptr += @_ptr & 1 # align
|
136
|
+
@_ptr += 2 # flags integer
|
137
|
+
current_command_args.push([parameter_name, parameter_code])
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def parse_class
|
142
|
+
name = _name
|
143
|
+
@_ptr += @_ptr & 1 # align
|
144
|
+
code = _word
|
145
|
+
@_ptr += 1 + _length # description string
|
146
|
+
@_ptr += @_ptr & 1 # align
|
147
|
+
is_plural = false
|
148
|
+
_integer.times do # properties
|
149
|
+
propname = _name
|
150
|
+
@_ptr += @_ptr & 1 # align
|
151
|
+
propcode = _word
|
152
|
+
@_ptr += 4 # datatype word
|
153
|
+
@_ptr += 1 + _length # description string
|
154
|
+
@_ptr += @_ptr & 1 # align
|
155
|
+
flags = _integer
|
156
|
+
if propcode != 'c@#^' # not a superclass definition (see kAEInheritedProperties)
|
157
|
+
if flags & 1 == 1 # indicates class name is plural (see kAESpecialClassProperties)
|
158
|
+
is_plural = true
|
159
|
+
elsif not @_found_properties.has_key?(propname + propcode)
|
160
|
+
@properties.push([propname, propcode]) # preserve ordering
|
161
|
+
@_found_properties[propname + propcode] = nil # add to found set
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
_integer.times do # skip elements
|
166
|
+
@_ptr += 4 # code word
|
167
|
+
count = _integer
|
168
|
+
@_ptr += 4 * count # reference forms
|
169
|
+
end
|
170
|
+
if is_plural
|
171
|
+
if not @_found_elements.has_key?(name + code)
|
172
|
+
@elements.push([name, code])
|
173
|
+
@_found_elements[name + code] = nil # add to found set
|
174
|
+
@_found_element_codes[code] = nil # add to found set
|
175
|
+
end
|
176
|
+
else
|
177
|
+
if not @_found_classes.has_key?(name + code)
|
178
|
+
@classes.push([name, code])
|
179
|
+
@_found_classes[name + code] = nil # add to found set
|
180
|
+
@_found_class_codes[code] = nil # add to found set
|
181
|
+
end
|
182
|
+
end
|
183
|
+
@_spare_class_names[code] = name
|
184
|
+
end
|
185
|
+
|
186
|
+
def parse_comparison # comparison info isn't used
|
187
|
+
@_ptr += 1 + _length # name string
|
188
|
+
@_ptr += @_ptr & 1 # align
|
189
|
+
@_ptr += 4 # code word
|
190
|
+
@_ptr += 1 + _length # description string
|
191
|
+
@_ptr += @_ptr & 1 # align
|
192
|
+
end
|
193
|
+
|
194
|
+
def parse_enumeration
|
195
|
+
@_ptr += 4 # code word
|
196
|
+
_integer.times do # enumerators
|
197
|
+
name = _name
|
198
|
+
@_ptr += @_ptr & 1 # align
|
199
|
+
code = _word
|
200
|
+
@_ptr += 1 + _length # description string
|
201
|
+
@_ptr += @_ptr & 1 # align
|
202
|
+
if not @_found_enumerators.has_key?(name + code)
|
203
|
+
@enumerators.push([name, code])
|
204
|
+
@_found_enumerators[name + code] = nil # add to found set
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
def parse_suite
|
210
|
+
@_ptr += 1 + _length # name string
|
211
|
+
@_ptr += 1 + _length # description string
|
212
|
+
@_ptr += @_ptr & 1 # align
|
213
|
+
@_ptr += 4 # code word
|
214
|
+
@_ptr += 4 # level, version integers
|
215
|
+
_integer.times { parse_command }
|
216
|
+
_integer.times { parse_class }
|
217
|
+
_integer.times { parse_comparison }
|
218
|
+
_integer.times { parse_enumeration }
|
219
|
+
end
|
220
|
+
|
221
|
+
def parse(aetes)
|
222
|
+
aetes.each do |aete|
|
223
|
+
if aete.is_a?(AE::AEDesc) and aete.type == KAE::TypeAETE and aete.data != ''
|
224
|
+
@_str = aete.data
|
225
|
+
@_ptr = 6 # version, language, script integers
|
226
|
+
_integer.times { parse_suite }
|
227
|
+
end
|
228
|
+
end
|
229
|
+
# singular names are normally used in the classes table and plural names in the elements table. However, if an aete defines a singular name but not a plural name then the missing plural name is substituted with the singular name; and vice-versa if there's no singular equivalent for a plural name.
|
230
|
+
missing_elements = @_found_class_codes.keys - @_found_element_codes.keys
|
231
|
+
missing_classes = @_found_element_codes.keys - @_found_class_codes.keys
|
232
|
+
missing_elements.each do |code|
|
233
|
+
@elements.push([@_spare_class_names[code], code])
|
234
|
+
end
|
235
|
+
missing_classes.each do |code|
|
236
|
+
@classes.push([@_spare_class_names[code], code])
|
237
|
+
end
|
238
|
+
return [@classes, @enumerators, @properties, @elements, @commands.values]
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
|
243
|
+
class LittleEndianParser < BigEndianParser
|
244
|
+
def _word
|
245
|
+
# Read a 4-byte string (really a long, but represented as an 4-character 8-bit string for readability).
|
246
|
+
return super.reverse # little-endian
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
|
251
|
+
#######
|
252
|
+
# Public
|
253
|
+
|
254
|
+
def TerminologyParser.build_tables_for_aetes(aetes)
|
255
|
+
if [1].pack('S') == "\000\001" # is it big-endian?
|
256
|
+
return BigEndianParser.new.parse(aetes)
|
257
|
+
else
|
258
|
+
return LittleEndianParser.new.parse(aetes)
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
end
|
263
|
+
|
264
|
+
|
265
|
+
######################################################################
|
266
|
+
# TERMINOLOGY TABLES BUILDER
|
267
|
+
######################################################################
|
268
|
+
|
269
|
+
module Terminology
|
270
|
+
|
271
|
+
require "aem"
|
272
|
+
require "_appscript/defaultterminology"
|
273
|
+
|
274
|
+
@@_terminology_cache = {}
|
275
|
+
|
276
|
+
def Terminology._make_type_table(classes, enums, properties)
|
277
|
+
# builds tables used for converting symbols to/from AEType, AEEnums
|
278
|
+
type_by_code = DefaultTerminology::TypeByCode.clone
|
279
|
+
type_by_name = DefaultTerminology::TypeByName.clone
|
280
|
+
[[AEM::AEType, properties], [AEM::AEEnum, enums], [AEM::AEType, classes]].each do |klass, table|
|
281
|
+
table.each_with_index do |item, i|
|
282
|
+
name, code = item
|
283
|
+
# If an application-defined name overlaps an existing type name but has a different code, append '_' to avoid collision:
|
284
|
+
name += '_' if DefaultTerminology::TypeCodeByName.fetch(name, code) != code
|
285
|
+
begin
|
286
|
+
type_by_code[code] = name.intern # to handle synonyms, if same code appears more than once then use name from last definition in list
|
287
|
+
rescue ArgumentError # ignore #intern error if name is empty string
|
288
|
+
end
|
289
|
+
name, code = table[-i - 1]
|
290
|
+
name += '_' if DefaultTerminology::TypeCodeByName.fetch(name, code) != code
|
291
|
+
begin
|
292
|
+
type_by_name[name.intern] = klass.new(code) # to handle synonyms, if same name appears more than once then use code from first definition in list
|
293
|
+
rescue ArgumentError # ignore #intern error if name is empty string
|
294
|
+
end
|
295
|
+
end
|
296
|
+
end
|
297
|
+
return [type_by_code, type_by_name]
|
298
|
+
end
|
299
|
+
|
300
|
+
def Terminology._make_reference_table(properties, elements, commands)
|
301
|
+
# builds tables used for constructing references and commands
|
302
|
+
reference_by_code = DefaultTerminology::ReferenceByCode.clone
|
303
|
+
reference_by_name = DefaultTerminology::ReferenceByName.clone
|
304
|
+
[[:element, elements, 'e'], [:property, properties, 'p']].each do |kind, table, prefix|
|
305
|
+
# note: if property and element names are same (e.g. 'file' in BBEdit), will pack as property specifier unless it's a special case (i.e. see :text below). Note that there is currently no way to override this, i.e. to force appscript to pack it as an all-elements specifier instead (in AS, this would be done by prepending the 'every' keyword), so clients would need to use aem for that (but could add an 'all' method to Reference class if there was demand for a built-in workaround)
|
306
|
+
table.each_with_index do |item, i|
|
307
|
+
name, code = item
|
308
|
+
# If an application-defined name overlaps an existing type name but has a different code, append '_' to avoid collision:
|
309
|
+
name += '_' if DefaultTerminology::TypeCodeByName.fetch(name, code) != code
|
310
|
+
reference_by_code[prefix + code] = name # to handle synonyms, if same code appears more than once then use name from last definition in list
|
311
|
+
name, code = table[-i - 1]
|
312
|
+
name += '_' if DefaultTerminology::TypeCodeByName.fetch(name, code) != code
|
313
|
+
begin
|
314
|
+
reference_by_name[name.intern] = [kind, code] # to handle synonyms, if same name appears more than once then use code from first definition in list
|
315
|
+
rescue ArgumentError # ignore #intern error if name is empty string
|
316
|
+
end
|
317
|
+
end
|
318
|
+
end
|
319
|
+
if reference_by_name.has_key?(:text) # special case: AppleScript always packs 'text of...' as all-elements specifier
|
320
|
+
reference_by_name[:text][0] = :element
|
321
|
+
end
|
322
|
+
commands.reverse.each do |name, code, args| # to handle synonyms, if two commands have same name but different codes, only the first definition should be used (iterating over the commands list in reverse ensures this)
|
323
|
+
# Avoid collisions between default commands and application-defined commands with same name but different code (e.g. 'get' and 'set' in InDesign CS2):
|
324
|
+
name += '_' if DefaultTerminology::CommandCodeByName.fetch(name, code) != code
|
325
|
+
dct = {}
|
326
|
+
args.each do |arg_name, arg_code|
|
327
|
+
begin
|
328
|
+
dct[arg_name.intern] = arg_code
|
329
|
+
rescue ArgumentError # ignore #intern error if name is empty string
|
330
|
+
end
|
331
|
+
end
|
332
|
+
begin
|
333
|
+
reference_by_name[name.intern] = [:command, [code, dct]]
|
334
|
+
rescue ArgumentError # ignore #intern error if name is empty string
|
335
|
+
end
|
336
|
+
end
|
337
|
+
return reference_by_code, reference_by_name
|
338
|
+
end
|
339
|
+
|
340
|
+
def Terminology.dump_tables(tables, module_name, source_path, out_path)
|
341
|
+
# Parse aete(s) into intermediate tables, suitable for use by Terminology#tables_for_module
|
342
|
+
if not(/^[A-Z][A-Za-z0-9_]*$/ === module_name)
|
343
|
+
raise RuntimeError, "Invalid module name."
|
344
|
+
end
|
345
|
+
# Write module
|
346
|
+
File.open(out_path, "w") do |f|
|
347
|
+
f.puts "module #{module_name}"
|
348
|
+
f.puts "\tVersion = 1.1"
|
349
|
+
f.puts "\tPath = #{source_path.inspect}"
|
350
|
+
f.puts
|
351
|
+
(["Classes", "Enumerators", "Properties", "Elements"].zip(tables[0,4])).each do |name, table|
|
352
|
+
f.puts "\t#{name} = ["
|
353
|
+
table.sort.each do |item|
|
354
|
+
f.puts "\t\t#{item.inspect},"
|
355
|
+
end
|
356
|
+
f.puts "\t]"
|
357
|
+
f.puts
|
358
|
+
end
|
359
|
+
f.puts "\tCommands = ["
|
360
|
+
tables[4].sort.each do |name, code, params|
|
361
|
+
f.puts "\t\t[#{name.inspect}, #{code.inspect}, ["
|
362
|
+
params.each do |item|
|
363
|
+
f.puts "\t\t\t#{item.inspect},"
|
364
|
+
end
|
365
|
+
f.puts "\t\t]],"
|
366
|
+
end
|
367
|
+
f.puts "\t]"
|
368
|
+
f.puts "end"
|
369
|
+
end
|
370
|
+
end
|
371
|
+
|
372
|
+
#######
|
373
|
+
# public
|
374
|
+
|
375
|
+
def Terminology.default_tables
|
376
|
+
# [typebycode, typebyname, referencebycode, referencebyname]
|
377
|
+
return _make_type_table([], [], []) + _make_reference_table([], [], [])
|
378
|
+
end
|
379
|
+
|
380
|
+
def Terminology.tables_for_aetes(aetes)
|
381
|
+
# Build terminology tables from a list of unpacked aete byte strings.
|
382
|
+
# Result : list of hash -- [typebycode, typebyname, referencebycode, referencebyname]
|
383
|
+
aetes = aetes.reject { |aete| not(aete.is_a?(AE::AEDesc) and aete.type == KAE::TypeAETE and aete.data != '') }
|
384
|
+
classes, enums, properties, elements, commands = TerminologyParser.build_tables_for_aetes(aetes)
|
385
|
+
return _make_type_table(classes, enums, properties) + _make_reference_table(properties, elements, commands)
|
386
|
+
end
|
387
|
+
|
388
|
+
##
|
389
|
+
|
390
|
+
def Terminology.tables_for_module(terms)
|
391
|
+
# Build terminology tables from a dumped terminology module.
|
392
|
+
# Result : list of hash -- [typebycode, typebyname, referencebycode, referencebyname]
|
393
|
+
if terms::Version != 1.1
|
394
|
+
raise RuntimeError, "Unsupported terminology module version: #{terms::Version} (requires version 1.1)."
|
395
|
+
end
|
396
|
+
return _make_type_table(terms::Classes, terms::Enumerators, terms::Properties) \
|
397
|
+
+ _make_reference_table(terms::Properties, terms::Elements, terms::Commands)
|
398
|
+
end
|
399
|
+
|
400
|
+
def Terminology.tables_for_parsed_sdef(terms)
|
401
|
+
# Build terminology tables from an SdefParser instance.
|
402
|
+
# Result : list of hash -- [typebycode, typebyname, referencebycode, referencebyname]
|
403
|
+
return _make_type_table(terms.classes, terms.enumerators, terms.properties) \
|
404
|
+
+ _make_reference_table(terms.properties, terms.elements, terms.commands)
|
405
|
+
end
|
406
|
+
|
407
|
+
def Terminology.aetes_for_app(aem_app)
|
408
|
+
begin
|
409
|
+
begin
|
410
|
+
aetes = aem_app.event('ascrgdte', {'----' => 0}).send(120 * 60)
|
411
|
+
rescue AEM::EventError => e
|
412
|
+
if e.number == -192 # aete resource not found
|
413
|
+
aetes = []
|
414
|
+
else
|
415
|
+
raise
|
416
|
+
end
|
417
|
+
end
|
418
|
+
rescue => err
|
419
|
+
raise RuntimeError, "Can't get terminology for application (#{aem_app}): #{err}"
|
420
|
+
end
|
421
|
+
aetes = [aetes] if not aetes.is_a?(Array)
|
422
|
+
return aetes
|
423
|
+
end
|
424
|
+
|
425
|
+
def Terminology.tables_for_app(aem_app)
|
426
|
+
# Build terminology tables for an application.
|
427
|
+
# app : AEM::Application
|
428
|
+
# Result : list of hash -- [typebycode, typebyname, referencebycode, referencebyname]
|
429
|
+
if not @@_terminology_cache.has_key?(aem_app.identity)
|
430
|
+
aetes = Terminology.aetes_for_app(aem_app)
|
431
|
+
@@_terminology_cache[aem_app.identity] = Terminology.tables_for_aetes(aetes)
|
432
|
+
end
|
433
|
+
return @@_terminology_cache[aem_app.identity]
|
434
|
+
end
|
435
|
+
|
436
|
+
#######
|
437
|
+
# public
|
438
|
+
|
439
|
+
def Terminology.dump(app_name, module_name, out_path)
|
440
|
+
# Export application terminology tables as a Ruby module
|
441
|
+
# app_path : string -- name or path of application
|
442
|
+
# module_name : string -- name of generated module (must be a valid Ruby constant)
|
443
|
+
# out_path : string -- module file to write
|
444
|
+
#
|
445
|
+
# Generates a Ruby module containing an application's basic terminology
|
446
|
+
# (names and codes) as used by appscript.
|
447
|
+
#
|
448
|
+
# Call the #dump method to dump faulty aetes to Ruby module, e.g.:
|
449
|
+
#
|
450
|
+
# Terminology.dump('MyApp', 'MyAppGlue', '/path/to/ruby/modules/myappglue.rb')
|
451
|
+
#
|
452
|
+
# Patch any errors by hand, then import the patched module into your script
|
453
|
+
# and pass it to appscript's app() constructor via its 'terms' argument, e.g.:
|
454
|
+
#
|
455
|
+
# require 'appscript'; include Appscript
|
456
|
+
# require 'myappglue'
|
457
|
+
#
|
458
|
+
# myapp = app('MyApp', terms => MyAppGlue)
|
459
|
+
#
|
460
|
+
# Note that dumped terminologies aren't used by appscript's built-in help system.
|
461
|
+
#
|
462
|
+
app_path = FindApp.by_name(app_name)
|
463
|
+
# Get aete(s)
|
464
|
+
aetes = Terminology.aetes_for_app(Application.by_path(app_path))
|
465
|
+
aetes.delete_if { |aete| not(aete.is_a?(AE::AEDesc) and aete.type == KAE::TypeAETE) }
|
466
|
+
tables = TerminologyParser.build_tables_for_aetes(aetes)
|
467
|
+
Terminology.dump_tables(tables, module_name, app_path, out_path)
|
468
|
+
end
|
469
|
+
|
470
|
+
end
|
471
|
+
|