-
Notifications
You must be signed in to change notification settings - Fork 0
/
QEQuery.h
312 lines (289 loc) · 13.1 KB
/
QEQuery.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
/*
* Copyright (c) 2006 Apple Computer, Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this
* file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_LICENSE_HEADER_END@
*/
#ifndef _QEQUERY_H_
#define _QEQUERY_H_
#include <CoreFoundation/CoreFoundation.h>
typedef struct __QEQuery * QEQueryRef;
/*******************************************************************************
* Flexible Query Engine
*
* This engine builds complex logical expression evaluators with predicates you
* define via callbacks. The engine directly supports AND, OR, NOT operators,
* as well as grouping. The engine is currently designed to build queries by
* serially adding elements, and figures out the grouping by itself. Operator
* precedence is explicit groups, then NOT, then AND, then OR.
*
* Query elements are dictionaries with three basic attributes:
*
* - Predicate: the 'verb' to evaluate
* - Arguments: data for use by the predicate
* - Negated: whether the evaluated result must be negated (this is handled
* by the engine, and your callbacks needn't worry about it)
*
* Your callbacks can stuff arbitrary key/value pairs into the dictionary in
* lieu of using the arguments array. Note that the engine reserves keys of the
* form '__QE...__', so don't use those.
*
********************************************************************************
* Using a Query
*
* You start by calling QEQueryCreate(), which takes a user data pointer for
* context info. Then you set parse and evaluation callbacks for the predicates
* you want to handle, add elements to your query using one of the two methods
* just below, and then use QEQueryEvaluate() on the objects you want to check.
*
* QEQueryEvaluate() returns true if the query matches the object you provide,
* and false if it doesn't or if an evaluation error occurs. If it does return
* false, you should call QEQueryLastError() and possibly cancel further queries
* using that query engine instance if an error did occur.
*
**********
* Building a Query from Strings
*
* If you set parse callbacks, you need only call QEQueryAppendElementFromArgs()
* repeatedly until it returns false. After doing that you should check whether
* there were any errors using QEQueryLastError().
*
* By default, the parse engine automatically handles the tokens '-and', '-or',
* '-not', '(', and ')', but does not give them any priority over your parse
* callbacks. That is, if your callback eats up an '-and' as an argument for a
* predicate, the query engine will just keep rolling along (and you might see a
* syntax error later).
*
* Your parse calllback will have as arguments:
*
* - The element being built, with its predicate already set to the token
* that triggered the callback.
* - A pointer to a list of strings following that token, which you can
* proceed to parse.
* - An in/out 'num used' parameter giving how many tokens have been parsed
* by the query engine as a whole; you should update this by the number of
* tokens you consume in parsing, if successful--if parsing fails, do not
* adjust it. This number is *not* to be used as an index into the list
* of argument strings; that starts at zero.
* - A pointer to the query user data.
* - A pointer to the query's error code, which you should set to
* kQEQueryErrorInvalidOrMissingArgument or kQEQueryErrorParseCallbackFailed
* if something goes wrong.
*
* Your callback should update the element by changing its predicate if necessary
* (so you can have a single keyword routed to different evaluation calbacks,
* or multiple keywords routed into a single evaluation callback), and reading
* as many arguments as needed from the list of strings and adding them to the
* elements arguments array. You can also set arbitrary properties on the element
* as noted above. It should update the num_used parameter as it successfully
* handles arguments. Upon successfully parsing all potential arguments, it
* should return true; otherwise, it should set the error code as appopriate
* and return false.
*
* Functions that you may find useful in a parse callback are:
*
* - QEQueryElementGetPredicate()
* - QEQueryElementSetPredicate()
* - QEQueryElementAppendArgument()
* - QEQueryElementSetArgumentsArray()
* - QEQueryElementSetArguments()
*
* NOTE: If you get a reference to query element's predicate, and then set the
* predicate, your original predicate will change! Always 're-fetch' the
* predicate if you need to check it after setting it. (I think this is a CF
* bug, but I haven't investigated it thoroughly).
*
**********
* Building a Query by Hand
*
* You can forgo using parse callbacks if you want to build query elements
* directly, using:
*
* - QEQueryCreateElement() - creates an element with a user-define predicate
* - QEQueryAppendElement()
* - QEQueryAppendAndOperator() and QEQueryAppendOrOperator()
* - QEQueryStartGroup() and QEQueryEndGroup()
*
* Just call them in sequence and the engine will produce the correct evaluation
* structure. These functions return true on success, and false if they would
* result in an incorrectly-structures query (for example, with two OR operators
* in a row). You can call QEQueryLastError() to find out the specific problem.
*
********************************************************************************
* Evaluating a Query
*
* Your evaluation callback will have as arguments:
*
* - The query element being evaluated.
* - A pointer to the object being checked.
* - A pointer to the query use data.
* - A pointer to the query's error code, which you should set to
* kQEQueryErrorEvaluationCallbackFailed if something goes wrong.
*
* Your callback should call QEQueryElementGetPredicate(),
* QEQueryElementGetArguments(), and QEQueryElementGetArgumentAtIndex()
* as necessary to apply its logic to the object and return a true or false
* value. It can also get arbitrary keys from the dictionary, as noted above.
* Your callback does not need to handle whether the query element is negated;
* the query engine does that.
*
* If your callback suffers a failure, it should set the error and return false.
*
* Note that evaluation callbacks can perform operations on or with the object
* as well as just checking them against a query predicate. For example, you
* could define a '-print' predicate that just prints data from the object
* and returns true.
********************************************************************************
* TO DO:
* XXX: Add functions that take CF strings?
* XXX: Allow for inspection/traversal of query, and insertion?
* XXX: Make QEQueryPrint() output friendlier (always record raw input tokens?)
* XXX: Add internal parse item counter and formatted error messages listing
* XXX: problem item and its number?
*******************************************************************************/
typedef enum {
kQEQueryErrorNone = 0,
kQEQueryErrorUnspecified,
kQEQueryErrorNoMemory,
kQEQueryErrorGroupNesting, // doesn't distinguish lib error from user....
kQEQueryErrorEmptyGroup,
kQEQueryErrorSyntax,
kQEQueryErrorNoParseCallback,
kQEQueryErrorNoEvaluationCallback,
kQEQueryErrorInvalidOrMissingArgument, // set by user parse callback
kQEQueryErrorParseCallbackFailed, // set by user parse callback
kQEQueryErrorEvaluationCallbackFailed, // set by user eval callback
} QEQueryError;
/* The default set of tokens understood by the engine.
* You may need to handle these specially while processing
* options.
*/
#define kQEQueryTokenAnd "-and"
#define kQEQueryTokenOr "-or"
#define kQEQueryTokenNot "-not"
#define kQEQueryTokenGroupStart "("
#define kQEQueryTokenGroupEnd ")"
typedef Boolean (*QEQueryParseCallback)(
CFMutableDictionaryRef element,
int argc,
char * const argv[],
uint32_t * num_used,
void * user_data,
QEQueryError * error);
typedef Boolean (*QEQueryEvaluationCallback)(
CFDictionaryRef element,
void * object,
void * user_data,
QEQueryError * error);
/*******************************************************************************
* Create and set up a query.
*******************************************************************************/
QEQueryRef QEQueryCreate(void * userData);
void QEQueryFree(QEQueryRef query);
/* Replace the builtin logical operators with your own. If you pass NULL
* for any one, the builtin is used (and for -not, ! is automatically registered
* as a synonym). If you just want to add synonyms for logical operators, or
* remove the default alias ! for -not, use QEQuerySetSynonymForPredicate().
*/
void QEQuerySetOperators(QEQueryRef query,
CFStringRef andPredicate,
CFStringRef orPredicate,
CFStringRef notPredicate,
CFStringRef groupStartPredicate,
CFStringRef groupEndPredicate);
/* Empty all custom parse information: predicates, synonyms, and callbacks.
*/
void QEQueryEmptyParseDictionaries(QEQueryRef query);
/* You can remove a single callback by passing NULL.
*/
void QEQuerySetParseCallbackForPredicate(
QEQueryRef query,
CFStringRef predicate,
QEQueryParseCallback parseCallback);
void QEQuerySetEvaluationCallbackForPredicate(
QEQueryRef query,
CFStringRef predicate,
QEQueryEvaluationCallback evaluationCallback);
/* Causes 'synonym' to be automatically replaced with 'predicate' during
* parsing and upon creation of an element dictionary with
* QEQueryCreateElement(). If 'predicate' is NULL, the synonym is unregistered.
*/
void QEQuerySetSynonymForPredicate(
QEQueryRef query,
CFStringRef synonym,
CFStringRef predicate);
Boolean QEQueryIsComplete(QEQueryRef query);
QEQueryError QEQueryLastError(QEQueryRef query);
/*******************************************************************************
* Evaluate a query.
*******************************************************************************/
void QEQuerySetShortCircuits(QEQueryRef query, Boolean flag);
Boolean QEQueryGetShortCircuits(QEQueryRef query);
Boolean QEQueryEvaluate(QEQueryRef query, void * object);
/*******************************************************************************
* Build a query from command-line arguments. See below for hand-building.
*******************************************************************************/
Boolean QEQueryAppendElementFromArgs(
QEQueryRef query,
int argc,
char * const argv[],
uint32_t * num_used);
/*******************************************************************************
* Functions for manually building a query.
*******************************************************************************/
CFMutableDictionaryRef QEQueryCreateElement(
QEQueryRef query,
CFStringRef predicate, // will be replaced if a synonym for another
CFArrayRef arguments, // may be NULL
Boolean negated);
/* Do not manually build a dictionary element; use QEQueryCreateElement(),
* which sets internal values needed by the query engine.
*/
Boolean QEQueryAppendElement(
QEQueryRef query,
CFMutableDictionaryRef element);
Boolean QEQueryAppendAndOperator(QEQueryRef query);
Boolean QEQueryAppendOrOperator(QEQueryRef query);
Boolean QEQueryStartGroup(QEQueryRef query, Boolean negated);
Boolean QEQueryEndGroup(QEQueryRef query);
/*******************************************************************************
* Functions for use by parse and evaluation callbacks.
*******************************************************************************/
CFStringRef QEQueryElementGetPredicate(CFDictionaryRef element);
CFMutableArrayRef QEQueryElementGetArguments(CFDictionaryRef element);
CFTypeRef QEQueryElementGetArgumentAtIndex(
CFDictionaryRef element,
CFIndex i);
/*******************************************************************************
* Functions for use by parse callbacks.
*******************************************************************************/
void QEQueryElementSetPredicate(CFMutableDictionaryRef element,
CFStringRef predicate);
void QEQueryElementAppendArgument(CFMutableDictionaryRef element,
CFTypeRef argument);
void QEQueryElementSetArgumentsArray(CFMutableDictionaryRef element,
CFArrayRef arguments);
Boolean QEQueryElementSetArguments(CFMutableDictionaryRef element,
uint32_t numArgs,
...);
/*******************************************************************************
* Print the raw CFPropertyList contents of a query.
*******************************************************************************/
void QEQueryPrint(QEQueryRef query);
#endif /* _QEQUERY_H_ */