rb-appscript 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (83) hide show
  1. data/CHANGES +243 -0
  2. data/LICENSE +1 -0
  3. data/README +42 -0
  4. data/TODO +31 -0
  5. data/doc/aem-manual/01_introduction.html +48 -0
  6. data/doc/aem-manual/02_apioverview.html +89 -0
  7. data/doc/aem-manual/03_packingandunpackingdata.html +98 -0
  8. data/doc/aem-manual/04_references.html +401 -0
  9. data/doc/aem-manual/05_targettingapplications.html +133 -0
  10. data/doc/aem-manual/06_buildingandsendingevents.html +175 -0
  11. data/doc/aem-manual/07_findapp.html +54 -0
  12. data/doc/aem-manual/08_examples.html +85 -0
  13. data/doc/aem-manual/09_notes.html +41 -0
  14. data/doc/aem-manual/aemreferenceinheritance.gif +0 -0
  15. data/doc/aem-manual/full.css +21 -0
  16. data/doc/aem-manual/index.html +43 -0
  17. data/doc/appscript-manual/01_introduction.html +82 -0
  18. data/doc/appscript-manual/02_aboutappscripting.html +244 -0
  19. data/doc/appscript-manual/03_quicktutorial.html +154 -0
  20. data/doc/appscript-manual/04_gettinghelp.html +101 -0
  21. data/doc/appscript-manual/05_keywordconversion.html +91 -0
  22. data/doc/appscript-manual/06_classesandenums.html +174 -0
  23. data/doc/appscript-manual/07_applicationobjects.html +181 -0
  24. data/doc/appscript-manual/08_realvsgenericreferences.html +86 -0
  25. data/doc/appscript-manual/09_referenceforms.html +232 -0
  26. data/doc/appscript-manual/10_referenceexamples.html +142 -0
  27. data/doc/appscript-manual/11_applicationcommands.html +204 -0
  28. data/doc/appscript-manual/12_commandexamples.html +129 -0
  29. data/doc/appscript-manual/13_performanceissues.html +115 -0
  30. data/doc/appscript-manual/14_problemapps.html +193 -0
  31. data/doc/appscript-manual/15_notes.html +84 -0
  32. data/doc/appscript-manual/application_architecture.gif +0 -0
  33. data/doc/appscript-manual/application_architecture2.gif +0 -0
  34. data/doc/appscript-manual/finder_to_textedit_event.gif +0 -0
  35. data/doc/appscript-manual/full.css +21 -0
  36. data/doc/appscript-manual/index.html +49 -0
  37. data/doc/appscript-manual/relationships_example.gif +0 -0
  38. data/doc/appscript-manual/ruby_to_itunes_event.gif +0 -0
  39. data/doc/index.html +30 -0
  40. data/doc/mactypes-manual/index.html +216 -0
  41. data/doc/osax-manual/index.html +169 -0
  42. data/extconf.rb +54 -0
  43. data/misc/adobeunittypes.rb +14 -0
  44. data/misc/dump.rb +72 -0
  45. data/rb-appscript.gemspec +20 -0
  46. data/sample/AB_list_people_with_emails.rb +8 -0
  47. data/sample/Create_daily_iCal_todos.rb +72 -0
  48. data/sample/Hello_world.rb +9 -0
  49. data/sample/List_iTunes_playlist_names.rb +7 -0
  50. data/sample/Make_Mail_message.rb +29 -0
  51. data/sample/Open_file_in_TextEdit.rb +9 -0
  52. data/sample/Organize_Mail_messages.rb +57 -0
  53. data/sample/Print_folder_tree.rb +12 -0
  54. data/sample/Select_all_HTML_files.rb +8 -0
  55. data/sample/Set_iChat_status.rb +20 -0
  56. data/sample/Simple_Finder_GUI_Scripting.rb +14 -0
  57. data/sample/Stagger_Finder_windows.rb +21 -0
  58. data/sample/TextEdit_demo.rb +126 -0
  59. data/sample/iTunes_top40_to_html.rb +64 -0
  60. data/src/lib/_aem/aemreference.rb +1006 -0
  61. data/src/lib/_aem/codecs.rb +617 -0
  62. data/src/lib/_aem/connect.rb +100 -0
  63. data/src/lib/_aem/findapp.rb +83 -0
  64. data/src/lib/_aem/mactypes.rb +228 -0
  65. data/src/lib/_aem/send.rb +257 -0
  66. data/src/lib/_aem/typewrappers.rb +57 -0
  67. data/src/lib/_appscript/defaultterminology.rb +245 -0
  68. data/src/lib/_appscript/referencerenderer.rb +132 -0
  69. data/src/lib/_appscript/reservedkeywords.rb +107 -0
  70. data/src/lib/_appscript/terminology.rb +314 -0
  71. data/src/lib/aem.rb +216 -0
  72. data/src/lib/appscript.rb +830 -0
  73. data/src/lib/kae.rb +1484 -0
  74. data/src/lib/osax.rb +171 -0
  75. data/src/rbae.c +766 -0
  76. data/test/README +1 -0
  77. data/test/test_aemreference.rb +112 -0
  78. data/test/test_appscriptreference.rb +102 -0
  79. data/test/test_codecs.rb +159 -0
  80. data/test/test_findapp.rb +24 -0
  81. data/test/test_mactypes.rb +67 -0
  82. data/test/testall.sh +9 -0
  83. metadata +143 -0
