/* 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. * * */