rb-scpt 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGES +497 -0
- data/doc/aem-manual/01_introduction.html +60 -0
- data/doc/aem-manual/02_apioverview.html +107 -0
- data/doc/aem-manual/03_packingandunpackingdata.html +135 -0
- data/doc/aem-manual/04_references.html +409 -0
- data/doc/aem-manual/05_targetingapplications.html +164 -0
- data/doc/aem-manual/06_buildingandsendingevents.html +229 -0
- data/doc/aem-manual/07_findapp.html +63 -0
- data/doc/aem-manual/08_examples.html +94 -0
- data/doc/aem-manual/aemreferenceinheritance.gif +0 -0
- data/doc/aem-manual/index.html +56 -0
- data/doc/appscript-manual/01_introduction.html +94 -0
- data/doc/appscript-manual/02_aboutappscripting.html +247 -0
- data/doc/appscript-manual/03_quicktutorial.html +167 -0
- data/doc/appscript-manual/04_gettinghelp.html +188 -0
- data/doc/appscript-manual/05_keywordconversion.html +106 -0
- data/doc/appscript-manual/06_classesandenums.html +192 -0
- data/doc/appscript-manual/07_applicationobjects.html +211 -0
- data/doc/appscript-manual/08_realvsgenericreferences.html +96 -0
- data/doc/appscript-manual/09_referenceforms.html +241 -0
- data/doc/appscript-manual/10_referenceexamples.html +154 -0
- data/doc/appscript-manual/11_applicationcommands.html +245 -0
- data/doc/appscript-manual/12_commandexamples.html +138 -0
- data/doc/appscript-manual/13_performanceissues.html +142 -0
- data/doc/appscript-manual/14_notes.html +80 -0
- data/doc/appscript-manual/application_architecture.gif +0 -0
- data/doc/appscript-manual/application_architecture2.gif +0 -0
- data/doc/appscript-manual/finder_to_textedit_event.gif +0 -0
- data/doc/appscript-manual/index.html +62 -0
- data/doc/appscript-manual/relationships_example.gif +0 -0
- data/doc/appscript-manual/ruby_to_itunes_event.gif +0 -0
- data/doc/full.css +106 -0
- data/doc/index.html +45 -0
- data/doc/mactypes-manual/01_introduction.html +54 -0
- data/doc/mactypes-manual/02_aliasclass.html +124 -0
- data/doc/mactypes-manual/03_fileurlclass.html +126 -0
- data/doc/mactypes-manual/04_unitsclass.html +100 -0
- data/doc/mactypes-manual/index.html +53 -0
- data/doc/osax-manual/01_introduction.html +67 -0
- data/doc/osax-manual/02_interface.html +147 -0
- data/doc/osax-manual/03_examples.html +73 -0
- data/doc/osax-manual/04_notes.html +61 -0
- data/doc/osax-manual/index.html +53 -0
- data/doc/rb-appscript-logo.png +0 -0
- data/extconf.rb +65 -0
- data/rb-scpt.gemspec +14 -0
- data/sample/AB_export_vcard.rb +31 -0
- data/sample/AB_list_people_with_emails.rb +13 -0
- data/sample/Add_iCal_event.rb +21 -0
- data/sample/Create_daily_iCal_todos.rb +75 -0
- data/sample/Export_Address_Book_phone_numbers.rb +59 -0
- data/sample/Hello_world.rb +21 -0
- data/sample/List_iTunes_playlist_names.rb +11 -0
- data/sample/Make_Mail_message.rb +33 -0
- data/sample/Open_file_in_TextEdit.rb +13 -0
- data/sample/Organize_Mail_messages.rb +61 -0
- data/sample/Print_folder_tree.rb +16 -0
- data/sample/Select_all_HTML_files.rb +14 -0
- data/sample/Set_iChat_status.rb +24 -0
- data/sample/Simple_Finder_GUI_Scripting.rb +18 -0
- data/sample/Stagger_Finder_windows.rb +25 -0
- data/sample/TextEdit_demo.rb +130 -0
- data/sample/iTunes_top40_to_html.rb +71 -0
- data/src/SendThreadSafe.c +380 -0
- data/src/SendThreadSafe.h +139 -0
- data/src/lib/_aem/aemreference.rb +1022 -0
- data/src/lib/_aem/codecs.rb +662 -0
- data/src/lib/_aem/connect.rb +205 -0
- data/src/lib/_aem/encodingsupport.rb +77 -0
- data/src/lib/_aem/findapp.rb +85 -0
- data/src/lib/_aem/mactypes.rb +251 -0
- data/src/lib/_aem/send.rb +279 -0
- data/src/lib/_aem/typewrappers.rb +59 -0
- data/src/lib/_appscript/defaultterminology.rb +277 -0
- data/src/lib/_appscript/referencerenderer.rb +245 -0
- data/src/lib/_appscript/reservedkeywords.rb +116 -0
- data/src/lib/_appscript/safeobject.rb +249 -0
- data/src/lib/_appscript/terminology.rb +471 -0
- data/src/lib/aem.rb +253 -0
- data/src/lib/appscript.rb +1075 -0
- data/src/lib/kae.rb +1489 -0
- data/src/lib/osax.rb +659 -0
- data/src/rbae.c +979 -0
- data/test/README +3 -0
- data/test/test_aemreference.rb +118 -0
- data/test/test_appscriptcommands.rb +152 -0
- data/test/test_appscriptreference.rb +106 -0
- data/test/test_codecs.rb +186 -0
- data/test/test_findapp.rb +26 -0
- data/test/test_mactypes.rb +79 -0
- data/test/test_osax.rb +54 -0
- data/test/testall.sh +10 -0
- metadata +145 -0
@@ -0,0 +1,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
|
+
|