@@ -0,0 +1,1006 @@
1
+ #!/usr/local/bin/ruby
2
+ # Copyright (C) 2006 HAS.
3
+ # Released under MIT License.
4
+
5
+ ######################################################################
6
+ # Endianness support
7
+
8
+ module BigEndianPackers
9
+
10
+ def pack_type(code)
11
+ return AE::AEDesc.new(KAE::TypeType, code)
12
+ end
13
+
14
+ def pack_enum(code)
15
+ return AE::AEDesc.new(KAE::TypeEnumeration, code)
16
+ end
17
+
18
+ def pack_absolute_ordinal(code)
19
+ return AE::AEDesc.new(KAE::TypeAbsoluteOrdinal, code)
20
+ end
21
+
22
+ end
23
+
24
+
25
+ module SmallEndianPackers
26
+
27
+ def pack_type(code)
28
+ return AE::AEDesc.new(KAE::TypeType, code.reverse)
29
+ end
30
+
31
+ def pack_enum(code)
32
+ return AE::AEDesc.new(KAE::TypeEnumeration, code.reverse)
33
+ end
34
+
35
+ def pack_absolute_ordinal(code)
36
+ return AE::AEDesc.new(KAE::TypeAbsoluteOrdinal, code.reverse)
37
+ end
38
+
39
+ end
40
+
41
+
42
+ ######################################################################
43
+
44
+ module AEMReference
45
+
46
+ require "ae"
47
+ require "kae"
48
+
49
+ ######################################################################
50
+ # SUPPORT FUNCTIONS
51
+ ######################################################################
52
+
53
+ extend([1].pack('s') == "\001\000" ? SmallEndianPackers : BigEndianPackers)
54
+
55
+ def AEMReference.pack_list_as(type, lst)
56
+ # used to pack object specifiers, etc.
57
+ # pack key-value pairs into an AEListDesc, then coerce it to the desired type
58
+ # (there are other AEM APIs for packing obj specs, but this way is easiest)
59
+ desc = AE::AEDesc.new_list(true)
60
+ lst.each { |key, value| desc.put_param(key, value) }
61
+ return desc.coerce(type)
62
+ end
63
+
64
+ class CollectComparable
65
+ # obtains the data needed to perform equality tests on references
66
+ # uses AEM_resolve to walk a reference, building up a list of method call names and their arguments
67
+
68
+ attr_reader :result
69
+
70
+ def initialize
71
+ @result = []
72
+ end
73
+
74
+ def method_missing(name, *args)
75
+ self.result.push([name] + args)
76
+ return self
77
+ end
78
+ end
79
+
80
+ ######################################################################
81
+ # BASE CLASS
82
+ ######################################################################
83
+
84
+ class Base
85
+
86
+ def AEM_comparable
87
+ # called by Base#==; returns the data needed to compare two aem references
88
+ if not @_comparable
89
+ collector = AEMReference::CollectComparable.new
90
+ AEM_resolve(collector)
91
+ @_comparable = collector.result
92
+ end
93
+ return @_comparable
94
+ end
95
+
96
+ def ==(val)
97
+ return (self.equal?(val) or (
98
+ self.class == val.class and
99
+ self.AEM_comparable == val.AEM_comparable))
100
+ end
101
+
102
+ alias_method :eql?, :==
103
+
104
+ def hash
105
+ return to_s.hash
106
+ end
107
+
108
+ def inspect
109
+ return to_s
110
+ end
111
+ end
112
+
113
+
114
+ ######################################################################
115
+ # BASE CLASS FOR ALL REFERENCE FORMS
116
+ ######################################################################
117
+
118
+ class Specifier < Base
119
+ # Base class for insertion specifier and all object specifier classes.
120
+
121
+ attr_reader :_key, :_container
122
+ protected :_key, :_container
123
+
124
+ def initialize(container, key)
125
+ @_container = container
126
+ @_key = key
127
+ end
128
+
129
+ def AEM_root
130
+ # Get reference's root node. Used by range and filter specifiers when determining type of reference
131
+ # passed as argument(s): range specifiers require absolute (app-based) or container (con-based)
132
+ # references; filter specifiers require an item (its-based) reference.
133
+ return @_container.AEM_root
134
+ end
135
+
136
+ def AEM_true_self
137
+ # Called by specifier classes when creating a reference to sub-element(s) of the current reference.
138
+ # - 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.
139
+ # - All other specifiers simply return themselves.
140
+ #
141
+ #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.
142
+ return self
143
+ end
144
+
145
+ def AEM_set_desc(desc)
146
+ @_desc = desc
147
+ end
148
+
149
+ def AEM_pack_self(codecs)
150
+ # Pack this Specifier; called by Codecs#pack, which passes itself so that specifiers in this reference can pack their selectors.
151
+ if not @_desc
152
+ @_desc = _pack_self(codecs) # once packed, cache this AEDesc for efficiency
153
+ end
154
+ return @_desc
155
+ end
156
+ end
157
+
158
+
159
+ ######################################################################
160
+ # INSERTION POINT REFERENCE FORM
161
+ ######################################################################
162
+
163
+ class InsertionSpecifier < Specifier
164
+ # A reference to an element insertion point.
165
+
166
+ # Syntax: all_elements_ref.start / all_elements_ref.end / element_ref.before / element_ref.after
167
+
168
+ def initialize(container, key, keyname)
169
+ super(container, key)
170
+ @_keyname = keyname
171
+ end
172
+
173
+ def to_s
174
+ return "#{@_container}.#{@_keyname}"
175
+ end
176
+
177
+ def _pack_self(codecs)
178
+ return AEMReference.pack_list_as(KAE::TypeInsertionLoc, [
179
+ [KAE::KeyAEObject, @_container.AEM_pack_self(codecs)],
180
+ [KAE::KeyAEPosition, @_key],
181
+ ])
182
+ end
183
+
184
+ def AEM_resolve(obj)
185
+ return @_container.AEM_resolve(obj).send(@_keyname)
186
+ end
187
+ end
188
+
189
+
190
+ ######################################################################
191
+ # BASE CLASS FOR ALL OBJECT REFERENCE FORMS
192
+ ######################################################################
193
+
194
+ class PositionSpecifier < Specifier
195
+ # All property and element reference forms inherit from this class. It provides most
196
+ # of the reference building methods; the rest are supplied by MultipleElements to
197
+ # those reference forms for which they're valid.
198
+
199
+ # Note that comparison and logic 'operator' methods are implemented on this class
200
+ # - these are only for use in constructing its-based references and shouldn't be used
201
+ # on app- and con-based references. Aem doesn't enforce this rule itself so as to
202
+ # minimise runtime overhead (the target application will raise an error if the user
203
+ # does something foolish).
204
+
205
+ Beginning = AEMReference.pack_enum(KAE::KAEBeginning)
206
+ End = AEMReference.pack_enum(KAE::KAEEnd)
207
+ Before = AEMReference.pack_enum(KAE::KAEBefore)
208
+ After = AEMReference.pack_enum(KAE::KAEAfter)
209
+ Previous = AEMReference.pack_enum(KAE::KAEPrevious)
210
+ Next = AEMReference.pack_enum(KAE::KAENext)
211
+
212
+ attr_reader :AEM_want
213
+
214
+ def initialize(wantcode, container, key)
215
+ @AEM_want = wantcode
216
+ super(container, key)
217
+ end
218
+
219
+ def to_s
220
+ return "#{@_container}.#{self.class::By}(#{@_key.inspect})"
221
+ end
222
+
223
+ def _pack_self(codecs)
224
+ return AEMReference.pack_list_as(KAE::TypeObjectSpecifier, [
225
+ [KAE::KeyAEDesiredClass, AEMReference.pack_type(@AEM_want)],
226
+ [KAE::KeyAEKeyForm, self.class::KeyForm],
227
+ [KAE::KeyAEKeyData, _pack_key(codecs)],
228
+ [KAE::KeyAEContainer, @_container.AEM_pack_self(codecs)],
229
+ ])
230
+ end
231
+
232
+ # Comparison tests; these should only be used on its-based references:
233
+
234
+ # Each of these methods returns a ComparisonTest subclass
235
+
236
+ def gt(val)
237
+ return GreaterThan.new(self, val)
238
+ end
239
+
240
+ def ge(val)
241
+ return GreaterOrEquals.new(self, val)
242
+ end
243
+
244
+ def eq(val)
245
+ return Equals.new(self, val)
246
+ end
247
+
248
+ def ne(val)
249
+ return NotEquals.new(self, val)
250
+ end
251
+
252
+ def lt(val)
253
+ return LessThan.new(self, val)
254
+ end
255
+
256
+ def le(val)
257
+ return LessOrEquals.new(self, val)
258
+ end
259
+
260
+ def starts_with(val)
261
+ return StartsWith.new(self, val)
262
+ end
263
+
264
+ def ends_with(val)
265
+ return EndsWith.new(self, val)
266
+ end
267
+
268
+ def contains(val)
269
+ return Contains.new(self, val)
270
+ end
271
+
272
+ def is_in(val)
273
+ return IsIn.new(self, val)
274
+ end
275
+
276
+ # Logic tests; these should only be used on its-based references:
277
+
278
+ # Note: these are convenience methods allowing boolean tests to be written in shorthand form;
279
+ # e.g. AEM.its.visible.and(...) will automatically expand to AEM.its.visible.eq(true).and(...)
280
+ # The Test class implements the actual logic test methods
281
+
282
+ def and(*operands)
283
+ return Equals.new(self, true).and(*operands)
284
+ end
285
+
286
+ def or(*operands)
287
+ return Equals.new(self, true).or(*operands)
288
+ end
289
+
290
+ def not
291
+ return Equals.new(self, true).not
292
+ end
293
+
294
+
295
+ # Insertion references:
296
+
297
+ # Thes can be called on any kind of element reference, and also on property references where the
298
+ # property represents a one-to-one relationship, e.g. textedit.documents[1].text.end is valid
299
+
300
+ def start
301
+ return InsertionSpecifier.new(self, Beginning, 'start')
302
+ end
303
+
304
+ def end
305
+ return InsertionSpecifier.new(self, End, 'end')
306
+ end
307
+
308
+ def before
309
+ return InsertionSpecifier.new(self, Before, 'before')
310
+ end
311
+
312
+ def after
313
+ return InsertionSpecifier.new(self, After, 'after')
314
+ end
315
+
316
+ # Property and element references can be used on any type of object reference:
317
+
318
+ def property(propertycode)
319
+ return Property.new(KAE::CProperty, self, propertycode)
320
+ end
321
+
322
+ def userproperty(name)
323
+ return UserProperty.new(KAE::CProperty, self, name)
324
+ end
325
+
326
+ def elements(elementcode)
327
+ return AllElements.new(elementcode, self)
328
+ end
329
+
330
+ # Relative position references
331
+
332
+ # these are unlikely to work on one-to-one relationships - but what the hey, putting them here
333
+ # simplifies the class structure a bit. As with all reference forms, it's mostly left to the client to
334
+ # ensure the references they construct can be understood by the target application.
335
+
336
+ def previous(elementcode)
337
+ return ElementByRelativePosition.new(elementcode, self, Previous, 'previous')
338
+ end
339
+
340
+ def next(elementcode)
341
+ return ElementByRelativePosition.new(elementcode, self, Next, 'next')
342
+ end
343
+ end
344
+
345
+
346
+ ######################################################################
347
+ # PROPERTY REFERENCE FORMS
348
+ ######################################################################
349
+
350
+ class Property < PositionSpecifier
351
+ # A reference to a user-defined property, where code is the code identifying the property.
352
+
353
+ # Syntax: ref.property(code)
354
+
355
+ By = 'property'
356
+ KeyForm = AEMReference.pack_enum(KAE::FormPropertyID)
357
+
358
+ def _pack_key(codecs)
359
+ return AEMReference.pack_type(@_key)
360
+ end
361
+
362
+ def AEM_resolve(obj)
363
+ return @_container.AEM_resolve(obj).property(@_key)
364
+ end
365
+ end
366
+
367
+
368
+ class UserProperty < PositionSpecifier
369
+ # A reference to a user-defined property, where name is a string representing the property's name.
370
+
371
+ # Scriptable applications shouldn't use this reference form, but OSA script applets can.
372
+ # Note that OSA languages may have additional rules regarding case sensitivity/conversion.
373
+
374
+ # Syntax: ref.userproperty(name)
375
+
376
+ By = 'userproperty'
377
+ KeyForm = AEMReference.pack_enum('usrp')
378
+
379
+ def _pack_key(codecs)
380
+ return codecs.pack(@_key).coerce(KAE::TypeChar)
381
+ end
382
+
383
+ def AEM_resolve(obj)
384
+ return @_container.AEM_resolve(obj).userproperty(@_key)
385
+ end
386
+ end
387
+
388
+
389
+ ######################################################################
390
+ # ELEMENT REFERENCE FORMS
391
+ ######################################################################
392
+
393
+ ###################################
394
+ # Single elements
395
+
396
+ class SingleElement < PositionSpecifier
397
+ # Base class for all single element specifiers.
398
+
399
+ def initialize(wantcode, container, key)
400
+ super(wantcode, container.AEM_true_self, key)
401
+ end
402
+
403
+ def _pack_key(codecs)
404
+ return codecs.pack(@_key)
405
+ end
406
+
407
+ def AEM_resolve(obj)
408
+ return @_container.AEM_resolve(obj).send(self.class::By, @_key)
409
+ end
410
+ end
411
+
412
+
413
+ #######
414
+
415
+ class ElementByName < SingleElement
416
+ # A reference to a single element by its name, where name is a string.
417
+
418
+ # Syntax: elements_ref.by_name(string)
419
+
420
+ By = 'by_name'
421
+ KeyForm = AEMReference.pack_enum(KAE::FormName)
422
+ end
423
+
424
+
425
+ class ElementByIndex < SingleElement
426
+ # A reference to a single element by its index, where index is a non-zero whole number.
427
+
428
+ # Syntax: elements_ref.by_index(integer)
429
+
430
+ # Note that a few apps (e.g. Finder) may allow other values as well (e.g. Aliases/FSRefs)
431
+
432
+ By = 'by_index'
433
+ KeyForm = AEMReference.pack_enum(KAE::FormAbsolutePosition)
434
+ end
435
+
436
+
437
+ class ElementByID < SingleElement
438
+ # A reference to a single element by its id.
439
+
440
+ # Syntax: elements_ref.by_id(anything)
441
+
442
+ By = 'by_id'
443
+ KeyForm = AEMReference.pack_enum(KAE::FormUniqueID)
444
+ end
445
+
446
+ ##
447
+
448
+ class ElementByOrdinal < SingleElement
449
+ # A reference to first/middle/last/any element.
450
+
451
+ # Syntax: elements_ref.first / elements_ref.middle / elements_ref.last / elements_ref.any
452
+
453
+ KeyForm = AEMReference.pack_enum(KAE::FormAbsolutePosition)
454
+
455
+ def initialize(wantcode, container, key, keyname)
456
+ @_keyname = keyname
457
+ super(wantcode, container, key)
458
+ end
459
+
460
+ def to_s
461
+ return "#{@_container}.#{@_keyname}"
462
+ end
463
+
464
+ def AEM_resolve(obj)
465
+ return @_container.AEM_resolve(obj).send(@_keyname)
466
+ end
467
+ end
468
+
469
+
470
+ class ElementByRelativePosition < PositionSpecifier
471
+ # A relative reference to previous/next element, where code
472
+ # is the class code of element to get (e.g. 'docu').
473
+
474
+ # Syntax: elements_ref.previous(code) / elements_ref.next(code)
475
+
476
+ # Note: this class subclasses PositionSpecifier, not SingleElement,
477
+ # as it needs the container reference intact. (SingleElement#initialize
478
+ # calls the container's AEM_true_self method, which breaks up
479
+ # AllElements specifiers - not what we want here.)
480
+
481
+ KeyForm = AEMReference.pack_enum(KAE::FormRelativePosition)
482
+
483
+ def initialize(wantcode, container, key, keyname)
484
+ @_keyname = keyname
485
+ super(wantcode, container, key)
486
+ end
487
+
488
+ def _pack_key(codecs)
489
+ return codecs.pack(@_key)
490
+ end
491
+
492
+ def to_s
493
+ return "#{@_container}.#{@_keyname}(#{@AEM_want.inspect})"
494
+ end
495
+
496
+ def AEM_resolve(obj)
497
+ return @_container.AEM_resolve(obj).send(@_keyname, @AEM_want)
498
+ end
499
+ end
500
+
501
+
502
+ ###################################
503
+ # Multiple elements
504
+
505
+ class MultipleElements < PositionSpecifier
506
+ # Base class for all multiple element specifiers.
507
+
508
+ First = AEMReference.pack_absolute_ordinal(KAE::KAEFirst)
509
+ Middle = AEMReference.pack_absolute_ordinal(KAE::KAEMiddle)
510
+ Last = AEMReference.pack_absolute_ordinal(KAE::KAELast)
511
+ Any = AEMReference.pack_absolute_ordinal(KAE::KAEAny)
512
+
513
+ def first
514
+ return ElementByOrdinal.new(@AEM_want, self, First, 'first')
515
+ end
516
+
517
+ def middle
518
+ return ElementByOrdinal.new(@AEM_want, self, Middle, 'middle')
519
+ end
520
+
521
+ def last
522
+ return ElementByOrdinal.new(@AEM_want, self, Last, 'last')
523
+ end
524
+
525
+ def any
526
+ return ElementByOrdinal.new(@AEM_want, self, Any, 'any')
527
+ end
528
+
529
+ def by_name(name)
530
+ return ElementByName.new(@AEM_want, self, name)
531
+ end
532
+
533
+ def by_index(index)
534
+ return ElementByIndex.new(@AEM_want, self, index)
535
+ end
536
+
537
+ def by_id(id)
538
+ return ElementByID.new(@AEM_want, self, id)
539
+ end
540
+
541
+ def by_range(start, stop)
542
+ return ElementsByRange.new(@AEM_want, self, [start, stop])
543
+ end
544
+
545
+ def by_filter(expression)
546
+ return ElementsByFilter.new(@AEM_want, self, expression)
547
+ end
548
+ end
549
+
550
+
551
+ #######
552
+
553
+ class ElementsByRange < MultipleElements
554
+ # A reference to a range of elements
555
+
556
+ # Syntax: elements_ref.by_range(start, stop)
557
+
558
+ # The start and stop args are con-based relative references to the first and last elements in range.
559
+ # Note that absolute (app-based) references are also acceptable.
560
+
561
+ KeyForm = AEMReference.pack_enum(KAE::FormRange)
562
+
563
+ def initialize(wantcode, container, key)
564
+ key.each do |item|
565
+ if not (item.is_a?(Specifier) and item.AEM_root != Its)
566
+ raise TypeError, "Bad selector: not an application (app) or container (con) based reference: #{item.inspect}"
567
+ end
568
+ end
569
+ super(wantcode, container.AEM_true_self, key)
570
+ end
571
+
572
+ def to_s
573
+ return "#{@_container}.by_range(#{@_key[0].inspect}, #{@_key[1].inspect})"
574
+ end
575
+
576
+ def _pack_key(codecs)
577
+ return AEMReference.pack_list_as(KAE::TypeRangeDescriptor, [
578
+ [KAE::KeyAERangeStart, codecs.pack(@_key[0])],
579
+ [KAE::KeyAERangeStop, codecs.pack(@_key[1])]
580
+ ])
581
+ end
582
+
583
+ def AEM_resolve(obj)
584
+ return @_container.AEM_resolve(obj).by_range(*@_key)
585
+ end
586
+ end
587
+
588
+
589
+ class ElementsByFilter < MultipleElements
590
+ # A reference to all elements that match a condition
591
+
592
+ # Syntax: elements_ref.by_filter(test)
593
+
594
+ # The test argument is a Test object constructed from an its-based reference.
595
+ # For convenience, an its-based reference can also be passed directly - this will
596
+ # be expanded to a Boolean equality test, e.g. AEM.its.visible -> AEM.its.visible.eq(true)
597
+
598
+ KeyForm = AEMReference.pack_enum(KAE::FormTest)
599
+
600
+ def initialize(wantcode, container, key)
601
+ if not key.is_a?(Test)
602
+ if key.is_a?(Specifier) and key.AEM_root == Its
603
+ key = key.eq(true)
604
+ else
605
+ raise TypeError, "Bad selector: not a test (its) based specifier: #{key.inspect}"
606
+ end
607
+ end
608
+ super(wantcode, container.AEM_true_self, key)
609
+ end
610
+
611
+ def to_s
612
+ return "#{@_container}.by_filter(#{@_key.inspect})"
613
+ end
614
+
615
+ def _pack_key(codecs)
616
+ return codecs.pack(@_key)
617
+ end
618
+
619
+ def AEM_resolve(obj)
620
+ return @_container.AEM_resolve(obj).by_filter(@_key)
621
+ end
622
+ end
623
+
624
+
625
+ class AllElements < MultipleElements
626
+ # A reference to all elements of the container reference.
627
+
628
+ # Syntax: ref.elements(code)
629
+
630
+ # The 'code' argument is the four-character class code of the desired elements (e.g. 'docu').
631
+
632
+ # Note that an AllElements object is a wrapper around an UnkeyedElements object.
633
+ # When sub-selecting these elements, e.g. ref.elements('docu').by_index(1), the AllElements
634
+ # wrapper is ignored and the UnkeyedElements object is used as the basis for the
635
+ # new specifier. e.g.
636
+ #
637
+ # AEM.app.elements('docu') # every document of application
638
+ #
639
+ # produces the following chain:
640
+ #
641
+ # ApplicationRoot -> UnkeyedElements -> AllElements
642
+ #
643
+ # Subselecting these elements, e.g.
644
+ #
645
+ # AEM.app.elements('docu').by_index(1) # document 1 of application
646
+ #
647
+ # produces the following chain:
648
+ #
649
+ # ApplicationRoot -> UnkeyedElements -> ElementByIndex
650
+ #
651
+ # As you can see, the previous UnkeyedElements object is retained, but the AllElements
652
+ # object isn't.
653
+ #
654
+ # The result of all this is that users can legally write a reference to all elements as (e.g.):
655
+ #
656
+ # AEM.app.elements('docu')
657
+ # AS.app.documents
658
+ #
659
+ # instead of:
660
+ #
661
+ # AEM.app.elements('docu').all
662
+ # AS.app.documents.all
663
+ #
664
+ # Compare with some other bridges (e.g. Frontier), where (e.g.) 'ref.documents' is not
665
+ # a legitimate reference in itself, and users must remember to add '.all' in order to specify
666
+ # all elements, or else it won't work correctly. This maps directly onto the underlying AEM
667
+ # API, which is easy to implement but isn't so good for usability. Whereas aem trades
668
+ # a bit of increased internal complexity for a simpler, more intuitive and foolproof external API.
669
+
670
+ KeyForm = AEMReference.pack_enum(KAE::FormAbsolutePosition)
671
+ All = AEMReference.pack_absolute_ordinal(KAE::KAEAll)
672
+
673
+ def initialize(wantcode, container)
674
+ super(wantcode, UnkeyedElements.new(wantcode, container), All)
675
+ end
676
+
677
+ def to_s
678
+ return @_container.to_s
679
+ end
680
+
681
+ def _pack_key(codecs)
682
+ return All
683
+ end
684
+
685
+ def AEM_true_self
686
+ # override default implementation to return the UnkeyedElements object stored inside of this AllElements instance
687
+ return @_container
688
+ end
689
+
690
+ def AEM_resolve(obj)
691
+ return @_container.AEM_resolve(obj) # forward to UnkeyedElements
692
+ end
693
+ end
694
+
695
+
696
+ ######################################################################
697
+ # SHIMS
698
+ ######################################################################
699
+
700
+ ###################################
701
+ # Multiple element shim
702
+
703
+ class UnkeyedElements < Specifier
704
+ # A partial elements reference, containing element code but no keyform/keydata. A shim.
705
+ # User is never exposed to this class directly. See comments in AllElements class.
706
+
707
+ attr_reader :AEM_want, :_container
708
+ protected :AEM_want, :_container
709
+
710
+ def initialize(wantcode, container)
711
+ @AEM_want = wantcode
712
+ @_container = container
713
+ end
714
+
715
+ def to_s
716
+ return "#{@_container}.elements(#{@AEM_want.inspect})"
717
+ end
718
+
719
+ def AEM_pack_self(codecs)
720
+ return @_container.AEM_pack_self(codecs)
721
+ end
722
+
723
+ def AEM_resolve(obj)
724
+ return @_container.AEM_resolve(obj).elements(@AEM_want)
725
+ end
726
+ end
727
+
728
+
729
+ ###################################
730
+ # Unresolved reference
731
+
732
+ class DeferredSpecifier < Base
733
+ def initialize(desc, codecs)
734
+ @_desc = desc
735
+ @_codecs = codecs
736
+ end
737
+
738
+ def _real_ref
739
+ if not @_ref
740
+ @_ref = @_codecs.fully_unpack_object_specifier(@_desc)
741
+ end
742
+ return @_ref
743
+ end
744
+
745
+ def AEM_true_self
746
+ return self
747
+ end
748
+
749
+ def to_s
750
+ return _real_ref.to_s
751
+ end
752
+
753
+ def AEM_root
754
+ return _real_ref.AEM_root
755
+ end
756
+
757
+ def AEM_resolve(obj)
758
+ return _real_ref.AEM_resolve(obj)
759
+ end
760
+ end
761
+
762
+
763
+ ######################################################################
764
+ # TEST CLAUSES
765
+ ######################################################################
766
+
767
+ ###################################
768
+ # Base class
769
+
770
+ class Test < Base
771
+
772
+ # Logical tests
773
+
774
+ def and(operand2, *operands)
775
+ return AND.new([self, operand2] + operands)
776
+ end
777
+
778
+ def or(operand2, * operands)
779
+ return OR.new([self, operand2] + operands)
780
+ end
781
+
782
+ def not
783
+ return NOT.new([self])
784
+ end
785
+ end
786
+
787
+
788
+ ###################################
789
+ # Comparison tests
790
+
791
+ class ComparisonTest < Test
792
+ attr_reader :_operand1, :_operand2
793
+ protected :_operand1, :_operand2
794
+
795
+ def initialize(operand1, operand2)
796
+ @_operand1 = operand1
797
+ @_operand2 = operand2
798
+ end
799
+
800
+ def to_s
801
+ return "#{@_operand1.inspect}.#{self.class::Name}(#{@_operand2.inspect})"
802
+ end
803
+
804
+ def AEM_resolve(obj)
805
+ return @_operand1.AEM_resolve(obj).send(self.class::Name, @_operand2)
806
+ end
807
+
808
+ def AEM_pack_self(codecs)
809
+ return AEMReference.pack_list_as(KAE::TypeCompDescriptor, [
810
+ [KAE::KeyAEObject1, codecs.pack(@_operand1)],
811
+ [KAE::KeyAECompOperator, self.class::Operator],
812
+ [KAE::KeyAEObject2, codecs.pack(@_operand2)]
813
+ ])
814
+ end
815
+ end
816
+
817
+ ##
818
+
819
+
820
+
821
+ class GreaterThan < ComparisonTest
822
+ Name = 'gt'
823
+ Operator = AEMReference.pack_enum(KAE::KAEGreaterThan)
824
+ end
825
+
826
+ class GreaterOrEquals < ComparisonTest
827
+ Name = 'ge'
828
+ Operator = AEMReference.pack_enum(KAE::KAEGreaterThanEquals)
829
+ end
830
+
831
+ class Equals < ComparisonTest
832
+ Name = 'eq'
833
+ Operator = AEMReference.pack_enum(KAE::KAEEquals)
834
+ end
835
+
836
+ class NotEquals < Equals
837
+ Name = 'ne'
838
+ OperatorNOT = AEMReference.pack_enum(KAE::KAENOT)
839
+
840
+ def AEM_pack_self(codecs)
841
+ return @_operand1.eq(@_operand2).not.AEM_pack_self(codecs)
842
+ end
843
+ end
844
+
845
+ class LessThan < ComparisonTest
846
+ Name = 'lt'
847
+ Operator = AEMReference.pack_enum(KAE::KAELessThan)
848
+ end
849
+
850
+ class LessOrEquals < ComparisonTest
851
+ Name = 'le'
852
+ Operator = AEMReference.pack_enum(KAE::KAELessThanEquals)
853
+ end
854
+
855
+ class StartsWith < ComparisonTest
856
+ Name = 'starts_with'
857
+ Operator = AEMReference.pack_enum(KAE::KAEBeginsWith)
858
+ end
859
+
860
+ class EndsWith < ComparisonTest
861
+ Name = 'ends_with'
862
+ Operator = AEMReference.pack_enum(KAE::KAEEndsWith)
863
+ end
864
+
865
+ class Contains < ComparisonTest
866
+ Name = 'contains'
867
+ Operator = AEMReference.pack_enum(KAE::KAEContains)
868
+ end
869
+
870
+ class IsIn < Contains
871
+ Name = 'is_in'
872
+
873
+ def AEM_pack_self(codecs)
874
+ return AEMReference.pack_list_as(KAE::TypeCompDescriptor, [
875
+ [KAE::KeyAEObject1, codecs.pack(@_operand2)],
876
+ [KAE::KeyAECompOperator, self.class::Operator],
877
+ [KAE::KeyAEObject2, codecs.pack(@_operand1)]
878
+ ])
879
+ end
880
+ end
881
+
882
+ ###################################
883
+ # Logical tests
884
+
885
+ class LogicalTest < Test
886
+ attr_reader :_operands
887
+ protected :_operands
888
+
889
+ def initialize(operands)
890
+ @_operands = operands
891
+ end
892
+
893
+ def to_s
894
+ op_str = (@_operands[1, @_operands.length].collect { |o| o.inspect }).join(', ')
895
+ return "#{@_operands[0].inspect}.#{self.class::Name}(#{op_str})"
896
+ end
897
+
898
+ def AEM_resolve(obj)
899
+ return @_operands[0].AEM_resolve(obj).send(self.class::Name, *@_operands[1, @_operands.length])
900
+ end
901
+
902
+ def AEM_pack_self(codecs)
903
+ return AEMReference.pack_list_as(KAE::TypeLogicalDescriptor, [
904
+ [KAE::KeyAELogicalOperator, self.class::Operator],
905
+ [KAE::KeyAELogicalTerms, codecs.pack(@_operands)]
906
+ ])
907
+ end
908
+ end
909
+
910
+ ##
911
+
912
+ class AND < LogicalTest
913
+ Operator = AEMReference.pack_enum(KAE::KAEAND)
914
+ Name = 'and'
915
+ end
916
+
917
+ class OR < LogicalTest
918
+ Operator = AEMReference.pack_enum(KAE::KAEOR)
919
+ Name = 'or'
920
+ end
921
+
922
+ class NOT < LogicalTest
923
+ Operator = AEMReference.pack_enum(KAE::KAENOT)
924
+ Name = 'not'
925
+
926
+ def to_s
927
+ return "#{@_operands[0].inspect}.not"
928
+ end
929
+
930
+ def AEM_resolve(obj)
931
+ return @_operands[0].AEM_resolve(obj).not
932
+ end
933
+ end
934
+
935
+
936
+ ######################################################################
937
+ # REFERENCE ROOTS
938
+ ######################################################################
939
+
940
+ ###################################
941
+ # Base class
942
+
943
+ class ReferenceRoot < PositionSpecifier
944
+
945
+ def initialize
946
+ end
947
+
948
+ def to_s
949
+ return "AEM.#{self.class::Name}"
950
+ end
951
+
952
+ def _pack_self(codecs)
953
+ return self.class::Type
954
+ end
955
+
956
+ def AEM_root
957
+ return self
958
+ end
959
+
960
+ def AEM_resolve(obj)
961
+ return obj.send(self.class::Name)
962
+ end
963
+ end
964
+
965
+
966
+ ###################################
967
+ # Concrete classes
968
+
969
+ class ApplicationRoot < ReferenceRoot
970
+ # Reference base; represents an application's application object. Used to construct full references.
971
+
972
+ # Syntax: app
973
+
974
+ Name = 'app'
975
+ Type = AE::AEDesc.new(KAE::TypeNull, '')
976
+ end
977
+
978
+
979
+ class CurrentContainer < ReferenceRoot
980
+ # Reference base; represents elements' container object. Used to construct by-range references.
981
+
982
+ # Syntax: con
983
+
984
+ Name = 'con'
985
+ Type = AE::AEDesc.new(KAE::TypeCurrentContainer, '')
986
+ end
987
+
988
+
989
+ class ObjectBeingExamined < ReferenceRoot
990
+ # Reference base; represents an element to be tested. Used to construct by-filter references.
991
+
992
+ # Syntax: its
993
+
994
+ Name = 'its'
995
+ Type = AE::AEDesc.new(KAE::TypeObjectBeingExamined, '')
996
+ end
997
+
998
+
999
+ ###################################
1000
+ # Reference root objects; used to construct new specifiers, e.g. AEM.app.property('pnam')
1001
+
1002
+ App = ApplicationRoot.new
1003
+ Con = CurrentContainer.new
1004
+ Its = ObjectBeingExamined.new
1005
+ end
1006
+