nano-store 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. data/.gitignore +3 -0
  2. data/.gitmodules +3 -0
  3. data/.rvmrc +1 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE +25 -0
  6. data/README.md +193 -0
  7. data/Rakefile +1 -0
  8. data/app/app_delegate.rb +5 -0
  9. data/lib/nano-store.rb +1 -0
  10. data/lib/nano_store/bag.rb +98 -0
  11. data/lib/nano_store/model.rb +142 -0
  12. data/lib/nano_store/nano_store.rb +36 -0
  13. data/lib/nano_store/store_extension.rb +150 -0
  14. data/lib/nano_store/version.rb +3 -0
  15. data/lib/nano_store.rb +14 -0
  16. data/nano-store.gemspec +17 -0
  17. data/resources/.gitignore +0 -0
  18. data/spec/bag_spec.rb +66 -0
  19. data/spec/model_spec.rb +130 -0
  20. data/spec/nano_store_spec.rb +48 -0
  21. data/spec/store_extension_spec.rb +110 -0
  22. data/vendor/NanoStore/Classes/Advanced/NSFNanoEngine.h +542 -0
  23. data/vendor/NanoStore/Classes/Advanced/NSFNanoEngine.m +1781 -0
  24. data/vendor/NanoStore/Classes/Advanced/NSFNanoResult.h +137 -0
  25. data/vendor/NanoStore/Classes/Advanced/NSFNanoResult.m +265 -0
  26. data/vendor/NanoStore/Classes/Private/NSFNanoBag_Private.h +37 -0
  27. data/vendor/NanoStore/Classes/Private/NSFNanoEngine_Private.h +69 -0
  28. data/vendor/NanoStore/Classes/Private/NSFNanoExpression_Private.h +35 -0
  29. data/vendor/NanoStore/Classes/Private/NSFNanoGlobals_Private.h +99 -0
  30. data/vendor/NanoStore/Classes/Private/NSFNanoObject_Private.h +35 -0
  31. data/vendor/NanoStore/Classes/Private/NSFNanoPredicate_Private.h +35 -0
  32. data/vendor/NanoStore/Classes/Private/NSFNanoResult_Private.h +43 -0
  33. data/vendor/NanoStore/Classes/Private/NSFNanoSearch_Private.h +48 -0
  34. data/vendor/NanoStore/Classes/Private/NSFNanoStore_Private.h +57 -0
  35. data/vendor/NanoStore/Classes/Private/NanoStore_Private.h +37 -0
  36. data/vendor/NanoStore/Classes/Public/NSFNanoBag.h +306 -0
  37. data/vendor/NanoStore/Classes/Public/NSFNanoBag.m +485 -0
  38. data/vendor/NanoStore/Classes/Public/NSFNanoExpression.h +125 -0
  39. data/vendor/NanoStore/Classes/Public/NSFNanoExpression.m +103 -0
  40. data/vendor/NanoStore/Classes/Public/NSFNanoGlobals.h +323 -0
  41. data/vendor/NanoStore/Classes/Public/NSFNanoGlobals.m +145 -0
  42. data/vendor/NanoStore/Classes/Public/NSFNanoObject.h +298 -0
  43. data/vendor/NanoStore/Classes/Public/NSFNanoObject.m +187 -0
  44. data/vendor/NanoStore/Classes/Public/NSFNanoObjectProtocol.h +119 -0
  45. data/vendor/NanoStore/Classes/Public/NSFNanoPredicate.h +123 -0
  46. data/vendor/NanoStore/Classes/Public/NSFNanoPredicate.m +130 -0
  47. data/vendor/NanoStore/Classes/Public/NSFNanoSearch.h +381 -0
  48. data/vendor/NanoStore/Classes/Public/NSFNanoSearch.m +835 -0
  49. data/vendor/NanoStore/Classes/Public/NSFNanoSortDescriptor.h +124 -0
  50. data/vendor/NanoStore/Classes/Public/NSFNanoSortDescriptor.m +79 -0
  51. data/vendor/NanoStore/Classes/Public/NSFNanoStore.h +475 -0
  52. data/vendor/NanoStore/Classes/Public/NSFNanoStore.m +1375 -0
  53. data/vendor/NanoStore/Classes/Public/NanoStore.h +463 -0
  54. data/vendor/NanoStore/LICENSE +25 -0
  55. data/vendor/NanoStore/NanoStore.bridgesupport +1215 -0
  56. data/vendor/NanoStore/README.md +411 -0
  57. metadata +118 -0
