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