rb-scrpt 1.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (96) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGES +497 -0
  3. data/README.md +8 -0
  4. data/Rakefile +32 -0
  5. data/doc/aem-manual/01_introduction.html +60 -0
  6. data/doc/aem-manual/02_apioverview.html +107 -0
  7. data/doc/aem-manual/03_packingandunpackingdata.html +135 -0
  8. data/doc/aem-manual/04_references.html +409 -0
  9. data/doc/aem-manual/05_targetingapplications.html +164 -0
  10. data/doc/aem-manual/06_buildingandsendingevents.html +229 -0
  11. data/doc/aem-manual/07_findapp.html +63 -0
  12. data/doc/aem-manual/08_examples.html +94 -0
  13. data/doc/aem-manual/aemreferenceinheritance.gif +0 -0
  14. data/doc/aem-manual/index.html +56 -0
  15. data/doc/appscript-manual/01_introduction.html +94 -0
  16. data/doc/appscript-manual/02_aboutappscripting.html +247 -0
  17. data/doc/appscript-manual/03_quicktutorial.html +167 -0
  18. data/doc/appscript-manual/04_gettinghelp.html +188 -0
  19. data/doc/appscript-manual/05_keywordconversion.html +106 -0
  20. data/doc/appscript-manual/06_classesandenums.html +192 -0
  21. data/doc/appscript-manual/07_applicationobjects.html +211 -0
  22. data/doc/appscript-manual/08_realvsgenericreferences.html +96 -0
  23. data/doc/appscript-manual/09_referenceforms.html +241 -0
  24. data/doc/appscript-manual/10_referenceexamples.html +154 -0
  25. data/doc/appscript-manual/11_applicationcommands.html +245 -0
  26. data/doc/appscript-manual/12_commandexamples.html +138 -0
  27. data/doc/appscript-manual/13_performanceissues.html +142 -0
  28. data/doc/appscript-manual/14_notes.html +80 -0
  29. data/doc/appscript-manual/application_architecture.gif +0 -0
  30. data/doc/appscript-manual/application_architecture2.gif +0 -0
  31. data/doc/appscript-manual/finder_to_textedit_event.gif +0 -0
  32. data/doc/appscript-manual/index.html +62 -0
  33. data/doc/appscript-manual/relationships_example.gif +0 -0
  34. data/doc/appscript-manual/ruby_to_itunes_event.gif +0 -0
  35. data/doc/full.css +106 -0
  36. data/doc/index.html +45 -0
  37. data/doc/mactypes-manual/01_introduction.html +54 -0
  38. data/doc/mactypes-manual/02_aliasclass.html +124 -0
  39. data/doc/mactypes-manual/03_fileurlclass.html +126 -0
  40. data/doc/mactypes-manual/04_unitsclass.html +100 -0
  41. data/doc/mactypes-manual/index.html +53 -0
  42. data/doc/osax-manual/01_introduction.html +67 -0
  43. data/doc/osax-manual/02_interface.html +147 -0
  44. data/doc/osax-manual/03_examples.html +73 -0
  45. data/doc/osax-manual/04_notes.html +61 -0
  46. data/doc/osax-manual/index.html +53 -0
  47. data/doc/rb-appscript-logo.png +0 -0
  48. data/extconf.rb +63 -0
  49. data/rb-scrpt.gemspec +16 -0
  50. data/sample/AB_export_vcard.rb +31 -0
  51. data/sample/AB_list_people_with_emails.rb +13 -0
  52. data/sample/Add_iCal_event.rb +21 -0
  53. data/sample/Create_daily_iCal_todos.rb +75 -0
  54. data/sample/Export_Address_Book_phone_numbers.rb +59 -0
  55. data/sample/Hello_world.rb +21 -0
  56. data/sample/List_iTunes_playlist_names.rb +11 -0
  57. data/sample/Make_Mail_message.rb +33 -0
  58. data/sample/Open_file_in_TextEdit.rb +13 -0
  59. data/sample/Organize_Mail_messages.rb +61 -0
  60. data/sample/Print_folder_tree.rb +16 -0
  61. data/sample/Select_all_HTML_files.rb +14 -0
  62. data/sample/Set_iChat_status.rb +24 -0
  63. data/sample/Simple_Finder_GUI_Scripting.rb +18 -0
  64. data/sample/Stagger_Finder_windows.rb +25 -0
  65. data/sample/TextEdit_demo.rb +130 -0
  66. data/sample/iTunes_top40_to_html.rb +69 -0
  67. data/src/SendThreadSafe.c +380 -0
  68. data/src/SendThreadSafe.h +139 -0
  69. data/src/lib/_aem/aemreference.rb +1021 -0
  70. data/src/lib/_aem/codecs.rb +661 -0
  71. data/src/lib/_aem/connect.rb +205 -0
  72. data/src/lib/_aem/encodingsupport.rb +74 -0
  73. data/src/lib/_aem/findapp.rb +85 -0
  74. data/src/lib/_aem/mactypes.rb +250 -0
  75. data/src/lib/_aem/send.rb +279 -0
  76. data/src/lib/_aem/typewrappers.rb +59 -0
  77. data/src/lib/_appscript/defaultterminology.rb +277 -0
  78. data/src/lib/_appscript/referencerenderer.rb +242 -0
  79. data/src/lib/_appscript/reservedkeywords.rb +116 -0
  80. data/src/lib/_appscript/safeobject.rb +250 -0
  81. data/src/lib/_appscript/terminology.rb +470 -0
  82. data/src/lib/aem.rb +253 -0
  83. data/src/lib/kae.rb +1489 -0
  84. data/src/lib/osax.rb +659 -0
  85. data/src/lib/rb-scrpt.rb +1073 -0
  86. data/src/lib/version.rb +3 -0
  87. data/src/rbae.c +979 -0
  88. data/test/README +3 -0
  89. data/test/test_aemreference.rb +115 -0
  90. data/test/test_appscriptcommands.rb +149 -0
  91. data/test/test_appscriptreference.rb +103 -0
  92. data/test/test_codecs.rb +181 -0
  93. data/test/test_findapp.rb +23 -0
  94. data/test/test_mactypes.rb +77 -0
  95. data/test/test_osax.rb +52 -0
  96. metadata +146 -0
@@ -0,0 +1,1021 @@
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 then
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