View Javadoc

1   /*
2   Wotonomy: OpenStep design patterns for pure Java applications.
3   Copyright (C) 2001 Michael Powers
4   
5   This library is free software; you can redistribute it and/or
6   modify it under the terms of the GNU Lesser General Public
7   License as published by the Free Software Foundation; either
8   version 2.1 of the License, or (at your option) any later version.
9   
10  This library is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  Lesser General Public License for more details.
14  
15  You should have received a copy of the GNU Lesser General Public
16  License along with this library; if not, see http://www.gnu.org
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; //FIXME: probably should clone?
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 		//flags
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 		//encode arrays
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 		//Sort orderings
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 		//raw rows
528 		arr = (NSArray)unarch.decodeObjectForKey("rawRowKeyPaths");
529 		if (arr != null && arr.count() > 0) {
530 			fs.setFetchesRawRows(true);
531 			fs.setRawRowKeyPaths(arr);
532 		}
533 		//qualifier
534 		fs.setQualifier((EOQualifier)unarch.decodeObjectForKey("qualifier"));
535 		return fs;
536 	}
537 
538 }
539 
540 /*
541  * $Log$
542  * Revision 1.2  2006/02/16 16:47:14  cgruber
543  * Move some classes in to "internal" packages and re-work imports, etc.
544  *
545  * Also use UnsupportedOperationExceptions where appropriate, instead of WotonomyExceptions.
546  *
547  * Revision 1.1  2006/02/16 13:19:57  cgruber
548  * Check in all sources in eclipse-friendly maven-enabled packages.
549  *
550  * Revision 1.4  2003/08/11 18:19:01  chochos
551  * encoding/decoding with EOKeyValueArchiving now works properly
552  *
553  * Revision 1.3  2003/08/09 01:22:20  chochos
554  * implements EOKeyValueArchiving (and unarchiving)
555  *
556  * Revision 1.2  2001/11/24 17:32:57  mpowers
557  * We now have a real implementation.
558  *
559  * Revision 1.1  2001/02/05 03:45:37  mpowers
560  * Starting work on EOEditingContext.
561  *
562  *
563  */
564     
565