@@ -0,0 +1,835 @@
1
+ /*
2
+ NSFNanoSearch.m
3
+ NanoStore
4
+
5
+ Copyright (c) 2010 Webbo, L.L.C. All rights reserved.
6
+
7
+ Redistribution and use in source and binary forms, with or without modification, are permitted
8
+ provided that the following conditions are met:
9
+
10
+ * Redistributions of source code must retain the above copyright notice, this list of conditions
11
+ and the following disclaimer.
12
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
13
+ and the following disclaimer in the documentation and/or other materials provided with the distribution.
14
+ * Neither the name of Webbo nor the names of its contributors may be used to endorse or promote
15
+ products derived from this software without specific prior written permission.
16
+
17
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
18
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
19
+ PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY
20
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
23
+ OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24
+ SUCH DAMAGE.
25
+ */
26
+
27
+ #import "NanoStore.h"
28
+ #import "NanoStore_Private.h"
29
+ #import "NSFNanoSearch_Private.h"
30
+
31
+ @implementation NSFNanoSearch
32
+ {
33
+ /** \cond */
34
+ @protected
35
+ NSFReturnType returnedObjectType;
36
+ /** \endcond */
37
+ }
38
+
39
+
40
+ @synthesize nanoStore, attributesToBeReturned, key, attribute, value, match, expressions, groupValues, sql, sort;
41
+
42
+ // ----------------------------------------------
43
+ // Initialization / Cleanup
44
+ // ----------------------------------------------
45
+
46
+ + (NSFNanoSearch*)searchWithStore:(NSFNanoStore *)store
47
+ {
48
+ return [[self alloc]initWithStore:store];
49
+ }
50
+
51
+ - (id)initWithStore:(NSFNanoStore *)store
52
+ {
53
+ if (nil == store) {
54
+ [[NSException exceptionWithName:NSFUnexpectedParameterException
55
+ reason:[NSString stringWithFormat:@"*** -[%@ %s]: store is nil.", [self class], _cmd]
56
+ userInfo:nil]raise];
57
+ }
58
+
59
+ if ((self = [self init])) {
60
+ nanoStore = store;
61
+ [self reset];
62
+ }
63
+
64
+ return self;
65
+ }
66
+
67
+ /** \cond */
68
+
69
+ - (void)dealloc
70
+ {
71
+ [self reset];
72
+ }
73
+
74
+ /** \endcond */
75
+
76
+ #pragma mark -
77
+
78
+ - (NSString *)sql
79
+ {
80
+ if (nil == sql)
81
+ return [self _preparedSQL];
82
+
83
+ return sql;
84
+ }
85
+
86
+ - (NSString*)description
87
+ {
88
+ NSMutableString *description = [NSMutableString string];
89
+
90
+ [description appendString:@"\n"];
91
+ [description appendString:[NSString stringWithFormat:@"NanoSearch address : 0x%x\n", self]];
92
+ [description appendString:[NSString stringWithFormat:@"Document store : 0x%x\n", nanoStore]];
93
+ [description appendString:[NSString stringWithFormat:@"Attributes to be returned : %@\n", (attributesToBeReturned ? [attributesToBeReturned componentsJoinedByString:@","] : @"All")]];
94
+ [description appendString:[NSString stringWithFormat:@"Key : %@\n", key]];
95
+ [description appendString:[NSString stringWithFormat:@"Attribute : %@\n", attribute]];
96
+ [description appendString:[NSString stringWithFormat:@"Value : %@\n", value]];
97
+ [description appendString:[NSString stringWithFormat:@"Match : %@\n", NSFStringFromMatchType(match)]];
98
+ [description appendString:[NSString stringWithFormat:@"Expressions : %@\n", expressions]];
99
+ [description appendString:[NSString stringWithFormat:@"Group Values? : %@\n", (groupValues ? @"YES" : @"NO")]];
100
+ [description appendString:[NSString stringWithFormat:@"Sort : %@\n", sort]];
101
+
102
+ return description;
103
+ }
104
+
105
+ #pragma mark -
106
+
107
+ - (id)executeSQL:(NSString *)theSQLStatement returnType:(NSFReturnType)theReturnType error:(out NSError **)outError
108
+ {
109
+ // Make sure we don't have any lingering parameters that could mess with the results, but keep the sort descriptor(s)
110
+ NSArray *savedSort = [self sort];
111
+ [self reset];
112
+ self.sort = savedSort;
113
+
114
+ returnedObjectType = theReturnType;
115
+ sql = [theSQLStatement copy];
116
+
117
+ NSDictionary *results = [self _retrieveDataWithError:outError];
118
+
119
+ return [self _sortResultsIfApplicable:results returnType:theReturnType];
120
+ }
121
+
122
+ - (NSFNanoResult *)executeSQL:(NSString *)theSQLStatement
123
+ {
124
+ // Make sure we don't have any lingering parameters that could mess with the results...
125
+ [self reset];
126
+
127
+ sql = theSQLStatement;
128
+
129
+ return [nanoStore _executeSQL:theSQLStatement];
130
+ }
131
+
132
+ - (NSFNanoResult *)explainSQL:(NSString *)theSQLStatement
133
+ {
134
+ if (nil == theSQLStatement) {
135
+ [[NSException exceptionWithName:NSFUnexpectedParameterException
136
+ reason:[NSString stringWithFormat:@"*** -[%@ %s]: the SQL statement is nil.", [self class], _cmd]
137
+ userInfo:nil]raise];
138
+ }
139
+
140
+ if (0 == [theSQLStatement length]) {
141
+ [[NSException exceptionWithName:NSFUnexpectedParameterException
142
+ reason:[NSString stringWithFormat:@"*** -[%@ %s]: the SQL statement is empty.", [self class], _cmd]
143
+ userInfo:nil]raise];
144
+ }
145
+
146
+ return [nanoStore _executeSQL:[NSString stringWithFormat:@"EXPLAIN %@", theSQLStatement]];
147
+ }
148
+
149
+ - (void)reset
150
+ {
151
+ attributesToBeReturned= nil;
152
+ key = nil;
153
+ attribute = nil;
154
+ value = nil;
155
+ match = NSFContains;
156
+ groupValues = NO;
157
+ sql = nil;
158
+ sort = nil;
159
+
160
+ returnedObjectType = NSFReturnObjects;
161
+ }
162
+
163
+ #pragma mark -
164
+
165
+ - (id)searchObjectsWithReturnType:(NSFReturnType)theReturnType error:(out NSError **)outError
166
+ {
167
+ returnedObjectType = theReturnType;
168
+
169
+ // Make sure we don't have a SQL statement around...
170
+ sql = nil;
171
+
172
+ id results = [self _retrieveDataWithError:outError];
173
+
174
+ return [self _sortResultsIfApplicable:results returnType:theReturnType];
175
+ }
176
+
177
+ - (id)searchObjectsAdded:(NSFDateMatchType)theDateMatch date:(NSDate *)theDate returnType:(NSFReturnType)theReturnType error:(out NSError **)outError
178
+ {
179
+ returnedObjectType = theReturnType;
180
+
181
+ // Make sure we don't have a SQL statement around...
182
+ sql = nil;
183
+
184
+ id results = [self _retrieveDataAdded:theDateMatch calendarDate:theDate error:outError];
185
+
186
+ if (NSFReturnKeys == theReturnType) {
187
+ results = [results allKeys];
188
+ }
189
+
190
+ return results;
191
+ }
192
+
193
+ - (NSNumber *)aggregateOperation:(NSFAggregateFunctionType)theFunctionType onAttribute:(NSString *)theAttribute
194
+ {
195
+ NSFReturnType savedObjectTypeReturned = returnedObjectType;
196
+ returnedObjectType = NSFReturnKeys;
197
+
198
+ NSString *savedSQL = sql;
199
+ sql = nil;
200
+
201
+ NSString *theSearchSQLStatement = [self sql];
202
+ NSMutableString *theAggregatedSQLStatement = [NSMutableString new];
203
+
204
+ switch (theFunctionType) {
205
+ case NSFAverage:
206
+ [theAggregatedSQLStatement appendString:[NSString stringWithFormat:@"SELECT avg(NSFValue) FROM NSFValues WHERE NSFAttribute = '%@' AND NSFKey IN (%@)", theAttribute, theSearchSQLStatement]];
207
+ break;
208
+ case NSFCount:
209
+ [theAggregatedSQLStatement appendString:[NSString stringWithFormat:@"SELECT count(*) FROM NSFValues WHERE NSFAttribute = '%@' AND NSFKey IN (%@)", theAttribute, theSearchSQLStatement]];
210
+ break;
211
+ case NSFMax:
212
+ [theAggregatedSQLStatement appendString:[NSString stringWithFormat:@"SELECT max(NSFValue) FROM NSFValues WHERE NSFAttribute = '%@' AND NSFKey IN (%@)", theAttribute, theSearchSQLStatement]];
213
+ break;
214
+ case NSFMin:
215
+ [theAggregatedSQLStatement appendString:[NSString stringWithFormat:@"SELECT min(NSFValue) FROM NSFValues WHERE NSFAttribute = '%@' AND NSFKey IN (%@)", theAttribute, theSearchSQLStatement]];
216
+ break;
217
+ case NSFTotal:
218
+ /* Note:
219
+ Sum() will throw an "integer overflow" exception if all inputs are integers or NULL and an integer overflow occurs at any point
220
+ during the computation. Total() never throws an integer overflow.
221
+ */
222
+ [theAggregatedSQLStatement appendString:[NSString stringWithFormat:@"SELECT total(NSFValue) FROM NSFValues WHERE NSFAttribute = '%@' AND NSFKey IN (%@)", theAttribute, theSearchSQLStatement]];
223
+ break;
224
+ default:
225
+ break;
226
+ }
227
+
228
+ NSFNanoResult *result = [nanoStore _executeSQL:theAggregatedSQLStatement];
229
+
230
+ returnedObjectType = savedObjectTypeReturned;
231
+ sql = savedSQL;
232
+
233
+ return [NSNumber numberWithFloat:[[result firstValue]floatValue]];
234
+ }
235
+
236
+ #pragma mark -
237
+ #pragma mark Private Methods
238
+ #pragma mark -
239
+
240
+ /** \cond */
241
+
242
+ - (NSDictionary *)_retrieveDataWithError:(out NSError **)outError
243
+ {
244
+ if (YES == [nanoStore isClosed]) {
245
+ return nil;
246
+ }
247
+
248
+ NSMutableDictionary *searchResults = [NSMutableDictionary dictionary];
249
+
250
+ NSString *aSQLQuery = sql;
251
+
252
+ if (nil != aSQLQuery) {
253
+ // We are going to check whether the user has specified the proper columns based on the search type selected.
254
+ // This is to avoid crashing, since the user shouldn't have to know which columns are involved on each type
255
+ // of search.
256
+ // We basically honor the specified query but replace the columns with the expected ones per returned type.
257
+
258
+ NSString *subStatement = [aSQLQuery substringFromIndex:[aSQLQuery rangeOfString:@"FROM" options:NSCaseInsensitiveSearch].location];
259
+ NSFReturnType returnType = returnedObjectType;
260
+ switch (returnType) {
261
+ case NSFReturnKeys:
262
+ aSQLQuery = [NSString stringWithFormat:@"SELECT NSFKey %@", subStatement];
263
+ break;
264
+ case NSFReturnObjects:
265
+ aSQLQuery = [NSString stringWithFormat:@"SELECT NSFKey, NSFPlist, NSFObjectClass %@", subStatement];
266
+ break;
267
+ }
268
+ } else {
269
+ aSQLQuery = [self _preparedSQL];
270
+ }
271
+
272
+ _NSFLog(@"_dataWithKey SQL query: %@", aSQLQuery);
273
+
274
+ sqlite3 *sqliteStore = [[nanoStore nanoStoreEngine]sqlite];
275
+ sqlite3_stmt *theSQLiteStatement = NULL;
276
+
277
+ int status = sqlite3_prepare_v2 (sqliteStore, [aSQLQuery UTF8String], -1, &theSQLiteStatement, NULL );
278
+
279
+ status = [NSFNanoEngine NSFP_stripBitsFromExtendedResultCode:status];
280
+
281
+ if (SQLITE_OK == status) {
282
+ switch (returnedObjectType) {
283
+ case NSFReturnKeys:
284
+ while (SQLITE_ROW == sqlite3_step (theSQLiteStatement)) {
285
+ // Sanity check: some queries return NULL, which would cause a crash below.
286
+ char *valueUTF8 = (char *)sqlite3_column_text (theSQLiteStatement, 0);
287
+ NSString *theValue = nil;
288
+ if (NULL != valueUTF8) {
289
+ theValue = [[NSString alloc]initWithUTF8String:valueUTF8];
290
+ } else {
291
+ theValue = [[NSNull null]description];
292
+ }
293
+
294
+ [searchResults setObject:[NSNull null] forKey:theValue];
295
+ }
296
+ break;
297
+ default:
298
+ while (SQLITE_ROW == sqlite3_step (theSQLiteStatement)) {
299
+ char *keyUTF8 = (char *)sqlite3_column_text (theSQLiteStatement, 0);
300
+ char *dictXMLUTF8 = (char *)sqlite3_column_text (theSQLiteStatement, 1);
301
+ char *objectClassUTF8 = (char *)sqlite3_column_text (theSQLiteStatement, 2);
302
+
303
+ // Sanity check: some queries return NULL, which would a crash below.
304
+ // Since these are values that are NanoStore's resposibility, they should *never* be NULL. Log it for posterity.
305
+ if ((NULL == keyUTF8) || (NULL == dictXMLUTF8) || (NULL == objectClassUTF8)) {
306
+ NSLog(@"*** Warning! These values are NanoStore's resposibility and should *never* be NULL: keyUTF8 (%s) - dictXMLUTF8 (%s) - objectClassUTF8 (%s)", keyUTF8, dictXMLUTF8, objectClassUTF8);
307
+ continue;
308
+ }
309
+
310
+ NSString *keyValue = [[NSString alloc]initWithUTF8String:keyUTF8];
311
+ NSString *dictXML = [[NSString alloc]initWithUTF8String:dictXMLUTF8];
312
+ NSString *objectClass = [[NSString alloc]initWithUTF8String:objectClassUTF8];
313
+
314
+ NSDictionary *info = [NSFNanoEngine _plistToDictionary:dictXML];
315
+ if (nil == info) {
316
+ continue;
317
+ }
318
+
319
+ if ([attributesToBeReturned count] == 0) {
320
+ // Will be released below...
321
+ } else {
322
+ // Since we want a subset of the attributes, we need to traverse
323
+ // the attribute list and find out whether the dictionary contains
324
+ // the specified attributes. If so, add them to a subset which will
325
+ // be returned as requested.
326
+
327
+ NSMutableDictionary *subset = [NSMutableDictionary new];
328
+
329
+ for (NSString *attributeValue in attributesToBeReturned) {
330
+ id theValue = [info valueForKeyPath:attributeValue];
331
+ if (nil != theValue) {
332
+ if (NSNotFound == [attributeValue rangeOfString:@"."].location) {
333
+ [subset setValue:theValue forKeyPath:attributeValue];
334
+ } else {
335
+ NSDictionary *subInfo = [self _dictionaryForKeyPath:attributeValue value:theValue];
336
+ if ([subInfo count] > 0) {
337
+ NSString *subInfoKey = [[subInfo allKeys]objectAtIndex:0];
338
+ NSString *subInfoValue = [subInfo objectForKey:subInfoKey];
339
+ [subset setValue:subInfoValue forKey:subInfoKey];
340
+ }
341
+ }
342
+ }
343
+ }
344
+
345
+ // Will be released below...
346
+ info = subset;
347
+ }
348
+
349
+ Class storedObjectClass = NSClassFromString(objectClass);
350
+ BOOL saveOriginalClassReference = NO;
351
+ if (nil == storedObjectClass) {
352
+ storedObjectClass = [NSFNanoObject class];
353
+ saveOriginalClassReference = YES;
354
+ }
355
+
356
+ id nanoObject = [[storedObjectClass alloc]initNanoObjectFromDictionaryRepresentation:info forKey:keyValue store:nanoStore];
357
+
358
+ // If this process does not have knowledge of the original class as was saved in the store, keep a reference
359
+ // so that we can later on restore the object properly (otherwise it would be stored as a NanoObject.)
360
+ if (YES == saveOriginalClassReference) {
361
+ [nanoObject _setOriginalClassString:objectClass];
362
+ }
363
+
364
+ [searchResults setObject:nanoObject forKey:keyValue];
365
+
366
+ }
367
+ break;
368
+ }
369
+
370
+ sqlite3_finalize (theSQLiteStatement);
371
+
372
+ } else {
373
+ if (nil != outError) {
374
+ NSString *msg = [NSString stringWithFormat:@"SQLite error ID: %ld", status];
375
+ *outError = [NSError errorWithDomain:NSFDomainKey
376
+ code:NSFNanoStoreErrorKey
377
+ userInfo:[NSDictionary dictionaryWithObject:[NSString stringWithFormat:@"*** -[%@ %s]: %@", [self class], _cmd, msg]
378
+ forKey:NSLocalizedFailureReasonErrorKey]];
379
+ }
380
+ searchResults = nil;
381
+ }
382
+
383
+ return searchResults;
384
+ }
385
+
386
+ - (NSDictionary *)_retrieveDataAdded:(NSFDateMatchType)aDateMatch calendarDate:(NSDate *)aDate error:(out NSError **)outError
387
+ {
388
+ if ([nanoStore isClosed] == YES) {
389
+ return nil;
390
+ }
391
+
392
+ NSString *theSQLStatement = nil;
393
+ NSString *normalizedDateString = [NSFNanoStore _calendarDateToString:aDate];
394
+
395
+ switch (aDateMatch) {
396
+ case NSFBeforeDate:
397
+ theSQLStatement = [[NSString alloc]initWithFormat:@"SELECT %@, %@, %@ FROM %@ WHERE %@ < '%@'", NSFKey, NSFPlist, NSFObjectClass, NSFKeys, NSFCalendarDate, normalizedDateString];
398
+ break;
399
+ case NSFOnDate:
400
+ theSQLStatement = [[NSString alloc]initWithFormat:@"SELECT %@, %@, %@ FROM %@ WHERE %@ = '%@'", NSFKey, NSFPlist, NSFObjectClass, NSFKeys, NSFCalendarDate, normalizedDateString];
401
+ break;
402
+ case NSFAfterDate:
403
+ theSQLStatement = [[NSString alloc]initWithFormat:@"SELECT %@, %@, %@ FROM %@ WHERE %@ > '%@'", NSFKey, NSFPlist, NSFObjectClass, NSFKeys, NSFCalendarDate, normalizedDateString];
404
+ break;
405
+ }
406
+
407
+ NSFNanoResult *result = [nanoStore _executeSQL:theSQLStatement];
408
+
409
+ NSMutableDictionary *searchResults = [NSMutableDictionary dictionaryWithCapacity:result.numberOfRows];
410
+
411
+ if (result.numberOfRows > 0) {
412
+ if (NSFReturnKeys == returnedObjectType) {
413
+ NSArray *resultsKeys = [result valuesForColumn:[NSString stringWithFormat:@"%@.%@", NSFKeys, NSFKey]];
414
+ for (NSString *resultKey in resultsKeys)
415
+ [searchResults setObject:[NSNull null] forKey:resultKey];
416
+ return searchResults;
417
+ } else {
418
+ NSArray *resultsObjectClass = [result valuesForColumn:[NSString stringWithFormat:@"%@.%@", NSFKeys, NSFObjectClass]];
419
+ NSArray *resultsObjects = [result valuesForColumn:[NSString stringWithFormat:@"%@.%@", NSFKeys, NSFPlist]];
420
+ NSArray *resultsKeys = [result valuesForColumn:[NSString stringWithFormat:@"%@.%@", NSFKeys, NSFKey]];
421
+ NSUInteger i, count = [resultsKeys count];
422
+
423
+ for (i = 0; i < count; i++) {
424
+ @autoreleasepool {
425
+ NSDictionary *info = [NSFNanoEngine _plistToDictionary:[resultsObjects objectAtIndex:i]];
426
+ if (nil != info) {
427
+ NSString *keyValue = [resultsKeys objectAtIndex:i];
428
+
429
+ NSString *className = [resultsObjectClass objectAtIndex:i];
430
+ Class storedObjectClass = NSClassFromString(className);
431
+ BOOL saveOriginalClassReference = NO;
432
+ if (nil == storedObjectClass) {
433
+ storedObjectClass = [NSFNanoObject class];
434
+ saveOriginalClassReference = YES;
435
+ }
436
+
437
+ id nanoObject = [[storedObjectClass alloc]initNanoObjectFromDictionaryRepresentation:info forKey:keyValue store:nanoStore];
438
+
439
+ // If this process does not have knowledge of the original class as was saved in the store, keep a reference
440
+ // so that we can later on restore the object properly (otherwise it would be stored as a NanoObject.)
441
+ if (YES == saveOriginalClassReference) {
442
+ [nanoObject _setOriginalClassString:className];
443
+ }
444
+
445
+ [searchResults setObject:nanoObject forKey:keyValue];
446
+ }
447
+ }
448
+ }
449
+ }
450
+ }
451
+
452
+ return searchResults;
453
+ }
454
+
455
+ - (NSString *)_preparedSQL
456
+ {
457
+ NSString *aSQLQuery = nil;
458
+
459
+ if (nil == expressions) {
460
+ aSQLQuery = [self _prepareSQLQueryStringWithKey:key attribute:attribute value:value matching:match];
461
+ } else {
462
+ aSQLQuery = [self _prepareSQLQueryStringWithExpressions:expressions];
463
+ }
464
+
465
+ return aSQLQuery;
466
+ }
467
+
468
+ - (NSString *)_prepareSQLQueryStringWithKey:(NSString *)aKey attribute:(NSString *)anAttribute value:(id)aValue matching:(NSFMatchType)aMatch
469
+ {
470
+ NSMutableString *theSQLStatement = nil;
471
+ NSString *attributes = nil;
472
+
473
+ if (nil != attributesToBeReturned) {
474
+ // Prepare the list of attributes we need to gather. Include NSFKEY as well.
475
+ NSMutableSet *set = [[NSMutableSet alloc]initWithArray:attributesToBeReturned];
476
+ NSArray *objects = [set allObjects];
477
+ NSMutableArray *quotedObjects = [NSMutableArray new];
478
+ for (NSString *object in objects) {
479
+ NSString *theValue = [[NSString alloc]initWithFormat:@"'%@'", object];
480
+ [quotedObjects addObject:theValue];
481
+ }
482
+ attributes = [quotedObjects componentsJoinedByString:@","];
483
+ }
484
+
485
+ NSFReturnType returnType = returnedObjectType;
486
+
487
+ if ((nil == aKey) && (nil == anAttribute) && (nil == aValue)) {
488
+ switch (returnType) {
489
+ case NSFReturnKeys:
490
+ return @"SELECT NSFKEY FROM NSFKeys";
491
+ break;
492
+ default:
493
+ return @"SELECT NSFKey, NSFPlist, NSFObjectClass FROM NSFKeys";
494
+ break;
495
+ }
496
+ } else {
497
+ switch (returnType) {
498
+ case NSFReturnKeys:
499
+ if (NO == groupValues) {
500
+ theSQLStatement = [NSMutableString stringWithString:@"SELECT DISTINCT (NSFKEY) FROM NSFValues WHERE "];
501
+ } else {
502
+ theSQLStatement = [NSMutableString stringWithString:@"SELECT NSFKEY FROM NSFValues WHERE "];
503
+ }
504
+ break;
505
+ default:
506
+ theSQLStatement = [NSMutableString stringWithString:@"SELECT NSFKEY FROM NSFValues WHERE "];
507
+ break;
508
+ }
509
+ }
510
+
511
+ NSString *segment = nil;
512
+ BOOL querySegmentWasAdded = NO;
513
+
514
+ if (nil != aKey) {
515
+ if ((nil == anAttribute) && (nil == aValue))
516
+ segment = [NSFNanoSearch _querySegmentForColumn:NSFKey value:aKey matching:aMatch];
517
+ else
518
+ segment = [NSFNanoSearch _querySegmentForColumn:NSFKey value:aKey matching:NSFEqualTo];
519
+ [theSQLStatement appendString:segment];
520
+ querySegmentWasAdded = YES;
521
+ }
522
+
523
+ if (nil != anAttribute) {
524
+ if (YES == querySegmentWasAdded) {
525
+ [theSQLStatement appendString:@" AND "];
526
+ }
527
+
528
+ // We need to introspect whether the attribute contains a dot "." or not. Based on the case, we'll need to GLOB the attribute
529
+ // or leave it as is.
530
+
531
+ if (NSNotFound == [anAttribute rangeOfString:@"."].location) {
532
+ segment = [NSFNanoSearch _querySegmentForAttributeColumnWithValue:anAttribute matching:aMatch valueColumnWithValue:aValue];
533
+ } else {
534
+ if (nil == aValue)
535
+ segment = [NSFNanoSearch _querySegmentForColumn:NSFAttribute value:anAttribute matching:aMatch];
536
+ else
537
+ segment = [NSFNanoSearch _querySegmentForColumn:NSFAttribute value:anAttribute matching:NSFEqualTo];
538
+ }
539
+
540
+ [theSQLStatement appendString:segment];
541
+ } else {
542
+ if (nil != aValue) {
543
+ if (YES == querySegmentWasAdded)
544
+ [theSQLStatement appendString:@" AND "];
545
+ segment = [NSFNanoSearch _querySegmentForColumn:NSFValue value:aValue matching:aMatch];
546
+ [theSQLStatement appendString:segment];
547
+ }
548
+ }
549
+
550
+ if (YES == groupValues) {
551
+ [theSQLStatement appendString:@" GROUP BY NSFValue"];
552
+ }
553
+
554
+ if (NSFReturnObjects == returnType) {
555
+ if (nil != attributes)
556
+ theSQLStatement = [NSString stringWithFormat:@"SELECT DISTINCT (NSFKey),NSFPlist,NSFObjectClass FROM NSFKeys WHERE NSFKey IN (%@)", theSQLStatement];
557
+ else
558
+ theSQLStatement = [NSString stringWithFormat:@"SELECT DISTINCT (NSFKey),NSFPlist,NSFObjectClass FROM NSFKeys WHERE NSFKey IN (%@)", theSQLStatement];
559
+ }
560
+
561
+ return theSQLStatement;
562
+ }
563
+
564
+ - (NSString *)_prepareSQLQueryStringWithExpressions:(NSArray *)someExpressions
565
+ {
566
+ NSUInteger i, count = [someExpressions count];
567
+ NSMutableArray *sqlComponents = [NSMutableArray new];
568
+ NSMutableString *parentheses = [NSMutableString new];
569
+ NSFReturnType returnType = returnedObjectType;
570
+
571
+ for (i = 0; i < count; i++) {
572
+ NSFNanoExpression *expression = [someExpressions objectAtIndex:i];
573
+ NSMutableString *theSQL = nil;;
574
+
575
+ if (NSFReturnObjects == returnType)
576
+ theSQL = [[NSMutableString alloc]initWithFormat:@"SELECT NSFKEY FROM NSFValues WHERE %@", [expression description]];
577
+ else
578
+ theSQL = [[NSMutableString alloc]initWithFormat:@"SELECT DISTINCT (NSFKEY) FROM NSFValues WHERE %@", [expression description]];
579
+
580
+ if ((count > 1) && (i < count-1)) {
581
+ [theSQL appendString:@" AND NSFKEY IN ("];
582
+ [parentheses appendString:@")"];
583
+ }
584
+
585
+ [sqlComponents addObject:theSQL];
586
+ }
587
+
588
+ if ([parentheses length] > 0)
589
+ [sqlComponents addObject:parentheses];
590
+
591
+ NSString *theValue = [sqlComponents componentsJoinedByString:@""];
592
+
593
+ if (NSFReturnObjects == returnType)
594
+ theValue = [NSString stringWithFormat:@"SELECT DISTINCT (NSFKey),NSFPlist,NSFObjectClass FROM NSFKeys WHERE NSFKey IN (%@)", theValue];
595
+
596
+ return theValue;
597
+ }
598
+
599
+ + (NSString *)_prepareSQLQueryStringWithKeys:(NSArray *)someKeys
600
+ {
601
+ //Prepare the keys by single quoting them...
602
+ NSMutableArray *preparedKeys = [NSMutableArray new];
603
+ for (NSString *theKey in someKeys) {
604
+ NSString *quotedKey = [[NSString alloc]initWithFormat:@"'%@'", theKey];
605
+ [preparedKeys addObject:quotedKey];
606
+ }
607
+
608
+ NSMutableString *theSQLStatement = [NSMutableString stringWithString:@"SELECT DISTINCT (NSFKEY) FROM NSFValues WHERE NSFKey IN ("];
609
+ [theSQLStatement appendString:[preparedKeys componentsJoinedByString:@","]];
610
+ [theSQLStatement appendString:@")"];
611
+ theSQLStatement = [NSString stringWithFormat:@"SELECT DISTINCT (NSFKey),NSFPlist,NSFObjectClass FROM NSFKeys WHERE NSFKey IN (%@)", theSQLStatement];
612
+
613
+ return theSQLStatement;
614
+ }
615
+
616
+ + (NSString *)_querySegmentForColumn:(NSString *)aColumn value:(id)aValue matching:(NSFMatchType)match
617
+ {
618
+ NSMutableString *segment = [NSMutableString string];
619
+ NSMutableString *value = nil;
620
+ NSMutableString *mutatedString = nil;
621
+ NSInteger mutatedStringLength = 0;
622
+ unichar sentinelChar;
623
+
624
+ if (YES == [aValue isKindOfClass:[NSString class]]) {
625
+ switch (match) {
626
+ case NSFEqualTo:
627
+ value = [[NSMutableString alloc]initWithFormat:@"%@ = '%@'", aColumn, aValue];
628
+ [segment appendString:value];
629
+ break;
630
+ case NSFBeginsWith:
631
+ sentinelChar = [aValue characterAtIndex:[aValue length] - 1] + 1;
632
+ value = [[NSMutableString alloc]initWithFormat:@"(%@ >= '%@' AND %@ < '%@%c')", aColumn, aValue, aColumn, aValue, sentinelChar];
633
+ [segment appendString:value];
634
+ break;
635
+ case NSFContains:
636
+ value = [[NSMutableString alloc]initWithFormat:@"%@ GLOB '*%@*'", aColumn, aValue];
637
+ [segment appendString:value];
638
+ break;
639
+ case NSFEndsWith:
640
+ value = [[NSMutableString alloc]initWithFormat:@"%@ GLOB '*%@'", aColumn, aValue];
641
+ [segment appendString:value];
642
+ break;
643
+ case NSFInsensitiveEqualTo:
644
+ value = [[NSMutableString alloc]initWithFormat:@"upper(%@) = '%@'", aColumn, [aValue uppercaseString]];
645
+ [segment appendString:value];
646
+ break;
647
+ case NSFInsensitiveBeginsWith:
648
+ mutatedString = [[NSMutableString alloc]initWithString:aValue];
649
+ mutatedStringLength = [aValue length];
650
+ value = [[NSMutableString alloc]initWithFormat:@"%c", [mutatedString characterAtIndex:mutatedStringLength - 1]+1];
651
+ [mutatedString replaceCharactersInRange:NSMakeRange(mutatedStringLength - 1, 1) withString:value];
652
+ value = [[NSMutableString alloc]initWithFormat:@"(upper(%@) >= '%@' AND upper(%@) < '%@')", aColumn, [aValue uppercaseString], aColumn, [mutatedString uppercaseString]];
653
+ [segment appendString:value];
654
+ break;
655
+ case NSFInsensitiveContains:
656
+ value = [[NSMutableString alloc]initWithFormat:@"%@ LIKE '%@%@%@'", aColumn, @"%", aValue, @"%"];
657
+ [segment appendString:value];
658
+ break;
659
+ case NSFInsensitiveEndsWith:
660
+ value = [[NSMutableString alloc]initWithFormat:@"%@ LIKE '%@%@'", aColumn, @"%", aValue];
661
+ [segment appendString:value];
662
+ break;
663
+ case NSFGreaterThan:
664
+ value = [[NSMutableString alloc]initWithFormat:@"%@ > '%@'", aColumn, aValue];
665
+ [segment appendString:value];
666
+ break;
667
+ case NSFLessThan:
668
+ value = [[NSMutableString alloc]initWithFormat:@"%@ < '%@'", aColumn, aValue];
669
+ [segment appendString:value];
670
+ break;
671
+ }
672
+ } else if (YES == [aValue isKindOfClass:[NSArray class]]) {
673
+ // Quote the parameters
674
+ NSMutableArray *quotedParameters = [[NSMutableArray alloc]initWithCapacity:[aValue count]];
675
+ value = [[NSMutableString alloc]initWithFormat:@"%@ IN (", aColumn];
676
+ for (NSString *parameter in aValue) {
677
+ NSString *quotedParameter = [[NSString alloc]initWithFormat:@"'%@'", parameter];
678
+ [quotedParameters addObject:quotedParameter];
679
+ }
680
+ //Add them to the string delimited by string
681
+ [value appendString:[quotedParameters componentsJoinedByString:@","]];
682
+ [value appendString:@")"];
683
+
684
+ // Complete the query segment
685
+ [segment appendString:value];
686
+
687
+ // Free allocated resources
688
+ }
689
+
690
+ return segment;
691
+ }
692
+
693
+ + (NSString *)_querySegmentForAttributeColumnWithValue:(id)anAttributeValue matching:(NSFMatchType)match valueColumnWithValue:(id)aValue
694
+ {
695
+ NSMutableString *segment = [NSMutableString string];
696
+ NSMutableString *value = nil;
697
+
698
+ if ((YES == [aValue isKindOfClass:[NSString class]]) || (nil == aValue)) {
699
+ if (nil == aValue) {
700
+ value = [[NSMutableString alloc]initWithFormat:@"(%@ = '%@') OR (%@ GLOB '%@.*') OR (%@ GLOB '*.%@.*') OR (%@ GLOB '*.%@')", NSFAttribute, anAttributeValue, NSFAttribute, anAttributeValue, NSFAttribute, anAttributeValue, NSFAttribute, anAttributeValue];
701
+ [segment appendString:value];
702
+ } else {
703
+ switch (match) {
704
+ case NSFEqualTo:
705
+ value = [[NSMutableString alloc]initWithFormat:@"(%@ = '%@' AND %@ = '%@') OR (%@ GLOB '%@.*' AND %@ = '%@') OR (%@ GLOB '*.%@.*' AND %@ = '%@') OR (%@ GLOB '*.%@' AND %@ = '%@')", NSFAttribute, anAttributeValue, NSFValue, aValue, NSFAttribute, anAttributeValue, NSFValue, aValue, NSFAttribute, anAttributeValue, NSFValue, aValue, NSFAttribute, anAttributeValue, NSFValue, aValue];
706
+ [segment appendString:value];
707
+ break;
708
+ case NSFBeginsWith:
709
+ value = [[NSMutableString alloc]initWithFormat:@"(%@ = '%@' AND %@ GLOB '%@*') OR (%@ GLOB '%@.*' AND %@ GLOB '%@*') OR (%@ GLOB '*.%@.*' AND %@ GLOB '%@*') OR (%@ GLOB '*.%@' AND %@ GLOB '%@*')", NSFAttribute, anAttributeValue, NSFValue, aValue, NSFAttribute, anAttributeValue, NSFValue, aValue, NSFAttribute, anAttributeValue, NSFValue, aValue, NSFAttribute, anAttributeValue, NSFValue, aValue];
710
+ [segment appendString:value];
711
+ break;
712
+ case NSFContains:
713
+ value = [[NSMutableString alloc]initWithFormat:@"(%@ = '%@' AND %@ GLOB '%@') OR (%@ GLOB '%@.*' AND %@ GLOB '%@') OR (%@ GLOB '*.%@.*' AND %@ GLOB '%@') OR (%@ GLOB '*.%@' AND %@ GLOB '%@')", NSFAttribute, anAttributeValue, NSFValue, aValue, NSFAttribute, anAttributeValue, NSFValue, aValue, NSFAttribute, anAttributeValue, NSFValue, aValue, NSFAttribute, anAttributeValue, NSFValue, aValue];
714
+ [segment appendString:value];
715
+ break;
716
+ case NSFEndsWith:
717
+ value = [[NSMutableString alloc]initWithFormat:@"(%@ = '%@' AND %@ GLOB '*%@') OR (%@ GLOB '%@.*' AND %@ GLOB '*%@') OR (%@ GLOB '*.%@.*' AND %@ GLOB '*%@') OR (%@ GLOB '*.%@' AND %@ GLOB '*%@')", NSFAttribute, anAttributeValue, NSFValue, aValue, NSFAttribute, anAttributeValue, NSFValue, aValue, NSFAttribute, anAttributeValue, NSFValue, aValue, NSFAttribute, anAttributeValue, NSFValue, aValue];
718
+ [segment appendString:value];
719
+ break;
720
+ case NSFInsensitiveEqualTo:
721
+ aValue = [aValue uppercaseString];
722
+ value = [[NSMutableString alloc]initWithFormat:@"(%@ = '%@' AND upper(%@) = '%@') OR (%@ GLOB '%@.*' AND upper(%@) = '%@') OR (%@ GLOB '*.%@.*' AND upper(%@) = '%@') OR (%@ GLOB '*.%@' AND upper(%@) = '%@')", NSFAttribute, anAttributeValue, NSFValue, aValue, NSFAttribute, anAttributeValue, NSFValue, aValue, NSFAttribute, anAttributeValue, NSFValue, aValue, NSFAttribute, anAttributeValue, NSFValue, aValue];
723
+ [segment appendString:value];
724
+ break;
725
+ case NSFInsensitiveBeginsWith:
726
+ aValue = [aValue uppercaseString];
727
+ value = [[NSMutableString alloc]initWithFormat:@"(%@ = '%@' AND upper(%@) GLOB '%@*') OR (%@ GLOB '%@.*' AND upper(%@) GLOB '%@*') OR (%@ GLOB '*.%@.*' AND upper(%@) GLOB '%@*') OR (%@ GLOB '*.%@' AND upper(%@) GLOB '%@*')", NSFAttribute, anAttributeValue, NSFValue, aValue, NSFAttribute, anAttributeValue, NSFValue, aValue, NSFAttribute, anAttributeValue, NSFValue, aValue, NSFAttribute, anAttributeValue, NSFValue, aValue];
728
+ [segment appendString:value];
729
+ break;
730
+ case NSFInsensitiveContains:
731
+ value = [[NSMutableString alloc]initWithFormat:@"(%@ = '%@' AND %@ LIKE '%@') OR (%@ GLOB '%@.*' AND %@ LIKE '%@') OR (%@ GLOB '*.%@.*' AND %@ LIKE '%@') OR (%@ GLOB '*.%@' AND %@ LIKE '%@')", NSFAttribute, anAttributeValue, NSFValue, aValue, NSFAttribute, anAttributeValue, NSFValue, aValue, NSFAttribute, anAttributeValue, NSFValue, aValue, NSFAttribute, anAttributeValue, NSFValue, aValue];
732
+ [segment appendString:value];
733
+ break;
734
+ case NSFInsensitiveEndsWith:
735
+ value = [[NSMutableString alloc]initWithFormat:@"(%@ = '%@' AND %@ LIKE '%%%@') OR (%@ GLOB '%@.*' AND %@ LIKE '%%%@') OR (%@ GLOB '*.%@.*' AND %@ LIKE '%%%@') OR (%@ GLOB '*.%@' AND %@ LIKE '%%%@')", NSFAttribute, anAttributeValue, NSFValue, aValue, NSFAttribute, anAttributeValue, NSFValue, aValue, NSFAttribute, anAttributeValue, NSFValue, aValue, NSFAttribute, anAttributeValue, NSFValue, aValue];
736
+ [segment appendString:value];
737
+ break;
738
+ case NSFGreaterThan:
739
+ value = [[NSMutableString alloc]initWithFormat:@"(%@ = '%@' AND %@ > '%@') OR (%@ GLOB '%@.*' AND %@ > '%@') OR (%@ GLOB '*.%@.*' AND %@ > '%@') OR (%@ GLOB '*.%@' AND %@ > '%@')", NSFAttribute, anAttributeValue, NSFValue, aValue, NSFAttribute, anAttributeValue, NSFValue, aValue, NSFAttribute, anAttributeValue, NSFValue, aValue, NSFAttribute, anAttributeValue, NSFValue, aValue];
740
+ [segment appendString:value];
741
+ break;
742
+ case NSFLessThan:
743
+ value = [[NSMutableString alloc]initWithFormat:@"(%@ = '%@' AND %@ < '%@') OR (%@ GLOB '%@.*' AND %@ < '%@') OR (%@ GLOB '*.%@.*' AND %@ < '%@') OR (%@ GLOB '*.%@' AND %@ < '%@')", NSFAttribute, anAttributeValue, NSFValue, aValue, NSFAttribute, anAttributeValue, NSFValue, aValue, NSFAttribute, anAttributeValue, NSFValue, aValue, NSFAttribute, anAttributeValue, NSFValue, aValue];
744
+ [segment appendString:value];
745
+ break;
746
+ }
747
+ }
748
+ } else if (YES == [aValue isKindOfClass:[NSArray class]]) {
749
+ // Quote the parameters
750
+ NSMutableArray *quotedParameters = [[NSMutableArray alloc]initWithCapacity:[aValue count]];
751
+ value = [[NSMutableString alloc]initWithFormat:@"%@ IN (", NSFAttribute];
752
+ for (NSString *parameter in aValue) {
753
+ NSString *quotedParameter = [[NSString alloc]initWithFormat:@"'%@'", parameter];
754
+ [quotedParameters addObject:quotedParameter];
755
+ }
756
+ //Add them to the string delimited by string
757
+ [value appendString:[quotedParameters componentsJoinedByString:@","]];
758
+ [value appendString:@")"];
759
+
760
+ // Complete the query segment
761
+ [segment appendString:value];
762
+
763
+ // Free allocated resources
764
+ }
765
+
766
+ return segment;
767
+ }
768
+
769
+ - (NSDictionary *)_dictionaryForKeyPath:(NSString *)keyPath value:(id)theValue
770
+ {
771
+ NSMutableDictionary *info = [NSMutableDictionary dictionary];
772
+ NSMutableArray *keys = [[keyPath componentsSeparatedByString:@"."]mutableCopy];
773
+
774
+ if ([keys count] == 1) {
775
+ [info setObject:theValue forKey:keyPath];
776
+ return info;
777
+ }
778
+
779
+ NSInteger i;
780
+
781
+ for (i = 0; i < [keys count]; i++) {
782
+ NSString *keyValue = [keys objectAtIndex:i];
783
+ [keys removeObjectAtIndex:0];
784
+ NSDictionary *subInfo = [self _dictionaryForKeyPath:[keys componentsJoinedByString:@"."] value:theValue];
785
+ if (nil != subInfo)
786
+ [info setObject:subInfo forKey:keyValue];
787
+ }
788
+
789
+ return info;
790
+ }
791
+
792
+ + (NSString *)_quoteStrings:(NSArray *)strings joiningWithDelimiter:(NSString *)delimiter
793
+ {
794
+ NSMutableArray *quotedParameters = [[NSMutableArray alloc]initWithCapacity:[strings count]];
795
+ for (NSString *string in strings) {
796
+ NSString *quotedParameter = [[NSString alloc]initWithFormat:@"\"%@\"", string];
797
+ [quotedParameters addObject:quotedParameter];
798
+ }
799
+
800
+ NSString *quotedString = [quotedParameters componentsJoinedByString:@","];
801
+
802
+
803
+ return quotedString;
804
+ }
805
+
806
+ - (id)_sortResultsIfApplicable:(NSDictionary *)results returnType:(NSFReturnType)theReturnType
807
+ {
808
+ id theResults = results;
809
+
810
+ if (nil != sort) {
811
+ NSMutableArray *cocoaSortDescriptors = [NSMutableArray new];
812
+
813
+ for (NSFNanoSortDescriptor *descriptor in sort) {
814
+ NSString *targetKeyPath = [[NSString alloc]initWithFormat:@"rootObject.%@", descriptor.attribute];
815
+ NSSortDescriptor *cocoaSort = [[NSSortDescriptor alloc]initWithKey:targetKeyPath ascending:descriptor.isAscending];
816
+ [cocoaSortDescriptors addObject:cocoaSort];
817
+ }
818
+
819
+ if (NSFReturnObjects == theReturnType) {
820
+ theResults = [[results allValues]sortedArrayUsingDescriptors:cocoaSortDescriptors];
821
+ } else {
822
+ theResults = [results allKeys];
823
+ }
824
+ }
825
+ else if (NSFReturnKeys == theReturnType)
826
+ {
827
+ theResults = [results allKeys];
828
+ }
829
+
830
+ return theResults;
831
+ }
832
+
833
+ /** \endcond */
834
+
835
+ @end