API 0.9.5
Foundation/CPPredicate/CPComparisonPredicate.j
Go to the documentation of this file.
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 }
 All Classes Files Functions Variables Defines