![]() |
API 0.9.5
|
00001 /* 00002 * CPComparisonPredicate.j 00003 * 00004 * Portions based on NSComparisonPredicate.m in Cocotron (http://www.cocotron.org/) 00005 * Copyright (c) 2006-2007 Christopher J. W. Lloyd 00006 * 00007 * Created by cacaodev. 00008 * Copyright 2010. 00009 * 00010 * This library is free software; you can redistribute it and/or 00011 * modify it under the terms of the GNU Lesser General Public 00012 * License as published by the Free Software Foundation; either 00013 * version 2.1 of the License, or (at your option) any later version. 00014 * 00015 * This library is distributed in the hope that it will be useful, 00016 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00017 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00018 * Lesser General Public License for more details. 00019 * 00020 * You should have received a copy of the GNU Lesser General Public 00021 * License along with this library; if not, write to the Free Software 00022 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 00023 */ 00024 00025 00031 CPDirectPredicateModifier = 0; 00039 CPAllPredicateModifier = 1; 00047 CPAnyPredicateModifier = 2; 00048 00054 CPCaseInsensitivePredicateOption = 1; 00060 CPDiacriticInsensitivePredicateOption = 2; 00061 CPDiacriticInsensitiveSearch = 128; 00062 00068 CPLessThanPredicateOperatorType = 0; 00074 CPLessThanOrEqualToPredicateOperatorType = 1; 00080 CPGreaterThanPredicateOperatorType = 2; 00086 CPGreaterThanOrEqualToPredicateOperatorType = 3; 00092 CPEqualToPredicateOperatorType = 4; 00098 CPNotEqualToPredicateOperatorType = 5; 00104 CPMatchesPredicateOperatorType = 6; 00110 CPLikePredicateOperatorType = 7; 00116 CPBeginsWithPredicateOperatorType = 8; 00122 CPEndsWithPredicateOperatorType = 9; 00130 CPInPredicateOperatorType = 10; 00138 CPCustomSelectorPredicateOperatorType = 11; 00146 CPContainsPredicateOperatorType = 99; 00154 CPBetweenPredicateOperatorType = 100; 00155 00156 var CPComparisonPredicateModifier, 00157 CPPredicateOperatorType; 00158 00166 @implementation CPComparisonPredicate : CPPredicate 00167 { 00168 CPExpression _left; 00169 CPExpression _right; 00170 00171 CPComparisonPredicateModifier _modifier; 00172 CPPredicateOperatorType _type; 00173 unsigned int _options; 00174 SEL _customSelector; 00175 } 00176 00177 // Constructors 00185 + (CPPredicate)predicateWithLeftExpression:(CPExpression)left rightExpression:(CPExpression)right customSelector:(SEL)selector 00186 { 00187 return [[self alloc] initWithLeftExpression:left rightExpression:right customSelector:selector]; 00188 } 00189 00199 + (CPPredicate)predicateWithLeftExpression:(CPExpression)left rightExpression:(CPExpression)right modifier:(CPComparisonPredicateModifier)modifier type:(int)type options:(unsigned)options 00200 { 00201 return [[self alloc] initWithLeftExpression:left rightExpression:right modifier:modifier type:type options:options]; 00202 } 00203 00211 - (id)initWithLeftExpression:(CPExpression)left rightExpression:(CPExpression)right customSelector:(SEL)selector 00212 { 00213 self = [super init]; 00214 00215 if (self) 00216 { 00217 _left = left; 00218 _right = right; 00219 _modifier = CPDirectPredicateModifier; 00220 _type = CPCustomSelectorPredicateOperatorType; 00221 _options = 0; 00222 _customSelector = selector; 00223 } 00224 00225 return self; 00226 } 00227 00237 - (id)initWithLeftExpression:(CPExpression)left rightExpression:(CPExpression)right modifier:(CPComparisonPredicateModifier)modifier type:(CPPredicateOperatorType)type options:(unsigned)options 00238 { 00239 self = [super init]; 00240 00241 if (self) 00242 { 00243 _left = left; 00244 _right = right; 00245 _modifier = modifier; 00246 _type = type; 00247 _options = (type != CPMatchesPredicateOperatorType && 00248 type != CPLikePredicateOperatorType && 00249 type != CPBeginsWithPredicateOperatorType && 00250 type != CPEndsWithPredicateOperatorType && 00251 type != CPInPredicateOperatorType && 00252 type != CPContainsPredicateOperatorType) ? 0 : options; 00253 00254 _customSelector = NULL; 00255 } 00256 00257 return self; 00258 } 00259 00260 // Getting Information About a Comparison Predicate 00265 - (CPComparisonPredicateModifier)comparisonPredicateModifier 00266 { 00267 return _modifier; 00268 } 00269 00274 - (SEL)customSelector 00275 { 00276 return _customSelector; 00277 } 00278 00283 - (CPExpression)leftExpression 00284 { 00285 return _left; 00286 } 00287 00292 - (unsigned)options 00293 { 00294 return _options; 00295 } 00296 00301 - (CPPredicateOperatorType)predicateOperatorType 00302 { 00303 return _type; 00304 } 00305 00310 - (CPExpression)rightExpression 00311 { 00312 return _right; 00313 } 00314 00315 00316 - (CPString)predicateFormat 00317 { 00318 var modifier; 00319 00320 switch (_modifier) 00321 { 00322 case CPDirectPredicateModifier: modifier = ""; 00323 break; 00324 case CPAllPredicateModifier: modifier = "ALL "; 00325 break; 00326 case CPAnyPredicateModifier: modifier = "ANY "; 00327 break; 00328 00329 default: modifier = ""; 00330 break; 00331 } 00332 00333 var options; 00334 00335 switch (_options) 00336 { 00337 case CPCaseInsensitivePredicateOption: options = "[c]"; 00338 break; 00339 case CPDiacriticInsensitivePredicateOption: options = "[d]"; 00340 break; 00341 case CPCaseInsensitivePredicateOption | CPDiacriticInsensitivePredicateOption: 00342 options = "[cd]"; 00343 break; 00344 00345 default: options = ""; 00346 break; 00347 } 00348 00349 var operator; 00350 00351 switch (_type) 00352 { 00353 case CPLessThanPredicateOperatorType: operator = "<"; 00354 break; 00355 case CPLessThanOrEqualToPredicateOperatorType: operator = "<="; 00356 break; 00357 case CPGreaterThanPredicateOperatorType: operator = ">"; 00358 break; 00359 case CPGreaterThanOrEqualToPredicateOperatorType: operator = ">="; 00360 break; 00361 case CPEqualToPredicateOperatorType: operator = "=="; 00362 break; 00363 case CPNotEqualToPredicateOperatorType: operator = "!="; 00364 break; 00365 case CPMatchesPredicateOperatorType: operator = "MATCHES"; 00366 break; 00367 case CPLikePredicateOperatorType: operator = "LIKE"; 00368 break; 00369 case CPBeginsWithPredicateOperatorType: operator = "BEGINSWITH"; 00370 break; 00371 case CPEndsWithPredicateOperatorType: operator = "ENDSWITH"; 00372 break; 00373 case CPInPredicateOperatorType: operator = "IN"; 00374 break; 00375 case CPContainsPredicateOperatorType: operator = "CONTAINS"; 00376 break; 00377 case CPCustomSelectorPredicateOperatorType: operator = CPStringFromSelector(_customSelector); 00378 break; 00379 } 00380 00381 return [CPString stringWithFormat:@"%s%s %s%s %s",modifier,[_left description],operator,options,[_right description]]; 00382 } 00383 00384 - (CPPredicate)predicateWithSubstitutionVariables:(CPDictionary)variables 00385 { 00386 var left = [_left _expressionWithSubstitutionVariables:variables], 00387 right = [_right _expressionWithSubstitutionVariables:variables]; 00388 00389 if (_type != CPCustomSelectorPredicateOperatorType) 00390 return [CPComparisonPredicate predicateWithLeftExpression:left rightExpression:right modifier:_modifier type:_type options:_options]; 00391 else 00392 return [CPComparisonPredicate predicateWithLeftExpression:left rightExpression:right customSelector:_customSelector]; 00393 } 00394 00395 - (BOOL)_evaluateValue:lhs rightValue:rhs 00396 { 00397 var leftIsNil = (lhs == nil || [lhs isEqual:[CPNull null]]), 00398 rightIsNil = (rhs == nil || [rhs isEqual:[CPNull null]]); 00399 00400 if ((leftIsNil || rightIsNil) && _type != CPCustomSelectorPredicateOperatorType) 00401 return (leftIsNil == rightIsNil && 00402 (_type == CPEqualToPredicateOperatorType || 00403 _type == CPLessThanOrEqualToPredicateOperatorType || 00404 _type == CPGreaterThanOrEqualToPredicateOperatorType)); 00405 00406 var string_compare_options = 0; 00407 00408 // left and right should be casted first [CAST()] following 10.5 rules. 00409 switch (_type) 00410 { 00411 case CPLessThanPredicateOperatorType: return ([lhs compare:rhs] == CPOrderedAscending); 00412 case CPLessThanOrEqualToPredicateOperatorType: return ([lhs compare:rhs] != CPOrderedDescending); 00413 case CPGreaterThanPredicateOperatorType: return ([lhs compare:rhs] == CPOrderedDescending); 00414 case CPGreaterThanOrEqualToPredicateOperatorType: return ([lhs compare:rhs] != CPOrderedAscending); 00415 case CPEqualToPredicateOperatorType: return [lhs isEqual:rhs]; 00416 case CPNotEqualToPredicateOperatorType: return (![lhs isEqual:rhs]); 00417 00418 case CPMatchesPredicateOperatorType: var commut = (_options & CPCaseInsensitivePredicateOption) ? "gi":"g"; 00419 if (_options & CPDiacriticInsensitivePredicateOption) 00420 { 00421 lhs = lhs.stripDiacritics(); 00422 rhs = rhs.stripDiacritics(); 00423 } 00424 return (new RegExp(rhs,commut)).test(lhs); 00425 00426 case CPLikePredicateOperatorType: if (_options & CPDiacriticInsensitivePredicateOption) 00427 { 00428 lhs = lhs.stripDiacritics(); 00429 rhs = rhs.stripDiacritics(); 00430 } 00431 var commut = (_options & CPCaseInsensitivePredicateOption) ? "gi":"g", 00432 reg = new RegExp(rhs.escapeForRegExp(),commut); 00433 return reg.test(lhs); 00434 00435 case CPBeginsWithPredicateOperatorType: var range = CPMakeRange(0, MIN([lhs length], [rhs length])); 00436 if (_options & CPCaseInsensitivePredicateOption) 00437 string_compare_options |= CPCaseInsensitiveSearch; 00438 if (_options & CPDiacriticInsensitivePredicateOption) 00439 string_compare_options |= CPDiacriticInsensitiveSearch; 00440 return ([lhs compare:rhs options:string_compare_options range:range] == CPOrderedSame); 00441 00442 case CPEndsWithPredicateOperatorType: var range = CPMakeRange(MAX([lhs length] - [rhs length], 0), MIN([lhs length], [rhs length])); 00443 if (_options & CPCaseInsensitivePredicateOption) 00444 string_compare_options |= CPCaseInsensitiveSearch; 00445 if (_options & CPDiacriticInsensitivePredicateOption) 00446 string_compare_options |= CPDiacriticInsensitiveSearch; 00447 return ([lhs compare:rhs options:string_compare_options range:range] == CPOrderedSame); 00448 00449 case CPCustomSelectorPredicateOperatorType: return [lhs performSelector:_customSelector withObject:rhs]; 00450 00451 case CPInPredicateOperatorType: var a = lhs; // swap 00452 lhs = rhs; 00453 rhs = a; 00454 case CPContainsPredicateOperatorType: if (![lhs isKindOfClass:[CPString class]]) 00455 { 00456 if (![lhs respondsToSelector: @selector(objectEnumerator)]) 00457 [CPException raise:CPInvalidArgumentException reason:@"The left/right hand side for a CONTAINS/IN operator must be a collection or a string"]; 00458 00459 return [lhs containsObject:rhs]; 00460 } 00461 00462 if (_options & CPCaseInsensitivePredicateOption) 00463 string_compare_options |= CPCaseInsensitiveSearch; 00464 if (_options & CPDiacriticInsensitivePredicateOption) 00465 string_compare_options |= CPDiacriticInsensitiveSearch; 00466 00467 return ([lhs rangeOfString:rhs options:string_compare_options].location != CPNotFound); 00468 00469 case CPBetweenPredicateOperatorType: if ([rhs count] < 2) 00470 [CPException raise:CPInvalidArgumentException reason:@"The right hand side for a BETWEEN operator must contain 2 objects"]; 00471 00472 return ([lhs compare:rhs[0]] == CPOrderedDescending && [lhs compare:rhs[1]] == CPOrderedAscending); 00473 00474 default: return NO; 00475 } 00476 } 00477 00478 - (BOOL)evaluateWithObject:(id)object 00479 { 00480 return [self evaluateWithObject:object substitutionVariables:nil]; 00481 } 00482 00483 - (BOOL)evaluateWithObject:(id)object substitutionVariables:(CPDictionary)variables 00484 { 00485 var leftValue = [_left expressionValueWithObject:object context:variables], 00486 rightValue = [_right expressionValueWithObject:object context:variables]; 00487 00488 if (_modifier == CPDirectPredicateModifier) 00489 return [self _evaluateValue:leftValue rightValue:rightValue]; 00490 else 00491 { 00492 if (![leftValue respondsToSelector:@selector(objectEnumerator)]) 00493 [CPException raise:CPInvalidArgumentException reason:@"The left hand side for an ALL or ANY operator must be either a CPArray or a CPSet"]; 00494 00495 var e = [leftValue objectEnumerator], 00496 result = (_modifier == CPAllPredicateModifier), 00497 value; 00498 00499 while (value = [e nextObject]) 00500 { 00501 var eval = [self _evaluateValue:value rightValue:rightValue]; 00502 if (eval != result) 00503 return eval; 00504 } 00505 00506 return result; 00507 } 00508 } 00509 00510 @end 00511 00512 @implementation CPComparisonPredicate (CPCoding) 00513 00514 - (id)initWithCoder:(CPCoder)coder 00515 { 00516 self = [super init]; 00517 if (self != nil) 00518 { 00519 _left = [coder decodeObjectForKey:@"CPComparisonPredicateLeftExpression"]; 00520 _right = [coder decodeObjectForKey:@"CPComparisonPredicateRightExpression"]; 00521 _modifier = [coder decodeIntForKey:@"CPComparisonPredicateModifier"]; 00522 _type = [coder decodeIntForKey:@"CPComparisonPredicateType"]; 00523 _options = [coder decodeIntForKey:@"CPComparisonPredicateOptions"]; 00524 _customSelector = [coder decodeObjectForKey:@"CPComparisonPredicateCustomSelector"]; 00525 } 00526 00527 return self; 00528 } 00529 00530 - (void)encodeWithCoder:(CPCoder)coder 00531 { 00532 [coder encodeObject:_left forKey:@"CPComparisonPredicateLeftExpression"]; 00533 [coder encodeObject:_right forKey:@"CPComparisonPredicateRightExpression"]; 00534 [coder encodeInt:_modifier forKey:@"CPComparisonPredicateModifier"]; 00535 [coder encodeInt:_type forKey:@"CPComparisonPredicateType"]; 00536 [coder encodeInt:_options forKey:@"CPComparisonPredicateOptions"]; 00537 [coder encodeObject:_customSelector forKey:@"CPComparisonPredicateCustomSelector"]; 00538 } 00539 00540 @end 00541 00542 var source = ['*','?','(',')','{','}','.','+','|','/',','^'], 00543 dest = ['.*','.?','\\(','\\)','\\{','\\}','\\.','\\+','\\|','\\/','\\$','\\^']; 00544 00545 String.prototype.escapeForRegExp = function() 00546 { 00547 var foundChar = false, 00548 i = 0; 00549 00550 for (; i < source.length; ++i) 00551 { 00552 if (this.indexOf(source[i]) !== -1) 00553 { 00554 foundChar = true; 00555 break; 00556 } 00557 } 00558 00559 if (!foundChar) 00560 return this; 00561 00562 var result = ""; 00563 00564 for (i = 0; i < this.length; ++i) 00565 { 00566 var sourceIndex = source.indexOf(this.charAt(i)); 00567 if (sourceIndex !== -1) 00568 result += dest[sourceIndex]; 00569 else 00570 result += this.charAt(i); 00571 } 00572 00573 return result; 00574 }