diff --git a/Foundation/CPArray+KVO.j b/Foundation/CPArray+KVO.j index 39ae6291d..ecd3d2a26 100644 --- a/Foundation/CPArray+KVO.j +++ b/Foundation/CPArray+KVO.j @@ -28,7 +28,7 @@ - (id)mutableArrayValueForKey:(id)aKey { - return [[_CPKVCArray alloc] initWithKey:aKey forProxyObject:self]; + return [[_CPKVCArray alloc] initWithKey:aKey forProxyObject:self]; } - (id)mutableArrayValueForKeyPath:(id)aKeyPath @@ -430,7 +430,7 @@ kvoOperators["avg"] = function avgOperator(self, _cmd, param) { var objects = [self valueForKeyPath:param], length = [objects count], - index = length; + index = length, average = 0.0; if (!length) @@ -525,7 +525,7 @@ kvoOperators["sum"] = function sumOperator(self, _cmd, param) [super addObserver:observer forKeyPath:aKeyPath options:options context:context]; } --(void)removeObserver:(id)observer forKeyPath:(CPString)aKeyPath +- (void)removeObserver:(id)observer forKeyPath:(CPString)aKeyPath { if ([isa instanceMethodForSelector:_cmd] === [CPArray instanceMethodForSelector:_cmd]) [CPException raise:CPInvalidArgumentException reason:"Unsupported method on CPArray"]; diff --git a/Foundation/CPArray.j b/Foundation/CPArray.j index e912d1d27..0a7250c6d 100755 --- a/Foundation/CPArray.j +++ b/Foundation/CPArray.j @@ -405,7 +405,7 @@ CPEnumerationReverse = 1 << 1; @param predicate The function to apply to elements of the array. The function receives two arguments: object The element in the array. index The index of the element in the array. - The predicate function should either return a Boolean value that indicates whether the object passed the test, + The predicate function should either return a Boolean value that indicates whether the object passed the test, or nil to stop the search, which will return CPNotFound to the sender. @return The index of the first matching object, or \c CPNotFound if there is no matching object. */ @@ -420,7 +420,7 @@ CPEnumerationReverse = 1 << 1; object The element in the array. index The index of the element in the array. context The object passed to the receiver in the aContext parameter. - The predicate function should either return a Boolean value that indicates whether the object passed the test, + The predicate function should either return a Boolean value that indicates whether the object passed the test, or nil to stop the search, which will return CPNotFound to the sender. @param context An object that contains context information you want passed to the predicate function. @return The index of the first matching object, or \c CPNotFound if there is no matching object. @@ -432,29 +432,29 @@ CPEnumerationReverse = 1 << 1; /*! Returns the index of the first object in the receiver that passes a test in a given Javascript function. - @param opts Specifies the direction in which the array is searched. Pass CPEnumerationNormal to search forwards + @param opts Specifies the direction in which the array is searched. Pass CPEnumerationNormal to search forwards or CPEnumerationReverse to search in reverse. @param predicate The function to apply to elements of the array. The function receives two arguments: object The element in the array. index The index of the element in the array. - The predicate function should either return a Boolean value that indicates whether the object passed the test, + The predicate function should either return a Boolean value that indicates whether the object passed the test, or nil to stop the search, which will return CPNotFound to the sender. @return The index of the first matching object, or \c CPNotFound if there is no matching object. */ - (unsigned)indexOfObjectWithOptions:(CPEnumerationOptions)opts passingTest:(Function)predicate { return [self indexOfObjectWithOptions:opts passingTest:predicate context:undefined]; -} +} /*! Returns the index of the first object in the receiver that passes a test in a given Javascript function. - @param opts Specifies the direction in which the array is searched. Pass CPEnumerationNormal to search forwards + @param opts Specifies the direction in which the array is searched. Pass CPEnumerationNormal to search forwards or CPEnumerationReverse to search in reverse. @param predicate The function to apply to elements of the array. The function receives two arguments: object The element in the array. index The index of the element in the array. context The object passed to the receiver in the aContext parameter. - The predicate function should either return a Boolean value that indicates whether the object passed the test, + The predicate function should either return a Boolean value that indicates whether the object passed the test, or nil to stop the search, which will return CPNotFound to the sender. @param context An object that contains context information you want passed to the predicate function. @return The index of the first matching object, or \c CPNotFound if there is no matching object. @@ -463,9 +463,9 @@ CPEnumerationReverse = 1 << 1; { // We don't use an enumerator because they return nil to indicate end of enumeration, // but nil may actually be the value we are looking for, so we have to loop over the array. - + var start, stop, increment; - + if (opts & CPEnumerationReverse) { start = [self count] - 1; @@ -478,17 +478,17 @@ CPEnumerationReverse = 1 << 1; stop = [self count]; increment = 1; } - + for (var i = start; i != stop; i += increment) { var result = predicate([self objectAtIndex:i], i, aContext); - + if (typeof result === 'boolean' && result) return i; else if (typeof result === 'object' && result == nil) return CPNotFound; } - + return CPNotFound; } @@ -615,7 +615,7 @@ CPEnumerationReverse = 1 << 1; result = CPOrderedSame; while (i < count) - if((result = [descriptors[i++] compareObject:lhs withObject:rhs]) != CPOrderedSame) + if ((result = [descriptors[i++] compareObject:lhs withObject:rhs]) != CPOrderedSame) return result; return result; @@ -1071,7 +1071,7 @@ CPEnumerationReverse = 1 << 1; result = CPOrderedSame; while (i < count) - if((result = [descriptors[i++] compareObject:lhs withObject:rhs]) != CPOrderedSame) + if ((result = [descriptors[i++] compareObject:lhs withObject:rhs]) != CPOrderedSame) return result; return result; @@ -1296,7 +1296,7 @@ CPEnumerationReverse = 1 << 1; { var h, i, j, k, l, m, n = [self count], o; var A, B = []; - + for (h = 1; h < n; h += h) { for (m = n - 1 - h; m >= 0; m -= h + h) @@ -1304,10 +1304,10 @@ CPEnumerationReverse = 1 << 1; l = m - h + 1; if (l < 0) l = 0; - + for (i = 0, j = l; j <= m; i++, j++) B[i] = self[j]; - + for (i = 0, k = l; k < j && j <= m + h; k++) { A = self[j]; @@ -1320,7 +1320,7 @@ CPEnumerationReverse = 1 << 1; j++; } } - + while (k < j) self[k++] = B[i++]; } @@ -1345,14 +1345,14 @@ var selectorCompare = function selectorCompare(object1, object2, selector) // sort using sort descriptors var compareObjectsUsingDescriptors= function compareObjectsUsingDescriptors(lhs, rhs, descriptors) -{ +{ var result = CPOrderedSame, - i = 0, + i = 0, n = [descriptors count]; - + while (i < n && result === CPOrderedSame) result = [descriptors[i++] compareObject:lhs withObject:rhs]; - + return result; } diff --git a/Foundation/CPAttributedString.j b/Foundation/CPAttributedString.j index 85bb3588f..4b61af4a7 100644 --- a/Foundation/CPAttributedString.j +++ b/Foundation/CPAttributedString.j @@ -390,11 +390,11 @@ */ - (BOOL)isEqualToAttributedString:(CPAttributedString)aString { - if (!aString) - return NO; + if (!aString) + return NO; - if (_string != [aString string]) - return NO; + if (_string != [aString string]) + return NO; var myRange = CPMakeRange(), comparisonRange = CPMakeRange(), @@ -424,13 +424,13 @@ */ - (BOOL)isEqual:(id)anObject { - if (anObject == self) - return YES; + if (anObject == self) + return YES; - if ([anObject isKindOfClass:[self class]]) - return [self isEqualToAttributedString:anObject]; + if ([anObject isKindOfClass:[self class]]) + return [self isEqualToAttributedString:anObject]; - return NO; + return NO; } //Extracting a Substring @@ -530,8 +530,8 @@ endingIndex = startingIndex + 1; - while(endingIndex < _rangeEntries.length) - _rangeEntries[endingIndex++].range.location+=additionalLength; + while (endingIndex < _rangeEntries.length) + _rangeEntries[endingIndex++].range.location += additionalLength; } /*! diff --git a/Foundation/CPCharacterSet.j b/Foundation/CPCharacterSet.j index fcc5c04d9..f75bff0f0 100644 --- a/Foundation/CPCharacterSet.j +++ b/Foundation/CPCharacterSet.j @@ -128,17 +128,17 @@ var _builtInCharacterSets = {}; + (id)_sharedCharacterSetWithName:(id)csname { var cs = _builtInCharacterSets[csname]; - if(cs == nil) + if (cs == nil) { var i, ranges = [CPArray array], rangeArray = eval(csname); - for(i = 0; i < rangeArray.length; i+= 2) + for (i = 0; i < rangeArray.length; i+= 2) { - var loc = rangeArray[i]; - var length = rangeArray[i+1]; - var range = CPMakeRange(loc,length); + var loc = rangeArray[i], + length = rangeArray[i + 1], + range = CPMakeRange(loc,length); [ranges addObject:range]; } cs = [[_CPRangeCharacterSet alloc] initWithRanges:ranges]; @@ -196,13 +196,13 @@ var _builtInCharacterSets = {}; - (BOOL)characterIsMember:(CPString)aCharacter { c = aCharacter.charCodeAt(0); - var enu = [_ranges objectEnumerator]; - var range; + var enu = [_ranges objectEnumerator], + range; while (range = [enu nextObject]) { if (CPLocationInRange(c, range)) - return !_inverted; + return !_inverted; } return _inverted; @@ -226,10 +226,10 @@ var _builtInCharacterSets = {}; { var i; - for(i = 0; i < aString.length; i++) + for (i = 0; i < aString.length; i++) { - var code = aString.charCodeAt(i); - var range = CPMakeRange(code,1); + var code = aString.charCodeAt(i), + range = CPMakeRange(code,1); [_ranges addObject:range]; } @@ -262,7 +262,7 @@ var _builtInCharacterSets = {}; return set; } --(id)invertedSet +- (id)invertedSet { var set = [[_CPStringContentCharacterSet alloc] initWithString:_string]; [set invert]; @@ -294,7 +294,7 @@ var _builtInCharacterSets = {}; - (void)addCharactersInRange:(CPRange)aRange // Needs _inverted support { var i; - for(i = aRange.location; i < aRange.location + aRange.length; i++) + for (i = aRange.location; i < aRange.location + aRange.length; i++) { var s = String.fromCharCode(i); @@ -307,7 +307,7 @@ var _builtInCharacterSets = {}; { var i; - for(i = 0; i < aString.length; i++) + for (i = 0; i < aString.length; i++) { var s = aString.charAt(i); @@ -330,34 +330,34 @@ _CPCharacterSetTrimAtEnd = 1 << 2; and the set is [CPCharacterSet whitespaceCharacterSet] the returned array would contain:
 ["Baku", "baku", "to", "jest", "", "skład."] 
- Adjacent occurences of the separator characters produce empty strings in the result. - @author Arkadiusz Młynarczyk + Adjacent occurences of the separator characters produce empty strings in the result. + @author Arkadiusz Młynarczyk @param A character set containing the characters to use to split the receiver. Must not be nil. @return An CPArray object containing substrings from the receiver that have been divided by characters in separator. */ - (CPArray)componentsSeparatedByCharactersInSet:(CPCharacterSet)separator { - if (!separator) - [CPException raise:CPInvalidArgumentException + if (!separator) + [CPException raise:CPInvalidArgumentException reason:"componentsSeparatedByCharactersInSet: the separator can't be 'nil'"]; - var components = [CPMutableArray array], - componentRange = CPMakeRange(0, 0); + var components = [CPMutableArray array], + componentRange = CPMakeRange(0, 0); - for (var i=0; i < self.length; i++) - { - if ([separator characterIsMember:self.charAt(i)]) - { - componentRange.length = i - componentRange.location; - [components addObject:[self substringWithRange:componentRange]]; - componentRange.location += componentRange.length + 1; - } - } + for (var i = 0; i < self.length; i++) + { + if ([separator characterIsMember:self.charAt(i)]) + { + componentRange.length = i - componentRange.location; + [components addObject:[self substringWithRange:componentRange]]; + componentRange.location += componentRange.length + 1; + } + } - componentRange.length = self.length - componentRange.location; - [components addObject:[self substringWithRange:componentRange]]; + componentRange.length = self.length - componentRange.location; + [components addObject:[self substringWithRange:componentRange]]; - return components; + return components; } // As per the Cocoa method. diff --git a/Foundation/CPDictionary.j b/Foundation/CPDictionary.j index 3d6995161..61634cd67 100755 --- a/Foundation/CPDictionary.j +++ b/Foundation/CPDictionary.j @@ -364,7 +364,7 @@ } return matchingKeys; -} +} /*! Returns an enumerator that enumerates over all the dictionary's keys. diff --git a/Foundation/CPFormatter.j b/Foundation/CPFormatter.j index 0ff278b1e..4098493d7 100644 --- a/Foundation/CPFormatter.j +++ b/Foundation/CPFormatter.j @@ -23,13 +23,13 @@ /*! @class CPFormatter @ingroup foundation - @brief CPFormatter is an abstract class that declares an interface for objects that create, interpret, - and validate the textual representation of cell contents. The Foundation framework provides two + @brief CPFormatter is an abstract class that declares an interface for objects that create, interpret, + and validate the textual representation of cell contents. The Foundation framework provides two concrete subclasses of CPFormatter to generate these objects: CPNumberFormatter and CPDateFormatter. - CPFormatter is intended for subclassing. A custom formatter can restrict the input and enhance the - display of data in novel ways. For example, you could have a custom formatter that ensures that serial - numbers entered by a user conform to predefined formats. Before you decide to create a custom formatter, + CPFormatter is intended for subclassing. A custom formatter can restrict the input and enhance the + display of data in novel ways. For example, you could have a custom formatter that ensures that serial + numbers entered by a user conform to predefined formats. Before you decide to create a custom formatter, make sure that you cannot configure the public subclasses CPDateFormatter and CPNumberFormatter to satisfy your requirements. */ @@ -40,10 +40,10 @@ /*! The default implementation of this method raises an exception. - When implementing a subclass, return the CPString object that textually represents - the view's object for display and if editingStringForObjectValue: is unimplemented for editing. - First test the passed-in object to see if it's of the correct class. If it isn't, return nil; - but if it is of the right class, return a properly formatted and, if necessary, localized string. + When implementing a subclass, return the CPString object that textually represents + the view's object for display and if editingStringForObjectValue: is unimplemented for editing. + First test the passed-in object to see if it's of the correct class. If it isn't, return nil; + but if it is of the right class, return a properly formatted and, if necessary, localized string. (See the specification of the CPString class for formatting and localizing details.) @param anObject The object for which a textual representation is returned @@ -65,9 +65,9 @@ /*! The default implementation of this method invokes stringForObjectValue:. - When implementing a subclass, override this method only when the string that users see and the string - that they edit are different. In your implementation, return an CPString object that is used for editing, - following the logic recommended for implementing stringForObjectValue:. As an example, you would implement + When implementing a subclass, override this method only when the string that users see and the string + that they edit are different. In your implementation, return an CPString object that is used for editing, + following the logic recommended for implementing stringForObjectValue:. As an example, you would implement this method if you want the dollar signs in displayed strings removed for editing. @param anObject the object for which to return an editing string @@ -82,11 +82,11 @@ /*! The default implementation of this method raises an exception. - When implementing a subclass, return by reference the object anObject after creating it from string. - Return YES if the conversion is successful. If you return NO, also return by indirection (in error) - a localized user-presentable CPString object that explains the reason why the conversion failed; the delegate - (if any) of the CPControl object managing the cell can then respond to the failure in - control:didFailToFormatString:errorDescription:. However, if error is nil, the sender is not interested in + When implementing a subclass, return by reference the object anObject after creating it from string. + Return YES if the conversion is successful. If you return NO, also return by indirection (in error) + a localized user-presentable CPString object that explains the reason why the conversion failed; the delegate + (if any) of the CPControl object managing the cell can then respond to the failure in + control:didFailToFormatString:errorDescription:. However, if error is nil, the sender is not interested in the error description, and you should not attempt to assign one. @param anObject if conversion is successful, upon return contains the object created from the string @@ -104,18 +104,18 @@ /*! Returns a Boolean value that indicates whether a partial string is valid. - This method is invoked each time the user presses a key while the cell has the keyboard focus it lets you verify and + This method is invoked each time the user presses a key while the cell has the keyboard focus it lets you verify and edit the cell text as the user types it. - In a subclass implementation, evaluate partialString according to the context, edit the text if necessary, and return - by reference any edited string in newString. Return YES if partialString is acceptable and NO if partialString is unacceptable. - If you return NO and newString is nil, the cell displays partialString minus the last character typed. If you return NO, you can - also return by indirection an CPString object (in error) that explains the reason why the validation failed; the delegate (if any) - of the CPControl object managing the cell can then respond to the failure in control:didFailToValidatePartialString:errorDescription:. + In a subclass implementation, evaluate partialString according to the context, edit the text if necessary, and return + by reference any edited string in newString. Return YES if partialString is acceptable and NO if partialString is unacceptable. + If you return NO and newString is nil, the cell displays partialString minus the last character typed. If you return NO, you can + also return by indirection an CPString object (in error) that explains the reason why the validation failed; the delegate (if any) + of the CPControl object managing the cell can then respond to the failure in control:didFailToValidatePartialString:errorDescription:. The selection range will always be set to the end of the text if replacement occurs. - This method is a compatibility method. If a subclass overrides this method and does not override - isPartialStringValid:proposedSelectedRange:originalString:originalSelectedRange:errorDescription:, this method will be called as before + This method is a compatibility method. If a subclass overrides this method and does not override + isPartialStringValid:proposedSelectedRange:originalString:originalSelectedRange:errorDescription:, this method will be called as before (isPartialStringValid:proposedSelectedRange:originalString:originalSelectedRange:errorDescription: just calls this one by default). @param aPartialString the text currently in the view. @@ -130,20 +130,20 @@ } /*! - This method should be implemented in subclasses that want to validate user changes to a string in a field, where the user changes are - not necessarily at the end of the string, and preserve the selection (or set a different one, such as selecting the erroneous part of + This method should be implemented in subclasses that want to validate user changes to a string in a field, where the user changes are + not necessarily at the end of the string, and preserve the selection (or set a different one, such as selecting the erroneous part of the string the user has typed). - In a subclass implementation, evaluate partialString according to the context. Return YES if partialStringPtr is acceptable and NO if partialStringPtr - is unacceptable. Assign a new string to partialStringPtr and a new range to proposedSelRangePtr and return NO if you want to replace the string and - change the selection range. If you return NO, you can also return by indirection an CPString object (in error) that explains the reason why the - validation failed; the delegate (if any) of the CPControl object managing the cell can then respond to the failure in + In a subclass implementation, evaluate partialString according to the context. Return YES if partialStringPtr is acceptable and NO if partialStringPtr + is unacceptable. Assign a new string to partialStringPtr and a new range to proposedSelRangePtr and return NO if you want to replace the string and + change the selection range. If you return NO, you can also return by indirection an CPString object (in error) that explains the reason why the + validation failed; the delegate (if any) of the CPControl object managing the cell can then respond to the failure in control:didFailToValidatePartialString:errorDescription:. @param aPartialString The new string to validate. @param aProposedSelectedRange The selection range that will be used if the string is accepted or replaced. @param originalString The original string, before the proposed change. - @param originalSelectedRange The selection range over which the change is to take place. + @param originalSelectedRange The selection range over which the change is to take place. @param error If non-nil, if validation fails contains an CPString object that descibes the problem. @return YES if aPartialString is acceptable, otherwise NO. @@ -161,7 +161,7 @@ - (void)encodeWithCoder:(CPCoder)aCoder { - + } @end diff --git a/Foundation/CPJSONPConnection.j b/Foundation/CPJSONPConnection.j index c95a362c5..f69a5c954 100644 --- a/Foundation/CPJSONPConnection.j +++ b/Foundation/CPJSONPConnection.j @@ -134,7 +134,7 @@ CPJSONPCallbackReplacementString = @"${JSONP_CALLBACK}"; { var head = document.getElementsByTagName("head").item(0); - if(_scriptTag && _scriptTag.parentNode == head) + if (_scriptTag && _scriptTag.parentNode == head) head.removeChild(_scriptTag); CPJSONPConnectionCallbacks["callback" + [self UID]] = nil; diff --git a/Foundation/CPPredicate/CPComparisonPredicate.j b/Foundation/CPPredicate/CPComparisonPredicate.j index 8f5a9726b..5ccd47b97 100644 --- a/Foundation/CPPredicate/CPComparisonPredicate.j +++ b/Foundation/CPPredicate/CPComparisonPredicate.j @@ -4,7 +4,6 @@ @import "CPEnumerator.j" @import "CPPredicate.j" @import "CPExpression.j" -@import "CPExpression_operator.j" /*! A predicate to compare directly the left and right hand sides. @@ -136,8 +135,8 @@ CPContainsPredicateOperatorType = 99; */ CPBetweenPredicateOperatorType = 100; -var CPComparisonPredicateModifier; -var CPPredicateOperatorType; +var CPComparisonPredicateModifier, + CPPredicateOperatorType; /*! @ingroup foundation @@ -426,8 +425,8 @@ var CPPredicateOperatorType; lhs = lhs.stripDiacritics(); rhs = rhs.stripDiacritics(); } - var commut = (_options & CPCaseInsensitivePredicateOption) ? "gi":"g"; - var reg = new RegExp(rhs.escapeForRegExp(),commut); + var commut = (_options & CPCaseInsensitivePredicateOption) ? "gi":"g", + reg = new RegExp(rhs.escapeForRegExp(),commut); return reg.test(lhs); case CPBeginsWithPredicateOperatorType: var range = CPMakeRange(0,[rhs length]); @@ -441,43 +440,19 @@ var CPPredicateOperatorType; if (_options & CPDiacriticInsensitivePredicateOption) string_compare_options |= CPDiacriticInsensitiveSearch; return ([lhs compare:rhs options:string_compare_options range:range] == CPOrderedSame); - case CPInPredicateOperatorType: - // Handle special case where rhs is a collection and lhs an element of it. - if (![rhs isKindOfClass: [CPString class]]) - { - if (![rhs respondsToSelector: @selector(objectEnumerator)]) - [CPException raise:CPInvalidArgumentException reason:@"The right hand side for an IN operator must be a collection"]; - - var e = [rhs objectEnumerator], - value; - while (value = [e nextObject]) - if ([value isEqual:lhs]) - return YES; - - return NO; - } - - if (_options & CPCaseInsensitivePredicateOption) - string_compare_options |= CPCaseInsensitiveSearch; - if (_options & CPDiacriticInsensitivePredicateOption) - string_compare_options |= CPDiacriticInsensitiveSearch; - - return ([rhs rangeOfString:lhs options:string_compare_options].location != CPNotFound); case CPCustomSelectorPredicateOperatorType: return [lhs performSelector:_customSelector withObject:rhs]; + case CPInPredicateOperatorType: + var a = lhs; // swap + lhs = rhs; + rhs = a; case CPContainsPredicateOperatorType: - if (![lhs isKindOfClass: [CPString class]]) + if (![lhs isKindOfClass:[CPString class]]) { if (![lhs respondsToSelector: @selector(objectEnumerator)]) - [CPException raise:CPInvalidArgumentException reason:@"The left hand side for a CONTAINS operator must be a collection or a string"]; + [CPException raise:CPInvalidArgumentException reason:@"The left/right hand side for a CONTAINS/IN operator must be a collection or a string"]; - var e = [lhs objectEnumerator], - value; - while (value = [e nextObject]) - if ([value isEqual:rhs]) - return YES; - - return NO; + return [lhs containsObject:rhs]; } if (_options & CPCaseInsensitivePredicateOption) @@ -487,13 +462,10 @@ var CPPredicateOperatorType; return ([lhs rangeOfString:rhs options:string_compare_options].location != CPNotFound); case CPBetweenPredicateOperatorType: - if ([lhs count] < 2) + if ([rhs count] < 2) [CPException raise:CPInvalidArgumentException reason:@"The right hand side for a BETWEEN operator must contain 2 objects"]; - var lower = [rhs objectAtIndex:0], - upper = [rhs objectAtIndex:1]; - - return ([lhs compare:lower] == CPOrderedDescending && [lhs compare:upper] == CPOrderedAscending); + return ([lhs compare:rhs[0]] == CPOrderedDescending && [lhs compare:rhs[1]] == CPOrderedAscending); default: return NO; } @@ -572,8 +544,8 @@ var CPPredicateOperatorType; @end -var source = ['*','?','(',')','{','}','.','+','|','/','$','^']; -var dest = ['.*','.?','\\(','\\)','\\{','\\}','\\.','\\+','\\|','\\/','\\$','\\^']; +var source = ['*','?','(',')','{','}','.','+','|','/','$','^'], + dest = ['.*','.?','\\(','\\)','\\{','\\}','\\.','\\+','\\|','\\/','\\$','\\^']; String.prototype.escapeForRegExp = function() { diff --git a/Foundation/CPPredicate/CPCompoundPredicate.j b/Foundation/CPPredicate/CPCompoundPredicate.j index 55cd1ed0b..0ea197261 100644 --- a/Foundation/CPPredicate/CPCompoundPredicate.j +++ b/Foundation/CPPredicate/CPCompoundPredicate.j @@ -126,7 +126,7 @@ var CPCompoundPredicateType; i; if (count == 0) - return @"TRUPREDICATE"; + return @"TRUPREDICATE"; for (i = 0; i < count; i++) { diff --git a/Foundation/CPPredicate/CPExpression.j b/Foundation/CPPredicate/CPExpression.j index 64b43375b..78c0dabb5 100644 --- a/Foundation/CPPredicate/CPExpression.j +++ b/Foundation/CPPredicate/CPExpression.j @@ -132,7 +132,7 @@ CPMinusSetExpressionType = 9; */ + (CPExpression)expressionForUnionSet:(CPExpression)left with:(CPExpression)right { - return [[CPExpression_unionset alloc] initWithLeft:left right:right]; + return [[CPExpression_set alloc] initWithType:CPUnionSetExpressionType left:left right:right]; } /*! @@ -143,7 +143,7 @@ CPMinusSetExpressionType = 9; */ + (CPExpression)expressionForIntersectSet:(CPExpression)left with:(CPExpression)right { - return [[CPExpression_intersectset alloc] initWithLeft:left right:right]; + return [[CPExpression_set alloc] initWithType:CPIntersectSetExpressionType left:left right:right]; } /*! @@ -154,7 +154,7 @@ CPMinusSetExpressionType = 9; */ + (CPExpression)expressionForMinusSet:(CPExpression)left with:(CPExpression)right { - return [[CPExpression_minusset alloc] initWithLeft:left right:right]; + return [[CPExpression_set alloc] initWithType:CPMinusSetExpressionType left:left right:right]; } // Creating an Expression for a Function @@ -319,6 +319,4 @@ CPMinusSetExpressionType = 9; @import "CPExpression_keypath.j" @import "CPExpression_function.j" @import "CPExpression_aggregate.j" -@import "CPExpression_unionset.j" -@import "CPExpression_intersectset.j" -@import "CPExpression_minusset.j" +@import "CPExpression_set.j" diff --git a/Foundation/CPPredicate/CPExpression_aggregate.j b/Foundation/CPPredicate/CPExpression_aggregate.j index 90b25c8ec..4414afffe 100644 --- a/Foundation/CPPredicate/CPExpression_aggregate.j +++ b/Foundation/CPPredicate/CPExpression_aggregate.j @@ -15,30 +15,14 @@ return self; } -+ (CPExpression)expressionForAggregate:(CPArray)collection -{ - return [[self alloc] initWithAggregate:collection]; -} - -- (id)initWithCoder:(CPCoder)coder -{ - var aggregate = [coder decodeObjectForKey:@"CPExpressionAggregate"]; - return [self initWithAggregate:aggregate]; -} - -- (void)encodeWithCoder:(CPCoder)coder -{ - [coder encodeObject:_aggregate forKey:@"CPExpressionAggregate"]; // subexpressions must be CPCoding compliant. -} - - (BOOL)isEqual:(id)object { if (self == object) return YES; - + if (object.isa != self.isa || [object expressionType] != [self expressionType] || ![[object collection] isEqual:[self collection]]) return NO; - + return YES; } @@ -47,32 +31,16 @@ return _aggregate; } -- (CPExpression)rightExpression -{ - if ([_aggregate count] > 0) - return [_aggregate lastObject]; - - return nil; -} - -- (CPExpression)leftExpression -{ - if ([_aggregate count] > 0) - return [_aggregate objectAtIndex:0]; - - return nil; -} - - (id)expressionValueWithObject:(id)object context:(CPDictionary)context { var eval_array = [CPArray array], collection = [_aggregate objectEnumerator], exp; - + while (exp = [collection nextObject]) { var eval = [exp expressionValueWithObject:object context:context]; - if (eval != nil)[eval_array addObject:eval]; + [eval_array addObject:eval]; } return eval_array; @@ -81,14 +49,14 @@ - (CPString)description { var i, - count = [_aggregate count], + count = [_aggregate count], result = "{"; - + for (i = 0; i < count; i++) result = result + [CPString stringWithFormat:@"%s%s", [[_aggregate objectAtIndex:i] description], (i + 1 < count) ? @", " : @""]; - + result = result + "}"; - + return result; } @@ -97,7 +65,7 @@ var subst_array = [CPArray array], count = [_aggregate count], i; - + for (i = 0; i < count; i++) [subst_array addObject:[[_aggregate objectAtIndex:i] _expressionWithSubstitutionVariables:variables]]; @@ -105,3 +73,20 @@ } @end + +var CPCollectionKey = @"CPCollection"; + +@implementation CPExpression_aggregate (CPCoding) + +- (id)initWithCoder:(CPCoder)coder +{ + var collection = [coder decodeObjectForKey:CPCollectionKey]; + return [self initWithAggregate:collection]; +} + +- (void)encodeWithCoder:(CPCoder)coder +{ + [coder encodeObject:_aggregate forKey:CPCollectionKey]; +} + +@end \ No newline at end of file diff --git a/Foundation/CPPredicate/CPExpression_assignment.j b/Foundation/CPPredicate/CPExpression_assignment.j deleted file mode 100644 index 771119fd4..000000000 --- a/Foundation/CPPredicate/CPExpression_assignment.j +++ /dev/null @@ -1,92 +0,0 @@ - -@import "CPExpression.j" -@import "CPExpression_variable.j" -@import - - -@implementation CPExpression_assignment: CPExpression -{ - CPExpression_variable _assignmentVariable; - CPExpression _subexpression; -} - -- (id)initWithAssignmentVariable:(CPString)variable expression:(CPExpression)expression -{ - _assignmentVariable = [CPExpression expressionForVariable:variable]; - _subexpression = expression; - - return self; -} - -- (id)initWithAssignmentExpression:(CPExpression)variableExpression expression:(CPExpression)expression -{ - _assignmentVariable = variableExpression; - _subexpression = expression; - - return self; -} - -- (id)initWithCoder:(CPCoder)coder -{ - var variable = [coder decodeObjectForKey:@"CPExpressionAssignmentVariable"]; - var expression = [coder decodeObjectForKey:@"CPExpressionAssignmentExpression"]; - - return [self initWithAssignmentVariable:variable expression:expression]; -} - -- (void)encodeWithCoder:(CPCoder)coder -{ - [coder encodeObject:_assignmentVariable forKey:@"CPExpressionAssignmentVariable"]; - [coder encodeObject:_subexpression forKey:@"CPExpressionAssignmentExpression"]; -} - -- (BOOL)isEqual:(id)object -{ - if (self == object) - return YES; - - if (object.isa != self.isa || [object expressionType] != [self expressionType] || ![[object subexpression] isEqual:[self subexpression]] || ![[object variable] isEqualToString:[self variable]]) - return NO; - - return YES; -} - -- (CPExpression)assignmentVariable -{ - return _assignmentVariable; -} - -- (CPExpression)subexpression -{ - return _subexpression; -} - -- (CPString)variable -{ - return [_assignmentVariable variable]; -} - -- (CPString)description -{ - var pretty = [_expression description]; - - if ([_subexpression isKindOfClass:[CPExpression_operator class]]) - pretty = [CPString stringWithFormat:@"(%@)", pretty]; - - return [CPString stringWithFormat:@"%@ := %@", [self variable], pretty]; -} - -- (id)expressionValueWithObject:(id)object context:(id)context -{ - // UNIMPLEMENTED - return nil; -} - -- (CPExpression)_expressionWithSubstitutionVariables:(CPDictionary)variables -{ - // UNIMPLEMENTED - return nil; -} - -@end - diff --git a/Foundation/CPPredicate/CPExpression_constant.j b/Foundation/CPPredicate/CPExpression_constant.j index af6ed19d4..4cd38fd99 100644 --- a/Foundation/CPPredicate/CPExpression_constant.j +++ b/Foundation/CPPredicate/CPExpression_constant.j @@ -11,30 +11,18 @@ { [super initWithExpressionType:CPConstantValueExpressionType]; _value = value; - + return self; } -- (id)initWithCoder:(CPCoder)coder -{ - var value = [coder decodeObjectForKey:@"CPExpressionConstantValue"]; - - return [self initWithValue:value]; -} - -- (void)encodeWithCoder:(CPCoder)coder -{ - [coder encodeObject:_value forKey:@"CPExpressionConstantValue"]; -} - - (BOOL)isEqual:(id)object { if (self == object) return YES; - + if (object.isa != self.isa || [object expressionType] != [self expressionType] || ![[object constantValue] isEqual:[self constantValue]]) return NO; - + return YES; } @@ -63,3 +51,19 @@ @end +var CPConstantValueKey = @"CPConstantValue"; + +@implementation CPExpression_constant (CPCoding) + +- (id)initWithCoder:(CPCoder)coder +{ + var value = [coder decodeObjectForKey:CPConstantValueKey]; + return [self initWithValue:value]; +} + +- (void)encodeWithCoder:(CPCoder)coder +{ + [coder encodeObject:_value forKey:CPConstantValueKey]; +} + +@end diff --git a/Foundation/CPPredicate/CPExpression_function.j b/Foundation/CPPredicate/CPExpression_function.j index 539f947f0..02209a495 100644 --- a/Foundation/CPPredicate/CPExpression_function.j +++ b/Foundation/CPPredicate/CPExpression_function.j @@ -12,59 +12,38 @@ int _argc; } -- (id)initWithSelector:(SEL)aselector arguments:(CPArray)parameters +- (id)initWithSelector:(SEL)aSelector arguments:(CPArray)parameters { - [super initWithExpressionType:CPFunctionExpressionType]; - - if (![self respondsToSelector:aselector]) - [CPException raise: CPInvalidArgumentException reason:@"Unknown function implementation: " + aselector]; - - _selector = aselector; - _operand = nil; - _arguments = parameters; - _argc = [parameters count]; - - return self; + var target = [CPPredicateUtilities class]; + if (![target respondsToSelector:aSelector]) + [CPException raise:CPInvalidArgumentException reason:@"Unknown function implementation: " + aSelector]; + + var operand = [CPExpression expressionForConstantValue:target]; + return [self initWithTarget:operand selector:aSelector arguments:parameters]; } -- (id)initWithTarget:(CPExpression)targetExpression selector:(SEL)aselector arguments:(CPArray)parameters +- (id)initWithTarget:(CPExpression)targetExpression selector:aSelector arguments:parameters { - [super initWithExpressionType:CPFunctionExpressionType]; - - var target = [targetExpression expressionValueWithObject:object context:context]; - if (![target respondsToSelector:aselector]) - [CPException raise: CPInvalidArgumentException reason:@"Unknown function implementation: " + aselector]; - - _selector = aselector; + [super initWithExpressionType:CPFunctionExpressionType]; + +// Cocoa doc: "This method throws an exception immediately if the selector is unknown" +// but targetExpression's value (selector's target) may be resolved only at runtime. + _selector = aSelector; _operand = targetExpression; _arguments = parameters; _argc = [parameters count]; - + return self; } -- (id)initWithCoder:(CPCoder)coder -{ - var selector = CPSelectorFromString([coder decodeObjectForKey:@"CPExpressionFunctionName"]); - var arguments = [coder decodeObjectForKey:@"CPExpressionFunctionArguments"]; - - return [self initWithSelector:selector arguments:arguments]; -} - -- (void)encodeWithCoder:(CPCoder)coder -{ - [coder encodeObject:[self _function] forKey:@"CPExpressionFunctionName"]; - [coder encodeObject:_arguments forKey:@"CPExpressionArguments"]; -} - - (BOOL)isEqual:(id)object { if (self == object) return YES; - + if (object.isa != self.isa || [object expressionType] != [self expressionType] || ![[object _function] isEqualToString:[self _function]] || ![[object operand] isEqual:[self operand]] || ![[object arguments] isEqualToArray:[self arguments]]) return NO; - + return YES; } @@ -90,284 +69,193 @@ - (id)expressionValueWithObject:(id)object context:(CPDictionary)context { - var eval_args = [CPArray array], + var target = [_operand expressionValueWithObject:object context:context], + objj_args = [target, _selector], i; for (i = 0; i < _argc; i++) { - var arg = [[_arguments objectAtIndex:i] expressionValueWithObject:object context:context]; - if (arg != nil) - [eval_args addObject:arg]; + var arg = [_arguments[i] expressionValueWithObject:object context:context]; + objj_args.push(arg); } - var target = (_operand == nil) ? self : [_operand expressionValueWithObject:object context:context]; - return [target performSelector:_selector withObject:eval_args]; + return objj_msgSend.apply(this, objj_args); } - (CPString)description -{ - var result = [CPString stringWithFormat:@"%@ %s(", [_operand description], [self _function]], - i; - - for (i = 0; i < _argc; i++) - result = result + [_arguments objectAtIndex:i] + (i+1<_argc) ? ", " : ""; - +{ + var result = _operand + [self _function] + "("; + + for (var i = 0; i < _argc; i++) + result = result + [_arguments[i] description] + ((i + 1 < _argc) ? ", " : ""); + result = result + ")"; - + return result ; } - - (CPExpression)_expressionWithSubstitutionVariables:(CPDictionary)variables { var array = [CPArray array], i; - + for (i = 0; i < _argc; i++) [array addObject:[[_arguments objectAtIndex:i] _expressionWithSubstitutionVariables:variables]]; return [CPExpression expressionForFunction:[self operand] selectorName:[self _function] arguments:array]; } -- (CPNumber)sum:(CPArray)parameters +@end + +var CPSelectorNameKey = @"CPSelectorName", + CPArgumentsKey = @"CPArguments", + CPOperandKey = @"CPOperand"; + +@implementation CPExpression_function (CPCoding) + +- (id)initWithCoder:(CPCoder)coder { - if (_argc < 1) - [CPException raise:CPInvalidArgumentException reason:"Invalid number of parameters"]; - - var i, - sum = 0.0; - - for (i = 0; i < _argc; i++) - sum += [[parameters objectAtIndex:i] doubleValue]; - - return [CPNumber numberWithDouble: sum]; + var target = [coder decodeObjectForKey:CPOperandKey], + selector = CPSelectorFromString([coder decodeObjectForKey:CPSelectorNameKey]), + arguments = [coder decodeObjectForKey:CPArgumentsKey]; + + return [self initWithTarget:target selector:selector arguments:arguments]; } -- (CPNumber)count:(CPArray)parameters +- (void)encodeWithCoder:(CPCoder)coder { - if (_argc < 1) - [CPException raise:CPInvalidArgumentException reason:"Invalid number of parameters"]; - - return [CPNumber numberWithUnsignedInt: [[parameters objectAtIndex:0] count]]; + [coder encodeObject:[self _function] forKey:CPSelectorNameKey]; + [coder encodeObject:_arguments forKey:CPArgumentsKey]; + [coder encodeObject:_operand forKey:CPOperandKey]; } -- (CPNumber)min:(CPArray)parameters +@end + +// Built-in functions +@implementation CPPredicateUtilities : CPObject { - if (_argc < 1) - [CPException raise:CPInvalidArgumentException reason:"Invalid number of parameters"]; - - return MIN([parameters objectAtIndex:0],[parameters objectAtIndex:1]); } -- (CPNumber)max:(CPArray)parameters ++ (float)sum:(CPArray)parameters { - if (_argc < 1) - [CPException raise:CPInvalidArgumentException reason:"Invalid number of parameters"]; - - return MAX([parameters objectAtIndex:0],[parameters objectAtIndex:1]); + var sum = 0, + count = parameters.length; + + while (count--) + sum += parameters[count]; + + return sum; } -- (CPNumber)average:(CPArray)parameters ++ (float)count:(CPArray)parameters { - if (_argc < 1) - [CPException raise:CPInvalidArgumentException reason:"Invalid number of parameters"]; - - var i, - sum = 0.0; - - for (i = 0; i < _argc; i++) - sum += [[parameters objectAtIndex:i] doubleValue]; - - return [CPNumber numberWithDouble: sum / _argc]; + return [parameters count]; } -- (CPNumber)add:to:(CPArray)parameters ++ (float)min:(CPArray)parameters { - if (_argc != 2) - [CPException raise:CPInvalidArgumentException reason:"Invalid number of parameters"]; - - var left = [parameters objectAtIndex:0], - right = [parameters objectAtIndex:1]; - - return [CPNumber numberWithDouble: [left doubleValue] + [right doubleValue]]; + return parameters.sort()[0]; } -- (CPNumber)from:subtract:(CPArray)parameters ++ (float)max:(CPArray)parameters { - if (_argc != 2) - [CPException raise:CPInvalidArgumentException reason:"Invalid number of parameters"]; - - var left = [parameters objectAtIndex:0], - right = [parameters objectAtIndex:1]; - - return [CPNumber numberWithDouble: [left doubleValue] - [right doubleValue]]; + return parameters.sort()[parameters.length - 1]; } -- (CPNumber)multiply:by:(CPArray)parameters ++ (float)average:(CPArray)parameters { - if (_argc != 2) - [CPException raise:CPInvalidArgumentException reason:"Invalid number of parameters"]; - - var left = [parameters objectAtIndex:0], - right = [parameters objectAtIndex:1]; - - return [CPNumber numberWithDouble: [left doubleValue] * [right doubleValue]]; + return [self sum:parameters] / parameters.length; } -- (CPNumber)divide:by:(CPArray)parameters ++ (id)first:(CPArray)parameters { - if (_argc != 2) - [CPException raise:CPInvalidArgumentException reason:"Invalid number of parameters"]; - - var left = [parameters objectAtIndex:0], - right = [parameters objectAtIndex:1]; - - return [CPNumber numberWithDouble: [left doubleValue] / [right doubleValue]]; + return parameters[0]; } -- (CPNumber)sqrt:(CPArray)parameters ++ (id)last:(CPArray)parameters { - if (_argc != 1) - [CPException raise:CPInvalidArgumentException reason:"Invalid number of parameters"]; - - var num = [[parameters objectAtIndex:0] doubleValue]; - - return [CPNumber numberWithDouble: SQRT(num)]; + return parameters[parameters.length - 1]; } -- (CPNumber)raise:to:(CPArray)parameters ++ (id)fromObject:(id)object index:(id)anIndex { - if (_argc < 2) - [CPException raise:CPInvalidArgumentException reason:"Invalid number of parameters"]; - - var num = [[parameters objectAtIndex:0] doubleValue], - power = [[parameters objectAtIndex:1] doubleValue]; - - return [CPNumber numberWithDouble: POW(num,power)]; + if ([object isKindOfClass:[CPDictionary class]]) + return [object objectForKey:anIndex]; + else ([object isKindOfClass:[CPArray class]]) + return [object objectAtIndex:anIndex]; + + [CPException raise:CPInvalidArgumentException reason:@"object[#] requires a CPDictionary or CPArray"]; } -- (CPNumber)abs:(CPArray)parameters ++ (float)add:(int)n to:(int)m { - if (_argc != 1) - [CPException raise:CPInvalidArgumentException reason:"Invalid number of parameters"]; - - var num = [[parameters objectAtIndex:0] doubleValue]; - - return [CPNumber numberWithDouble:ABS(num)]; + return n + m; } -- (CPDate)now ++ (float)from:(int)n substract:(int)m +{ + return n - m; +} + ++ (float)multiply:(float)n by:(int)m +{ + return n * m; +} + ++ (float)divide:(float)n by:(float)m +{ + return n / m; +} + ++ (float)sqrt:(float)n +{ + return SQRT(n); +} + ++ (float)raise:(float)num to:(int)power +{ + return POW(num, power); +} + ++ (float)abs:(float)num +{ + return ABS(num); +} + ++ (CPDate)now { return [CPDate date]; } -- (CPNumber)ln:(CPArray)parameters ++ (float)ln:(float)num { - if (_argc != 1) - [CPException raise:CPInvalidArgumentException reason:"Invalid number of parameters"]; - - var num = [[parameters objectAtIndex:0] doubleValue]; - - return [CPNumber numberWithDouble:Math.log(num)]; + return LN10(num); } -- (CPNumber)exp:(CPArray)parameters ++ (float)exp:(float)num { - if (_argc != 1) - [CPException raise:CPInvalidArgumentException reason:"Invalid number of parameters"]; - - var num = [[parameters objectAtIndex:0] doubleValue]; - - return [CPNumber numberWithDouble:EXP(num)]; + return EXP(num); } -- (CPNumber)ceiling:(CPArray)parameters ++ (float)ceiling:(float)num { - if (_argc != 1) - [CPException raise:CPInvalidArgumentException reason:"Invalid number of parameters"]; - - var num = [[parameters objectAtIndex:0] doubleValue]; - - return [CPNumber numberWithDouble:CEIL(num)]; + return CEIL(num); } -- (CPNumber)random ++ (int)random:(int)num { - return [CPNumber numberWithDouble:RAND()]; + return ROUND(RAND() * num); } -- (CPNumber)modulus:by:(CPArray)parameters ++ (int)modulus:(int)n by:(int)n { - if (_argc != 2) - [CPException raise:CPInvalidArgumentException reason:"Invalid number of parameters"]; - - var left = [parameters objectAtIndex:0], - right = [parameters objectAtIndex:1]; - - return [CPNumber numberWithInt:([left intValue] % [right intValue])]; + return n % m; } - -- (id)first:(CPArray)parameters ++ (float)chs:(int)num { - if (_argc == 0) - [CPException raise:CPInvalidArgumentException reason:"Invalid number of parameters"]; - - return [[parameters objectAtIndex:0] objectAtIndex:0]; + return -num; } -- (id)last:(CPArray)parameters -{ - if (_argc == 0) - [CPException raise:CPInvalidArgumentException reason:"Invalid number of parameters"]; - - return [[parameters objectAtIndex:0] lastObject]; -} - -- (CPNumber)chs:(CPArray)parameters -{ - if (_argc == 0) - [CPException raise:CPInvalidArgumentException reason:"Invalid number of parameters"]; - - return [CPNumber numberWithInt: - [[parameters objectAtIndex:0] intValue]]; -} - -- (id)index:(CPArray)parameters -{ - if (_argc < 2) - [CPException raise:CPInvalidArgumentException reason:"Invalid number of parameters"]; - - var left = [parameters objectAtIndex:0], - right = [parameters objectAtIndex:1]; - - if ([left isKindOfClass: [CPDictionary class]]) - return [left objectForKey:right]; - else - return [left objectAtIndex: [right intValue]]; -} - -/* -- (CPNumber)median:(CPArray)parameters -{ -} -- (CPNumber)mode:(CPArray)parameters -{ -} -- (CPNumber)stddev:(CPArray)parameters -{ -} -- (CPNumber)log:(CPArray)parameters -{ -} -- (CPNumber)raise:to:(CPArray)parameters -{ -} -- (CPNumber)trunc:(CPArray)parameters -{ -} - -// These functions are used when parsing -*/ - @end diff --git a/Foundation/CPPredicate/CPExpression_intersectset.j b/Foundation/CPPredicate/CPExpression_intersectset.j deleted file mode 100644 index f2f6f3a23..000000000 --- a/Foundation/CPPredicate/CPExpression_intersectset.j +++ /dev/null @@ -1,86 +0,0 @@ - -@import "CPExpression.j" - -@implementation CPExpression_intersectset : CPExpression -{ - CPExpression _left; - CPExpression _right; -} - -- (id)initWithLeft:(CPExpression)left right:(CPExpression)right -{ - [super initWithExpressionType:CPIntersectSetExpressionType]; - _left = left ; - _right = right; - - return self; -} - -- (id)initWithCoder:(CPCoder)coder -{ - var left = [coder decodeObjectForKey:@"CPExpressionUnionSetLeftExpression"]; - var right = [coder decodeObjectForKey:@"CPExpressionUnionSetRightExpression"]; - - return [self initWithLeft:left right:right]; -} - -- (void)encodeWithCoder:(CPCoder)coder -{ - [coder encodeObject:_left forKey:@"CPExpressionUnionSetLeftExpression"]; - [coder encodeObject:_right forKey:@"CPExpressionUnionSetRightExpression"]; -} - -- (BOOL)isEqual:(id)object -{ - if (self == object) - return YES; - - if (object.isa != self.isa || [object expressionType] != [self expressionType] || ![[object leftExpression] isEqual:[self leftExpression]] || ![[object rightExpression] isEqual:[self rightExpression]]) - return NO; - - return YES; -} - -- (id)expressionValueWithObject:object context:(CPDictionary)context -{ - var right = [_right expressionValueWithObject:object context:context]; - if (![right respondsToSelector: @selector(objectEnumerator)]) - [CPException raise:CPInvalidArgumentException reason:@"The right expression for a CPIntersectSetExpressionType expression must be either a CPArray, CPDictionary or CPSet"]; - - var left = [_left expressionValueWithObject:object context:context]; - if (![left isKindOfClass:[CPSet set]]) - [CPException raise:CPInvalidArgumentException reason:@"The left expression for a CPIntersectSetExpressionType expression must a CPSet"]; - - var set = [CPSet setWithSet:left], - e = [right objectEnumerator], - item; - - while (item = [e nextObject]) - if ([left containsObject:item]) - [set addObject:item]; - - return [CPExpression expressionForConstantValue:set]; -} - -- (CPExpression )_expressionWithSubstitutionVariables:(CPDictionary )variables -{ - return self; -} - -- (CPExpression)leftExpression -{ - return _left; -} - -- (CPExpression)rightExpression -{ - return _right; -} - -- (CPString )description -{ - return [_left description] +" INTERSECT "+ [_right description]; -} - -@end - diff --git a/Foundation/CPPredicate/CPExpression_keypath.j b/Foundation/CPPredicate/CPExpression_keypath.j index e74c77b71..f5302a20c 100644 --- a/Foundation/CPPredicate/CPExpression_keypath.j +++ b/Foundation/CPPredicate/CPExpression_keypath.j @@ -2,61 +2,39 @@ @import "CPExpression.j" @import @import +@import "CPExpression_function.j" -@implementation CPExpression_keypath : CPExpression +@implementation CPExpression_keypath : CPExpression_function { - CPString _keyPath; } - (id)initWithKeyPath:(CPString)keyPath { - [super initWithExpressionType:CPKeyPathExpressionType]; - _keyPath = keyPath ; - + return [self initWithOperand:[CPExpression expressionForEvaluatedObject] andKeyPath:keyPath]; +} + +- (id)initWithOperand:(CPExpression)operand andKeyPath:(CPString)keyPath +{ + var arg = [CPExpression expressionForConstantValue:keyPath]; + // Cocoa: if it's a direct path selector is valueForKey: + self = [super initWithTarget:operand selector:@selector(valueForKeyPath:) arguments:[arg]]; + return self; } -- (id)initWithCoder:(CPCoder)coder +- (CPExpression)pathExpression { - var keyPath = [coder decodeObjectForKey:@"CPExpressionKeyPath"]; - - return [self initWithKeyPath:keyPath]; -} - -- (void)encodeWithCoder:(CPCoder)coder -{ - [coder encodeObject:_keyPath forKey:@"CPExpressionKeyPath"]; -} - -- (BOOL)isEqual:(id)object -{ - if (self == object) - return YES; - - if (object.isa != self.isa || [object expressionType] != [self expressionType] || ![[object keyPath] isEqualToString:[self keyPath]]) - return NO; - - return YES; + return [[self arguments] objectAtIndex:0]; } - (CPString)keyPath { - return _keyPath; -} - -- (id)expressionValueWithObject:object context:(CPDictionary)context -{ - return [object valueForKeyPath:_keyPath]; -} - -- (CPExpression)_expressionWithSubstitutionVariables:(CPDictionary)variables -{ - return self; + return [[self pathExpression] constantValue]; } - (CPString)description { - return _keyPath; + return [self keyPath]; } @end diff --git a/Foundation/CPPredicate/CPExpression_minusset.j b/Foundation/CPPredicate/CPExpression_minusset.j deleted file mode 100644 index 528580a9b..000000000 --- a/Foundation/CPPredicate/CPExpression_minusset.j +++ /dev/null @@ -1,81 +0,0 @@ - -@import "CPExpression.j" - -@implementation CPExpression_minusset : CPExpression - -- (id)initWithLeft:(CPExpression)left right:(CPExpression)right -{ - [super initWithExpressionType:CPMinusSetExpressionType]; - _left = left ; - _right = right; - - return self; -} - -- (id)initWithCoder:(CPCoder)coder -{ - var left = [coder decodeObjectForKey:@"CPExpressionMinusSetLeftExpression"]; - var right = [coder decodeObjectForKey:@"CPExpressionMinusSetRightExpression"]; - - return [self initWithLeft:left right:right]; -} - -- (void)encodeWithCoder:(CPCoder)coder -{ - [coder encodeObject:_left forKey:@"CPExpressionMinusSetLeftExpression"]; - [coder encodeObject:_right forKey:@"CPExpressionMinusSetRightExpression"]; -} - -- (BOOL)isEqual:(id)object -{ - if (self == object) - return YES; - - if (object.isa != self.isa || [object expressionType] != [self expressionType] || ![[object leftExpression] isEqual:[self leftExpression]] || ![[object rightExpression] isEqual:[self rightExpression]]) - return NO; - - return YES; -} - -- (id)expressionValueWithObject:object context:(CPDictionary)context -{ - var right = [_right expressionValueWithObject:object context:context]; - if (![right respondsToSelector: @selector(objectEnumerator)]) - [CPException raise:CPInvalidArgumentException reason:@"The right expression for a CPIntersectSetExpressionType expression must be either a CPArray, CPDictionary or CPSet"]; - - var left = [_left expressionValueWithObject:object context:context]; - if (![left isKindOfClass:[CPSet set]]) - [CPException raise:CPInvalidArgumentException reason:@"The left expression for a CPIntersectSetExpressionType expression must a CPSet"]; - - var set = [CPSet setWithSet:left], - e = [right objectEnumerator], - item; - - while (item = [e nextObject]) - [set removeObject:item]; - - return [CPExpression expressionForConstantValue:set]; -} - -- (CPExpression )_expressionWithSubstitutionVariables:(CPDictionary )variables -{ - return self; -} - -- (CPExpression)leftExpression -{ - return _left; -} - -- (CPExpression)rightExpression -{ - return _right; -} - -- (CPString )description -{ - return [_left description] +" MINUS "+ [_right description]; -} - -@end - diff --git a/Foundation/CPPredicate/CPExpression_operator.j b/Foundation/CPPredicate/CPExpression_operator.j deleted file mode 100644 index 3ac7d6450..000000000 --- a/Foundation/CPPredicate/CPExpression_operator.j +++ /dev/null @@ -1,117 +0,0 @@ - -@import "CPExpression.j" -@import -@import -@import - -var CPExpressionOperatorNegate = "CPExpressionOperatorNegate"; -var CPExpressionOperatorAdd = "CPExpressionOperatorAdd"; -var CPExpressionOperatorSubtract = "CPExpressionOperatorSubtract"; -var CPExpressionOperatorMultiply = "CPExpressionOperatorMultiply"; -var CPExpressionOperatorDivide = "CPExpressionOperatorDivide"; -var CPExpressionOperatorExp = "CPExpressionOperatorExp"; -var CPExpressionOperatorAssign = "CPExpressionOperatorAssign"; -var CPExpressionOperatorKeypath = "CPExpressionOperatorKeypath"; -var CPExpressionOperatorIndex = "CPExpressionOperatorIndex"; -var CPExpressionOperatorIndexFirst = "CPExpressionOperatorIndexFirst"; -var CPExpressionOperatorIndexLast = "CPExpressionOperatorIndexLast"; -var CPExpressionOperatorIndexSize = "CPExpressionOperatorIndexSize"; - -@implementation CPExpression_operator : CPExpression -{ - int _operator; - CPArray _arguments; -} - -- (id)initWithOperator:(int)operator arguments:(CPArray)arguments -{ - _operator = operator; - _arguments = arguments; - return self; -} - -+ (CPExpression)expressionForOperator:(CPExpressionOperator)operator arguments:(CPArray)arguments -{ - return [self initWithOperator:operator arguments:arguments]; -} - -- (CPArray)arguments -{ - return _arguments; -} - -- (CPString)description -{ - var result = [CPString string], - args = [CPArray array], - count = [_arguments count], - i; - - for (i = 0; i < count; i++) - { - var check = [_arguments objectAtIndex:i], - precedence = [check description]; - - if ([check isKindOfClass:[CPExpression_operator class]]) - precedence = [CPString stringWithFormat:@"(%@)", precedence]; - - [args addObject:precedence]; - } - - switch (_operator) - { - case CPExpressionOperatorNegate : - result = result + [CPString stringWithFormat:@"-%@", [args objectAtIndex:0]]; - break; - case CPExpressionOperatorAdd : - result = result + [CPString stringWithFormat:@"%@ + %@", [args objectAtIndex:0], [args objectAtIndex:1]]; - break; - case CPExpressionOperatorSubtract : - result = result + [CPString stringWithFormat:@"%@ - %@", [args objectAtIndex:0], [args objectAtIndex:1]]; - break; - case CPExpressionOperatorMultiply : - result = result + [CPString stringWithFormat:@"%@ * %@", [args objectAtIndex:0], [args objectAtIndex:1]]; - break; - case CPExpressionOperatorDivide : - result = result + [CPString stringWithFormat:@"%@ / %@", [args objectAtIndex:0], [args objectAtIndex:1]]; - break; - case CPExpressionOperatorExp : - result = result + [CPString stringWithFormat:@"%@ ** %@", [args objectAtIndex:0], [args objectAtIndex:1]]; - break; - case CPExpressionOperatorAssign : - result = result + [CPString stringWithFormat:@"%@ := %@", [args objectAtIndex:0], [args objectAtIndex:1]]; - break; - case CPExpressionOperatorKeypath : - result = result + [CPString stringWithFormat:@"%@.%@", [args objectAtIndex:0], [args objectAtIndex:1]]; - break; - case CPExpressionOperatorIndex : - result = result + [CPString stringWithFormat:@"%@[%@]", [args objectAtIndex:0], [args objectAtIndex:1]]; - break; - case CPExpressionOperatorIndexFirst : - result = result + [CPString stringWithFormat:@"%@[FIRST]", [args objectAtIndex:0]]; - break; - case CPExpressionOperatorIndexLast : - result = result + [CPString stringWithFormat:@"%@[LAST]", [args objectAtIndex:0]]; - break; - case CPExpressionOperatorIndexSize : - result = result + [CPString stringWithFormat:@"%@[SIZE]", [args objectAtIndex:0]]; - break; - } - - return result; -} - -- (CPExpression)_expressionWithSubstitutionVariables:(CPDictionary)variables -{ - var array = [CPArray array], - count = [_arguments count], - i; - - for (i = 0; i < count; i++) - [array addObject:[[_arguments objectAtIndex:i] _expressionWithSubstitutionVariables:variables]]; - - return [CPExpression_operator expressionForOperator:_operator arguments:array]; -} - -@end - diff --git a/Foundation/CPPredicate/CPExpression_self.j b/Foundation/CPPredicate/CPExpression_self.j index bfad66fe5..1173fe48c 100644 --- a/Foundation/CPPredicate/CPExpression_self.j +++ b/Foundation/CPPredicate/CPExpression_self.j @@ -4,13 +4,20 @@ @import @import -@implementation CPExpression_self : CPExpression{} +var defaultInstance = nil; +@implementation CPExpression_self : CPExpression +{ +} - (id)init { - [super initWithExpressionType:CPEvaluatedObjectExpressionType]; + if (defaultInstance == nil) + { + [super initWithExpressionType:CPEvaluatedObjectExpressionType]; + defaultInstance = self; + } - return self; + return defaultInstance; } - (id)initWithCoder:(CPCoder)coder @@ -24,10 +31,10 @@ - (BOOL)isEqual:(id)object { - return (object == self); + return (object === self); } -- (id)expressionValueWithObject:object context:(CPDictionary)context +- (id)expressionValueWithObject:(id)object context:(CPDictionary)context { return object; } diff --git a/Foundation/CPPredicate/CPExpression_set.j b/Foundation/CPPredicate/CPExpression_set.j new file mode 100644 index 000000000..38f5a542f --- /dev/null +++ b/Foundation/CPPredicate/CPExpression_set.j @@ -0,0 +1,115 @@ + +@import "CPExpression.j" + +@implementation CPExpression_set : CPExpression +{ + CPExpression _left; + CPExpression _right; +} + +- (id)initWithType:(int)type left:(CPExpression)left right:(CPExpression)right +{ + [super initWithExpressionType:type]; + _left = left; + _right = right; + + return self; +} + +- (BOOL)isEqual:(id)object +{ + if (self == object) + return YES; + + if (object.isa != self.isa || [object expressionType] != [self expressionType] || ![[object leftExpression] isEqual:[self leftExpression]] || ![[object rightExpression] isEqual:[self rightExpression]]) + return NO; + + return YES; +} + +- (id)expressionValueWithObject:object context:(CPDictionary)context +{ + var right = [_right expressionValueWithObject:object context:context]; + if ([right isKindOfClass:[CPArray class]]) // Or we could do [[right objectEnumerator] allObjects] + right = [CPSet setWithArray:right]; + else if ([right isKindOfClass:[CPDictionary class]]) + right = [CPSet setWithArray:[right allValues]]; + else if (![right isKindOfClass:[CPSet class]]) + [CPException raise:CPInvalidArgumentException reason:@"The right expression for a CP*SetExpressionType expression must evaluate to a CPArray, CPDictionary or CPSet"]; + + var left = [_left expressionValueWithObject:object context:context]; + if (![left isKindOfClass:[CPSet class]]) + [CPException raise:CPInvalidArgumentException reason:@"The left expression for a CP*SetExpressionType expression must evaluate to a CPSet"]; + + var result = [left copy]; + switch (_type) + { + case CPIntersectSetExpressionType : [result intersectSet:right]; + break; + case CPUnionSetExpressionType : [result unionSet:right]; + break; + case CPMinusSetExpressionType : [result minusSet:right]; + break; + default: + } + + return [CPExpression expressionForConstantValue:result]; +} + +- (CPExpression )_expressionWithSubstitutionVariables:(CPDictionary )variables +{ + return self; +} + +- (CPExpression)leftExpression +{ + return _left; +} + +- (CPExpression)rightExpression +{ + return _right; +} + +- (CPString)description +{ + var desc; + switch (_type) + { + case CPIntersectSetExpressionType : desc = @" INTERSECT "; + break; + case CPUnionSetExpressionType : desc = @" UNION "; + break; + case CPMinusSetExpressionType : desc = @" MINUS "; + break; + default: + } + + return [_left description] + desc + [_right description]; +} + +@end + +var CPLeftExpressionKey = @"CPLeftExpression", + CPRightExpressionKey = @"CPRightExpression", + CPExpressionType = @"CPExpressionType"; + +@implementation CPExpression_set (CPCoding) + +- (id)initWithCoder:(CPCoder)coder +{ + var left = [coder decodeObjectForKey:CPLeftExpressionKey], + right = [coder decodeObjectForKey:CPRightExpressionKey], + type = [coder decodeIntForKey:CPExpressionType]; + + return [self initWithType:type left:left right:right]; +} + +- (void)encodeWithCoder:(CPCoder)coder +{ + [coder encodeObject:_left forKey:CPLeftExpressionKey]; + [coder encodeObject:_right forKey:CPRightExpressionKey]; + [coder encodeInt:_type forKey:CPExpressionType]; +} + +@end diff --git a/Foundation/CPPredicate/CPExpression_unionset.j b/Foundation/CPPredicate/CPExpression_unionset.j deleted file mode 100644 index 45c9d137e..000000000 --- a/Foundation/CPPredicate/CPExpression_unionset.j +++ /dev/null @@ -1,84 +0,0 @@ - -@import "CPExpression.j" - -@implementation CPExpression_unionset : CPExpression - -- (id)initWithLeft:(CPExpression)left right:(CPExpression)right -{ - [super initWithExpressionType:CPUnionSetExpressionType]; - _left = left; - _right = right; - - return self; -} - -- (id)initWithCoder:(CPCoder)coder -{ - var left = [coder decodeObjectForKey:@"CPExpressionUnionSetLeftExpression"], - right = [coder decodeObjectForKey:@"CPExpressionUnionSetRightExpression"]; - - return [self initWithLeft:left right:right]; -} - -- (void)encodeWithCoder:(CPCoder)coder -{ - [coder encodeObject:_left forKey:@"CPExpressionUnionSetLeftExpression"]; - [coder encodeObject:_right forKey:@"CPExpressionUnionSetRightExpression"]; -} - -- (BOOL)isEqual:(id)object -{ - if (self == object) - return YES; - - if (object.isa != self.isa - || [object expressionType] != [self expressionType] - || ![[object leftExpression] isEqual:[self leftExpression]] - || ![[object rightExpression] isEqual:[self rightExpression]]) - return NO; - - return YES; -} - -- (id)expressionValueWithObject:object context:(CPDictionary )context -{ - var right = [_right expressionValueWithObject:object context:context]; - if (![right respondsToSelector: @selector(objectEnumerator)]) - [CPException raise:CPInvalidArgumentException reason:@"The right expression for a CPIntersectSetExpressionType expression must be either a CPArray, CPDictionary or CPSet"]; - - var left = [_left expressionValueWithObject:object context:context]; - if (![left isKindOfClass:[CPSet set]]) - [CPException raise:CPInvalidArgumentException reason:@"The left expression for a CPIntersectSetExpressionType expression must a CPSet"]; - - var unionset = [CPSet setWithSet:left], - e = [right objectEnumerator], - item; - - while (item = [e nextObject]) - [unionset addObject:item]; - - return [CPExpression expressionForConstantValue:unionset]; -} - -- (CPExpression )_expressionWithSubstitutionVariables:(CPDictionary )variables -{ - return self; -} - -- (CPExpression)leftExpression -{ - return _left; -} - -- (CPExpression)rightExpression -{ - return _right; -} - -- (CPString )description -{ - return [_left description] +" UNION "+ [_right description]; -} - -@end - diff --git a/Foundation/CPPredicate/CPExpression_variable.j b/Foundation/CPPredicate/CPExpression_variable.j index 1b7db6b28..f87c4fd4f 100644 --- a/Foundation/CPPredicate/CPExpression_variable.j +++ b/Foundation/CPPredicate/CPExpression_variable.j @@ -16,17 +16,6 @@ return self; } -- (id)initWithCoder:(CPCoder)coder -{ - var variable = [coder decodeObjectForKey:@"CPExpressionVariable"]; - return [self initWithVariable:variable]; -} - -- (void)encodeWithCoder:(CPCoder)coder -{ - [coder encodeObject:_variable forKey:@"CPExpressionVariable"]; -} - - (BOOL)isEqual:(id)object { if (self == object) @@ -63,6 +52,22 @@ return self; } +@end + +var CPVariableKey = @"CPVariable"; + +@implementation CPExpression_variable (CPCoding) + +- (id)initWithCoder:(CPCoder)coder +{ + var variable = [coder decodeObjectForKey:CPVariableKey]; + return [self initWithVariable:variable]; +} + +- (void)encodeWithCoder:(CPCoder)coder +{ + [coder encodeObject:_variable forKey:CPVariableKey]; +} @end diff --git a/Foundation/CPPredicate/CPPredicate.j b/Foundation/CPPredicate/CPPredicate.j index ff5f5f092..2041e7e71 100644 --- a/Foundation/CPPredicate/CPPredicate.j +++ b/Foundation/CPPredicate/CPPredicate.j @@ -37,7 +37,7 @@ @param … A comma-separated list of arguments to substitute into format. @return A new predicate formed by creating a new string with format and parsing the result. */ -+ (CPPredicate)predicateWithFormat:(CPString) format, ... ++ (CPPredicate)predicateWithFormat:(CPString)format, ... { if (!format) [CPException raise:CPInvalidArgumentException reason:_cmd + " the format can't be 'nil'"]; @@ -238,7 +238,7 @@ function(newValue)\ unsigned _retrieved; } -- (id) initWithString:(CPString)format args:(CPArray)args +- (id)initWithString:(CPString)format args:(CPArray)args { self = [super initWithString:format]; if (self != nil) @@ -248,15 +248,15 @@ function(newValue)\ return self; } -- (id) nextArg +- (id)nextArg { return [_args nextObject]; } - (BOOL)scanPredicateKeyword:(CPString)key { - var loc = [self scanLocation]; - var c; + var loc = [self scanLocation], + c; [self setCaseSensitive:NO]; if (![self scanString:key intoString:NULL]) @@ -274,7 +274,7 @@ function(newValue)\ return NO; } -- (CPPredicate) parse +- (CPPredicate)parse { var r = nil; @@ -296,12 +296,12 @@ function(newValue)\ return r; } -- (CPPredicate) parsePredicate +- (CPPredicate)parsePredicate { return [self parseAnd]; } -- (CPPredicate) parseAnd +- (CPPredicate)parseAnd { var l = [self parseOr]; @@ -333,7 +333,7 @@ function(newValue)\ return l; } -- (CPPredicate) parseNot +- (CPPredicate)parseNot { if ([self scanString:@"(" intoString:NULL]) { @@ -361,7 +361,7 @@ function(newValue)\ return [self parseComparison]; } -- (CPPredicate) parseOr +- (CPPredicate)parseOr { var l = [self parseNot]; while ([self scanPredicateKeyword:@"OR"] || [self scanPredicateKeyword:@"||"]) @@ -392,7 +392,7 @@ function(newValue)\ return l; } -- (CPPredicate) parseComparison +- (CPPredicate)parseComparison { var modifier = CPDirectPredicateModifier, type = 0, @@ -400,8 +400,7 @@ function(newValue)\ left, right, p, - negate = NO, - swap = NO; + negate = NO; if ([self scanPredicateKeyword:@"ANY"]) { @@ -469,36 +468,11 @@ function(newValue)\ } else if ([self scanPredicateKeyword:@"CONTAINS"]) { - type = CPInPredicateOperatorType; - swap = YES; + type = CPContainsPredicateOperatorType; } else if ([self scanPredicateKeyword:@"BETWEEN"]) { - var exp = [self parseSimpleExpression], - a = [exp constantValue], - lower, upper, - lexp, uexp, - lp, up; - - if (![a isKindOfClass:[CPArray class]]) - [CPException raise:CPInvalidArgumentException reason:@"BETWEEN operator requires array argument"]; - - lower = [a objectAtIndex:0]; - upper = [a objectAtIndex:1]; - lexp = [CPExpression expressionForConstantValue:lower]; - uexp = [CPExpression expressionForConstantValue:upper]; - lp = [CPComparisonPredicate predicateWithLeftExpression:left - rightExpression:lexp - modifier:modifier - type:CPGreaterThanPredicateOperatorType - options:opts]; - up = [CPComparisonPredicate predicateWithLeftExpression:left - rightExpression:uexp - modifier:modifier - type:CPLessThanPredicateOperatorType - options:opts]; - return [CPCompoundPredicate andPredicateWithSubpredicates: - [CPArray arrayWithObjects:lp, up]]; + type = CPBetweenPredicateOperatorType; } else [CPException raise:CPInvalidArgumentException reason:@"Invalid comparison predicate: "+ [[self string] substringFromIndex: [self scanLocation]]]; @@ -518,14 +492,6 @@ function(newValue)\ right = [self parseExpression]; - if (swap == YES) - { - var tmp = left; - - left = right; - right = tmp; - } - p = [CPComparisonPredicate predicateWithLeftExpression:left rightExpression:right modifier:modifier @@ -535,12 +501,12 @@ function(newValue)\ return negate ? [CPCompoundPredicate notPredicateWithSubpredicate:p]:p; } -- (CPExpression) parseExpression +- (CPExpression)parseExpression { return [self parseBinaryExpression]; } -- (CPExpression) parseSimpleExpression +- (CPExpression)parseSimpleExpression { var identifier, location, @@ -548,11 +514,11 @@ function(newValue)\ dbl; if ([self scanDouble:REFERENCE(dbl)]) - return [CPExpression expressionForConstantValue:[CPNumber numberWithDouble:dbl]]; + return [CPExpression expressionForConstantValue:dbl]; // FIXME: handle integer, hex constants, 0x 0o 0b if ([self scanString:@"-" intoString:NULL]) - return [CPExpression expressionForFunction:@"chs" arguments:[CPArray arrayWithObject:[self parseExpression]]]; + return [CPExpression expressionForFunction:@"chs:" arguments:[CPArray arrayWithObject:[self parseExpression]]]; if ([self scanString:@"(" intoString:NULL]) { @@ -578,7 +544,7 @@ function(newValue)\ if (![self scanString:@"}" intoString:NULL]) [CPException raise:CPInvalidArgumentException reason:@"Missing } in aggregate"]; - return [CPExpression expressionForConstantValue:a]; + return [CPExpression expressionForAggregate:a]; } if ([self scanPredicateKeyword:@"NULL"] || [self scanPredicateKeyword:@"NIL"]) @@ -676,33 +642,36 @@ function(newValue)\ if ([self scanString:@"\"" intoString:NULL]) { var skip = [self charactersToBeSkipped], - str; + str = @""; [self setCharactersToBeSkipped:nil]; - if ([self scanUpToString:@"\"" intoString:REFERENCE(str)] == NO) + [self scanUpToString:@"\"" intoString:REFERENCE(str)]; + + if ([self scanString:@"\"" intoString:NULL] == NO) { [self setCharactersToBeSkipped:skip]; [CPException raise:CPInvalidArgumentException reason:@"Invalid double quoted literal at "+location]; } - [self scanString:@"\"" intoString:NULL]; [self setCharactersToBeSkipped:skip]; return [CPExpression expressionForConstantValue:str]; } + if ([self scanString:@"'" intoString:NULL]) { var skip = [self charactersToBeSkipped], - str; + str = @""; [self setCharactersToBeSkipped:nil]; - if ([self scanUpToString:@"'" intoString:REFERENCE(str)] == NO) + [self scanUpToString:@"'" intoString:REFERENCE(str)]; + + if ([self scanString:@"'" intoString:NULL] == NO) { [self setCharactersToBeSkipped:skip]; - [CPException raise:CPInvalidArgumentException reason:@"Invalid single quoted literal at "+location]; + [CPException raise:CPInvalidArgumentException reason:@"Invalid double quoted literal at " + location]; } - [self scanString:@"'" intoString:NULL]; [self setCharactersToBeSkipped:skip]; return [CPExpression expressionForConstantValue:str]; @@ -713,9 +682,9 @@ function(newValue)\ var e = [self parseExpression]; if (![e keyPath]) - [CPException raise:CPInvalidArgumentException reason:@"Invalid keypath identifier: "+e]; + [CPException raise:CPInvalidArgumentException reason:@"Invalid keypath identifier: " + e]; - return [CPExpression expressionForKeyPath:[e keyPath]+"@"]; + return [CPExpression expressionForKeyPath:[e keyPath] + "@"]; } [self scanString:@"#" intoString:NULL]; @@ -759,28 +728,6 @@ function(newValue)\ } left = [CPExpression expressionForFunction:[left keyPath] arguments:args]; } - else if ([self scanString:@"[" intoString:NULL]) - { - // index expression - if ([self scanPredicateKeyword:@"FIRST"]) - { - left = [CPExpression expressionForFunction:@"first" arguments:[CPArray arrayWithObject:[self parseExpression]]]; - } - else if ([self scanPredicateKeyword:@"LAST"]) - { - left = [CPExpression expressionForFunction:@"last" arguments:[CPArray arrayWithObject:[self parseExpression]]]; - } - else if ([self scanPredicateKeyword:@"SIZE"]) - { - left = [CPExpression expressionForFunction:@"count" arguments:[CPArray arrayWithObject:[self parseExpression]]]; - } - else - { - left = [CPExpression expressionForFunction:@"index" arguments:[CPArray arrayWithObjects:left, [self parseExpression]]]; - } - if (![self scanString:@"]" intoString:NULL]) - [CPException raise:CPInvalidArgumentException reason:@"Missing ] in index argument"]; - } else if ([self scanString:@"." intoString:NULL]) { // keypath - this parser allows for (a).(b.c) @@ -791,12 +738,38 @@ function(newValue)\ if (![left keyPath]) [CPException raise:CPInvalidArgumentException reason:@"Invalid left keypath:" + left]; - var right = [self parseExpression]; + var right = [self parseSimpleExpression]; if (![right keyPath]) [CPException raise:CPInvalidArgumentException reason:@"Invalid right keypath:" + right]; // concatenate left = [CPExpression expressionForKeyPath:[left keyPath]+ "." + [right keyPath]]; + + } + else if ([self scanString:@"[" intoString:NULL]) + { + // index expression + if ([self scanPredicateKeyword:@"FIRST"]) + { + left = [CPExpression expressionForFunction:@"first:" arguments:[CPArray arrayWithObject:left]]; + } + else if ([self scanPredicateKeyword:@"LAST"]) + { + left = [CPExpression expressionForFunction:@"last:" arguments:[CPArray arrayWithObject:left]]; + } + else if ([self scanPredicateKeyword:@"SIZE"]) + { + left = [CPExpression expressionForFunction:@"count:" arguments:[CPArray arrayWithObject:left]]; + } + else + { + var integer; + if ([self scanInt:REFERENCE(integer)]) + left = [CPExpression expressionForFunction:@"fromObject:index:" arguments:[CPArray arrayWithObjects:left, [CPExpression expressionForConstantValue:integer]]]; + } + + if (![self scanString:@"]" intoString:NULL]) + [CPException raise:CPInvalidArgumentException reason:@"Missing ] in index argument"]; } else { @@ -817,7 +790,7 @@ function(newValue)\ if ([self scanString:@"**" intoString:NULL]) { right = [self parseFunctionalExpression]; - left = [CPExpression expressionForFunction:@"pow" arguments:[CPArray arrayWithObjects:left, right]]; + left = [CPExpression expressionForFunction:@"raise:to:" arguments:[CPArray arrayWithObjects:left, right]]; } else { @@ -837,12 +810,12 @@ function(newValue)\ if ([self scanString:@"*" intoString:NULL]) { right = [self parsePowerExpression]; - left = [CPExpression expressionForFunction:@"_mul" arguments:[CPArray arrayWithObjects:left, right]]; + left = [CPExpression expressionForFunction:@"multiply:by:" arguments:[CPArray arrayWithObjects:left, right]]; } else if ([self scanString:@"/" intoString:NULL]) { right = [self parsePowerExpression]; - left = [CPExpression expressionForFunction:@"_div" arguments:[CPArray arrayWithObjects:left, right]]; + left = [CPExpression expressionForFunction:@"divide:by:" arguments:[CPArray arrayWithObjects:left, right]]; } else { @@ -862,12 +835,12 @@ function(newValue)\ if ([self scanString:@"+" intoString:NULL]) { right = [self parseMultiplicationExpression]; - left = [CPExpression expressionForFunction:@"_add" arguments:[CPArray arrayWithObjects:left, right]]; + left = [CPExpression expressionForFunction:@"add:to:" arguments:[CPArray arrayWithObjects:left, right]]; } else if ([self scanString:@"-" intoString:NULL]) { right = [self parseMultiplicationExpression]; - left = [CPExpression expressionForFunction:@"_sub" arguments:[CPArray arrayWithObjects:left, right]]; + left = [CPExpression expressionForFunction:@"from:substract:" arguments:[CPArray arrayWithObjects:left, right]]; } else { @@ -903,6 +876,3 @@ function(newValue)\ @import "CPComparisonPredicate.j" @import "CPExpression.j" -@import "CPExpression_operator.j" -@import "CPExpression_aggregate.j" -@import "CPExpression_assignment.j" diff --git a/Objective-J/sprintf.js b/Objective-J/sprintf.js index d5df1b2a5..9ad4723a5 100644 --- a/Objective-J/sprintf.js +++ b/Objective-J/sprintf.js @@ -22,8 +22,8 @@ // sprintf: -var formatRegex = new RegExp("([^%]+|%[\\+\\-\\ \\#0]*[0-9\\*]*(.[0-9\\*]+)?[hlL]?[cbBdieEfgGosuxXpn%@])", "g"); -var tagRegex = new RegExp("(%)([\\+\\-\\ \\#0]*)([0-9\\*]*)((.[0-9\\*]+)?)([hlL]?)([cbBdieEfgGosuxXpn%@])"); +var formatRegex = new RegExp("([^%]+|%(?:\\d+\\$)?[\\+\\-\\ \\#0]*[0-9\\*]*(.[0-9\\*]+)?[hlL]?[cbBdieEfgGosuxXpn%@])", "g"); +var tagRegex = new RegExp("(%)(?:(\\d+)\\$)?([\\+\\-\\ \\#0]*)([0-9\\*]*)((?:.[0-9\\*]+)?)([hlL]?)([cbBdieEfgGosuxXpn%@])"); exports.sprintf = function(format) { @@ -55,21 +55,27 @@ exports.sprintf = function(format) } var percentSign = subtokens[1], - flags = subtokens[2], - widthString = subtokens[3], - precisionString = subtokens[4], + argIndex = subtokens[2], + flags = subtokens[3], + widthString = subtokens[4], + precisionString = subtokens[5], length = subtokens[6], specifier = subtokens[7]; + if (argIndex === undefined || argIndex === null || argIndex === "") + argIndex = arg++; + else + argIndex = Number(argIndex); + var width = null; if (widthString == "*") - width = arguments[arg++]; + width = arguments[argIndex]; else if (widthString != "") width = Number(widthString); var precision = null; if (precisionString == ".*") - precision = arguments[arg++]; + precision = arguments[argIndex]; else if (precisionString != "") precision = Number(precisionString.substring(1)); @@ -80,7 +86,7 @@ exports.sprintf = function(format) if (RegExp("[bBdiufeExXo]").test(specifier)) { - var num = Number(arguments[arg++]); + var num = Number(arguments[argIndex]); var sign = ""; if (num < 0) @@ -154,12 +160,11 @@ exports.sprintf = function(format) if (specifier == "%") subresult = "%"; else if (specifier == "c") - subresult = String(arguments[arg++]).charAt(0); + subresult = String(arguments[argIndex]).charAt(0); else if (specifier == "s" || specifier == "@") - subresult = String(arguments[arg++]); + subresult = String(arguments[argIndex]); else if (specifier == "p" || specifier == "n") { - arg++; subresult = ""; } diff --git a/Tests/Foundation/CPPredicateTest.j b/Tests/Foundation/CPPredicateTest.j index 242ac8efc..921667ba5 100644 --- a/Tests/Foundation/CPPredicateTest.j +++ b/Tests/Foundation/CPPredicateTest.j @@ -14,7 +14,7 @@ objects; dict = [[CPDictionary alloc] init]; - [dict setObject: @"A Title" forKey:@"title"]; + [dict setObject: @"Title" forKey:@"title"]; var keys = [CPArray arrayWithObjects:@"Name",@"Age",@"Children"]; objects = [CPArray arrayWithObjects:@"John",[CPNumber numberWithInt:34],[CPArray arrayWithObjects:@"Kid1", @"Kid2"]]; @@ -35,7 +35,8 @@ { var expression_keypath = [CPExpression expressionForKeyPath:@"name"]; [self assertNotNull:expression_keypath message:"KeyPath Expression should not be nil"]; - + [self assertTrue:[expression_keypath keyPath] == @"name" message:"-keyPath should not be \"name\""]; + var expression_str = [CPExpression expressionForConstantValue:@"j[a-z]an"]; [self assertNotNull:expression_str message:"ConstantValue Expression should not be nil"]; @@ -51,17 +52,20 @@ var expression_function = [CPExpression expressionForFunction:@"sum:" arguments:[CPArray arrayWithObjects:expression_num,expression_num]]; [self assertNotNull:expression_function message:"Function Expression should not be nil"]; - var expression_self = [CPExpression expressionForEvaluatedObject]; - [self assertNotNull:expression_self message:"Function Expression should not be nil"]; + expression_function = [CPExpression expressionForFunction:expression_num selectorName:@"isEqual:" arguments:[CPArray arrayWithObjects:expression_num]]; + [self assertNotNull:expression_function message:"Function Expression with target and selector should not be nil"]; - var expression_aggregate = [CPExpression expressionForAggregate:[CPArray arrayWithObjects:expression_str,expression_num,expression_function]]; + var expression_self = [CPExpression expressionForEvaluatedObject]; + [self assertNotNull:expression_self message:"Self Expression should not be nil"]; + + var expression_aggregate = [CPExpression expressionForAggregate:[CPArray arrayWithObjects:expression_str,expression_num]]; [self assertNotNull:expression_aggregate message:"Aggregate Expression should not be nil"]; var expression_subquery = [CPExpression expressionForSubquery:expression_collection usingIteratorVariable:@"self" predicate:[CPPredicate predicateWithValue:YES]]; // [self assertNotNull:expression_subquery message:"Subquery Expression should not be nil"]; - var set = [CPSet setWithObjects:@"a",@"b",@"d"]; - var array = [CPArray arrayWithObjects:@"a",@"b",@"d"]; + var set = [CPExpression expressionForConstantValue:[CPSet setWithObjects:@"a",@"b",@"c"]]; + var array = [CPExpression expressionForConstantValue:[CPArray arrayWithObjects:@"a",@"b",@"d"]]; var expression_intersect = [CPExpression expressionForIntersectSet:set with:array]; [self assertNotNull:expression_intersect message:"IntersectSet Expression should not be nil"]; @@ -73,16 +77,40 @@ [self assertNotNull:expression_minusset message:"MinusSet Expression should not be nil"]; } -- (void)testFunctionExpression +- (void)testSetExpressionEvaluation { - var function_exp = [CPExpression expressionForFunction:"sum:" arguments:[CPArray arrayWithObjects:[CPExpression expressionForConstantValue:1],[CPExpression expressionForConstantValue:2],[CPExpression expressionForConstantValue:3]]]; + var left = [CPExpression expressionForConstantValue:[CPSet setWithObjects:@"a",@"b",@"c"]]; + var right = [CPExpression expressionForConstantValue:[CPArray arrayWithObjects:@"a",@"b",@"d"]]; - var pred = [[CPComparisonPredicate alloc] initWithLeftExpression:function_exp rightExpression:[CPExpression expressionForConstantValue:3] modifier:CPDirectPredicateModifier type:CPGreaterThanPredicateOperatorType options:0]; + var expression = [CPExpression expressionForIntersectSet:left with:right]; + var eval = [[expression expressionValueWithObject:nil context:nil] constantValue]; + [self assertTrue:[eval isEqualToSet:[CPSet setWithObjects:@"a",@"b"]] message:"Result should be {(a, b)}, is " + eval]; + expression = [CPExpression expressionForUnionSet:left with:right]; + eval = [[expression expressionValueWithObject:nil context:nil] constantValue]; + [self assertTrue:[eval isEqualToSet:[CPSet setWithObjects:@"a",@"b",@"c",@"d"]] message:"Result should be {(a, b, c, d)}, is " + eval]; + + expression = [CPExpression expressionForMinusSet:left with:right]; + eval = [[expression expressionValueWithObject:nil context:nil] constantValue]; + [self assertTrue:[eval isEqualToSet:[CPSet setWithObjects:@"c"]] message:"Result should be {(c)}, is " + eval]; +} + +- (void)testFunctionExpressionEvaluation +{ + var expression = [CPExpression expressionForConstantValue:[1,2,3]]; + var function_exp = [CPExpression expressionForFunction:"sum:" arguments:[expression]]; + + var pred = [[CPComparisonPredicate alloc] initWithLeftExpression:function_exp rightExpression:[CPExpression expressionForConstantValue:6] modifier:CPDirectPredicateModifier type:CPEqualToPredicateOperatorType options:0]; + [self assertTrue:[pred evaluateWithObject:nil] message:[pred description] + " should be true"]; + + var operand = [CPExpression expressionForConstantValue:@"text"]; + var arg = [CPExpression expressionForConstantValue:2]; + function_exp = [CPExpression expressionForFunction:operand selectorName:@"substringFromIndex:" arguments:[arg]]; + pred = [[CPComparisonPredicate alloc] initWithLeftExpression:function_exp rightExpression:[CPExpression expressionForConstantValue:@"xt"] modifier:CPDirectPredicateModifier type:CPEqualToPredicateOperatorType options:0]; [self assertTrue:[pred evaluateWithObject:nil] message:[pred description] + " should be true"]; } -- (void)testVariableExpression +- (void)testVariableExpressionEvaluation { var variable_exp = [CPExpression expressionForVariable:@"variable"]; var pred = [[CPComparisonPredicate alloc] initWithLeftExpression:[CPExpression expressionForKeyPath:@"Record1.Age"] rightExpression:variable_exp modifier:CPDirectPredicateModifier type:CPGreaterThanPredicateOperatorType options:0]; @@ -205,12 +233,56 @@ predicate = [CPPredicate predicateWithFormat: @"%f > %K", 40.5, @"Record1.Age"]; [self assertTrue:[predicate evaluateWithObject:dict] message:[predicate description] + " should be true"]; -// TEST Aggregate +// TEST KeyPath predicate = [CPPredicate predicateWithFormat: @"%@ IN %K", @"Kid1", @"Record1.Children"]; [self assertTrue:[predicate evaluateWithObject:dict] message:[predicate description] + " should be true"]; + predicate = [CPPredicate predicateWithFormat: @"%K CONTAINS %@", @"Record1.Children", @"Kid1"]; + [self assertTrue:[predicate evaluateWithObject:dict] message:[predicate description] + " should be true"]; + predicate = [CPPredicate predicateWithFormat: @"ANY %K == %@", @"Record2.Children", @"Girl1"]; [self assertTrue:[predicate evaluateWithObject:dict] message:[predicate description] + " should be true"]; + +// Test Aggregate + predicate = [CPPredicate predicateWithFormat:@"{Record1 .Name, Record1.Age} = {'John',34}"]; + [self assertTrue:[predicate evaluateWithObject:dict] message:[predicate description] + " should be true"]; + +// Test Symbolic token + predicate = [CPPredicate predicateWithFormat:@"Record1.Children[FIRST] = 'Kid1'"]; + [self assertTrue:[predicate evaluateWithObject:dict] message:[predicate description] + " should be true"]; + + predicate = [CPPredicate predicateWithFormat:@"Record1.Children[1] = 'Kid2'"]; + [self assertTrue:[predicate evaluateWithObject:dict] message:[predicate description] + " should be true"]; + +// Test arithm + var n = 2; + predicate = [CPPredicate predicateWithFormat:@"SELF +1 = 3"]; + [self assertTrue:[predicate evaluateWithObject:n] message:[predicate description] + " should be true"]; + + predicate = [CPPredicate predicateWithFormat:@"SELF- 1 = 1"]; + [self assertTrue:[predicate evaluateWithObject:n] message:[predicate description] + " should be true"]; + + predicate = [CPPredicate predicateWithFormat:@"SELF / 2 = 1"]; + [self assertTrue:[predicate evaluateWithObject:n] message:[predicate description] + " should be true"]; + + predicate = [CPPredicate predicateWithFormat:@"SELF / 2 = 1"]; + [self assertTrue:[predicate evaluateWithObject:n] message:[predicate description] + " should be true"]; + + predicate = [CPPredicate predicateWithFormat:@"SELF* 2 = 4"]; + [self assertTrue:[predicate evaluateWithObject:n] message:[predicate description] + " should be true"]; + + predicate = [CPPredicate predicateWithFormat:@"SELF** 3 = 8"]; + [self assertTrue:[predicate evaluateWithObject:n] message:[predicate description] + " should be true"]; + +// TEST Operator type + predicate = [CPPredicate predicateWithFormat: @"a CONTAINS[c] \"b\""]; + [self assertTrue:([predicate predicateOperatorType] == CPContainsPredicateOperatorType) message:[predicate description] + " operator should be a CPContainsPredicateOperatorType"]; + + predicate = [CPPredicate predicateWithFormat: @"a BETWEEN {%f,%f}", 20, 40]; + [self assertTrue:([predicate predicateOperatorType] == CPBetweenPredicateOperatorType) message:[predicate description] + " operator should be a CPBetweenPredicateOperatorType"]; + + predicate = [CPPredicate predicateWithFormat: @"a CONTAINS \"\""]; + [self assertNotNull:predicate message:[predicate description] + " should not be nil"]; } @end diff --git a/Tests/Objective-J/sprintfTest.j b/Tests/Objective-J/sprintfTest.j index b51bb7cd0..785c7707c 100644 --- a/Tests/Objective-J/sprintfTest.j +++ b/Tests/Objective-J/sprintfTest.j @@ -1,20 +1,37 @@ - -// TODO: add many many more of these... -var sprintfTestCases = [ - [["[%@]", "hello world"], "[hello world]"], - [["[%d]", 123], "[123]"], - [["[%f]", 123.1234], "[123.1234]"], - [["[%d]", 123.1234], "[123]"] -]; +var sprintf = ObjectiveJ.sprintf; @implementation sprintfTest : OJTestCase -- (void)test_sprintf +// TODO: add many many more of these... + +- (void)testObjectWithPrefixAndSuffix { - for (var i = 0; i < sprintfTestCases.length; i++) - { - [self assert:ObjectiveJ.sprintf.apply(null, sprintfTestCases[i][0]) equals:sprintfTestCases[i][1]]; - } + [self assert:@"[hello world]" equals:sprintf(@"[%@]", @"hello world")]; +} + +- (void)testDecimalWithPrefixAndSuffix +{ + [self assert:@"[123]" equals:sprintf(@"[%d]", 123)]; +} + +- (void)testFloatWithPrefixAndSuffix +{ + [self assert:@"[123.1234]" equals:sprintf(@"[%f]", 123.1234)]; +} + +- (void)testZeroPaddingWithWidthAndPercentEscaping +{ + [self assert:@"099%" equals:sprintf(@"%03d%%", 99)]; +} + +- (void)testOutOfOrderExplicitFormatParameterIndexes +{ + [self assert:@"2 > 1" equals:sprintf(@"%2$d > %1$d", 1, 2)]; +} + +- (void)testMixingImplicitAndExplicitFormatParameterIndexes +{ + [self assert:@"a < b && b > a" equals:sprintf(@"%@ < %2$@ && %@ > %1$@", @"a", @"b")]; } @end