From 40a9d99496e098562f090fb7ffce9e749011b131 Mon Sep 17 00:00:00 2001 From: Benjamin Culkin Date: Mon, 20 May 2024 17:58:16 -0400 Subject: Formatting pass --- .../net/wotonomy/control/AbstractObjectStore.java | 1350 +++++++++----------- 1 file changed, 630 insertions(+), 720 deletions(-) (limited to 'projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/AbstractObjectStore.java') diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/AbstractObjectStore.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/AbstractObjectStore.java index ddaacf5..1989582 100644 --- a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/AbstractObjectStore.java +++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/AbstractObjectStore.java @@ -35,728 +35,638 @@ import net.wotonomy.foundation.NSSelector; import net.wotonomy.foundation.internal.WotonomyException; /** -* An abstract implementation of object store that -* implements common functionality. Subclasses must -* implement data object creation, initialization, and -* refault logic, as well as logic to commit an editing -* context. -*/ -public abstract class AbstractObjectStore extends EOObjectStore -{ - private NSMutableArray insertedIDsBuffer; - private NSMutableArray updatedIDsBuffer; - private NSMutableArray deletedIDsBuffer; - private NSMutableArray invalidatedIDsBuffer; - - private Map snapshots; - private List exceptionList; - - /** - * Constructs a new instance of this object store. - */ - public AbstractObjectStore() - { - snapshots = new HashMap(); - exceptionList = null; - - insertedIDsBuffer = new NSMutableArray(); - updatedIDsBuffer = new NSMutableArray(); - deletedIDsBuffer = new NSMutableArray(); - invalidatedIDsBuffer = new NSMutableArray(); - - // register for notifications - NSSelector handleNotification = - new NSSelector( "handleNotification", - new Class[] { NSNotification.class } ); - NSNotificationCenter.defaultCenter().addObserver( - this, - handleNotification, - EOClassDescription.ClassDescriptionNeededForEntityNameNotification, - null ); - } - - /** - * This implementation returns an appropriately configured array fault. - */ - public NSArray arrayFaultWithSourceGlobalID( EOGlobalID aGlobalID, - String aRelationship, EOEditingContext aContext ) - { // System.out.println( "arrayFaultWithSourceGlobalID: " + aGlobalID + " : " + aRelationship ); - return new ArrayFault( - aGlobalID, aRelationship, aContext ); - } - - /** - * This implementation returns the actual object for the specified id. - */ - public /*EOEnterpriseObject*/Object faultForGlobalID( EOGlobalID aGlobalID, - EOEditingContext aContext ) - { // System.out.println( "faultForGlobalID: " + aGlobalID ); - return /*(EOEnterpriseObject)*/createInstanceWithEditingContext( aGlobalID, aContext ); - } - - /** - * Returns a fault representing an object of the specified entity type with - * values from the specified dictionary. The fault should belong to the - * specified editing context. - * NOTE: Faults are not supported yet. - */ - public /*EOEnterpriseObject*/Object faultForRawRow( Map aDictionary, String anEntityName, - EOEditingContext aContext ) - { - //TODO: raw rows are not yet supported - throw new WotonomyException( "Faults are not yet supported." ); - } - - /** - * Given a newly instantiated object, this method initializes its - * properties to values appropriate for the specified id. The object - * should belong to the specified editing context. This method is called - * to populate faults. - */ - public void initializeObject( Object anObject, EOGlobalID aGlobalID, - EOEditingContext aContext ) - { //System.out.println( "initializeObject: " + aGlobalID ); - try - { - String entity = entityForGlobalIDOrObject( aGlobalID, null ); - EOClassDescription classDesc = - EOClassDescription.classDescriptionForEntityName( entity ); - if ( classDesc == null ) - { - throw new WotonomyException( "Unknown entity type: " + entity ); - } - - Collection attributes = classDesc.attributeKeys(); - Map data = readFromCache( aGlobalID, attributes ); - String key; - Iterator iterator = attributes.iterator(); - while ( iterator.hasNext() ) - { - key = iterator.next().toString(); - - // write the snapshot's reference into the object - if ( anObject instanceof EOKeyValueCoding ) - { - ((EOKeyValueCoding)anObject).takeStoredValueForKey( data.get( key ), key ); - } - else - { - EOKeyValueCodingSupport.takeStoredValueForKey( anObject, data.get( key ), key ); - } - - //NOTE: our objects are expected to make a copy - // of their data before it is modified, so it's okay - // to return them our copy of the data: - // we trust that they will not modify it. - } - } - catch ( Exception exc ) - { - exc.printStackTrace(); - } - } - - /** - * Reads the local data snapshot for the specified id. - * If no snapshot exists, a new snapshot is created. - * If the specified keys are not in the snapshot, - * new data is fetched into the snapshot. - * If null is specified, all known keys are returned. - * Will not return null. - * Result will have values for those keys and only - * those keys requested. Missing keys indicate an - * error occurred. - */ - protected Map readFromCache( EOGlobalID aGlobalID, Collection keys ) - { - Map snapshot = (Map) snapshots.get( aGlobalID ); - - // if no snapshot for this id, create an empty one - if ( snapshot == null ) - { - snapshot = new HashMap(); - snapshots.put( aGlobalID, snapshot ); - } - - // if we don't have all the necessary keys - if ( ( keys == null ) || ( ! snapshot.keySet().containsAll( keys ) ) ) - { - // we need to make a server call - try - { - Map data = readObject( aGlobalID, keys ); - - // compare timestamps - Comparable localTimestamp = (Comparable) timestampForData( snapshot ); - // if our local snapshot has an timestamp (new snapshots don't have timestamp) - if ( localTimestamp != null ) - { - Comparable incomingTimestamp = (Comparable) timestampForData( data ); - if ( incomingTimestamp == null ) - { - // not allowed to happen - new RuntimeException( "Server returned data without an timestamp" ).printStackTrace(); - // however, we can just assume it's a newer timestamp and continue - } - - // if timestamps don't match - if ( ( incomingTimestamp == null ) || ( ! incomingTimestamp.equals( localTimestamp ) ) ) - { - // dump our existing snapshot's data - snapshot.clear(); - // queue for a notification on this oid as updated - //TODO: implement this - } - } - - // copy new data into our local snapshot - snapshot.putAll( data ); - } - catch ( Exception exc ) - { - exc.printStackTrace(); - } - } - - // return just the requested keys from our updated snapshot - Map result = new HashMap(); - if ( keys == null ) - { - result.putAll( snapshot ); - } - else - { - Object key; - Iterator iterator = keys.iterator(); - while ( iterator.hasNext() ) - { - key = iterator.next(); - result.put( key, snapshot.get( key ) ); - } - } - return snapshot; - } - - /** - * Returns a comparable object (typically a Date or Long) for - * the given data map or snapshot. This is used to determine - * whether a local snapshot should be dumped in favor of fetched - * data from the server. - * Returns null if no timestamp can be determined, in which - * case the fetched data will assumed to be more recent than - * any local snapshot. - */ - abstract protected Comparable timestampForData( Map aDataMap ); - - /** - * Extracts the global id for the fetched data or snapshot. - * Some entities have multi-attribute keys that would be - * assembled into a single instance of EOGlobalID. - */ - abstract protected EOGlobalID globalIDForData( Map aDataMap ); - - /** - * Returns the entity that corresponds to the specified global id - * and/or object. Either may be null, but both will not be null. - * //FIXME: This is less than elegant. - */ - abstract protected String entityForGlobalIDOrObject( - EOGlobalID aGlobalID, Object anObject ); - - /** - * Returns the keys that have changed on the specified object. - * If null, all keys are presumed changed, including relationships. - */ - abstract protected Collection changedKeysForObject( Object anObject ); - - /** - * Returns the data for the row corresponding to the specified id - * containing at least the specified keys. Implementations are allowed - * to return more data than requested, and callers are advised to take - * advantage of the returned data. - */ - abstract protected Map readObject( EOGlobalID aGlobalID, Collection keys ); - - /** - * Returns the data for the row corresponding to the specified id. - * //TODO: Need a better return value? How to return invalidated list? - */ - abstract protected Map insertObject( EOGlobalID aGlobalID, Map aDataMap ); - - /** - * Returns the data for the row corresponding to the specified id. - * //TODO: Need a better return value? How to return invalidated list? - */ - abstract protected Object updateObject( EOGlobalID aGlobalID, Map aDataMap ); - - /** - * Returns the data for the row corresponding to the specified id. - * //TODO: Need a better return value? How to return invalidated list? - */ - abstract protected Object deleteObject( EOGlobalID aGlobalID ); - - /** - * Creates a new instance of an object that corresponds to the - * specified global id and is registered in the specified context. - * This implementation extracts the entity type from getEntityForGlobaID - * and construct a new instance from the class description that - * corresponds to the entity type. Override to change this behavior. - */ - protected Object createInstanceWithEditingContext( - EOGlobalID aGlobalID, EOEditingContext aContext ) - { - String entity = entityForGlobalIDOrObject( aGlobalID, null ); - EOClassDescription classDesc = - EOClassDescription.classDescriptionForEntityName( entity ); - if ( classDesc == null ) - { - throw new WotonomyException( "Unknown entity type: " + entity ); - } - - Object result = classDesc.createInstanceWithEditingContext( aContext, aGlobalID ); - if ( result instanceof EOFaulting ) - { - ((EOFaulting)result).turnIntoFault( null ); - } - return result; - } - - /** - * Dumps the snapshot corresponding to the specified id. - */ - protected void invalidateObject( EOGlobalID aGlobalID ) - { - snapshots.remove( aGlobalID ); - } - - /** - * Dumps all snapshots. - */ - protected void invalidateAllCache() - { - snapshots.clear(); - } - - /** - * Remove all values from all objects in memory, turning them into faults, - * and posts a notification that all objects have been invalidated. - */ - public void invalidateAllObjects() - { - invalidateAllCache(); - - // post notification - NSNotificationQueue.defaultQueue().enqueueNotification( - new NSNotification( - InvalidatedAllObjectsInStoreNotification, this ), - NSNotificationQueue.PostNow ); - } - - /** - * Removes values with the specified ids from memory, turning them into - * faults, and posts a notification that those objects have been invalidated. - */ - public void invalidateObjectsWithGlobalIDs( List aList ) - { - NSArray empty = new NSArray(); - NSMutableArray invalidated = new NSMutableArray(); - - Object object; - Iterator iterator = aList.iterator(); - while ( iterator.hasNext() ) - { - object = iterator.next(); - invalidateObject( (EOGlobalID) object ); - invalidated.addObject( object ); - } - - NSMutableDictionary info = new NSMutableDictionary(); - info.setObjectForKey( empty, InsertedKey ); - info.setObjectForKey( empty, UpdatedKey ); - info.setObjectForKey( empty, DeletedKey ); - info.setObjectForKey( invalidated, InvalidatedKey ); - - // post notification - NSNotificationQueue.defaultQueue(). - enqueueNotificationWithCoalesceMaskForModes( new NSNotification( - ObjectsChangedInStoreNotification, this, info ), - NSNotificationQueue.PostNow, - NSNotificationQueue.NotificationNoCoalescing, null ); - } - - /** - * Returns false because locking is not currently permitted. - */ - public boolean isObjectLockedWithGlobalID( EOGlobalID aGlobalID, - EOEditingContext aContext ) - { - return false; - } - - /** - * Does nothing because locking is not currently permitted. - */ - public void lockObjectWithGlobalID( EOGlobalID aGlobalID, - EOEditingContext aContext ) - { - // does nothing - } - - /** - * Returns a List of objects associated with the object - * with the specified id for the specified property - * relationship. This method may not return an array fault - * because array faults call this method to fetch on demand. - * All objects must be registered the specified editing context. - * The specified relationship key must produce a result of - * type Collection for the source object or an exception is thrown. - */ - public NSArray objectsForSourceGlobalID( EOGlobalID aGlobalID, - String aRelationship, EOEditingContext aContext ) - { // System.out.println( "objectsForSourceGlobalID: " + aGlobalID + " : " + aRelationship + " : " ); - - Map snapshot = readFromCache( aGlobalID, new NSArray( aRelationship ) ); - Object value = snapshot.get( aRelationship ); - if ( value == null ) value = new NSArray(); // empty list - if ( ! ( value instanceof Collection ) ) - { - throw new RuntimeException( "Specified relationship is not a collection: " - + aRelationship + " : " + aGlobalID + " : " + value ); - } - - NSArray result = new NSMutableArray(); - - // get fault for each id - EOGlobalID id; - Object fault; - Iterator iterator = ((Collection)value).iterator(); - while ( iterator.hasNext() ) - { - id = (EOGlobalID) iterator.next(); - - // get registered fault - fault = aContext.faultForGlobalID( id, aContext ); - - // assert fault - if ( fault == null ) - { - // this should never happen - throw new RuntimeException( - "Could not find fault for ID: " + id ); - } - - result.add( fault ); - } - - fireObjectsChangedInStore(); + * An abstract implementation of object store that implements common + * functionality. Subclasses must implement data object creation, + * initialization, and refault logic, as well as logic to commit an editing + * context. + */ +public abstract class AbstractObjectStore extends EOObjectStore { + private NSMutableArray insertedIDsBuffer; + private NSMutableArray updatedIDsBuffer; + private NSMutableArray deletedIDsBuffer; + private NSMutableArray invalidatedIDsBuffer; + + private Map snapshots; + private List exceptionList; + + /** + * Constructs a new instance of this object store. + */ + public AbstractObjectStore() { + snapshots = new HashMap(); + exceptionList = null; + + insertedIDsBuffer = new NSMutableArray(); + updatedIDsBuffer = new NSMutableArray(); + deletedIDsBuffer = new NSMutableArray(); + invalidatedIDsBuffer = new NSMutableArray(); + + // register for notifications + NSSelector handleNotification = new NSSelector("handleNotification", new Class[] { NSNotification.class }); + NSNotificationCenter.defaultCenter().addObserver(this, handleNotification, + EOClassDescription.ClassDescriptionNeededForEntityNameNotification, null); + } + + /** + * This implementation returns an appropriately configured array fault. + */ + public NSArray arrayFaultWithSourceGlobalID(EOGlobalID aGlobalID, String aRelationship, EOEditingContext aContext) { // System.out.println( + // "arrayFaultWithSourceGlobalID: + // " + // + + // aGlobalID + // + + // " + // : + // " + // + + // aRelationship + // ); + return new ArrayFault(aGlobalID, aRelationship, aContext); + } + + /** + * This implementation returns the actual object for the specified id. + */ + public /* EOEnterpriseObject */Object faultForGlobalID(EOGlobalID aGlobalID, EOEditingContext aContext) { // System.out.println( + // "faultForGlobalID: + // " + + // aGlobalID + // ); + return /* (EOEnterpriseObject) */createInstanceWithEditingContext(aGlobalID, aContext); + } + + /** + * Returns a fault representing an object of the specified entity type with + * values from the specified dictionary. The fault should belong to the + * specified editing context. NOTE: Faults are not supported yet. + */ + public /* EOEnterpriseObject */Object faultForRawRow(Map aDictionary, String anEntityName, + EOEditingContext aContext) { + // TODO: raw rows are not yet supported + throw new WotonomyException("Faults are not yet supported."); + } + + /** + * Given a newly instantiated object, this method initializes its properties to + * values appropriate for the specified id. The object should belong to the + * specified editing context. This method is called to populate faults. + */ + public void initializeObject(Object anObject, EOGlobalID aGlobalID, EOEditingContext aContext) { // System.out.println( + // "initializeObject: + // " + aGlobalID + // ); + try { + String entity = entityForGlobalIDOrObject(aGlobalID, null); + EOClassDescription classDesc = EOClassDescription.classDescriptionForEntityName(entity); + if (classDesc == null) { + throw new WotonomyException("Unknown entity type: " + entity); + } + + Collection attributes = classDesc.attributeKeys(); + Map data = readFromCache(aGlobalID, attributes); + String key; + Iterator iterator = attributes.iterator(); + while (iterator.hasNext()) { + key = iterator.next().toString(); + + // write the snapshot's reference into the object + if (anObject instanceof EOKeyValueCoding) { + ((EOKeyValueCoding) anObject).takeStoredValueForKey(data.get(key), key); + } else { + EOKeyValueCodingSupport.takeStoredValueForKey(anObject, data.get(key), key); + } + + // NOTE: our objects are expected to make a copy + // of their data before it is modified, so it's okay + // to return them our copy of the data: + // we trust that they will not modify it. + } + } catch (Exception exc) { + exc.printStackTrace(); + } + } + + /** + * Reads the local data snapshot for the specified id. If no snapshot exists, a + * new snapshot is created. If the specified keys are not in the snapshot, new + * data is fetched into the snapshot. If null is specified, all known keys are + * returned. Will not return null. Result will have values for those keys and + * only those keys requested. Missing keys indicate an error occurred. + */ + protected Map readFromCache(EOGlobalID aGlobalID, Collection keys) { + Map snapshot = (Map) snapshots.get(aGlobalID); + + // if no snapshot for this id, create an empty one + if (snapshot == null) { + snapshot = new HashMap(); + snapshots.put(aGlobalID, snapshot); + } + + // if we don't have all the necessary keys + if ((keys == null) || (!snapshot.keySet().containsAll(keys))) { + // we need to make a server call + try { + Map data = readObject(aGlobalID, keys); + + // compare timestamps + Comparable localTimestamp = (Comparable) timestampForData(snapshot); + // if our local snapshot has an timestamp (new snapshots don't have timestamp) + if (localTimestamp != null) { + Comparable incomingTimestamp = (Comparable) timestampForData(data); + if (incomingTimestamp == null) { + // not allowed to happen + new RuntimeException("Server returned data without an timestamp").printStackTrace(); + // however, we can just assume it's a newer timestamp and continue + } + + // if timestamps don't match + if ((incomingTimestamp == null) || (!incomingTimestamp.equals(localTimestamp))) { + // dump our existing snapshot's data + snapshot.clear(); + // queue for a notification on this oid as updated + // TODO: implement this + } + } + + // copy new data into our local snapshot + snapshot.putAll(data); + } catch (Exception exc) { + exc.printStackTrace(); + } + } + + // return just the requested keys from our updated snapshot + Map result = new HashMap(); + if (keys == null) { + result.putAll(snapshot); + } else { + Object key; + Iterator iterator = keys.iterator(); + while (iterator.hasNext()) { + key = iterator.next(); + result.put(key, snapshot.get(key)); + } + } + return snapshot; + } + + /** + * Returns a comparable object (typically a Date or Long) for the given data map + * or snapshot. This is used to determine whether a local snapshot should be + * dumped in favor of fetched data from the server. Returns null if no timestamp + * can be determined, in which case the fetched data will assumed to be more + * recent than any local snapshot. + */ + abstract protected Comparable timestampForData(Map aDataMap); + + /** + * Extracts the global id for the fetched data or snapshot. Some entities have + * multi-attribute keys that would be assembled into a single instance of + * EOGlobalID. + */ + abstract protected EOGlobalID globalIDForData(Map aDataMap); + + /** + * Returns the entity that corresponds to the specified global id and/or object. + * Either may be null, but both will not be null. //FIXME: This is less than + * elegant. + */ + abstract protected String entityForGlobalIDOrObject(EOGlobalID aGlobalID, Object anObject); + + /** + * Returns the keys that have changed on the specified object. If null, all keys + * are presumed changed, including relationships. + */ + abstract protected Collection changedKeysForObject(Object anObject); + + /** + * Returns the data for the row corresponding to the specified id containing at + * least the specified keys. Implementations are allowed to return more data + * than requested, and callers are advised to take advantage of the returned + * data. + */ + abstract protected Map readObject(EOGlobalID aGlobalID, Collection keys); + + /** + * Returns the data for the row corresponding to the specified id. //TODO: Need + * a better return value? How to return invalidated list? + */ + abstract protected Map insertObject(EOGlobalID aGlobalID, Map aDataMap); + + /** + * Returns the data for the row corresponding to the specified id. //TODO: Need + * a better return value? How to return invalidated list? + */ + abstract protected Object updateObject(EOGlobalID aGlobalID, Map aDataMap); + + /** + * Returns the data for the row corresponding to the specified id. //TODO: Need + * a better return value? How to return invalidated list? + */ + abstract protected Object deleteObject(EOGlobalID aGlobalID); + + /** + * Creates a new instance of an object that corresponds to the specified global + * id and is registered in the specified context. This implementation extracts + * the entity type from getEntityForGlobaID and construct a new instance from + * the class description that corresponds to the entity type. Override to change + * this behavior. + */ + protected Object createInstanceWithEditingContext(EOGlobalID aGlobalID, EOEditingContext aContext) { + String entity = entityForGlobalIDOrObject(aGlobalID, null); + EOClassDescription classDesc = EOClassDescription.classDescriptionForEntityName(entity); + if (classDesc == null) { + throw new WotonomyException("Unknown entity type: " + entity); + } + + Object result = classDesc.createInstanceWithEditingContext(aContext, aGlobalID); + if (result instanceof EOFaulting) { + ((EOFaulting) result).turnIntoFault(null); + } + return result; + } + + /** + * Dumps the snapshot corresponding to the specified id. + */ + protected void invalidateObject(EOGlobalID aGlobalID) { + snapshots.remove(aGlobalID); + } + + /** + * Dumps all snapshots. + */ + protected void invalidateAllCache() { + snapshots.clear(); + } + + /** + * Remove all values from all objects in memory, turning them into faults, and + * posts a notification that all objects have been invalidated. + */ + public void invalidateAllObjects() { + invalidateAllCache(); + + // post notification + NSNotificationQueue.defaultQueue().enqueueNotification( + new NSNotification(InvalidatedAllObjectsInStoreNotification, this), NSNotificationQueue.PostNow); + } + + /** + * Removes values with the specified ids from memory, turning them into faults, + * and posts a notification that those objects have been invalidated. + */ + public void invalidateObjectsWithGlobalIDs(List aList) { + NSArray empty = new NSArray(); + NSMutableArray invalidated = new NSMutableArray(); + + Object object; + Iterator iterator = aList.iterator(); + while (iterator.hasNext()) { + object = iterator.next(); + invalidateObject((EOGlobalID) object); + invalidated.addObject(object); + } + + NSMutableDictionary info = new NSMutableDictionary(); + info.setObjectForKey(empty, InsertedKey); + info.setObjectForKey(empty, UpdatedKey); + info.setObjectForKey(empty, DeletedKey); + info.setObjectForKey(invalidated, InvalidatedKey); + + // post notification + NSNotificationQueue.defaultQueue().enqueueNotificationWithCoalesceMaskForModes( + new NSNotification(ObjectsChangedInStoreNotification, this, info), NSNotificationQueue.PostNow, + NSNotificationQueue.NotificationNoCoalescing, null); + } + + /** + * Returns false because locking is not currently permitted. + */ + public boolean isObjectLockedWithGlobalID(EOGlobalID aGlobalID, EOEditingContext aContext) { + return false; + } + + /** + * Does nothing because locking is not currently permitted. + */ + public void lockObjectWithGlobalID(EOGlobalID aGlobalID, EOEditingContext aContext) { + // does nothing + } + + /** + * Returns a List of objects associated with the object with the specified id + * for the specified property relationship. This method may not return an array + * fault because array faults call this method to fetch on demand. All objects + * must be registered the specified editing context. The specified relationship + * key must produce a result of type Collection for the source object or an + * exception is thrown. + */ + public NSArray objectsForSourceGlobalID(EOGlobalID aGlobalID, String aRelationship, EOEditingContext aContext) { // System.out.println( + // "objectsForSourceGlobalID: + // " + // + + // aGlobalID + // + + // " + // : + // " + // + + // aRelationship + // + + // " + // : + // " + // ); + + Map snapshot = readFromCache(aGlobalID, new NSArray(aRelationship)); + Object value = snapshot.get(aRelationship); + if (value == null) + value = new NSArray(); // empty list + if (!(value instanceof Collection)) { + throw new RuntimeException( + "Specified relationship is not a collection: " + aRelationship + " : " + aGlobalID + " : " + value); + } + + NSArray result = new NSMutableArray(); + + // get fault for each id + EOGlobalID id; + Object fault; + Iterator iterator = ((Collection) value).iterator(); + while (iterator.hasNext()) { + id = (EOGlobalID) iterator.next(); + + // get registered fault + fault = aContext.faultForGlobalID(id, aContext); + + // assert fault + if (fault == null) { + // this should never happen + throw new RuntimeException("Could not find fault for ID: " + id); + } + + result.add(fault); + } + + fireObjectsChangedInStore(); //System.out.println( "done" ); - return result; - } - - /** - * Returns a List of objects the meet the criteria of - * the supplied specification. Faults are not allowed in the array. - * Each object is registered with the specified editing context. - * If any object is already fetched in the specified context, - * it is not refetched and that object should be used in the array. - */ - public NSArray objectsWithFetchSpecification( - EOFetchSpecification aFetchSpec, EOEditingContext aContext ) - { - NSMutableArray result = new NSMutableArray(); - - //TODO: implement this - - return result; - } - - /** - * Fires ObjectsChangedInStoreNotification - * with contents of buffers and then clears buffers. - * If buffers are empty, does nothing. - */ - private void fireObjectsChangedInStore() - { - // check for changes to broadcast - if ( insertedIDsBuffer.size() + updatedIDsBuffer.size() + - deletedIDsBuffer.size() + invalidatedIDsBuffer.size() == 0 ) - { - return; - } - - // broadcast ObjectsChangedInStoreNotification - // for the benefit of child editing contexts - - NSMutableDictionary storeInfo = new NSMutableDictionary(); - - storeInfo.setObjectForKey( - new NSArray( (Collection) insertedIDsBuffer ), - EOObjectStore.InsertedKey ); - storeInfo.setObjectForKey( - new NSArray( (Collection) updatedIDsBuffer ), - EOObjectStore.UpdatedKey ); - storeInfo.setObjectForKey( - new NSArray( (Collection) deletedIDsBuffer ), - EOObjectStore.DeletedKey ); - storeInfo.setObjectForKey( - new NSArray( (Collection) invalidatedIDsBuffer ), - EOObjectStore.InvalidatedKey ); - - // clear buffers - - insertedIDsBuffer.removeAllObjects(); - updatedIDsBuffer.removeAllObjects(); - deletedIDsBuffer.removeAllObjects(); - invalidatedIDsBuffer.removeAllObjects(); - - // post notification - NSNotificationQueue.defaultQueue(). - enqueueNotificationWithCoalesceMaskForModes( new NSNotification( - ObjectsChangedInStoreNotification, this, storeInfo ), - NSNotificationQueue.PostNow, - NSNotificationQueue.NotificationNoCoalescing, null ); - } - - /** - * Removes all values from the specified object, - * converting it into a fault for the specified id. - * New or deleted objects should not be refaulted. - */ - public void refaultObject( Object anObject, EOGlobalID aGlobalID, - EOEditingContext aContext ) - { + return result; + } + + /** + * Returns a List of objects the meet the criteria of the supplied + * specification. Faults are not allowed in the array. Each object is registered + * with the specified editing context. If any object is already fetched in the + * specified context, it is not refetched and that object should be used in the + * array. + */ + public NSArray objectsWithFetchSpecification(EOFetchSpecification aFetchSpec, EOEditingContext aContext) { + NSMutableArray result = new NSMutableArray(); + + // TODO: implement this + + return result; + } + + /** + * Fires ObjectsChangedInStoreNotification with contents of buffers and then + * clears buffers. If buffers are empty, does nothing. + */ + private void fireObjectsChangedInStore() { + // check for changes to broadcast + if (insertedIDsBuffer.size() + updatedIDsBuffer.size() + deletedIDsBuffer.size() + + invalidatedIDsBuffer.size() == 0) { + return; + } + + // broadcast ObjectsChangedInStoreNotification + // for the benefit of child editing contexts + + NSMutableDictionary storeInfo = new NSMutableDictionary(); + + storeInfo.setObjectForKey(new NSArray((Collection) insertedIDsBuffer), EOObjectStore.InsertedKey); + storeInfo.setObjectForKey(new NSArray((Collection) updatedIDsBuffer), EOObjectStore.UpdatedKey); + storeInfo.setObjectForKey(new NSArray((Collection) deletedIDsBuffer), EOObjectStore.DeletedKey); + storeInfo.setObjectForKey(new NSArray((Collection) invalidatedIDsBuffer), EOObjectStore.InvalidatedKey); + + // clear buffers + + insertedIDsBuffer.removeAllObjects(); + updatedIDsBuffer.removeAllObjects(); + deletedIDsBuffer.removeAllObjects(); + invalidatedIDsBuffer.removeAllObjects(); + + // post notification + NSNotificationQueue.defaultQueue().enqueueNotificationWithCoalesceMaskForModes( + new NSNotification(ObjectsChangedInStoreNotification, this, storeInfo), NSNotificationQueue.PostNow, + NSNotificationQueue.NotificationNoCoalescing, null); + } + + /** + * Removes all values from the specified object, converting it into a fault for + * the specified id. New or deleted objects should not be refaulted. + */ + public void refaultObject(Object anObject, EOGlobalID aGlobalID, EOEditingContext aContext) { //System.out.println( "refaultObject: " + aGlobalID ); //new net.wotonomy.ui.swing.util.StackTraceInspector(); - if ( anObject instanceof EOFaulting ) - { - ((EOFaulting)anObject).turnIntoFault( null ); - } - } - - /** - * Writes all changes in the specified editing context - * to the respository. - */ - public void saveChangesInEditingContext ( EOEditingContext aContext ) - { - Object result; // need a container result? - Map updateMap; - Object object; - EOGlobalID id; - Iterator iterator; - - //TODO: the ordering of operations here - // needs to be a lot more sophisticated. - - // process deletes first - iterator = aContext.deletedObjects().iterator(); - while ( iterator.hasNext() ) - { - object = iterator.next(); - id = aContext.globalIDForObject( object ); - try - { - result = deleteObject( id ); - } - catch ( Exception exc ) - { - System.out.println( "Error deleting object: " + id ); - exc.printStackTrace(); - } - } - - // process inserts next - iterator = aContext.insertedObjects().iterator(); - while ( iterator.hasNext() ) - { - object = iterator.next(); - processInsert( aContext, object ); - } - - // process updates last - iterator = aContext.updatedObjects().iterator(); - while ( iterator.hasNext() ) - { - object = iterator.next(); - id = aContext.globalIDForObject( object ); - try - { - updateMap = getUpdateMap( aContext, object ); - result = updateObject( id, updateMap ); - } - catch ( Exception exc ) - { - System.out.println( "Error updating object: " + id ); - exc.printStackTrace(); - } - } - - //aContext.invalidateAllObjects(); - } - - protected Object processInsert( EOEditingContext aContext, Object object ) - { - Map result = null; - EOGlobalID id = aContext.globalIDForObject( object ); - try - { - Map updateMap; - updateMap = getUpdateMap( aContext, object ); - result = insertObject( id, updateMap ); - id = globalIDForData( result ); // read new permanent id - - // broadcast that the global id has changed. - NSMutableDictionary userInfo = new NSMutableDictionary(); - userInfo.setObjectForKey( id, aContext.globalIDForObject( object ) ); - NSNotificationQueue.defaultQueue().enqueueNotification( - new NSNotification( EOGlobalID.GlobalIDChangedNotification, - null , userInfo ), NSNotificationQueue.PostNow ); - - } - catch ( Exception exc ) - { - System.out.println( "Error inserting object: " + id ); - exc.printStackTrace(); - } - return result; - } - - /** - * This method returns a map containing just the keys that are modified - * for a given object, converting any to-one or to-many relationships - * to id references. - */ - protected Map getUpdateMap( EOEditingContext aContext, Object anObject ) - { - Map result = new HashMap(); - EOEditingContext context = aContext; - - String entity = entityForGlobalIDOrObject( null, anObject ); - EOClassDescription classDesc = - EOClassDescription.classDescriptionForEntityName( entity ); - if ( classDesc == null ) - { - throw new WotonomyException( "Unknown entity type: " + entity ); - } - - NSArray oneKeys = classDesc.toOneRelationshipKeys(); - NSArray manyKeys = classDesc.toManyRelationshipKeys(); - - String key; - Object value; - EOGlobalID id; - - Collection changedKeys = changedKeysForObject( anObject ); - if ( changedKeys == null ) - { - // assume all keys changed - changedKeys = classDesc.attributeKeys(); - changedKeys.addAll( oneKeys ); - changedKeys.addAll( manyKeys ); - } - Iterator iterator = changedKeys.iterator(); - while ( iterator.hasNext() ) - { - key = iterator.next().toString(); - if ( anObject instanceof EOKeyValueCoding ) - { - value = ((EOKeyValueCoding)anObject).storedValueForKey( key ); - } - else - { - value = EOKeyValueCodingSupport.storedValueForKey( anObject, key ); - } - - // convert to-one relationship to oid - if ( oneKeys.contains( key ) ) - { - id = context.globalIDForObject( value ); - - // if this id hasn't been persisted, save it first - // NOTE: this won't work for self-referential graphs of objects! - if ( id.isTemporary() ) - { - processInsert( aContext, value ); - id = context.globalIDForObject( value ); - } - - value = id; - } - else - // convert to-many relationship list to oid list - if ( manyKeys.contains( key ) ) - { - //NOTE: we can assume that array faults that - // are marked as changed have been fired. - if ( value instanceof Collection ) - { - Object object; - Collection newValue = new LinkedList(); - Iterator jiterator = ((Collection)value).iterator(); - while ( jiterator.hasNext() ) - { - object = jiterator.next(); - id = context.globalIDForObject( object ); - - // if this id hasn't been persisted, save it first - // NOTE: this won't work for self-referential graphs of objects! - if ( id.isTemporary() ) - { - processInsert( aContext, object ); - id = context.globalIDForObject( object ); - } - newValue.add( id ); - } - value = newValue; - } - else - { - // should never happen - new RuntimeException( - "Can't update to-many relationship because it's not a Collection." ) - .printStackTrace(); - } - } - - // place value in map - result.put( key, value ); - } - -System.out.println( result ); - return result; - } - -/* - * $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.5 2003/12/18 15:37:38 mpowers - * Changes to retain ability to work with objects that don't necessarily - * implement EOEnterpriseObject. I would still like to preserve this case - * for general usage, however the access package is free to assume that - * those objects will be EOs and cast appropriately. - * - * Revision 1.4 2003/08/19 01:53:12 chochos - * EOObjectStore had some incompatible return types (Object instead of EOEnterpriseObject, in fault methods mostly). It's internally consistent but I hope it doesn't break anything based on this, even though fault methods mostly throw exceptions for now. - * - * Revision 1.3 2002/10/24 18:18:12 mpowers - * NSArray's are now considered read-only, so we can return our internal - * representation to reduce unnecessary object allocation. - * - * Revision 1.2 2002/01/19 17:27:49 mpowers - * Implemented most of it. - * - * Revision 1.1 2001/11/25 22:44:02 mpowers - * Contributing draft of AbstractObjectStore. - * - * - */ + if (anObject instanceof EOFaulting) { + ((EOFaulting) anObject).turnIntoFault(null); + } + } + + /** + * Writes all changes in the specified editing context to the respository. + */ + public void saveChangesInEditingContext(EOEditingContext aContext) { + Object result; // need a container result? + Map updateMap; + Object object; + EOGlobalID id; + Iterator iterator; + + // TODO: the ordering of operations here + // needs to be a lot more sophisticated. + + // process deletes first + iterator = aContext.deletedObjects().iterator(); + while (iterator.hasNext()) { + object = iterator.next(); + id = aContext.globalIDForObject(object); + try { + result = deleteObject(id); + } catch (Exception exc) { + System.out.println("Error deleting object: " + id); + exc.printStackTrace(); + } + } + + // process inserts next + iterator = aContext.insertedObjects().iterator(); + while (iterator.hasNext()) { + object = iterator.next(); + processInsert(aContext, object); + } + + // process updates last + iterator = aContext.updatedObjects().iterator(); + while (iterator.hasNext()) { + object = iterator.next(); + id = aContext.globalIDForObject(object); + try { + updateMap = getUpdateMap(aContext, object); + result = updateObject(id, updateMap); + } catch (Exception exc) { + System.out.println("Error updating object: " + id); + exc.printStackTrace(); + } + } + + // aContext.invalidateAllObjects(); + } + + protected Object processInsert(EOEditingContext aContext, Object object) { + Map result = null; + EOGlobalID id = aContext.globalIDForObject(object); + try { + Map updateMap; + updateMap = getUpdateMap(aContext, object); + result = insertObject(id, updateMap); + id = globalIDForData(result); // read new permanent id + + // broadcast that the global id has changed. + NSMutableDictionary userInfo = new NSMutableDictionary(); + userInfo.setObjectForKey(id, aContext.globalIDForObject(object)); + NSNotificationQueue.defaultQueue().enqueueNotification( + new NSNotification(EOGlobalID.GlobalIDChangedNotification, null, userInfo), + NSNotificationQueue.PostNow); + + } catch (Exception exc) { + System.out.println("Error inserting object: " + id); + exc.printStackTrace(); + } + return result; + } + + /** + * This method returns a map containing just the keys that are modified for a + * given object, converting any to-one or to-many relationships to id + * references. + */ + protected Map getUpdateMap(EOEditingContext aContext, Object anObject) { + Map result = new HashMap(); + EOEditingContext context = aContext; + + String entity = entityForGlobalIDOrObject(null, anObject); + EOClassDescription classDesc = EOClassDescription.classDescriptionForEntityName(entity); + if (classDesc == null) { + throw new WotonomyException("Unknown entity type: " + entity); + } + + NSArray oneKeys = classDesc.toOneRelationshipKeys(); + NSArray manyKeys = classDesc.toManyRelationshipKeys(); + + String key; + Object value; + EOGlobalID id; + + Collection changedKeys = changedKeysForObject(anObject); + if (changedKeys == null) { + // assume all keys changed + changedKeys = classDesc.attributeKeys(); + changedKeys.addAll(oneKeys); + changedKeys.addAll(manyKeys); + } + Iterator iterator = changedKeys.iterator(); + while (iterator.hasNext()) { + key = iterator.next().toString(); + if (anObject instanceof EOKeyValueCoding) { + value = ((EOKeyValueCoding) anObject).storedValueForKey(key); + } else { + value = EOKeyValueCodingSupport.storedValueForKey(anObject, key); + } + + // convert to-one relationship to oid + if (oneKeys.contains(key)) { + id = context.globalIDForObject(value); + + // if this id hasn't been persisted, save it first + // NOTE: this won't work for self-referential graphs of objects! + if (id.isTemporary()) { + processInsert(aContext, value); + id = context.globalIDForObject(value); + } + + value = id; + } else + // convert to-many relationship list to oid list + if (manyKeys.contains(key)) { + // NOTE: we can assume that array faults that + // are marked as changed have been fired. + if (value instanceof Collection) { + Object object; + Collection newValue = new LinkedList(); + Iterator jiterator = ((Collection) value).iterator(); + while (jiterator.hasNext()) { + object = jiterator.next(); + id = context.globalIDForObject(object); + + // if this id hasn't been persisted, save it first + // NOTE: this won't work for self-referential graphs of objects! + if (id.isTemporary()) { + processInsert(aContext, object); + id = context.globalIDForObject(object); + } + newValue.add(id); + } + value = newValue; + } else { + // should never happen + new RuntimeException("Can't update to-many relationship because it's not a Collection.") + .printStackTrace(); + } + } + + // place value in map + result.put(key, value); + } + + System.out.println(result); + return result; + } + + /* + * $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.5 2003/12/18 15:37:38 mpowers Changes to retain ability to work + * with objects that don't necessarily implement EOEnterpriseObject. I would + * still like to preserve this case for general usage, however the access + * package is free to assume that those objects will be EOs and cast + * appropriately. + * + * Revision 1.4 2003/08/19 01:53:12 chochos EOObjectStore had some incompatible + * return types (Object instead of EOEnterpriseObject, in fault methods mostly). + * It's internally consistent but I hope it doesn't break anything based on + * this, even though fault methods mostly throw exceptions for now. + * + * Revision 1.3 2002/10/24 18:18:12 mpowers NSArray's are now considered + * read-only, so we can return our internal representation to reduce unnecessary + * object allocation. + * + * Revision 1.2 2002/01/19 17:27:49 mpowers Implemented most of it. + * + * Revision 1.1 2001/11/25 22:44:02 mpowers Contributing draft of + * AbstractObjectStore. + * + * + */ } - -- cgit v1.2.3