/* Wotonomy: OpenStep design patterns for pure Java applications. Copyright (C) 2001 Michael Powers This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, see http://www.gnu.org */ package net.wotonomy.control; import java.util.Collection; import java.util.List; import java.util.Map; import net.wotonomy.foundation.NSArray; import net.wotonomy.foundation.NSDictionary; import net.wotonomy.foundation.NSMutableArray; import net.wotonomy.foundation.NSMutableDictionary; import net.wotonomy.foundation.NSSelector; import net.wotonomy.foundation.internal.WotonomyException; /** * EOFetchSpecification defines the parameters used to request * objects from an EOObjectStore. They are commonly created * and passed to a EODataSource which fetches from its * EOEditingContext, which passes the call up to its root * EOObjectStore's objectsWithFetchSpecification method. * * @author michael@mpowers.net * @author $Author: cgruber $ * @version $Revision: 894 $ */ public class EOFetchSpecification implements EOKeyValueArchiving { private boolean fetchesRawRows; private String entityName; private NSDictionary hints; private boolean deep; private int fetchLimit; private boolean locksObjects; private NSArray prefetchingRelationshipKeyPaths; private boolean promptsAfterFetchLimit; private EOQualifier qualifier; private NSArray rawRowKeyPaths; private boolean refreshesRefetchedObjects; private boolean requiresAllQualifierBindingVariables; private NSArray sortOrderings; private boolean distinct; /** * Default constructor initializes internal state. */ public EOFetchSpecification() { fetchesRawRows = false; entityName = null; hints = null; deep = true; fetchLimit = 0; locksObjects = false; prefetchingRelationshipKeyPaths = null; promptsAfterFetchLimit = false; qualifier = null; rawRowKeyPaths = null; refreshesRefetchedObjects = false; requiresAllQualifierBindingVariables = false; sortOrderings = null; distinct = false; } /** * Constructs a fetch specification for the specified entity type using * the specified qualifier and sort ordering. */ public EOFetchSpecification( String anEntityName, EOQualifier aQualifier, List aSortOrderingList ) { this(); entityName = anEntityName; qualifier = aQualifier; sortOrderings = new NSArray( (Collection) aSortOrderingList ); } /** * Constructs a fetch specification for the specified entity type using * the specified qualifier and sort ordering, distinct flag, deep flag, * and hints dictionary. */ public EOFetchSpecification( String anEntityName, EOQualifier aQualifier, NSArray aSortOrderingList, boolean usesDistinct, boolean isDeep, Map aHintMap) { this(); entityName = anEntityName; qualifier = aQualifier; sortOrderings = new NSArray( (Collection) aSortOrderingList ); distinct = usesDistinct; deep = isDeep; hints = new NSMutableDictionary( (Map) aHintMap ); } /** * Convenience to return the named fetch specification from the class description * corresponding to the specified entity name. Returns null if either entityName * or spec name cannot be resolved. */ public static EOFetchSpecification fetchSpecificationNamed( String name, String entityName) { EOClassDescription classDesc = EOClassDescription.classDescriptionForEntityName( entityName ); if ( classDesc == null ) return null; return classDesc.fetchSpecificationNamed( name ); } /** * Implemented to return a new fetch specification that is a deep copy of this one. */ public Object clone() { EOFetchSpecification clone = new EOFetchSpecification(); clone.fetchesRawRows = this.fetchesRawRows; clone.entityName = this.entityName; if ( this.hints != null ) clone.hints = new NSDictionary( (Map) this.hints ); clone.deep = this.deep; clone.locksObjects = this.locksObjects; if ( this.prefetchingRelationshipKeyPaths != null ) clone.prefetchingRelationshipKeyPaths = new NSArray( (List) prefetchingRelationshipKeyPaths ); clone.promptsAfterFetchLimit = this.promptsAfterFetchLimit; if ( this.qualifier != null ) clone.qualifier = this.qualifier; //FIXME: probably should clone? if ( this.rawRowKeyPaths != null ) clone.rawRowKeyPaths = new NSArray( (List) this.rawRowKeyPaths ); clone.refreshesRefetchedObjects = this.refreshesRefetchedObjects; clone.requiresAllQualifierBindingVariables = this.requiresAllQualifierBindingVariables; if ( this.sortOrderings != null ) clone.sortOrderings = new NSArray( (List) this.sortOrderings ); clone.distinct = this.distinct; return clone; } /** * Returns the name of the entity fetched by this fetch spec. */ public String entityName() { return entityName; } /** * Returns the current fetch limit. * A fetch limit of zero indicates no fetch limit. * Zero is the default. */ public int fetchLimit() { return fetchLimit; } /** * Returns whether this fetch spec will fetch raw rows. * Default is false. */ public boolean fetchesRawRows() { return fetchesRawRows; } /** * Returns a fetch specification that resolves the bindings * in the specified map. */ public EOFetchSpecification fetchSpecificationWithQualifierBindings(Map aBindingMap) { throw new WotonomyException( "Not implemented yet" ); } /** * Returns a Map containing the hints used by this fetch specification, * or null if no hints have been specified. */ public NSDictionary hints() { if ( hints == null ) return null; return new NSDictionary( (NSDictionary) hints ); } /** * Returns whether entities related to the primary * entities are fetched by this fetch spec. If true, all relationships * whose destinations meet the qualifier criteria will be returned * in addition to primary results. If false, only the primary entities * will be returned. Default is true. */ public boolean isDeep() { return deep; } /** * Returns whether this data source should lock objects that * are fetched. Default is false. */ public boolean locksObjects() { return locksObjects; } /*** * Returns a List of relationships for the fetched objects that * should also be fetched, or null if no such list has been specified. * Use this to avoid additional calls to the server to fetch * relationships that you know you will use. * NOTE: wotonomy allows you to specify non-relational keys * as well. */ public NSArray prefetchingRelationshipKeyPaths() { return prefetchingRelationshipKeyPaths; } /** * Returns whether the user should be prompted to continue * when the fetch limit has been exceeded. * Default is false. */ public boolean promptsAfterFetchLimit() { return promptsAfterFetchLimit; } /** * Returns the qualifier used by this fetch specification, * or null if none has been specified. */ public EOQualifier qualifier() { return qualifier; } /** * Returns a List of keys or key paths for which * values should be returned when fetching raw rows, * or null if no raw row key paths have been specified. */ public NSArray rawRowKeyPaths() { return rawRowKeyPaths; } /** * Returns whether fetched objects should replace * modified versions already fetched into an editing context. * If true, those changes will be lost. * Default is false. */ public boolean refreshesRefetchedObjects() { return refreshesRefetchedObjects; } /** * Returns whether all qualifier bindings must be specified * in order to fetch. If true, an exception is thrown if * unspecified bindings exist. If false, unspecified bindings * will be removed from the qualifier. Default is false. */ public boolean requiresAllQualifierBindingVariables() { return requiresAllQualifierBindingVariables; } /** * Sets the name of the entity fetched by this spec. */ public void setEntityName(String aName) { entityName = aName; } /** * Sets whether this fetch spec will return raw rows. */ public void setFetchesRawRows(boolean shouldFetchRawRows) { fetchesRawRows = shouldFetchRawRows; } /** * Sets the limit on the number of records returned for this fetch spec. * Zero indicates no limit on fetches. */ public void setFetchLimit(int aLimit) { fetchLimit = aLimit; } /** * Sets the hints passed by this fetch spec. */ public void setHints(Map aHintMap) { if ( aHintMap == null ) { hints = null; } else { hints = new NSDictionary( (Map) aHintMap ); } } /** * Sets whether this fetch specification fetches deeply. */ public void setIsDeep(boolean isDeep) { deep = isDeep; } /** * Sets whether this fetch spec locks objects that * are returned by the fetch. */ public void setLocksObjects(boolean shouldLockObjects) { locksObjects = shouldLockObjects; } /** * Sets the prefetch key paths that should be used as an optimization * hint to the server. NOTE: wotonomy allows you to specify non-relationship * keys as well. */ public void setPrefetchingRelationshipKeyPaths(List aKeyPathList) { if ( aKeyPathList == null ) { prefetchingRelationshipKeyPaths = null; } else { prefetchingRelationshipKeyPaths = new NSArray( (List) aKeyPathList ); } } /** * Sets whether the user should be prompted when the fetch limit has been * reached. */ public void setPromptsAfterFetchLimit(boolean shouldPrompt) { promptsAfterFetchLimit = shouldPrompt; } /** * Sets the qualifier used by this fetch specification. */ public void setQualifier(EOQualifier aQualifier) { qualifier = aQualifier; } /** * Sets the key paths to be returned if this fetch spec * is returning raw rows. */ public void setRawRowKeyPaths(List aKeyPathList) { if ( aKeyPathList == null ) { rawRowKeyPaths = null; } else { rawRowKeyPaths = new NSArray( (List) aKeyPathList ); } } /** * Sets whether modified objects in an editing context should * be replaced by newer versions returned by this fetch spec. */ public void setRefreshesRefetchedObjects(boolean shouldRefresh) { refreshesRefetchedObjects = shouldRefresh; } /** * Sets whether this fetch spec should require all bindings to be * resolved before executing. */ public void setRequiresAllQualifierBindingVariables(boolean shouldRequireAll) { requiresAllQualifierBindingVariables = shouldRequireAll; } /** * Sets the sort orderings used by this fetch spec. */ public void setSortOrderings(List aSortList) { if ( aSortList == null ) { sortOrderings = null; } else { sortOrderings = new NSArray( (List) aSortList ); } } /** * Sets whether this fetch spec should return only distinct * objects. */ public void setUsesDistinct(boolean shouldUseDistinct) { distinct = shouldUseDistinct; } /** * Returns a List of the sort orderings used by this fetch spec, * or null if none have been specified. */ public NSArray sortOrderings() { return sortOrderings; } /** * Returns a string representation of this fetch specification. */ public String toString() { return "[FetchSpecification:qualifier=("+qualifier+"),sortOrderings="+sortOrderings+"]"; } /** * Returns whether this fetch specification will return only one * reference to each distinct object returned by the fetch. * Default is false. */ public boolean usesDistinct() { return distinct; } public void encodeWithKeyValueArchiver(EOKeyValueArchiver arch) { arch.encodeObject("EOFetchSpecification", "class"); arch.encodeObject(entityName(), "entityName"); arch.encodeInt(fetchLimit(), "fetchLimit"); //flags if (isDeep()) arch.encodeObject("YES", "isDeep"); arch.encodeObject(qualifier(), "qualifier"); if (refreshesRefetchedObjects()) arch.encodeObject("YES", "refreshesRefetchedObjects"); if (locksObjects()) arch.encodeObject("YES", "locksObjects"); if (fetchesRawRows()) arch.encodeObject("YES", "fetchesRawRows"); if (promptsAfterFetchLimit()) arch.encodeObject("YES", "promptsAfterFetchLimit"); if (requiresAllQualifierBindingVariables()) arch.encodeObject("YES", "requiresAllQualifierBindingVariables"); if (usesDistinct()) arch.encodeObject("YES", "usesDistinct"); //encode arrays if (sortOrderings() != null) { NSMutableArray arr = new NSMutableArray(sortOrderings().count()); for (int i = 0; i < sortOrderings.count(); i++) { EOSortOrdering so = (EOSortOrdering)sortOrderings().objectAtIndex(i); EOKeyValueArchiver ar2 = new EOKeyValueArchiver(); so.encodeWithKeyValueArchiver(ar2); arr.addObject(ar2.dictionary()); } arch.encodeObject(arr, "sortOrderings"); } if (rawRowKeyPaths != null && rawRowKeyPaths.count() > 0) arch.encodeObject(rawRowKeyPaths, "rawRowKeyPaths"); if (prefetchingRelationshipKeyPaths != null && prefetchingRelationshipKeyPaths.count() > 0) arch.encodeObject(rawRowKeyPaths, "prefetchingRelationshipKeyPaths"); if (hints != null && hints.count() > 0) arch.encodeObject(hints, "hints"); } public static Object decodeWithKeyValueUnarchiver(EOKeyValueUnarchiver unarch) { EOFetchSpecification fs = new EOFetchSpecification(); fs.setEntityName((String)unarch.decodeObjectForKey("entityName")); fs.setFetchLimit(unarch.decodeIntForKey("fetchLimit")); fs.setIsDeep(unarch.decodeBoolForKey("isDeep")); fs.setRefreshesRefetchedObjects(unarch.decodeBoolForKey("refreshesRefetchedObjects")); //Sort orderings NSArray arr = (NSArray)unarch.decodeObjectForKey("sortOrderings"); if (arr != null && arr.count() > 0) { NSMutableArray orderings = new NSMutableArray(arr.count()); for (int i = 0; i < arr.count(); i++) { NSDictionary so = (NSDictionary)arr.objectAtIndex(i); String selname = (String)so.objectForKey("selectorName"); NSSelector selector = EOSortOrdering.CompareAscending; if (selname.startsWith("compareDescending")) selector = EOSortOrdering.CompareDescending; else if (selname.startsWith("compareCaseInsensitiveAscending")) selector = EOSortOrdering.CompareCaseInsensitiveAscending; else if (selname.startsWith("compareCaseInsensitiveDescending")) selector = EOSortOrdering.CompareCaseInsensitiveDescending; EOSortOrdering eoso = new EOSortOrdering((String)so.objectForKey("key"), selector); orderings.addObject(eoso); } fs.setSortOrderings(orderings); } //raw rows arr = (NSArray)unarch.decodeObjectForKey("rawRowKeyPaths"); if (arr != null && arr.count() > 0) { fs.setFetchesRawRows(true); fs.setRawRowKeyPaths(arr); } //qualifier fs.setQualifier((EOQualifier)unarch.decodeObjectForKey("qualifier")); return fs; } } /* * $Log$ * Revision 1.2 2006/02/16 16:47:14 cgruber * Move some classes in to "internal" packages and re-work imports, etc. * * Also use UnsupportedOperationExceptions where appropriate, instead of WotonomyExceptions. * * Revision 1.1 2006/02/16 13:19:57 cgruber * Check in all sources in eclipse-friendly maven-enabled packages. * * Revision 1.4 2003/08/11 18:19:01 chochos * encoding/decoding with EOKeyValueArchiving now works properly * * Revision 1.3 2003/08/09 01:22:20 chochos * implements EOKeyValueArchiving (and unarchiving) * * Revision 1.2 2001/11/24 17:32:57 mpowers * We now have a real implementation. * * Revision 1.1 2001/02/05 03:45:37 mpowers * Starting work on EOEditingContext. * * */