1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package net.wotonomy.control;
20
21 import java.util.Collection;
22 import java.util.List;
23 import java.util.Map;
24
25 import net.wotonomy.foundation.NSArray;
26 import net.wotonomy.foundation.NSDictionary;
27 import net.wotonomy.foundation.NSMutableArray;
28 import net.wotonomy.foundation.NSMutableDictionary;
29 import net.wotonomy.foundation.NSSelector;
30 import net.wotonomy.foundation.internal.WotonomyException;
31
32 /***
33 * EOFetchSpecification defines the parameters used to request
34 * objects from an EOObjectStore. They are commonly created
35 * and passed to a EODataSource which fetches from its
36 * EOEditingContext, which passes the call up to its root
37 * EOObjectStore's objectsWithFetchSpecification method.
38 *
39 * @author michael@mpowers.net
40 * @author $Author: cgruber $
41 * @version $Revision: 894 $
42 */
43 public class EOFetchSpecification implements EOKeyValueArchiving {
44 private boolean fetchesRawRows;
45 private String entityName;
46 private NSDictionary hints;
47 private boolean deep;
48 private int fetchLimit;
49 private boolean locksObjects;
50 private NSArray prefetchingRelationshipKeyPaths;
51 private boolean promptsAfterFetchLimit;
52 private EOQualifier qualifier;
53 private NSArray rawRowKeyPaths;
54 private boolean refreshesRefetchedObjects;
55 private boolean requiresAllQualifierBindingVariables;
56 private NSArray sortOrderings;
57 private boolean distinct;
58
59 /***
60 * Default constructor initializes internal state.
61 */
62 public EOFetchSpecification()
63 {
64 fetchesRawRows = false;
65 entityName = null;
66 hints = null;
67 deep = true;
68 fetchLimit = 0;
69 locksObjects = false;
70 prefetchingRelationshipKeyPaths = null;
71 promptsAfterFetchLimit = false;
72 qualifier = null;
73 rawRowKeyPaths = null;
74 refreshesRefetchedObjects = false;
75 requiresAllQualifierBindingVariables = false;
76 sortOrderings = null;
77 distinct = false;
78 }
79
80 /***
81 * Constructs a fetch specification for the specified entity type using
82 * the specified qualifier and sort ordering.
83 */
84 public EOFetchSpecification( String anEntityName, EOQualifier aQualifier, List aSortOrderingList )
85 {
86 this();
87 entityName = anEntityName;
88 qualifier = aQualifier;
89 sortOrderings = new NSArray( (Collection) aSortOrderingList );
90 }
91
92 /***
93 * Constructs a fetch specification for the specified entity type using
94 * the specified qualifier and sort ordering, distinct flag, deep flag,
95 * and hints dictionary.
96 */
97 public EOFetchSpecification( String anEntityName, EOQualifier aQualifier, NSArray aSortOrderingList,
98 boolean usesDistinct, boolean isDeep, Map aHintMap)
99 {
100 this();
101 entityName = anEntityName;
102 qualifier = aQualifier;
103 sortOrderings = new NSArray( (Collection) aSortOrderingList );
104 distinct = usesDistinct;
105 deep = isDeep;
106 hints = new NSMutableDictionary( (Map) aHintMap );
107 }
108
109 /***
110 * Convenience to return the named fetch specification from the class description
111 * corresponding to the specified entity name. Returns null if either entityName
112 * or spec name cannot be resolved.
113 */
114 public static EOFetchSpecification fetchSpecificationNamed( String name, String entityName)
115 {
116 EOClassDescription classDesc = EOClassDescription.classDescriptionForEntityName( entityName );
117 if ( classDesc == null ) return null;
118 return classDesc.fetchSpecificationNamed( name );
119 }
120
121 /***
122 * Implemented to return a new fetch specification that is a deep copy of this one.
123 */
124 public Object clone()
125 {
126 EOFetchSpecification clone = new EOFetchSpecification();
127
128 clone.fetchesRawRows = this.fetchesRawRows;
129 clone.entityName = this.entityName;
130 if ( this.hints != null )
131 clone.hints = new NSDictionary( (Map) this.hints );
132 clone.deep = this.deep;
133 clone.locksObjects = this.locksObjects;
134 if ( this.prefetchingRelationshipKeyPaths != null )
135 clone.prefetchingRelationshipKeyPaths =
136 new NSArray( (List) prefetchingRelationshipKeyPaths );
137 clone.promptsAfterFetchLimit = this.promptsAfterFetchLimit;
138 if ( this.qualifier != null )
139 clone.qualifier = this.qualifier;
140 if ( this.rawRowKeyPaths != null )
141 clone.rawRowKeyPaths = new NSArray( (List) this.rawRowKeyPaths );
142 clone.refreshesRefetchedObjects = this.refreshesRefetchedObjects;
143 clone.requiresAllQualifierBindingVariables =
144 this.requiresAllQualifierBindingVariables;
145 if ( this.sortOrderings != null )
146 clone.sortOrderings = new NSArray( (List) this.sortOrderings );
147 clone.distinct = this.distinct;
148
149 return clone;
150 }
151
152 /***
153 * Returns the name of the entity fetched by this fetch spec.
154 */
155 public String entityName()
156 {
157 return entityName;
158 }
159
160 /***
161 * Returns the current fetch limit.
162 * A fetch limit of zero indicates no fetch limit.
163 * Zero is the default.
164 */
165 public int fetchLimit()
166 {
167 return fetchLimit;
168 }
169
170 /***
171 * Returns whether this fetch spec will fetch raw rows.
172 * Default is false.
173 */
174 public boolean fetchesRawRows()
175 {
176 return fetchesRawRows;
177 }
178
179 /***
180 * Returns a fetch specification that resolves the bindings
181 * in the specified map.
182 */
183 public EOFetchSpecification
184 fetchSpecificationWithQualifierBindings(Map aBindingMap)
185 {
186 throw new WotonomyException( "Not implemented yet" );
187 }
188
189 /***
190 * Returns a Map containing the hints used by this fetch specification,
191 * or null if no hints have been specified.
192 */
193 public NSDictionary hints()
194 {
195 if ( hints == null ) return null;
196 return new NSDictionary( (NSDictionary) hints );
197 }
198
199 /***
200 * Returns whether entities related to the primary
201 * entities are fetched by this fetch spec. If true, all relationships
202 * whose destinations meet the qualifier criteria will be returned
203 * in addition to primary results. If false, only the primary entities
204 * will be returned. Default is true.
205 */
206 public boolean isDeep()
207 {
208 return deep;
209 }
210
211 /***
212 * Returns whether this data source should lock objects that
213 * are fetched. Default is false.
214 */
215 public boolean locksObjects()
216 {
217 return locksObjects;
218 }
219
220 /****
221 * Returns a List of relationships for the fetched objects that
222 * should also be fetched, or null if no such list has been specified.
223 * Use this to avoid additional calls to the server to fetch
224 * relationships that you know you will use.
225 * NOTE: wotonomy allows you to specify non-relational keys
226 * as well.
227 */
228 public NSArray prefetchingRelationshipKeyPaths()
229 {
230 return prefetchingRelationshipKeyPaths;
231 }
232
233 /***
234 * Returns whether the user should be prompted to continue
235 * when the fetch limit has been exceeded.
236 * Default is false.
237 */
238 public boolean promptsAfterFetchLimit()
239 {
240 return promptsAfterFetchLimit;
241 }
242
243 /***
244 * Returns the qualifier used by this fetch specification,
245 * or null if none has been specified.
246 */
247 public EOQualifier qualifier()
248 {
249 return qualifier;
250 }
251
252 /***
253 * Returns a List of keys or key paths for which
254 * values should be returned when fetching raw rows,
255 * or null if no raw row key paths have been specified.
256 */
257 public NSArray rawRowKeyPaths()
258 {
259 return rawRowKeyPaths;
260 }
261
262 /***
263 * Returns whether fetched objects should replace
264 * modified versions already fetched into an editing context.
265 * If true, those changes will be lost.
266 * Default is false.
267 */
268 public boolean refreshesRefetchedObjects()
269 {
270 return refreshesRefetchedObjects;
271 }
272
273 /***
274 * Returns whether all qualifier bindings must be specified
275 * in order to fetch. If true, an exception is thrown if
276 * unspecified bindings exist. If false, unspecified bindings
277 * will be removed from the qualifier. Default is false.
278 */
279 public boolean requiresAllQualifierBindingVariables()
280 {
281 return requiresAllQualifierBindingVariables;
282 }
283
284 /***
285 * Sets the name of the entity fetched by this spec.
286 */
287 public void setEntityName(String aName)
288 {
289 entityName = aName;
290 }
291
292 /***
293 * Sets whether this fetch spec will return raw rows.
294 */
295 public void setFetchesRawRows(boolean shouldFetchRawRows)
296 {
297 fetchesRawRows = shouldFetchRawRows;
298 }
299
300 /***
301 * Sets the limit on the number of records returned for this fetch spec.
302 * Zero indicates no limit on fetches.
303 */
304 public void setFetchLimit(int aLimit)
305 {
306 fetchLimit = aLimit;
307 }
308
309 /***
310 * Sets the hints passed by this fetch spec.
311 */
312 public void setHints(Map aHintMap)
313 {
314 if ( aHintMap == null )
315 {
316 hints = null;
317 }
318 else
319 {
320 hints = new NSDictionary( (Map) aHintMap );
321 }
322 }
323
324 /***
325 * Sets whether this fetch specification fetches deeply.
326 */
327 public void setIsDeep(boolean isDeep)
328 {
329 deep = isDeep;
330 }
331
332 /***
333 * Sets whether this fetch spec locks objects that
334 * are returned by the fetch.
335 */
336 public void setLocksObjects(boolean shouldLockObjects)
337 {
338 locksObjects = shouldLockObjects;
339 }
340
341 /***
342 * Sets the prefetch key paths that should be used as an optimization
343 * hint to the server. NOTE: wotonomy allows you to specify non-relationship
344 * keys as well.
345 */
346 public void setPrefetchingRelationshipKeyPaths(List aKeyPathList)
347 {
348 if ( aKeyPathList == null )
349 {
350 prefetchingRelationshipKeyPaths = null;
351 }
352 else
353 {
354 prefetchingRelationshipKeyPaths = new NSArray( (List) aKeyPathList );
355 }
356 }
357
358 /***
359 * Sets whether the user should be prompted when the fetch limit has been
360 * reached.
361 */
362 public void setPromptsAfterFetchLimit(boolean shouldPrompt)
363 {
364 promptsAfterFetchLimit = shouldPrompt;
365 }
366
367 /***
368 * Sets the qualifier used by this fetch specification.
369 */
370 public void setQualifier(EOQualifier aQualifier)
371 {
372 qualifier = aQualifier;
373 }
374
375 /***
376 * Sets the key paths to be returned if this fetch spec
377 * is returning raw rows.
378 */
379 public void setRawRowKeyPaths(List aKeyPathList)
380 {
381 if ( aKeyPathList == null )
382 {
383 rawRowKeyPaths = null;
384 }
385 else
386 {
387 rawRowKeyPaths = new NSArray( (List) aKeyPathList );
388 }
389 }
390
391 /***
392 * Sets whether modified objects in an editing context should
393 * be replaced by newer versions returned by this fetch spec.
394 */
395 public void setRefreshesRefetchedObjects(boolean shouldRefresh)
396 {
397 refreshesRefetchedObjects = shouldRefresh;
398 }
399
400 /***
401 * Sets whether this fetch spec should require all bindings to be
402 * resolved before executing.
403 */
404 public void setRequiresAllQualifierBindingVariables(boolean shouldRequireAll)
405 {
406 requiresAllQualifierBindingVariables = shouldRequireAll;
407 }
408
409 /***
410 * Sets the sort orderings used by this fetch spec.
411 */
412 public void setSortOrderings(List aSortList)
413 {
414 if ( aSortList == null )
415 {
416 sortOrderings = null;
417 }
418 else
419 {
420 sortOrderings = new NSArray( (List) aSortList );
421 }
422 }
423
424 /***
425 * Sets whether this fetch spec should return only distinct
426 * objects.
427 */
428 public void setUsesDistinct(boolean shouldUseDistinct)
429 {
430 distinct = shouldUseDistinct;
431 }
432
433 /***
434 * Returns a List of the sort orderings used by this fetch spec,
435 * or null if none have been specified.
436 */
437 public NSArray sortOrderings()
438 {
439 return sortOrderings;
440 }
441
442 /***
443 * Returns a string representation of this fetch specification.
444 */
445 public String toString()
446 {
447 return "[FetchSpecification:qualifier=("+qualifier+"),sortOrderings="+sortOrderings+"]";
448 }
449
450 /***
451 * Returns whether this fetch specification will return only one
452 * reference to each distinct object returned by the fetch.
453 * Default is false.
454 */
455 public boolean usesDistinct()
456 {
457 return distinct;
458 }
459
460 public void encodeWithKeyValueArchiver(EOKeyValueArchiver arch) {
461 arch.encodeObject("EOFetchSpecification", "class");
462 arch.encodeObject(entityName(), "entityName");
463 arch.encodeInt(fetchLimit(), "fetchLimit");
464
465
466 if (isDeep())
467 arch.encodeObject("YES", "isDeep");
468 arch.encodeObject(qualifier(), "qualifier");
469 if (refreshesRefetchedObjects())
470 arch.encodeObject("YES", "refreshesRefetchedObjects");
471 if (locksObjects())
472 arch.encodeObject("YES", "locksObjects");
473 if (fetchesRawRows())
474 arch.encodeObject("YES", "fetchesRawRows");
475 if (promptsAfterFetchLimit())
476 arch.encodeObject("YES", "promptsAfterFetchLimit");
477 if (requiresAllQualifierBindingVariables())
478 arch.encodeObject("YES", "requiresAllQualifierBindingVariables");
479 if (usesDistinct())
480 arch.encodeObject("YES", "usesDistinct");
481
482
483 if (sortOrderings() != null) {
484 NSMutableArray arr = new NSMutableArray(sortOrderings().count());
485 for (int i = 0; i < sortOrderings.count(); i++) {
486 EOSortOrdering so = (EOSortOrdering)sortOrderings().objectAtIndex(i);
487 EOKeyValueArchiver ar2 = new EOKeyValueArchiver();
488 so.encodeWithKeyValueArchiver(ar2);
489 arr.addObject(ar2.dictionary());
490 }
491 arch.encodeObject(arr, "sortOrderings");
492 }
493 if (rawRowKeyPaths != null && rawRowKeyPaths.count() > 0)
494 arch.encodeObject(rawRowKeyPaths, "rawRowKeyPaths");
495 if (prefetchingRelationshipKeyPaths != null && prefetchingRelationshipKeyPaths.count() > 0)
496 arch.encodeObject(rawRowKeyPaths, "prefetchingRelationshipKeyPaths");
497 if (hints != null && hints.count() > 0)
498 arch.encodeObject(hints, "hints");
499 }
500
501 public static Object decodeWithKeyValueUnarchiver(EOKeyValueUnarchiver unarch) {
502 EOFetchSpecification fs = new EOFetchSpecification();
503 fs.setEntityName((String)unarch.decodeObjectForKey("entityName"));
504 fs.setFetchLimit(unarch.decodeIntForKey("fetchLimit"));
505 fs.setIsDeep(unarch.decodeBoolForKey("isDeep"));
506 fs.setRefreshesRefetchedObjects(unarch.decodeBoolForKey("refreshesRefetchedObjects"));
507
508
509 NSArray arr = (NSArray)unarch.decodeObjectForKey("sortOrderings");
510 if (arr != null && arr.count() > 0) {
511 NSMutableArray orderings = new NSMutableArray(arr.count());
512 for (int i = 0; i < arr.count(); i++) {
513 NSDictionary so = (NSDictionary)arr.objectAtIndex(i);
514 String selname = (String)so.objectForKey("selectorName");
515 NSSelector selector = EOSortOrdering.CompareAscending;
516 if (selname.startsWith("compareDescending"))
517 selector = EOSortOrdering.CompareDescending;
518 else if (selname.startsWith("compareCaseInsensitiveAscending"))
519 selector = EOSortOrdering.CompareCaseInsensitiveAscending;
520 else if (selname.startsWith("compareCaseInsensitiveDescending"))
521 selector = EOSortOrdering.CompareCaseInsensitiveDescending;
522 EOSortOrdering eoso = new EOSortOrdering((String)so.objectForKey("key"), selector);
523 orderings.addObject(eoso);
524 }
525 fs.setSortOrderings(orderings);
526 }
527
528 arr = (NSArray)unarch.decodeObjectForKey("rawRowKeyPaths");
529 if (arr != null && arr.count() > 0) {
530 fs.setFetchesRawRows(true);
531 fs.setRawRowKeyPaths(arr);
532 }
533
534 fs.setQualifier((EOQualifier)unarch.decodeObjectForKey("qualifier"));
535 return fs;
536 }
537
538 }
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565