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