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,139 @@
1
+ /*
2
+ File: AESendThreadSafe.h
3
+
4
+ Contains: Code to send Apple events in a thread-safe manner.
5
+
6
+ Written by: DTS
7
+
8
+ Copyright: Copyright (c) 2007 Apple Inc. All Rights Reserved.
9
+
10
+ Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc.
11
+ ("Apple") in consideration of your agreement to the following
12
+ terms, and your use, installation, modification or
13
+ redistribution of this Apple software constitutes acceptance of
14
+ these terms. If you do not agree with these terms, please do
15
+ not use, install, modify or redistribute this Apple software.
16
+
17
+ In consideration of your agreement to abide by the following
18
+ terms, and subject to these terms, Apple grants you a personal,
19
+ non-exclusive license, under Apple's copyrights in this
20
+ original Apple software (the "Apple Software"), to use,
21
+ reproduce, modify and redistribute the Apple Software, with or
22
+ without modifications, in source and/or binary forms; provided
23
+ that if you redistribute the Apple Software in its entirety and
24
+ without modifications, you must retain this notice and the
25
+ following text and disclaimers in all such redistributions of
26
+ the Apple Software. Neither the name, trademarks, service marks
27
+ or logos of Apple Inc. may be used to endorse or promote
28
+ products derived from the Apple Software without specific prior
29
+ written permission from Apple. Except as expressly stated in
30
+ this notice, no other rights or licenses, express or implied,
31
+ are granted by Apple herein, including but not limited to any
32
+ patent rights that may be infringed by your derivative works or
33
+ by other works in which the Apple Software may be incorporated.
34
+
35
+ The Apple Software is provided by Apple on an "AS IS" basis.
36
+ APPLE MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
37
+ WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT,
38
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING
39
+ THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
40
+ COMBINATION WITH YOUR PRODUCTS.
41
+
42
+ IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT,
43
+ INCIDENTAL OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
44
+ TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
45
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY
46
+ OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
47
+ OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY
48
+ OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR
49
+ OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF
50
+ SUCH DAMAGE.
51
+
52
+ Change History (most recent first):
53
+
54
+ $Log: AESendThreadSafe.h,v $
55
+ Revision 1.2 2007/02/12 11:58:43
56
+ Corrected grammo in comment.
57
+
58
+ Revision 1.1 2007/02/09 10:55:27
59
+ First checked in.
60
+
61
+
62
+ */
63
+
64
+ /*
65
+
66
+ 2007/06/24 -- Modified by HAS to make AESendMessageThreadSafeSynchronous API-compatible with AESendMessage.
67
+
68
+ */
69
+
70
+ #ifndef _SENDTHREADSAFE_H
71
+ #define _SENDTHREADSAFE_H
72
+
73
+ #include <ApplicationServices/ApplicationServices.h>
74
+
75
+ /////////////////////////////////////////////////////////////////
76
+
77
+ /*
78
+ Introduction
79
+ ------------
80
+ Since Mac OS X 10.2 it has been possible to synchronously send an Apple event
81
+ from a thread other than the main thread. The technique for doing this is
82
+ documented in Technote 2053 "Mac OS X 10.2".
83
+
84
+ <http://developer.apple.com/technotes/tn2002/tn2053.html>
85
+
86
+ Unfortunately, this technique isn't quite right. Specifically, due to a bug
87
+ in Apple Event Manager <rdar://problem/4976113>, it is not safe to dispose of
88
+ the Mach port (using mach_port_destroy, as documented in the technote, or, more
89
+ correctly, using mach_port_mod_refs) that you created to use as the reply port.
90
+ Doing this triggers a race condition that, very rarely, can cause the system
91
+ to destroy some other, completely unrelated, Mach port within your process.
92
+ This could cause all sorts of problems. One common symptom is that, after
93
+ accidentally destroying the Mach port associated with a thread, your program
94
+ dies with the following message:
95
+
96
+ /SourceCache/Libc/Libc-320.1.3/pthreads/pthread.c:897: failed assertion `ret == MACH_MSG_SUCCESS'
97
+
98
+ The best workaround to this problem is to not dispose of the Mach port that
99
+ you use as the Apple event reply port. If you have a limited number of
100
+ secondary threads from which you need to send Apple events, it's relatively
101
+ easy to allocate an Apple event reply port for each thread and then never
102
+ dispose it. However, if you have general case code, it might be tricky
103
+ to track down all of the threads that send Apple events and make sure they
104
+ have reply ports. This module was designed as a general case solution to
105
+ the problem.
106
+
107
+ The module exports a single function, AESendMessageThreadSafeSynchronous, which,
108
+ as its name suggests, sends an Apple event and waits for the reply (that is,
109
+ a synchronous IPC) and is safe to call from an arbitrary thread. It's basically
110
+ a wrapper around the system function AESendMessage, with added smarts to manage
111
+ a per-thread Apple event reply port.
112
+
113
+ When <rdar://problem/4976113> is fixed, this module should be unnecessary but
114
+ benign.
115
+
116
+ For information about how this works, see the comments in the implementation.
117
+ */
118
+
119
+ /////////////////////////////////////////////////////////////////
120
+
121
+ #ifdef __cplusplus
122
+ extern "C" {
123
+ #endif
124
+
125
+ OSStatus SendMessageThreadSafe(
126
+ AppleEvent * eventPtr,
127
+ AppleEvent * replyPtr,
128
+ AESendMode sendMode,
129
+ long timeOutInTicks
130
+ );
131
+ // A thread-safe replacement for AESend. This is very much like AESendMessage,
132
+ // except that it takes care of setting up the reply port when you use it
133
+ // from a thread other than the main thread.
134
+
135
+ #ifdef __cplusplus
136
+ }
137
+ #endif
138
+
139
+ #endif
@@ -0,0 +1,1022 @@
1
+ #
2
+ # rb-appscript
3
+ #
4
+ # aemreference -- an object-oriented API for constructing object specifier AEDescs
5
+ #
6
+
7
+ ######################################################################
8
+ # Endianness support
9
+
10
+ module BigEndianPackers
11
+
12
+ def pack_type(code)
13
+ return AE::AEDesc.new(KAE::TypeType, code)
14
+ end
15
+
16
+ def pack_enum(code)
17
+ return AE::AEDesc.new(KAE::TypeEnumeration, code)
18
+ end
19
+
20
+ def pack_absolute_ordinal(code)
21
+ return AE::AEDesc.new(KAE::TypeAbsoluteOrdinal, code)
22
+ end
23
+
24
+ end
25
+
26
+
27
+ module SmallEndianPackers
28
+
29
+ def pack_type(code)
30
+ return AE::AEDesc.new(KAE::TypeType, code.reverse)
31
+ end
32
+
33
+ def pack_enum(code)
34
+ return AE::AEDesc.new(KAE::TypeEnumeration, code.reverse)
35
+ end
36
+
37
+ def pack_absolute_ordinal(code)
38
+ return AE::AEDesc.new(KAE::TypeAbsoluteOrdinal, code.reverse)
39
+ end
40
+
41
+ end
42
+
43
+
44
+ ######################################################################
45
+
46
+ module AEMReference
47
+
48
+ require "ae"
49
+ require "kae"
50
+
51
+ ######################################################################
52
+ # SUPPORT FUNCTIONS
53
+ ######################################################################
54
+
55
+ extend([1].pack('s') == "\001\000" ? SmallEndianPackers : BigEndianPackers)
56
+
57
+ def AEMReference.pack_list_as(type, lst)
58
+ # used to pack object specifiers, etc.
59
+ # pack key-value pairs into an AEListDesc, then coerce it to the desired type
60
+ # (there are other AEM APIs for packing obj specs, but this way is easiest)
61
+ desc = AE::AEDesc.new_list(true)
62
+ lst.each { |key, value| desc.put_param(key, value) }
63
+ return desc.coerce(type)
64
+ end
65
+
66
+ class CollectComparable
67
+ # obtains the data needed to perform equality tests on references
68
+ # uses AEM_resolve to walk a reference, building up a list of method call names and their arguments
69
+
70
+ attr_reader :result
71
+
72
+ def initialize
73
+ @result = []
74
+ end
75
+
76
+ def send(name, *args)
77
+ self.result.push([name] + args)
78
+ return self
79
+ end
80
+ end
81
+
82
+ ######################################################################
83
+ # BASE CLASS
84
+ ######################################################################
85
+
86
+ class Query
87
+
88
+ def initialize
89
+ @_comparable = nil
90
+ end
91
+
92
+ def AEM_comparable
93
+ # called by Query#==; returns the data needed to compare two aem references
94
+ if not @_comparable
95
+ collector = AEMReference::CollectComparable.new
96
+ AEM_resolve(collector)
97
+ @_comparable = collector.result
98
+ end
99
+ return @_comparable
100
+ end
101
+
102
+ def ==(val)
103
+ return (self.equal?(val) or (
104
+ self.class == val.class and
105
+ self.AEM_comparable == val.AEM_comparable))
106
+ end
107
+
108
+ alias_method :eql?, :==
109
+
110
+ def hash
111
+ return to_s.hash
112
+ end
113
+
114
+ def inspect
115
+ return to_s
116
+ end
117
+ end
118
+
119
+
120
+ ######################################################################
121
+ # BASE CLASS FOR ALL REFERENCE FORMS
122
+ ######################################################################
123
+
124
+ class Specifier < Query
125
+ # Base class for insertion specifier and all object specifier classes.
126
+
127
+ attr_reader :_key, :_container
128
+ protected :_key, :_container
129
+
130
+ def initialize(container, key)
131
+ super()
132
+ @_desc = nil
133
+ @_container = container
134
+ @_key = key
135
+ end
136
+
137
+ def AEM_root
138
+ # Get reference's root node. Used by range and filter specifiers when determining type of reference
139
+ # passed as argument(s): range specifiers require absolute (app-based) or container (con-based)
140
+ # references; filter specifiers require an item (its-based) reference.
141
+ return @_container.AEM_root
142
+ end
143
+
144
+ def AEM_true_self
145
+ # Called by specifier classes when creating a reference to sub-element(s) of the current reference.
146
+ # - An AllElements specifier (which contains 'want', 'form', 'seld' and 'from' values) will return an UnkeyedElements object (which contains 'want' and 'from' data only). The new specifier object (ElementByIndex, ElementsByRange, etc.) wraps itself around this stub and supply its own choice of 'form' and 'seld' values.
147
+ # - All other specifiers simply return themselves.
148
+ #
149
+ #This sleight-of-hand allows foo.elements('bar ') to produce a legal reference to all elements, so users don't need to write foo.elements('bar ').all to achieve the same goal. This isn't a huge deal for aem, but makes a significant difference to the usability of user-friendly wrappers like appscript, and dealing with the mechanics of it here helps keep other layers simple.
150
+ return self
151
+ end
152
+
153
+ def AEM_set_desc(desc)
154
+ @_desc = desc
155
+ end
156
+
157
+ def AEM_pack_self(codecs)
158
+ # Pack this Specifier; called by Codecs#pack, which passes itself so that specifiers in this reference can pack their selectors.
159
+ if not @_desc
160
+ @_desc = _pack_self(codecs) # once packed, cache this AEDesc for efficiency
161
+ end
162
+ return @_desc
163
+ end
164
+ end
165
+
166
+
167
+ ######################################################################
168
+ # INSERTION POINT REFERENCE FORM
169
+ ######################################################################
170
+
171
+ class InsertionSpecifier < Specifier
172
+ # A reference to an element insertion point.
173
+
174
+ # Syntax: all_elements_ref.beginning / all_elements_ref.end / element_ref.before / element_ref.after
175
+
176
+ def initialize(container, key, keyname)
177
+ super(container, key)
178
+ @_keyname = keyname
179
+ end
180
+
181
+ def to_s
182
+ return "#{@_container}.#{@_keyname}"
183
+ end
184
+
185
+ def _pack_self(codecs)
186
+ return AEMReference.pack_list_as(KAE::TypeInsertionLoc, [
187
+ [KAE::KeyAEObject, @_container.AEM_pack_self(codecs)],
188
+ [KAE::KeyAEPosition, @_key],
189
+ ])
190
+ end
191
+
192
+ def AEM_resolve(obj)
193
+ return @_container.AEM_resolve(obj).send(@_keyname) end
194
+ end
195
+
196
+
197
+ ######################################################################
198
+ # BASE CLASS FOR ALL OBJECT REFERENCE FORMS
199
+ ######################################################################
200
+
201
+ class PositionSpecifier < Specifier
202
+ # All property and element reference forms inherit from this class. It provides most
203
+ # of the reference building methods; the rest are supplied by MultipleElements to
204
+ # those reference forms for which they're valid.
205
+
206
+ # Note that comparison and logic 'operator' methods are implemented on this class
207
+ # - these are only for use in constructing its-based references and shouldn't be used
208
+ # on app- and con-based references. Aem doesn't enforce this rule itself so as to
209
+ # minimise runtime overhead (the target application will raise an error if the user
210
+ # does something foolish).
211
+
212
+ Beginning = AEMReference.pack_enum(KAE::KAEBeginning)
213
+ End = AEMReference.pack_enum(KAE::KAEEnd)
214
+ Before = AEMReference.pack_enum(KAE::KAEBefore)
215
+ After = AEMReference.pack_enum(KAE::KAEAfter)
216
+ Previous = AEMReference.pack_enum(KAE::KAEPrevious)
217
+ Next = AEMReference.pack_enum(KAE::KAENext)
218
+
219
+ attr_reader :AEM_want
220
+
221
+ def initialize(wantcode, container, key)
222
+ @AEM_want = wantcode
223
+ super(container, key)
224
+ end
225
+
226
+ def to_s
227
+ return "#{@_container}.#{self.class::By}(#{@_key.inspect})"
228
+ end
229
+
230
+ def _pack_self(codecs)
231
+ return AEMReference.pack_list_as(KAE::TypeObjectSpecifier, [
232
+ [KAE::KeyAEDesiredClass, AEMReference.pack_type(@AEM_want)],
233
+ [KAE::KeyAEKeyForm, self.class::KeyForm],
234
+ [KAE::KeyAEKeyData, _pack_key(codecs)],
235
+ [KAE::KeyAEContainer, @_container.AEM_pack_self(codecs)],
236
+ ])
237
+ end
238
+
239
+ # Comparison tests; these should only be used on its-based references:
240
+
241
+ # Each of these methods returns a ComparisonTest subclass
242
+
243
+ def gt(val)
244
+ return GreaterThan.new(self, val)
245
+ end
246
+
247
+ def ge(val)
248
+ return GreaterOrEquals.new(self, val)
249
+ end
250
+
251
+ def eq(val)
252
+ return Equals.new(self, val)
253
+ end
254
+
255
+ def ne(val)
256
+ return NotEquals.new(self, val)
257
+ end
258
+
259
+ def lt(val)
260
+ return LessThan.new(self, val)
261
+ end
262
+
263
+ def le(val)
264
+ return LessOrEquals.new(self, val)
265
+ end
266
+
267
+ def begins_with(val)
268
+ return BeginsWith.new(self, val)
269
+ end
270
+
271
+ def ends_with(val)
272
+ return EndsWith.new(self, val)
273
+ end
274
+
275
+ def contains(val)
276
+ return Contains.new(self, val)
277
+ end
278
+
279
+ def is_in(val)
280
+ return IsIn.new(self, val)
281
+ end
282
+
283
+ # Insertion references:
284
+
285
+ # Thes can be called on any kind of element reference, and also on property references where the
286
+ # property represents a one-to-one relationship, e.g. textedit.documents[1].text.end is valid
287
+
288
+ def beginning
289
+ return InsertionSpecifier.new(self, Beginning, :beginning)
290
+ end
291
+
292
+ def end
293
+ return InsertionSpecifier.new(self, End, :end)
294
+ end
295
+
296
+ def before
297
+ return InsertionSpecifier.new(self, Before, :before)
298
+ end
299
+
300
+ def after
301
+ return InsertionSpecifier.new(self, After, :after)
302
+ end
303
+
304
+ # Property and element references can be used on any type of object reference:
305
+
306
+ def property(code)
307
+ return Property.new(KAE::CProperty, self, code)
308
+ end
309
+
310
+ def user_property(name)
311
+ return UserProperty.new(KAE::CProperty, self, name)
312
+ end
313
+
314
+ def elements(code)
315
+ return AllElements.new(code, self)
316
+ end
317
+
318
+ # Relative position references
319
+
320
+ # these are unlikely to work on one-to-one relationships - but what the hey, putting them here
321
+ # simplifies the class structure a bit. As with all reference forms, it's mostly left to the client to
322
+ # ensure the references they construct can be understood by the target application.
323
+
324
+ def previous(code)
325
+ return ElementByRelativePosition.new(code, self, Previous, :previous)
326
+ end
327
+
328
+ def next(code)
329
+ return ElementByRelativePosition.new(code, self, Next, :next)
330
+ end
331
+ end
332
+
333
+
334
+ ######################################################################
335
+ # PROPERTY REFERENCE FORMS
336
+ ######################################################################
337
+
338
+ class Property < PositionSpecifier
339
+ # A reference to a user-defined property, where code is the code identifying the property.
340
+
341
+ # Syntax: ref.property(code)
342
+
343
+ By = :property
344
+ KeyForm = AEMReference.pack_enum(KAE::FormPropertyID)
345
+
346
+ def _pack_key(codecs)
347
+ return AEMReference.pack_type(@_key)
348
+ end
349
+
350
+ def AEM_resolve(obj)
351
+ return @_container.AEM_resolve(obj).send(:property, @_key)
352
+ end
353
+ end
354
+
355
+
356
+ class UserProperty < PositionSpecifier
357
+ # A reference to a user-defined property, where name is a string representing the property's name.
358
+
359
+ # Scriptable applications shouldn't use this reference form, but OSA script applets can.
360
+ # Note that OSA languages may have additional rules regarding case sensitivity/conversion.
361
+
362
+ # Syntax: ref.user_property(name)
363
+
364
+ By = :user_property
365
+ KeyForm = AEMReference.pack_enum(KAE::FormUserPropertyID)
366
+
367
+ def _pack_key(codecs)
368
+ return codecs.pack(@_key).coerce(KAE::TypeChar)
369
+ end
370
+
371
+ def AEM_resolve(obj)
372
+ return @_container.AEM_resolve(obj).send(:user_property, @_key)
373
+ end
374
+ end
375
+
376
+
377
+ ######################################################################
378
+ # ELEMENT REFERENCE FORMS
379
+ ######################################################################
380
+
381
+ ###################################
382
+ # Single elements
383
+
384
+ class SingleElement < PositionSpecifier
385
+ # Base class for all single element specifiers.
386
+
387
+ def initialize(wantcode, container, key)
388
+ super(wantcode, container.AEM_true_self, key)
389
+ end
390
+
391
+ def _pack_key(codecs)
392
+ return codecs.pack(@_key)
393
+ end
394
+
395
+ def AEM_resolve(obj)
396
+ return @_container.AEM_resolve(obj).send(self.class::By, @_key)
397
+ end
398
+ end
399
+
400
+
401
+ #######
402
+
403
+ class ElementByName < SingleElement
404
+ # A reference to a single element by its name, where name is a string.
405
+
406
+ # Syntax: elements_ref.by_name(string)
407
+
408
+ By = :by_name
409
+ KeyForm = AEMReference.pack_enum(KAE::FormName)
410
+ end
411
+
412
+
413
+ class ElementByIndex < SingleElement
414
+ # A reference to a single element by its index, where index is a non-zero whole number.
415
+
416
+ # Syntax: elements_ref.by_index(integer)
417
+
418
+ # Note that a few apps (e.g. Finder) may allow other values as well (e.g. Aliases/FSRefs)
419
+
420
+ By = :by_index
421
+ KeyForm = AEMReference.pack_enum(KAE::FormAbsolutePosition)
422
+ end
423
+
424
+
425
+ class ElementByID < SingleElement
426
+ # A reference to a single element by its id.
427
+
428
+ # Syntax: elements_ref.by_id(anything)
429
+
430
+ By = :by_id
431
+ KeyForm = AEMReference.pack_enum(KAE::FormUniqueID)
432
+ end
433
+
434
+ ##
435
+
436
+ class ElementByOrdinal < SingleElement
437
+ # A reference to first/middle/last/any element.
438
+
439
+ # Syntax: elements_ref.first / elements_ref.middle / elements_ref.last / elements_ref.any
440
+
441
+ KeyForm = AEMReference.pack_enum(KAE::FormAbsolutePosition)
442
+
443
+ def initialize(wantcode, container, key, keyname)
444
+ @_keyname = keyname
445
+ super(wantcode, container, key)
446
+ end
447
+
448
+ def to_s
449
+ return "#{@_container}.#{@_keyname}"
450
+ end
451
+
452
+ def AEM_resolve(obj)
453
+ return @_container.AEM_resolve(obj).send(@_keyname)
454
+ end
455
+ end
456
+
457
+
458
+ class ElementByRelativePosition < PositionSpecifier
459
+ # A relative reference to previous/next element, where code
460
+ # is the class code of element to get (e.g. 'docu').
461
+
462
+ # Syntax: elements_ref.previous(code) / elements_ref.next(code)
463
+
464
+ # Note: this class subclasses PositionSpecifier, not SingleElement,
465
+ # as it needs the container reference intact. (SingleElement#initialize
466
+ # calls the container's AEM_true_self method, which breaks up
467
+ # AllElements specifiers - not what we want here.)
468
+
469
+ KeyForm = AEMReference.pack_enum(KAE::FormRelativePosition)
470
+
471
+ def initialize(wantcode, container, key, keyname)
472
+ @_keyname = keyname
473
+ super(wantcode, container, key)
474
+ end
475
+
476
+ def _pack_key(codecs)
477
+ return codecs.pack(@_key)
478
+ end
479
+
480
+ def to_s
481
+ return "#{@_container}.#{@_keyname}(#{@AEM_want.inspect})"
482
+ end
483
+
484
+ def AEM_resolve(obj)
485
+ return @_container.AEM_resolve(obj).send(@_keyname, @AEM_want)
486
+ end
487
+ end
488
+
489
+
490
+ ###################################
491
+ # Multiple elements
492
+
493
+ class MultipleElements < PositionSpecifier
494
+ # Base class for all multiple element specifiers.
495
+
496
+ First = AEMReference.pack_absolute_ordinal(KAE::KAEFirst)
497
+ Middle = AEMReference.pack_absolute_ordinal(KAE::KAEMiddle)
498
+ Last = AEMReference.pack_absolute_ordinal(KAE::KAELast)
499
+ Any = AEMReference.pack_absolute_ordinal(KAE::KAEAny)
500
+
501
+ def first
502
+ return ElementByOrdinal.new(@AEM_want, self, First, :first)
503
+ end
504
+
505
+ def middle
506
+ return ElementByOrdinal.new(@AEM_want, self, Middle, :middle)
507
+ end
508
+
509
+ def last
510
+ return ElementByOrdinal.new(@AEM_want, self, Last, :last)
511
+ end
512
+
513
+ def any
514
+ return ElementByOrdinal.new(@AEM_want, self, Any, :any)
515
+ end
516
+
517
+ def by_name(name)
518
+ return ElementByName.new(@AEM_want, self, name)
519
+ end
520
+
521
+ def by_index(index)
522
+ return ElementByIndex.new(@AEM_want, self, index)
523
+ end
524
+
525
+ def by_id(id)
526
+ return ElementByID.new(@AEM_want, self, id)
527
+ end
528
+
529
+ def by_range(start, stop)
530
+ return ElementsByRange.new(@AEM_want, self, [start, stop])
531
+ end
532
+
533
+ def by_filter(test)
534
+ return ElementsByFilter.new(@AEM_want, self, test)
535
+ end
536
+ end
537
+
538
+
539
+ #######
540
+
541
+ class ElementsByRange < MultipleElements
542
+ # A reference to a range of elements
543
+
544
+ # Syntax: elements_ref.by_range(start, stop)
545
+
546
+ # The start and stop args are con-based relative references to the first and last elements in range.
547
+ # Note that absolute (app-based) references are also acceptable.
548
+
549
+ KeyForm = AEMReference.pack_enum(KAE::FormRange)
550
+
551
+ def initialize(wantcode, container, key)
552
+ super(wantcode, container.AEM_true_self, key)
553
+ end
554
+
555
+ def to_s
556
+ return "#{@_container}.by_range(#{@_key[0].inspect}, #{@_key[1].inspect})"
557
+ end
558
+
559
+ def _pack_key(codecs)
560
+ range_selectors = [
561
+ [KAE::KeyAERangeStart, @_key[0]],
562
+ [KAE::KeyAERangeStop, @_key[1]]
563
+ ].collect do |key, selector|
564
+ case selector
565
+ when Specifier
566
+ # use selector as-is (note: its-based roots aren't appropriate, but this isn't checked for)
567
+ when String
568
+ selector = AEMReference::Con.elements(@AEM_want).by_name(selector)
569
+ else
570
+ selector = AEMReference::Con.elements(@AEM_want).by_index(selector)
571
+ end
572
+ [key, codecs.pack(selector)]
573
+ end
574
+ return AEMReference.pack_list_as(KAE::TypeRangeDescriptor, range_selectors)
575
+ end
576
+
577
+ def AEM_resolve(obj)
578
+ return @_container.AEM_resolve(obj).send(:by_range, *@_key)
579
+ end
580
+ end
581
+
582
+
583
+ class ElementsByFilter < MultipleElements
584
+ # A reference to all elements that match a condition
585
+
586
+ # Syntax: elements_ref.by_filter(test)
587
+
588
+ # The test argument is a Test object constructed from an its-based reference.
589
+
590
+ KeyForm = AEMReference.pack_enum(KAE::FormTest)
591
+
592
+ def initialize(wantcode, container, key)
593
+ if not key.is_a?(Test)
594
+ raise TypeError, "Bad selector: not a test (its) based specifier: #{key.inspect}"
595
+ end
596
+ super(wantcode, container.AEM_true_self, key)
597
+ end
598
+
599
+ def to_s
600
+ return "#{@_container}.by_filter(#{@_key.inspect})"
601
+ end
602
+
603
+ def _pack_key(codecs)
604
+ return codecs.pack(@_key)
605
+ end
606
+
607
+ def AEM_resolve(obj)
608
+ return @_container.AEM_resolve(obj).send(:by_filter, @_key)
609
+ end
610
+ end
611
+
612
+
613
+ class AllElements < MultipleElements
614
+ # A reference to all elements of the container reference.
615
+
616
+ # Syntax: ref.elements(code)
617
+
618
+ # The 'code' argument is the four-character class code of the desired elements (e.g. 'docu').
619
+
620
+ # Note that an AllElements object is a wrapper around an UnkeyedElements object.
621
+ # When sub-selecting these elements, e.g. ref.elements('docu').by_index(1), the AllElements
622
+ # wrapper is ignored and the UnkeyedElements object is used as the basis for the
623
+ # new specifier. e.g.
624
+ #
625
+ # AEM.app.elements('docu') # every document of application
626
+ #
627
+ # produces the following chain:
628
+ #
629
+ # ApplicationRoot -> UnkeyedElements -> AllElements
630
+ #
631
+ # Subselecting these elements, e.g.
632
+ #
633
+ # AEM.app.elements('docu').by_index(1) # document 1 of application
634
+ #
635
+ # produces the following chain:
636
+ #
637
+ # ApplicationRoot -> UnkeyedElements -> ElementByIndex
638
+ #
639
+ # As you can see, the previous UnkeyedElements object is retained, but the AllElements
640
+ # object isn't.
641
+ #
642
+ # The result of all this is that users can legally write a reference to all elements as (e.g.):
643
+ #
644
+ # AEM.app.elements('docu')
645
+ # AS.app.documents
646
+ #
647
+ # instead of:
648
+ #
649
+ # AEM.app.elements('docu').all
650
+ # AS.app.documents.all
651
+ #
652
+ # Compare with some other bridges (e.g. Frontier), where (e.g.) 'ref.documents' is not
653
+ # a legitimate reference in itself, and users must remember to add '.all' in order to specify
654
+ # all elements, or else it won't work correctly. This maps directly onto the underlying AEM
655
+ # API, which is easy to implement but isn't so good for usability. Whereas aem trades
656
+ # a bit of increased internal complexity for a simpler, more intuitive and foolproof external API.
657
+
658
+ KeyForm = AEMReference.pack_enum(KAE::FormAbsolutePosition)
659
+ All = AEMReference.pack_absolute_ordinal(KAE::KAEAll)
660
+
661
+ def initialize(wantcode, container)
662
+ super(wantcode, UnkeyedElements.new(wantcode, container), All)
663
+ end
664
+
665
+ def to_s
666
+ return @_container.to_s
667
+ end
668
+
669
+ def _pack_key(codecs)
670
+ return All
671
+ end
672
+
673
+ def AEM_true_self
674
+ # override default implementation to return the UnkeyedElements object stored inside of this AllElements instance
675
+ return @_container
676
+ end
677
+
678
+ def AEM_resolve(obj)
679
+ return @_container.AEM_resolve(obj) # forward to UnkeyedElements
680
+ end
681
+ end
682
+
683
+
684
+ ######################################################################
685
+ # SHIMS
686
+ ######################################################################
687
+
688
+ ###################################
689
+ # Multiple element shim
690
+
691
+ class UnkeyedElements < Specifier
692
+ # A partial elements reference, containing element code but no keyform/keydata. A shim.
693
+ # User is never exposed to this class directly. See comments in AllElements class.
694
+
695
+ attr_reader :AEM_want, :_container
696
+ protected :AEM_want, :_container
697
+
698
+ def initialize(wantcode, container)
699
+ @AEM_want = wantcode
700
+ @_container = container
701
+ end
702
+
703
+ def to_s
704
+ return "#{@_container}.elements(#{@AEM_want.inspect})"
705
+ end
706
+
707
+ def AEM_pack_self(codecs)
708
+ return @_container.AEM_pack_self(codecs)
709
+ end
710
+
711
+ def AEM_resolve(obj)
712
+ return @_container.AEM_resolve(obj).send(:elements, @AEM_want)
713
+ end
714
+ end
715
+
716
+
717
+ ###################################
718
+ # Unresolved reference
719
+
720
+ class DeferredSpecifier < Query
721
+ def initialize(desc, codecs)
722
+ @_ref = nil
723
+ @_desc = desc
724
+ @_codecs = codecs
725
+ end
726
+
727
+ def _real_ref
728
+ if not @_ref
729
+ @_ref = @_codecs.fully_unpack_object_specifier(@_desc)
730
+ end
731
+ return @_ref
732
+ end
733
+
734
+ def AEM_true_self
735
+ return self
736
+ end
737
+
738
+ def to_s
739
+ return _real_ref.to_s
740
+ end
741
+
742
+ def AEM_root
743
+ return _real_ref.AEM_root
744
+ end
745
+
746
+ def AEM_resolve(obj)
747
+ return _real_ref.AEM_resolve(obj)
748
+ end
749
+ end
750
+
751
+
752
+ ######################################################################
753
+ # TEST CLAUSES
754
+ ######################################################################
755
+
756
+ ###################################
757
+ # Base class
758
+
759
+ class Test < Query
760
+
761
+ # Logical tests
762
+
763
+ def and(operand2, *operands)
764
+ return AND.new([self, operand2] + operands)
765
+ end
766
+
767
+ def or(operand2, * operands)
768
+ return OR.new([self, operand2] + operands)
769
+ end
770
+
771
+ def not
772
+ return NOT.new([self])
773
+ end
774
+ end
775
+
776
+
777
+ ###################################
778
+ # Comparison tests
779
+
780
+ class ComparisonTest < Test
781
+ attr_reader :_operand1, :_operand2
782
+ protected :_operand1, :_operand2
783
+
784
+ def initialize(operand1, operand2)
785
+ super()
786
+ @_operand1 = operand1
787
+ @_operand2 = operand2
788
+ end
789
+
790
+ def to_s
791
+ return "#{@_operand1.inspect}.#{self.class::Name}(#{@_operand2.inspect})"
792
+ end
793
+
794
+ def AEM_resolve(obj)
795
+ return @_operand1.AEM_resolve(obj).send(self.class::Name, @_operand2)
796
+ end
797
+
798
+ def AEM_pack_self(codecs)
799
+ return AEMReference.pack_list_as(KAE::TypeCompDescriptor, [
800
+ [KAE::KeyAEObject1, codecs.pack(@_operand1)],
801
+ [KAE::KeyAECompOperator, self.class::Operator],
802
+ [KAE::KeyAEObject2, codecs.pack(@_operand2)]
803
+ ])
804
+ end
805
+ end
806
+
807
+ ##
808
+
809
+
810
+
811
+ class GreaterThan < ComparisonTest
812
+ Name = :gt
813
+ Operator = AEMReference.pack_enum(KAE::KAEGreaterThan)
814
+ end
815
+
816
+ class GreaterOrEquals < ComparisonTest
817
+ Name = :ge
818
+ Operator = AEMReference.pack_enum(KAE::KAEGreaterThanEquals)
819
+ end
820
+
821
+ class Equals < ComparisonTest
822
+ Name = :eq
823
+ Operator = AEMReference.pack_enum(KAE::KAEEquals)
824
+ end
825
+
826
+ class NotEquals < Equals
827
+ Name = :ne
828
+ OperatorNOT = AEMReference.pack_enum(KAE::KAENOT)
829
+
830
+ def AEM_pack_self(codecs)
831
+ return @_operand1.eq(@_operand2).not.AEM_pack_self(codecs)
832
+ end
833
+ end
834
+
835
+ class LessThan < ComparisonTest
836
+ Name = :lt
837
+ Operator = AEMReference.pack_enum(KAE::KAELessThan)
838
+ end
839
+
840
+ class LessOrEquals < ComparisonTest
841
+ Name = :le
842
+ Operator = AEMReference.pack_enum(KAE::KAELessThanEquals)
843
+ end
844
+
845
+ class BeginsWith < ComparisonTest
846
+ Name = :begins_with
847
+ Operator = AEMReference.pack_enum(KAE::KAEBeginsWith)
848
+ end
849
+
850
+ class EndsWith < ComparisonTest
851
+ Name = :ends_with
852
+ Operator = AEMReference.pack_enum(KAE::KAEEndsWith)
853
+ end
854
+
855
+ class Contains < ComparisonTest
856
+ Name = :contains
857
+ Operator = AEMReference.pack_enum(KAE::KAEContains)
858
+ end
859
+
860
+ class IsIn < Contains
861
+ Name = :is_in
862
+
863
+ def AEM_pack_self(codecs)
864
+ return AEMReference.pack_list_as(KAE::TypeCompDescriptor, [
865
+ [KAE::KeyAEObject1, codecs.pack(@_operand2)],
866
+ [KAE::KeyAECompOperator, self.class::Operator],
867
+ [KAE::KeyAEObject2, codecs.pack(@_operand1)]
868
+ ])
869
+ end
870
+ end
871
+
872
+ ###################################
873
+ # Logical tests
874
+
875
+ class LogicalTest < Test
876
+ attr_reader :_operands
877
+ protected :_operands
878
+
879
+ def initialize(operands)
880
+ super()
881
+ @_operands = operands
882
+ end
883
+
884
+ def to_s
885
+ op_str = (@_operands[1, @_operands.length].collect { |o| o.inspect }).join(', ')
886
+ return "#{@_operands[0].inspect}.#{self.class::Name}(#{op_str})"
887
+ end
888
+
889
+ def AEM_resolve(obj)
890
+ return @_operands[0].AEM_resolve(obj).send(self.class::Name, *@_operands[1, @_operands.length])
891
+ end
892
+
893
+ def AEM_pack_self(codecs)
894
+ return AEMReference.pack_list_as(KAE::TypeLogicalDescriptor, [
895
+ [KAE::KeyAELogicalOperator, self.class::Operator],
896
+ [KAE::KeyAELogicalTerms, codecs.pack(@_operands)]
897
+ ])
898
+ end
899
+ end
900
+
901
+ ##
902
+
903
+ class AND < LogicalTest
904
+ Operator = AEMReference.pack_enum(KAE::KAEAND)
905
+ Name = :and
906
+ end
907
+
908
+ class OR < LogicalTest
909
+ Operator = AEMReference.pack_enum(KAE::KAEOR)
910
+ Name = :or
911
+ end
912
+
913
+ class NOT < LogicalTest
914
+ Operator = AEMReference.pack_enum(KAE::KAENOT)
915
+ Name = :not
916
+
917
+ def to_s
918
+ return "#{@_operands[0].inspect}.not"
919
+ end
920
+
921
+ def AEM_resolve(obj)
922
+ return @_operands[0].AEM_resolve(obj).send(:not)
923
+ end
924
+ end
925
+
926
+
927
+ ######################################################################
928
+ # REFERENCE ROOTS
929
+ ######################################################################
930
+
931
+ ###################################
932
+ # Base class
933
+
934
+ class ReferenceRoot < PositionSpecifier
935
+
936
+ def initialize
937
+ super(nil, nil, nil)
938
+ end
939
+
940
+ def to_s
941
+ return "AEM.#{self.class::Name}"
942
+ end
943
+
944
+ def _pack_self(codecs)
945
+ return self.class::Type
946
+ end
947
+
948
+ def AEM_root
949
+ return self
950
+ end
951
+
952
+ def AEM_resolve(obj)
953
+ return obj.send(self.class::Name)
954
+ end
955
+ end
956
+
957
+
958
+ ###################################
959
+ # Concrete classes
960
+
961
+ class ApplicationRoot < ReferenceRoot
962
+ # Reference base; represents an application's application object. Used to construct full references.
963
+
964
+ # Syntax: AEM.app
965
+
966
+ Name = :app
967
+ Type = AE::AEDesc.new(KAE::TypeNull, '')
968
+ end
969
+
970
+
971
+ class CurrentContainer < ReferenceRoot
972
+ # Reference base; represents elements' container object. Used to construct by-range references.
973
+
974
+ # Syntax: AEM.con
975
+
976
+ Name = :con
977
+ Type = AE::AEDesc.new(KAE::TypeCurrentContainer, '')
978
+ end
979
+
980
+
981
+ class ObjectBeingExamined < ReferenceRoot
982
+ # Reference base; represents an element to be tested. Used to construct by-filter references.
983
+
984
+ # Syntax: AEM.its
985
+
986
+ Name = :its
987
+ Type = AE::AEDesc.new(KAE::TypeObjectBeingExamined, '')
988
+ end
989
+
990
+
991
+ class CustomRoot < ReferenceRoot
992
+ # Reference base; represents an arbitrary root object, e.g. an AEAddressDesc in a fully qualified reference
993
+
994
+ # Syntax: AEM.custom_root(value)
995
+
996
+ def initialize(value)
997
+ @value = value
998
+ super()
999
+ end
1000
+
1001
+ def to_s
1002
+ return "AEM.custom_root(#{@value.inspect})"
1003
+ end
1004
+
1005
+ def _pack_self(codecs)
1006
+ return codecs.pack(@value)
1007
+ end
1008
+
1009
+ def AEM_resolve(obj)
1010
+ return obj.send(:custom_root, @value)
1011
+ end
1012
+ end
1013
+
1014
+
1015
+ ###################################
1016
+ # Reference root objects; used to construct new specifiers, e.g. AEM.app.property('pnam')
1017
+
1018
+ App = ApplicationRoot.new
1019
+ Con = CurrentContainer.new
1020
+ Its = ObjectBeingExamined.new
1021
+ end
1022
+