Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 280 lines (244 sloc) 11.384 kb
c00daa8 @davedelong Started implementing SKFetchRequest. API calls are generated based on t...
authored
1 //
2 // NSPredicate+SKAdditions.m
3 // StackKit
5944879 @davedelong added MIT license information
authored
4 /**
6740b4b @davedelong Updating license and copyright info
authored
5 Copyright (c) 2011 Dave DeLong
5944879 @davedelong added MIT license information
authored
6
7 Permission is hereby granted, free of charge, to any person obtaining a copy
8 of this software and associated documentation files (the "Software"), to deal
9 in the Software without restriction, including without limitation the rights
10 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 copies of the Software, and to permit persons to whom the Software is
12 furnished to do so, subject to the following conditions:
13
14 The above copyright notice and this permission notice shall be included in
15 all copies or substantial portions of the Software.
16
17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 THE SOFTWARE.
24 **/
c00daa8 @davedelong Started implementing SKFetchRequest. API calls are generated based on t...
authored
25
26 #import "NSPredicate+SKAdditions.h"
605aa1c @davedelong Beginning to add support for more complex predicates. Also, some SKComm...
authored
27 #import "SKFunctions.h"
c00daa8 @davedelong Started implementing SKFetchRequest. API calls are generated based on t...
authored
28
29 @implementation NSPredicate (SKAdditions)
30
c2c90fe @davedelong more predicate verification
authored
31 - (BOOL) isComparisonPredicateWithLeftKeyPaths:(NSArray *)leftKeyPaths operator:(NSPredicateOperatorType)operator rightExpressionType:(NSExpressionType)rightType {
32 if ([self isKindOfClass:[NSComparisonPredicate class]] == NO) { return NO; }
33 NSComparisonPredicate * comp = (NSComparisonPredicate *)self;
34
35 NSExpression * left = [comp leftExpression];
36 if ([left expressionType] != NSKeyPathExpressionType) { return NO; }
37
38 NSString * leftKeyPath = [left keyPath];
39 if ([leftKeyPaths containsObject:leftKeyPath] == NO) { return NO; }
40
605aa1c @davedelong Beginning to add support for more complex predicates. Also, some SKComm...
authored
41 //pass SKAnyPredicateOperator to ignore operator
42 if (operator != SKAnyPredicateOperator) {
c2c90fe @davedelong more predicate verification
authored
43 if ([comp predicateOperatorType] != operator) { return NO; }
44 }
45
46 NSExpression * right = [comp rightExpression];
47 if ([right expressionType] != rightType) { return NO; }
48
49 return YES;
50 }
51
b1fd0ee @davedelong Validating SKComment predicates
authored
52 - (BOOL) isPredicateWithConstantValueEqualToLeftKeyPath:(NSString *)leftKeyPath {
53 return [self isComparisonPredicateWithLeftKeyPaths:[NSArray arrayWithObject:leftKeyPath]
54 operator:NSEqualToPredicateOperatorType
55 rightExpressionType:NSConstantValueExpressionType];
56 }
57
d2f9d3c @davedelong Started implementing fromdate and todate in SKUserActivity. Doesn't see...
authored
58 - (NSArray *) subPredicatesWithLeftExpression:(NSExpression *)left {
c00daa8 @davedelong Started implementing SKFetchRequest. API calls are generated based on t...
authored
59 if ([self isKindOfClass:[NSCompoundPredicate class]]) {
60 NSCompoundPredicate * compound = (NSCompoundPredicate *)self;
61 NSArray * subPredicates = [compound subpredicates];
d2f9d3c @davedelong Started implementing fromdate and todate in SKUserActivity. Doesn't see...
authored
62
63 NSMutableArray * matches = [NSMutableArray array];
c00daa8 @davedelong Started implementing SKFetchRequest. API calls are generated based on t...
authored
64 for (NSPredicate * subPredicate in subPredicates) {
d2f9d3c @davedelong Started implementing fromdate and todate in SKUserActivity. Doesn't see...
authored
65 NSArray * subPredicateMatches = [subPredicate subPredicatesWithLeftExpression:left];
66 [matches addObjectsFromArray:subPredicateMatches];
c00daa8 @davedelong Started implementing SKFetchRequest. API calls are generated based on t...
authored
67 }
d2f9d3c @davedelong Started implementing fromdate and todate in SKUserActivity. Doesn't see...
authored
68 return matches;
c00daa8 @davedelong Started implementing SKFetchRequest. API calls are generated based on t...
authored
69 } else if ([self isKindOfClass:[NSComparisonPredicate class]]) {
70 NSComparisonPredicate * comparison = (NSComparisonPredicate *)self;
71 NSExpression * leftExpression = [comparison leftExpression];
7364429 @davedelong Added the ability to filter for an SKUser via the SKUserDisplayName in a...
authored
72 if ([leftExpression isEqual:left]) {
d2f9d3c @davedelong Started implementing fromdate and todate in SKUserActivity. Doesn't see...
authored
73 return [NSArray arrayWithObject:self];
c00daa8 @davedelong Started implementing SKFetchRequest. API calls are generated based on t...
authored
74 }
75 }
76 return nil;
77 }
78
605aa1c @davedelong Beginning to add support for more complex predicates. Also, some SKComm...
authored
79 - (NSArray *) subPredicatesWithLeftKeyPath:(NSString *)left {
80 return [self subPredicatesWithLeftExpression:[NSExpression expressionForKeyPath:left]];
81 }
82
d2f9d3c @davedelong Started implementing fromdate and todate in SKUserActivity. Doesn't see...
authored
83 - (NSPredicate *) subPredicateForLeftExpression:(NSExpression *)left {
84 NSArray * matches = [self subPredicatesWithLeftExpression:left];
85 if ([matches count] > 0) {
86 return [matches objectAtIndex:0];
87 }
88 return nil;
89 }
90
f451582 @davedelong Refactoring range checking and predicate validation. Most endpoints now...
authored
91 - (NSPredicate *) subPredicateForLeftKeyPath:(NSString *)left {
92 return [self subPredicateForLeftExpression:[NSExpression expressionForKeyPath:left]];
93 }
94
7364429 @davedelong Added the ability to filter for an SKUser via the SKUserDisplayName in a...
authored
95 - (id) constantValueForLeftExpression:(NSExpression *)left {
96 NSComparisonPredicate * comparison = (NSComparisonPredicate *)[self subPredicateForLeftExpression:left];
97 if (comparison == nil) { return nil; }
98 if ([[comparison rightExpression] expressionType] != NSConstantValueExpressionType) { return nil; }
99
100 return [[comparison rightExpression] constantValue];
101 }
102
b1fd0ee @davedelong Validating SKComment predicates
authored
103 - (id) constantValueForLeftKeyPath:(NSString *)left {
104 return [self constantValueForLeftExpression:[NSExpression expressionForKeyPath:left]];
105 }
106
605aa1c @davedelong Beginning to add support for more complex predicates. Also, some SKComm...
authored
107 - (id) constantValueForOperator:(NSPredicateOperatorType)operator {
108 if ([self isKindOfClass:[NSComparisonPredicate class]] == NO) { return nil; }
109 NSComparisonPredicate * comparison = (NSComparisonPredicate *)self;
110 if ([comparison predicateOperatorType] != operator) { return nil; }
111 NSExpression * rightExpression = [comparison rightExpression];
112 if ([rightExpression expressionType] != NSConstantValueExpressionType) { return nil; }
113 return [rightExpression constantValue];
114 }
115
116 - (id) constantValueForOneOfOperators:(NSArray *)operators {
117 for (NSNumber * op in operators) {
118 NSPredicateOperatorType operator = [op unsignedIntegerValue];
119 id value = [self constantValueForOperator:operator];
120 if (value) { return value; }
121 }
122 return nil;
123 }
124
f26a389 @davedelong Added SKBadges, with unit tests.
authored
125 - (NSPredicate *) predicateByRemovingSubPredicateWithLeftExpression:(NSExpression *)left {
126 if ([self isKindOfClass:[NSCompoundPredicate class]]) {
127 NSCompoundPredicate * compound = (NSCompoundPredicate *)self;
128 NSMutableArray * newSubpredicates = [NSMutableArray array];
129 NSArray * currentSubpredicates = [compound subpredicates];
130
131 for (NSPredicate * subPredicate in currentSubpredicates) {
132 NSPredicate * newVersion = [subPredicate predicateByRemovingSubPredicateWithLeftExpression:left];
133 if (newVersion != nil) {
134 [newSubpredicates addObject:newVersion];
135 }
136 }
137
138 if ([newSubpredicates count] > 0) {
139 NSCompoundPredicate * newCompound = [[NSCompoundPredicate alloc] initWithType:[compound compoundPredicateType] subpredicates:newSubpredicates];
140 return [newCompound autorelease];
141 } else {
142 return nil;
143 }
144 } else if ([self isKindOfClass:[NSComparisonPredicate class]]) {
145 NSComparisonPredicate * comparison = (NSComparisonPredicate *)self;
146 NSExpression * leftExpression = [comparison leftExpression];
147 if ([leftExpression isEqual:left]) {
148 return nil;
149 }
150 return comparison;
151 }
152 return nil;
153 }
154
8519cec @davedelong Added mobile project, and changed uses of Cocoa.h to Foundation.h (to al...
authored
155 - (NSPredicate *) predicateByReplacingLeftKeyPathsFromMapping:(NSDictionary *)mapping {
156 if ([self isKindOfClass:[NSComparisonPredicate class]]) {
157 NSComparisonPredicate * compP = (NSComparisonPredicate *)self;
29496b6 @davedelong Added predicate cleaning. Predicates that are using the @property name ...
authored
158 NSExpression * left = [compP leftExpression];
159 if ([left expressionType] == NSKeyPathExpressionType) {
160 NSString * keyPath = [left keyPath];
161 NSString * mappedKeyPath = [mapping objectForKey:keyPath];
162 if (mappedKeyPath != nil) {
163 NSComparisonPredicate * translated = [[NSComparisonPredicate alloc] initWithLeftExpression:[NSExpression expressionForKeyPath:mappedKeyPath]
164 rightExpression:[compP rightExpression]
165 modifier:[compP comparisonPredicateModifier]
166 type:[compP predicateOperatorType]
167 options:[compP options]];
168 return [translated autorelease];
169 }
170 }
8519cec @davedelong Added mobile project, and changed uses of Cocoa.h to Foundation.h (to al...
authored
171 } else if ([self isKindOfClass:[NSCompoundPredicate class]]) {
29496b6 @davedelong Added predicate cleaning. Predicates that are using the @property name ...
authored
172 NSCompoundPredicate * compoundP = (NSCompoundPredicate *)self;
173 NSArray * subpredicates = [compoundP subpredicates];
174 NSMutableArray * translated = [NSMutableArray array];
175 for (NSPredicate * subpredicate in subpredicates) {
176 NSPredicate * newSubpredicate = [subpredicate predicateByReplacingLeftKeyPathsFromMapping:mapping];
177 if (newSubpredicate != nil) {
178 [translated addObject:newSubpredicate];
179 }
180 }
605aa1c @davedelong Beginning to add support for more complex predicates. Also, some SKComm...
authored
181 if ([translated count] == 0) { return nil; }
182 NSCompoundPredicate * newCompound = [[NSCompoundPredicate alloc] initWithType:[compoundP compoundPredicateType]
183 subpredicates:translated];
184 return [newCompound autorelease];
8519cec @davedelong Added mobile project, and changed uses of Cocoa.h to Foundation.h (to al...
authored
185 } else {
186 return nil;
187 }
29496b6 @davedelong Added predicate cleaning. Predicates that are using the @property name ...
authored
188 return self;
8519cec @davedelong Added mobile project, and changed uses of Cocoa.h to Foundation.h (to al...
authored
189 }
190
605aa1c @davedelong Beginning to add support for more complex predicates. Also, some SKComm...
authored
191 - (BOOL) isSimpleAndPredicate {
192 if ([self isKindOfClass:[NSCompoundPredicate class]] == NO) { return NO; }
193 NSCompoundPredicate * compound = (NSCompoundPredicate *)self;
194 if ([compound compoundPredicateType] != NSAndPredicateType) { return NO; }
195
196 NSArray * subpredicates = [compound subpredicates];
197 BOOL ok = YES;
198 for (NSPredicate * subpredicate in subpredicates) {
199 ok &= [subpredicate isKindOfClass:[NSComparisonPredicate class]];
200 }
201
202 return ok;
203 }
204
205 - (SKRange) rangeOfConstantValuesForLeftKeyPath:(NSString *)left {
206 SKRange result = SKRangeNotFound;
207 if ([self isComparisonPredicateWithLeftKeyPaths:[NSArray arrayWithObject:left]
208 operator:SKAnyPredicateOperator
209 rightExpressionType:NSConstantValueExpressionType]) {
210 NSComparisonPredicate * comparison = (NSComparisonPredicate *)self;
211
212 id upperValue = [comparison constantValueForOneOfOperators:[NSArray arrayWithObjects:
213 [NSNumber numberWithUnsignedInteger:NSLessThanPredicateOperatorType],
214 [NSNumber numberWithUnsignedInteger:NSLessThanOrEqualToPredicateOperatorType],
215 nil]];
216 if (upperValue) {
b1cf13d @davedelong unlinking endpoints, changing SKRange to use ids
authored
217 result.upper = upperValue;
605aa1c @davedelong Beginning to add support for more complex predicates. Also, some SKComm...
authored
218 }
219
220 id lowerValue = [comparison constantValueForOneOfOperators:[NSArray arrayWithObjects:
221 [NSNumber numberWithUnsignedInteger:NSGreaterThanPredicateOperatorType],
222 [NSNumber numberWithUnsignedInteger:NSGreaterThanOrEqualToPredicateOperatorType],
223 nil]];
224 if (lowerValue) {
b1cf13d @davedelong unlinking endpoints, changing SKRange to use ids
authored
225 result.lower = lowerValue;
605aa1c @davedelong Beginning to add support for more complex predicates. Also, some SKComm...
authored
226 }
227 } else if ([self isKindOfClass:[NSCompoundPredicate class]]) {
228 NSCompoundPredicate * compound = (NSCompoundPredicate *)self;
229 NSArray * subpredicates = [compound subPredicatesWithLeftKeyPath:left];
230 for (NSPredicate * predicate in subpredicates) {
231 SKRange subpredicateRange = [predicate rangeOfConstantValuesForLeftKeyPath:left];
232 if (result.lower == SKNotFound && subpredicateRange.lower != SKNotFound) {
233 result.lower = subpredicateRange.lower;
234 }
235 if (result.upper == SKNotFound && subpredicateRange.upper != SKNotFound) {
236 result.upper = subpredicateRange.upper;
237 }
238 if (result.lower != SKNotFound && result.upper != SKNotFound) { break; }
239 }
240 }
241 return result;
242 }
243
9b01fe1 @davedelong Leftover changes; beginning rework of request URL generation system.
authored
244 - (NSSet *) leftKeyPaths {
245 NSMutableSet * paths = [NSMutableSet set];
246 if ([self isKindOfClass:[NSCompoundPredicate class]]) {
247 NSCompoundPredicate * compound = (NSCompoundPredicate *)self;
248 for (NSPredicate * subpredicate in [compound subpredicates]) {
249 [paths unionSet:[subpredicate leftKeyPaths]];
250 }
251 } else {
252 NSComparisonPredicate * comparison = (NSComparisonPredicate *)self;
253 if ([[comparison leftExpression] expressionType] == NSKeyPathExpressionType) {
254 [paths addObject:[[comparison leftExpression] keyPath]];
255 }
256 }
257 return paths;
258 }
259
faae919 @davedelong Most of request building is in place. starting specific implementation
authored
260 //this has to return an NSNumber. If it returned a BOOL, then we'd get an EXC_BAD_ACCESS when using it in our FUNCTION() predicate
261 - (NSNumber *) sk_matchesRecognizedKeyPathsAndOperators:(NSDictionary *)keyPaths {
262 BOOL matches = YES;
263 for (NSString * keyPath in keyPaths) {
264 NSArray * allowedOperatorsForKeyPath = [keyPaths objectForKey:keyPath];
265 NSArray * subPredicatesUsingKeyPath = [self subPredicatesWithLeftKeyPath:keyPath];
266 for (NSComparisonPredicate * subPredicate in subPredicatesUsingKeyPath) {
267 NSPredicateOperatorType operator = [subPredicate predicateOperatorType];
268 NSNumber * boxed = [NSNumber numberWithUnsignedInteger:operator];
269 if ([allowedOperatorsForKeyPath containsObject:boxed] == NO) {
270 matches = NO;
271 break;
272 }
273 }
274 if (matches == NO) { break; }
275 }
276 return [NSNumber numberWithBool:matches];
277 }
278
c00daa8 @davedelong Started implementing SKFetchRequest. API calls are generated based on t...
authored
279 @end
Something went wrong with that request. Please try again.