summaryrefslogtreecommitdiff
path: root/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOEditingContext.java
diff options
context:
space:
mode:
authorBenjamin Culkin <scorpress@gmail.com>2024-05-20 17:58:16 -0400
committerBenjamin Culkin <scorpress@gmail.com>2024-05-20 17:58:16 -0400
commit40a9d99496e098562f090fb7ffce9e749011b131 (patch)
tree437df24d65470582e943e494a52db8ed65a881ae /projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOEditingContext.java
parentff072dfe782f6f22123cd4ba050828d35c0d0fbd (diff)
Formatting pass
Diffstat (limited to 'projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOEditingContext.java')
-rw-r--r--projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOEditingContext.java5547
1 files changed, 2467 insertions, 3080 deletions
diff --git a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOEditingContext.java b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOEditingContext.java
index b01727d..3a39035 100644
--- a/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOEditingContext.java
+++ b/projects/net.wotonomy.persistence/src/main/java/net/wotonomy/control/EOEditingContext.java
@@ -42,3206 +42,2593 @@ import net.wotonomy.foundation.internal.WotonomyException;
//import javax.swing.undo.UndoManager;
/**
-* EOEditingContext provides transactional support for
-* fetching, editing, and committing changes made on a
-* collection of objects to a parent object store. <br><br>
-*
-* EOEditingContext is itself a subclass of EOObjectStore,
-* and this means that EOEditingContexts can use other
-* EOEditingContexts as their parent. However, there
-* still must exist an EOObjectStore as the root of the
-* editing hierarchy that can maintain persistent state.
-*
-* @author michael@mpowers.net
-* @author $Author: cgruber $
-* @version $Revision: 894 $
-*/
-public class EOEditingContext
- extends EOObjectStore
- implements EOObserving
-{
- /**
- * Key for the NSNotification posted after this editing context
- * saves changes. Object of the notification will be this editing
- * context, and user info will contain InsertedKey, UpdatedKey,
- * and DeletedKey (keys are defined in EOObjectStore).
- */
- public static final String
- EditingContextDidSaveChangesNotification =
- "EOEditingContextDidSaveChangesNotification";
-
- /**
- * Key for the NSNotification posted after this editing context
- * observes changes. Object of the notification will be this editing
- * context, and user info will contain InsertedKey, UpdatedKey, InvalidatedKey,
- * and DeletedKey (keys are defined in EOObjectStore), however
- * the objects in the corresponding Lists will be the actual
- * objects, not their ids.
- */
- public static final String
- ObjectsChangedInEditingContextNotification =
- "EOObjectsChangedInEditingContextNotification";
-
- /**
- * The default run loop ordering processes recent changes
- * before delayed observers are notified and before dispatching
- * the AWT event queue.
- */
- public static int
- EditingContextFlushChangesRunLoopOrdering = 300000;
-
- private static NSSelector runLaterSelector =
- new NSSelector( "flushRecentChanges",
- new Class[] { Object.class } );
-
- private static EOObjectStore defaultParentObjectStore = null;
- private static double defaultFetchTimestampLag = 0;
- private static boolean retainsRegisteredObjects = true;
-
- private EOObjectStore parentStore;
- private WeakReference delegate;
- private WeakReference messageHandler;
- private List editorSet;
- private double fetchTimestamp;
- private boolean lockBeforeModify;
- private boolean propagateDeletesAfterEvent;
- private boolean stopValidationAfterError;
- private NSMutableArray insertedObjects;
- private NSMutableArray insertedObjectsBuffer;
- private NSArray insertedObjectsProxy;
- private NSMutableArray updatedObjects;
- private NSMutableArray updatedObjectsBuffer;
- private NSArray updatedObjectsProxy;
- private NSMutableArray deletedObjects;
- private NSMutableArray deletedObjectsBuffer;
- private NSArray deletedObjectsProxy;
- private NSMutableArray deletedIDsBuffer;
- private NSMutableArray invalidatedObjectsBuffer;
- private NSMutableArray invalidatedIDsBuffer;
- private Registrar registrar;
+ * EOEditingContext provides transactional support for fetching, editing, and
+ * committing changes made on a collection of objects to a parent object store.
+ * <br>
+ * <br>
+ *
+ * EOEditingContext is itself a subclass of EOObjectStore, and this means that
+ * EOEditingContexts can use other EOEditingContexts as their parent. However,
+ * there still must exist an EOObjectStore as the root of the editing hierarchy
+ * that can maintain persistent state.
+ *
+ * @author michael@mpowers.net
+ * @author $Author: cgruber $
+ * @version $Revision: 894 $
+ */
+public class EOEditingContext extends EOObjectStore implements EOObserving {
+ /**
+ * Key for the NSNotification posted after this editing context saves changes.
+ * Object of the notification will be this editing context, and user info will
+ * contain InsertedKey, UpdatedKey, and DeletedKey (keys are defined in
+ * EOObjectStore).
+ */
+ public static final String EditingContextDidSaveChangesNotification = "EOEditingContextDidSaveChangesNotification";
+
+ /**
+ * Key for the NSNotification posted after this editing context observes
+ * changes. Object of the notification will be this editing context, and user
+ * info will contain InsertedKey, UpdatedKey, InvalidatedKey, and DeletedKey
+ * (keys are defined in EOObjectStore), however the objects in the corresponding
+ * Lists will be the actual objects, not their ids.
+ */
+ public static final String ObjectsChangedInEditingContextNotification = "EOObjectsChangedInEditingContextNotification";
+
+ /**
+ * The default run loop ordering processes recent changes before delayed
+ * observers are notified and before dispatching the AWT event queue.
+ */
+ public static int EditingContextFlushChangesRunLoopOrdering = 300000;
+
+ private static NSSelector runLaterSelector = new NSSelector("flushRecentChanges", new Class[] { Object.class });
+
+ private static EOObjectStore defaultParentObjectStore = null;
+ private static double defaultFetchTimestampLag = 0;
+ private static boolean retainsRegisteredObjects = true;
+
+ private EOObjectStore parentStore;
+ private WeakReference delegate;
+ private WeakReference messageHandler;
+ private List editorSet;
+ private double fetchTimestamp;
+ private boolean lockBeforeModify;
+ private boolean propagateDeletesAfterEvent;
+ private boolean stopValidationAfterError;
+ private NSMutableArray insertedObjects;
+ private NSMutableArray insertedObjectsBuffer;
+ private NSArray insertedObjectsProxy;
+ private NSMutableArray updatedObjects;
+ private NSMutableArray updatedObjectsBuffer;
+ private NSArray updatedObjectsProxy;
+ private NSMutableArray deletedObjects;
+ private NSMutableArray deletedObjectsBuffer;
+ private NSArray deletedObjectsProxy;
+ private NSMutableArray deletedIDsBuffer;
+ private NSMutableArray invalidatedObjectsBuffer;
+ private NSMutableArray invalidatedIDsBuffer;
+ private Registrar registrar;
// private UndoManager undoManager;
- // so we don't have to trouble EOObserverCenter
- private boolean ignoreChanges;
-
- // for delayed handling of processRecentChanges
- private boolean willRunLater;
-
- // for handling of notifications posted
- // while we're in the saveChanges method
- private boolean isInvalidating;
-
- // for i18n or other customization
- static protected String MessageChangeConflict =
- "Another user changed an object you are editing: ";
-
- /**
- * Default constructor creates a new editing context
- * that uses the default object store. If the default
- * object store has not been set, an exception is thrown.
- */
- public EOEditingContext()
- {
- this( defaultParentObjectStore() );
- }
-
- /**
- * Creates a new editing context that uses the specified
- * object store as its parent object store.
- */
- public EOEditingContext( EOObjectStore anObjectStore )
- {
- if ( anObjectStore == null )
- {
- throw new IllegalArgumentException(
- "A parent object store must be specified." );
- }
-
- parentStore = anObjectStore;
- delegate = null;
- messageHandler = null;
- editorSet = new LinkedList();
- fetchTimestamp = 0;
- lockBeforeModify = false;
- propagateDeletesAfterEvent = true;
- stopValidationAfterError = true;
- insertedObjects = new NSMutableArray();
- insertedObjectsBuffer = new NSMutableArray();
- insertedObjectsProxy = NSArray.arrayBackedByList( insertedObjects );
- updatedObjects = new NSMutableArray();
- updatedObjectsBuffer = new NSMutableArray();
- updatedObjectsProxy = NSArray.arrayBackedByList( updatedObjects );
- deletedObjects = new NSMutableArray();
- deletedObjectsBuffer = new NSMutableArray();
- deletedObjectsProxy = NSArray.arrayBackedByList( deletedObjects );
- deletedIDsBuffer = new NSMutableArray();
- invalidatedObjectsBuffer = new NSMutableArray();
- invalidatedIDsBuffer = new NSMutableArray();
-
- if ( instancesRetainRegisteredObjects() )
- {
- registrar = new Registrar( this );
- }
- else
- {
- registrar = new WeakRegistrar( this );
- }
-
- ignoreChanges = false;
- willRunLater = false;
- isInvalidating = false;
-
- // create undo manager
- //TODO: this should be NSUndoManager
+ // so we don't have to trouble EOObserverCenter
+ private boolean ignoreChanges;
+
+ // for delayed handling of processRecentChanges
+ private boolean willRunLater;
+
+ // for handling of notifications posted
+ // while we're in the saveChanges method
+ private boolean isInvalidating;
+
+ // for i18n or other customization
+ static protected String MessageChangeConflict = "Another user changed an object you are editing: ";
+
+ /**
+ * Default constructor creates a new editing context that uses the default
+ * object store. If the default object store has not been set, an exception is
+ * thrown.
+ */
+ public EOEditingContext() {
+ this(defaultParentObjectStore());
+ }
+
+ /**
+ * Creates a new editing context that uses the specified object store as its
+ * parent object store.
+ */
+ public EOEditingContext(EOObjectStore anObjectStore) {
+ if (anObjectStore == null) {
+ throw new IllegalArgumentException("A parent object store must be specified.");
+ }
+
+ parentStore = anObjectStore;
+ delegate = null;
+ messageHandler = null;
+ editorSet = new LinkedList();
+ fetchTimestamp = 0;
+ lockBeforeModify = false;
+ propagateDeletesAfterEvent = true;
+ stopValidationAfterError = true;
+ insertedObjects = new NSMutableArray();
+ insertedObjectsBuffer = new NSMutableArray();
+ insertedObjectsProxy = NSArray.arrayBackedByList(insertedObjects);
+ updatedObjects = new NSMutableArray();
+ updatedObjectsBuffer = new NSMutableArray();
+ updatedObjectsProxy = NSArray.arrayBackedByList(updatedObjects);
+ deletedObjects = new NSMutableArray();
+ deletedObjectsBuffer = new NSMutableArray();
+ deletedObjectsProxy = NSArray.arrayBackedByList(deletedObjects);
+ deletedIDsBuffer = new NSMutableArray();
+ invalidatedObjectsBuffer = new NSMutableArray();
+ invalidatedIDsBuffer = new NSMutableArray();
+
+ if (instancesRetainRegisteredObjects()) {
+ registrar = new Registrar(this);
+ } else {
+ registrar = new WeakRegistrar(this);
+ }
+
+ ignoreChanges = false;
+ willRunLater = false;
+ isInvalidating = false;
+
+ // create undo manager
+ // TODO: this should be NSUndoManager
// undoManager = new UndoManager();
- // register for notifications
- NSSelector handleNotification =
- new NSSelector( "handleNotification",
- new Class[] { NSNotification.class } );
- // any from parent store
- NSNotificationCenter.defaultCenter().addObserver(
- this, handleNotification, null, parentStore );
- // global id change from any
- NSNotificationCenter.defaultCenter().addObserver(
- this, handleNotification, EOGlobalID.GlobalIDChangedNotification, null );
+ // register for notifications
+ NSSelector handleNotification = new NSSelector("handleNotification", new Class[] { NSNotification.class });
+ // any from parent store
+ NSNotificationCenter.defaultCenter().addObserver(this, handleNotification, null, parentStore);
+ // global id change from any
+ NSNotificationCenter.defaultCenter().addObserver(this, handleNotification,
+ EOGlobalID.GlobalIDChangedNotification, null);
//new net.wotonomy.ui.swing.NotificationInspector( null, parentStore );
- }
-
- /**
- * Registers the specified object as an editor for this
- * context. The object is expected to implement
- * EOEditingContext.Editor.
- */
- public void addEditor ( Object anEditor )
- {
- if ( anEditor == null ) return;
- editorSet.add( new WeakReference( anEditor ) );
- }
-
- /**
- * Returns a read-only List of objects associated with the object
- * with the specified id for the specified property
- * relationship, or may return a placeholder array that
- * will defer the fetch until needed (aka an array fault).
- * All objects must be registered in the specified editing context.
- * This implementation calls to its parent object store's
- * implementation if the requested source object is not
- * registered in this editing context.
- * The specified relationship key must produce a result of
- * type Collection for the source object or an exception is thrown.
- */
- public NSArray arrayFaultWithSourceGlobalID (
- EOGlobalID aGlobalID,
- String aRelationshipKey,
- EOEditingContext aContext )
- {
- NSArray result = null;
- Object source = registrar.objectForGlobalID( aGlobalID );
-
- // if not registered in our context
- if ( source == null )
- {
- // get the object registered into our context
- result = parentStore.arrayFaultWithSourceGlobalID(
- aGlobalID, aRelationshipKey, this );
- }
- else // source is registered in our context
- {
- // get existing value
- Object value;
- if ( source instanceof EOKeyValueCoding )
- {
- value = ((EOKeyValueCoding)source).storedValueForKey(
- aRelationshipKey );
- }
- else // handle directly
- {
- value = EOKeyValueCodingSupport.storedValueForKey(
- source, aRelationshipKey );
- }
-
- if ( value == null )
- {
- // do the same as if the source was null
- result = parentStore.arrayFaultWithSourceGlobalID(
- aGlobalID, aRelationshipKey, this );
- }
- else
- if ( value instanceof NSArray )
- {
- result = (NSArray) value;
- }
- else // not NSArray
- if ( value instanceof Collection )
- {
- // convert to NSArray
- result = new NSArray( (Collection) value );
- }
- else
- {
- throw new WotonomyException(
- "Relationship key did not return a collection: "
- + aGlobalID + " : " + aRelationshipKey );
- }
- }
-
- // if our context is not the specified context
- if ( aContext != this )
- {
- result = (NSArray) clone( this, result, aContext );
- }
-
- return result;
- }
-
- /**
- * Returns a snapshot of the specified object as it
- * existed when it was last read or committed to the
- * parent object store.
- */
- public NSDictionary committedSnapshotForObject (
- Object anObject )
- {
- byte[] snapshot = (byte[])
- registrar.getCommitSnapshot( anObject );
- if ( snapshot == null )
- {
- // this object not modified: take a current snapshot
- snapshot = takeSnapshot( anObject );
- }
- return convertSnapshotToDictionary( snapshot );
- }
-
- /**
- * Returns a snapshot of the specified object as it
- * existed before the edits triggered by the current
- * event loop were processed.
- */
- public NSDictionary currentEventSnapshotForObject (
- Object anObject )
- {
- byte[] result = (byte[])
- registrar.getCurrentSnapshot( anObject );
- if ( result == null )
- {
- return committedSnapshotForObject( anObject );
- }
- return convertSnapshotToDictionary( result );
- }
-
- /**
- * Returns the delegate for this editing context,
- * or null if no delegate has been set.
- */
- public Object delegate ()
- {
- if ( delegate == null ) return null;
- return delegate.get();
- }
-
- /**
- * Deletes the specified object from this editing context.
- * The editing context marks the object as deleted and
- * will notify the parent store when changes are committed.
- */
- public void deleteObject (
- Object anObject )
- {
- willChange();
+ }
+
+ /**
+ * Registers the specified object as an editor for this context. The object is
+ * expected to implement EOEditingContext.Editor.
+ */
+ public void addEditor(Object anEditor) {
+ if (anEditor == null)
+ return;
+ editorSet.add(new WeakReference(anEditor));
+ }
+
+ /**
+ * Returns a read-only List of objects associated with the object with the
+ * specified id for the specified property relationship, or may return a
+ * placeholder array that will defer the fetch until needed (aka an array
+ * fault). All objects must be registered in the specified editing context. This
+ * implementation calls to its parent object store's implementation if the
+ * requested source object is not registered in this editing context. The
+ * specified relationship key must produce a result of type Collection for the
+ * source object or an exception is thrown.
+ */
+ public NSArray arrayFaultWithSourceGlobalID(EOGlobalID aGlobalID, String aRelationshipKey,
+ EOEditingContext aContext) {
+ NSArray result = null;
+ Object source = registrar.objectForGlobalID(aGlobalID);
+
+ // if not registered in our context
+ if (source == null) {
+ // get the object registered into our context
+ result = parentStore.arrayFaultWithSourceGlobalID(aGlobalID, aRelationshipKey, this);
+ } else // source is registered in our context
+ {
+ // get existing value
+ Object value;
+ if (source instanceof EOKeyValueCoding) {
+ value = ((EOKeyValueCoding) source).storedValueForKey(aRelationshipKey);
+ } else // handle directly
+ {
+ value = EOKeyValueCodingSupport.storedValueForKey(source, aRelationshipKey);
+ }
+
+ if (value == null) {
+ // do the same as if the source was null
+ result = parentStore.arrayFaultWithSourceGlobalID(aGlobalID, aRelationshipKey, this);
+ } else if (value instanceof NSArray) {
+ result = (NSArray) value;
+ } else // not NSArray
+ if (value instanceof Collection) {
+ // convert to NSArray
+ result = new NSArray((Collection) value);
+ } else {
+ throw new WotonomyException(
+ "Relationship key did not return a collection: " + aGlobalID + " : " + aRelationshipKey);
+ }
+ }
+
+ // if our context is not the specified context
+ if (aContext != this) {
+ result = (NSArray) clone(this, result, aContext);
+ }
+
+ return result;
+ }
+
+ /**
+ * Returns a snapshot of the specified object as it existed when it was last
+ * read or committed to the parent object store.
+ */
+ public NSDictionary committedSnapshotForObject(Object anObject) {
+ byte[] snapshot = (byte[]) registrar.getCommitSnapshot(anObject);
+ if (snapshot == null) {
+ // this object not modified: take a current snapshot
+ snapshot = takeSnapshot(anObject);
+ }
+ return convertSnapshotToDictionary(snapshot);
+ }
+
+ /**
+ * Returns a snapshot of the specified object as it existed before the edits
+ * triggered by the current event loop were processed.
+ */
+ public NSDictionary currentEventSnapshotForObject(Object anObject) {
+ byte[] result = (byte[]) registrar.getCurrentSnapshot(anObject);
+ if (result == null) {
+ return committedSnapshotForObject(anObject);
+ }
+ return convertSnapshotToDictionary(result);
+ }
+
+ /**
+ * Returns the delegate for this editing context, or null if no delegate has
+ * been set.
+ */
+ public Object delegate() {
+ if (delegate == null)
+ return null;
+ return delegate.get();
+ }
+
+ /**
+ * Deletes the specified object from this editing context. The editing context
+ * marks the object as deleted and will notify the parent store when changes are
+ * committed.
+ */
+ public void deleteObject(Object anObject) {
+ willChange();
int i;
// remove from added objects if necessary
- i = insertedObjects.indexOfIdenticalObject( anObject );
- if ( i != NSArray.NotFound )
+ i = insertedObjects.indexOfIdenticalObject(anObject);
+ if (i != NSArray.NotFound) {
+ insertedObjects.removeObjectAtIndex(i);
+
+ // if in the inserted objects buffer
+ int index = insertedObjectsBuffer.indexOfIdenticalObject(anObject);
+ if (index != NSArray.NotFound) {
+ // remove from inserted objects buffer
+ insertedObjectsBuffer.removeObjectAtIndex(index);
+ }
+
+ // now forget the object ever existed.
+ forgetObject(anObject);
+
+ // we're done
+ return;
+ } else // otherwise add to deleted objects list
{
- insertedObjects.removeObjectAtIndex( i );
-
- // if in the inserted objects buffer
- int index = insertedObjectsBuffer.indexOfIdenticalObject( anObject );
- if ( index != NSArray.NotFound )
- {
- // remove from inserted objects buffer
- insertedObjectsBuffer.removeObjectAtIndex( index );
- }
-
- // now forget the object ever existed.
- forgetObject( anObject );
-
- // we're done
- return;
- }
- else // otherwise add to deleted objects list
- {
- deletedObjects.addObject( anObject );
+ deletedObjects.addObject(anObject);
}
-
+
// remove from updated objects if necessary
- i = updatedObjects.indexOfIdenticalObject( anObject );
- if ( i != NSArray.NotFound )
- {
- updatedObjects.removeObjectAtIndex( i );
- }
-
- // add to buffer
- deletedObjectsBuffer.addObject( anObject );
- deletedIDsBuffer.addObject( globalIDForObject( anObject ) );
- }
-
- /**
- * Returns a read-only List of all objects marked as deleted
- * in this editing context.
- */
- public NSArray deletedObjects ()
- {
- return deletedObjectsProxy;
- }
-
- /**
- * Called by child editing contexts when they no longer
- * need to track the specified id.
- * This implementation forwards the call to the parent store.
- */
- public void editingContextDidForgetObjectWithGlobalID (
- EOEditingContext aContext,
- EOGlobalID aGlobalID )
- {
- parentStore.editingContextDidForgetObjectWithGlobalID(
- aContext, aGlobalID );
- }
-
- /**
- * Returns a read-only List of registered editors of this
- * editing context.
- */
- public NSArray editors ()
- {
- NSMutableArray result = new NSMutableArray();
- Object o;
- Iterator i = editorSet.iterator();
- while ( i.hasNext() )
- {
- o = ((WeakReference)i.next()).get();
- if ( o != null )
- {
- result.addObject( o );
- }
- else
- {
- i.remove();
- }
- }
- return result;
- }
-
-/*
- public static void encodeObjectWithCoder (
- Object anObject,
- NSCoder aCoder )
- {
- throw new net.wotonomy.util.WotonomyException("Not implemented yet.");
- }
-*/
+ i = updatedObjects.indexOfIdenticalObject(anObject);
+ if (i != NSArray.NotFound) {
+ updatedObjects.removeObjectAtIndex(i);
+ }
- /**
- * Returns the object for the specified id.
- * If the object is registered in in this context
- * but not in the specified context,
- * this implementation will create a copy of the object
- * and register it in the specified context.
- * Otherwise it will forward the call to the parent
- * object store.
- */
- public /*EOEnterpriseObject*/ Object faultForGlobalID (
- EOGlobalID aGlobalID,
- EOEditingContext aContext )
- {
- Object result = registrar.objectForGlobalID( aGlobalID );
-
- // if not registered in our context
- if ( result == null )
- {
- // get the object registered into our context
- result = parentStore.faultForGlobalID( aGlobalID, this );
- }
-
- // if our context is not the specified context
- if ( aContext != this )
- {
- result = registerClone( aGlobalID, this, result, aContext );
- }
-
- return result;
- }
-
- /**
- * Returns a fault representing an object of
- * the specified entity type with values from
- * the specified dictionary.
- * This implementation calls faultForRawRow
- * on the parent store.
- */
- public Object faultForRawRow (
- Map aDictionary,
- String anEntityName )
- {
- return parentStore.faultForRawRow(
- aDictionary, anEntityName, this );
- }
-
- /**
- * 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.
- * This implementation forwards the call to
- * the parent store.
- */
- public /*EOEnterpriseObject*/ Object faultForRawRow (
- Map aDictionary,
- String anEntityName,
- EOEditingContext aContext )
- {
- return parentStore.faultForRawRow(
- aDictionary, anEntityName, aContext );
- }
-
- /**
- * Returns the fetch timestamp for this editing context.
- */
- public double fetchTimestamp ()
- {
- return fetchTimestamp;
- }
-
- /**
- * Unregisters the specified object from this editing context,
- * removing all references to it. Use this method to remove
- * an object from the context without marking it for deletion.
- */
- public void forgetObject (
- Object anObject )
- {
- EOGlobalID id = registrar.globalIDForObject( anObject );
- if ( id == null )
- {
- System.err.println(
- "EOEditingContext.forgetObject: not registered: " + anObject );
- return;
- }
-
- // unregister object
- registrar.forgetObject( anObject );
-
- // remove from all, inserted, updated, and deleted lists
- int index;
- index = updatedObjects.indexOfIdenticalObject( anObject );
- if ( index != NSArray.NotFound )
- {
- updatedObjects.removeObjectAtIndex( index );
- }
- index = insertedObjects.indexOfIdenticalObject( anObject );
- if ( index != NSArray.NotFound )
- {
- insertedObjects.removeObjectAtIndex( index );
- return;
- }
- index = deletedObjects.indexOfIdenticalObject( anObject );
- if ( index != NSArray.NotFound )
- {
- deletedObjects.removeObjectAtIndex( index );
- return;
- }
-
- // notify parent context
- parentStore.editingContextDidForgetObjectWithGlobalID( this, id );
- }
-
- /**
- * Returns the id for the specified object, or null
- * if the object is not registered in this context.
- */
- public EOGlobalID globalIDForObject (
- Object anObject )
- {
- return registrar.globalIDForObject( anObject );
- }
-
- /**
- * Returns an array of ids for an array of objects.
- */
- private NSArray globalIDsForObjects(
- List anObjectList )
- {
- NSMutableArray result = new NSMutableArray();
- Iterator it = anObjectList.iterator();
- while ( it.hasNext() )
- {
- result.add( globalIDForObject( it.next() ) );
- }
- return result;
- }
-
- /**
- * Returns whether this editing context has changes that
- * have not yet been committed to the parent object store.
- */
- public boolean hasChanges ()
- {
- if ( updatedObjects.count() > 0 ) return true;
- if ( insertedObjects.count() > 0 ) return true;
- if ( deletedObjects.count() > 0 ) return true;
- return false;
- }
-
- /**
- * Given a newly instantiated object, this method
- * initializes its properties to values appropriate
- * for the specified id. The object should already
- * belong to the specified editing context.
- * This method is called to populate faults.
- * This implementation will try to apply the values
- * from an object with a matching id in this editing
- * context if possible, calling to the parent object
- * store only if such an object is not found.
- */
- public void initializeObject (
- /*EOEnterpriseObject*/ Object anObject,
- EOGlobalID aGlobalID,
- EOEditingContext aContext )
- {
- Object existingObject = registrar.objectForGlobalID( aGlobalID );
-
- // if not registered in our context
- if ( existingObject == null )
- {
- // get the object registered into our context
- existingObject = parentStore.faultForGlobalID( aGlobalID, this );
- }
-
- if ( aContext == this )
- {
- // initialize the object
- parentStore.initializeObject(
- /*(EOEnterpriseObject)*/existingObject, aGlobalID, this );
- }
- else // ( aContext != this )
- {
- // translates child relationships
- copy( this, existingObject, aContext, anObject );
- }
-
- aContext.registrar.setCommitSnapshot( anObject, null );
- aContext.registrar.setCurrentSnapshot( anObject, null );
- }
-
- /**
- * Inserts the specified object into this editing context.
- * This implementation calls insertObjectWithGlobalID
- * with an EOTemporaryGlobalID.
- */
- public void insertObject ( Object anObject )
- {
- insertObjectWithGlobalID(
- anObject, new EOTemporaryGlobalID() );
- }
-
- /**
- * Inserts the specified object into this editing context
- * with the specified id, which is expected to be a
- * temporary id.
- */
- public void insertObjectWithGlobalID (
- Object anObject,
- EOGlobalID aGlobalID )
- {
- willChange();
-
- // if this object was marked for deletion
- int index = deletedObjects.indexOfIdenticalObject( anObject );
- if ( index != NSArray.NotFound )
- {
- // don't need to re-register: just update the lists
-
- // remove from deleted list
- deletedObjects.removeObjectAtIndex( index );
-
- // if in the deleted ids buffer
- index = deletedIDsBuffer.indexOfIdenticalObject( anObject );
- if ( index != NSArray.NotFound )
- {
- // remove from deleted ids buffer
- deletedIDsBuffer.removeObjectAtIndex( index );
- }
-
- // if in the deleted objects buffer
- index = deletedObjectsBuffer.indexOfIdenticalObject( anObject );
- if ( index != NSArray.NotFound )
- {
- // remove from deleted objects buffer
- deletedObjectsBuffer.removeObjectAtIndex( index );
- }
- else // not in the deleted objects buffer
- {
- // add to the inserted objects buffer
- insertedObjectsBuffer.addObject( anObject );
- }
-
- // we're done
- return;
- }
-
- // make sure object is not already in editing context
- if ( objectForGlobalID( aGlobalID ) != null )
- {
- throw new WotonomyException(
- "Tried to insert but object was already registered:"
- + aGlobalID );
- }
-
- // register object
- recordObject( anObject, aGlobalID );
-
- // add to inserted list
- insertedObjects.addObject( anObject );
- // add to buffer
- insertedObjectsBuffer.addObject( anObject );
- }
-
- /**
- * Returns a read-only List of the objects that have been
- * inserted into this editing context.
- */
- public NSArray insertedObjects ()
- {
- return insertedObjectsProxy;
- }
-
- /**
- * Turn all objects in this editing context into faults,
- * so that they will be fetched the next time they are
- * accessed, and calls invalidateObjectsWithGlobalIDs
- * on the parent object store.
- */
- public void invalidateAllObjects ()
- {
- // register change so processRecentChanges is called
- willChange();
-
- invalidateAllObjectsQuietly();
-
- // post notification
- NSNotificationCenter.defaultCenter().postNotification(
- new NSNotification(
- InvalidatedAllObjectsInStoreNotification, this ) );
- }
-
- /**
- * Only refaults all objects, does not notify will change
- * nor post notification, but does call parent store.
- * Called by invalidateAllObjects() and handleNotification().
- */
- private void invalidateAllObjectsQuietly()
- {
- // remember the ids
- NSMutableArray ids = new NSMutableArray( registrar.registeredGlobalIDs() );
-
- // track of discarded IDs (from inserted objects)
- NSMutableArray discardedIDs = new NSMutableArray();
-
- // refault all objects
- EOGlobalID id;
- Object o;
- Enumeration e = ids.objectEnumerator();
- while ( e.hasMoreElements() )
- {
- id = (EOGlobalID) e.nextElement();
- o = objectForGlobalID( id );
-
- // some objects may have been manually discarded
- if ( o != null )
- {
- // don't refault newly inserted objects
- if ( insertedObjects.indexOfIdenticalObject( o ) == NSArray.NotFound )
- {
- refaultObject( o, id, this );
- }
- else
- {
- // discard inserted objects
- forgetObject( o );
- discardedIDs.add( id );
- }
- invalidatedObjectsBuffer.add( o );
- }
- invalidatedIDsBuffer.add( id );
- }
- ids.removeAll( discardedIDs );
-
- // call to parent store (should call this after posting instead?)
- isInvalidating = true;
- parentStore.invalidateObjectsWithGlobalIDs( ids );
- isInvalidating = false;
- }
-
- /**
- * Turns the objects with the specified ids into faults,
- * so that they will be fetched the next time they are
- * accessed, and forwards the call to the parent object store.
- */
- public void invalidateObjectsWithGlobalIDs (
- List anArray )
- {
- // register change so processRecentChanges is called
- willChange();
-
- // call to parent to invalidate objects
- parentStore.invalidateObjectsWithGlobalIDs( anArray );
-
- Object o;
- EOGlobalID id;
- Iterator it = anArray.iterator();
- while ( it.hasNext() )
- {
- id = (EOGlobalID) it.next();
- if ( id != null )
- {
- o = objectForGlobalID( id );
- if ( o != null )
- {
- Object result = notifyDelegate(
- "editingContextShouldInvalidateObject",
- new Class[] { EOEditingContext.class, Object.class, EOGlobalID.class },
- new Object[] { this, o, id } );
- if ( ( result == null ) || ( Boolean.TRUE.equals( result ) ) )
- {
- // refault the object
- refaultObject( o, id, this );
- invalidatedObjectsBuffer.add( o );
- invalidatedIDsBuffer.add( id );
- }
- }
- }
- else
- {
- throw new WotonomyException(
- "Attempted to invalidate a null global id: " + anArray );
- }
- }
-
- }
-/*
- public boolean invalidatesObjectsWhenFinalized ( )
- {
- throw new net.wotonomy.util.WotonomyException("Not implemented yet.");
- }
-
- public boolean invalidatesObjectsWhenFreed ( )
- {
- throw new net.wotonomy.util.WotonomyException("Not implemented yet.");
- }
-*/
- /**
- * Returns whether the object referenced by the
- * specified id is locked.
- * This implementation simply forwards the call to
- * the parent object store.
- */
- public boolean isObjectLockedWithGlobalID (
- EOGlobalID aGlobalID,
- EOEditingContext aContext)
- {
- return parentStore.isObjectLockedWithGlobalID(
- aGlobalID, aContext );
- }
-
-/*
- public void lock ()
- {
- throw new net.wotonomy.util.WotonomyException("Not implemented yet.");
- }
-*/
- /**
- * Locks the specified object in this editing context
- * by calling lockObjectWithGlobalID on the parent store.
- */
- public void lockObject (
- Object anObject )
- {
- parentStore.lockObjectWithGlobalID(
- globalIDForObject( anObject ), this );
- }
-
- /**
- * Locks the object referenced by the specified id
- * in the specified editing context.
- * This implementation simply forwards the call to
- * the parent object store.
- */
- public void lockObjectWithGlobalID (
- EOGlobalID aGlobalID,
- EOEditingContext aContext)
- {
- parentStore.lockObjectWithGlobalID(
- aGlobalID, aContext );
- }
-
- /**
- * Returns whether this editing context attempts to
- * lock objects when they are first modified.
- */
- public boolean locksObjectsBeforeFirstModification ()
- {
- return lockBeforeModify;
- }
-
- /**
- * Returns the message handler for this editing context,
- * or null if no message handler has been set.
- */
- public Object messageHandler ()
- {
- if ( messageHandler == null ) return null;
- return messageHandler.get();
- }
-
- /**
- * Returns the object registered in this editing context
- * for the specified id, or null if that id is not
- * registered.
- */
- public Object objectForGlobalID (
- EOGlobalID aGlobalID )
- {
- return registrar.objectForGlobalID( aGlobalID );
- }
-
- /**
- * Returns a read-only 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 aRelationshipKey,
- EOEditingContext aContext )
- {
+ // add to buffer
+ deletedObjectsBuffer.addObject(anObject);
+ deletedIDsBuffer.addObject(globalIDForObject(anObject));
+ }
+
+ /**
+ * Returns a read-only List of all objects marked as deleted in this editing
+ * context.
+ */
+ public NSArray deletedObjects() {
+ return deletedObjectsProxy;
+ }
+
+ /**
+ * Called by child editing contexts when they no longer need to track the
+ * specified id. This implementation forwards the call to the parent store.
+ */
+ public void editingContextDidForgetObjectWithGlobalID(EOEditingContext aContext, EOGlobalID aGlobalID) {
+ parentStore.editingContextDidForgetObjectWithGlobalID(aContext, aGlobalID);
+ }
+
+ /**
+ * Returns a read-only List of registered editors of this editing context.
+ */
+ public NSArray editors() {
+ NSMutableArray result = new NSMutableArray();
+ Object o;
+ Iterator i = editorSet.iterator();
+ while (i.hasNext()) {
+ o = ((WeakReference) i.next()).get();
+ if (o != null) {
+ result.addObject(o);
+ } else {
+ i.remove();
+ }
+ }
+ return result;
+ }
+
+ /*
+ * public static void encodeObjectWithCoder ( Object anObject, NSCoder aCoder )
+ * { throw new net.wotonomy.util.WotonomyException("Not implemented yet."); }
+ */
+
+ /**
+ * Returns the object for the specified id. If the object is registered in in
+ * this context but not in the specified context, this implementation will
+ * create a copy of the object and register it in the specified context.
+ * Otherwise it will forward the call to the parent object store.
+ */
+ public /* EOEnterpriseObject */ Object faultForGlobalID(EOGlobalID aGlobalID, EOEditingContext aContext) {
+ Object result = registrar.objectForGlobalID(aGlobalID);
+
+ // if not registered in our context
+ if (result == null) {
+ // get the object registered into our context
+ result = parentStore.faultForGlobalID(aGlobalID, this);
+ }
+
+ // if our context is not the specified context
+ if (aContext != this) {
+ result = registerClone(aGlobalID, this, result, aContext);
+ }
+
+ return result;
+ }
+
+ /**
+ * Returns a fault representing an object of the specified entity type with
+ * values from the specified dictionary. This implementation calls
+ * faultForRawRow on the parent store.
+ */
+ public Object faultForRawRow(Map aDictionary, String anEntityName) {
+ return parentStore.faultForRawRow(aDictionary, anEntityName, this);
+ }
+
+ /**
+ * 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. This implementation forwards the call to the
+ * parent store.
+ */
+ public /* EOEnterpriseObject */ Object faultForRawRow(Map aDictionary, String anEntityName,
+ EOEditingContext aContext) {
+ return parentStore.faultForRawRow(aDictionary, anEntityName, aContext);
+ }
+
+ /**
+ * Returns the fetch timestamp for this editing context.
+ */
+ public double fetchTimestamp() {
+ return fetchTimestamp;
+ }
+
+ /**
+ * Unregisters the specified object from this editing context, removing all
+ * references to it. Use this method to remove an object from the context
+ * without marking it for deletion.
+ */
+ public void forgetObject(Object anObject) {
+ EOGlobalID id = registrar.globalIDForObject(anObject);
+ if (id == null) {
+ System.err.println("EOEditingContext.forgetObject: not registered: " + anObject);
+ return;
+ }
+
+ // unregister object
+ registrar.forgetObject(anObject);
+
+ // remove from all, inserted, updated, and deleted lists
+ int index;
+ index = updatedObjects.indexOfIdenticalObject(anObject);
+ if (index != NSArray.NotFound) {
+ updatedObjects.removeObjectAtIndex(index);
+ }
+ index = insertedObjects.indexOfIdenticalObject(anObject);
+ if (index != NSArray.NotFound) {
+ insertedObjects.removeObjectAtIndex(index);
+ return;
+ }
+ index = deletedObjects.indexOfIdenticalObject(anObject);
+ if (index != NSArray.NotFound) {
+ deletedObjects.removeObjectAtIndex(index);
+ return;
+ }
+
+ // notify parent context
+ parentStore.editingContextDidForgetObjectWithGlobalID(this, id);
+ }
+
+ /**
+ * Returns the id for the specified object, or null if the object is not
+ * registered in this context.
+ */
+ public EOGlobalID globalIDForObject(Object anObject) {
+ return registrar.globalIDForObject(anObject);
+ }
+
+ /**
+ * Returns an array of ids for an array of objects.
+ */
+ private NSArray globalIDsForObjects(List anObjectList) {
+ NSMutableArray result = new NSMutableArray();
+ Iterator it = anObjectList.iterator();
+ while (it.hasNext()) {
+ result.add(globalIDForObject(it.next()));
+ }
+ return result;
+ }
+
+ /**
+ * Returns whether this editing context has changes that have not yet been
+ * committed to the parent object store.
+ */
+ public boolean hasChanges() {
+ if (updatedObjects.count() > 0)
+ return true;
+ if (insertedObjects.count() > 0)
+ return true;
+ if (deletedObjects.count() > 0)
+ return true;
+ return false;
+ }
+
+ /**
+ * Given a newly instantiated object, this method initializes its properties to
+ * values appropriate for the specified id. The object should already belong to
+ * the specified editing context. This method is called to populate faults. This
+ * implementation will try to apply the values from an object with a matching id
+ * in this editing context if possible, calling to the parent object store only
+ * if such an object is not found.
+ */
+ public void initializeObject(/* EOEnterpriseObject */ Object anObject, EOGlobalID aGlobalID,
+ EOEditingContext aContext) {
+ Object existingObject = registrar.objectForGlobalID(aGlobalID);
+
+ // if not registered in our context
+ if (existingObject == null) {
+ // get the object registered into our context
+ existingObject = parentStore.faultForGlobalID(aGlobalID, this);
+ }
+
+ if (aContext == this) {
+ // initialize the object
+ parentStore.initializeObject(/* (EOEnterpriseObject) */existingObject, aGlobalID, this);
+ } else // ( aContext != this )
+ {
+ // translates child relationships
+ copy(this, existingObject, aContext, anObject);
+ }
+
+ aContext.registrar.setCommitSnapshot(anObject, null);
+ aContext.registrar.setCurrentSnapshot(anObject, null);
+ }
+
+ /**
+ * Inserts the specified object into this editing context. This implementation
+ * calls insertObjectWithGlobalID with an EOTemporaryGlobalID.
+ */
+ public void insertObject(Object anObject) {
+ insertObjectWithGlobalID(anObject, new EOTemporaryGlobalID());
+ }
+
+ /**
+ * Inserts the specified object into this editing context with the specified id,
+ * which is expected to be a temporary id.
+ */
+ public void insertObjectWithGlobalID(Object anObject, EOGlobalID aGlobalID) {
+ willChange();
+
+ // if this object was marked for deletion
+ int index = deletedObjects.indexOfIdenticalObject(anObject);
+ if (index != NSArray.NotFound) {
+ // don't need to re-register: just update the lists
+
+ // remove from deleted list
+ deletedObjects.removeObjectAtIndex(index);
+
+ // if in the deleted ids buffer
+ index = deletedIDsBuffer.indexOfIdenticalObject(anObject);
+ if (index != NSArray.NotFound) {
+ // remove from deleted ids buffer
+ deletedIDsBuffer.removeObjectAtIndex(index);
+ }
+
+ // if in the deleted objects buffer
+ index = deletedObjectsBuffer.indexOfIdenticalObject(anObject);
+ if (index != NSArray.NotFound) {
+ // remove from deleted objects buffer
+ deletedObjectsBuffer.removeObjectAtIndex(index);
+ } else // not in the deleted objects buffer
+ {
+ // add to the inserted objects buffer
+ insertedObjectsBuffer.addObject(anObject);
+ }
+
+ // we're done
+ return;
+ }
+
+ // make sure object is not already in editing context
+ if (objectForGlobalID(aGlobalID) != null) {
+ throw new WotonomyException("Tried to insert but object was already registered:" + aGlobalID);
+ }
+
+ // register object
+ recordObject(anObject, aGlobalID);
+
+ // add to inserted list
+ insertedObjects.addObject(anObject);
+ // add to buffer
+ insertedObjectsBuffer.addObject(anObject);
+ }
+
+ /**
+ * Returns a read-only List of the objects that have been inserted into this
+ * editing context.
+ */
+ public NSArray insertedObjects() {
+ return insertedObjectsProxy;
+ }
+
+ /**
+ * Turn all objects in this editing context into faults, so that they will be
+ * fetched the next time they are accessed, and calls
+ * invalidateObjectsWithGlobalIDs on the parent object store.
+ */
+ public void invalidateAllObjects() {
+ // register change so processRecentChanges is called
+ willChange();
+
+ invalidateAllObjectsQuietly();
+
+ // post notification
+ NSNotificationCenter.defaultCenter()
+ .postNotification(new NSNotification(InvalidatedAllObjectsInStoreNotification, this));
+ }
+
+ /**
+ * Only refaults all objects, does not notify will change nor post notification,
+ * but does call parent store. Called by invalidateAllObjects() and
+ * handleNotification().
+ */
+ private void invalidateAllObjectsQuietly() {
+ // remember the ids
+ NSMutableArray ids = new NSMutableArray(registrar.registeredGlobalIDs());
+
+ // track of discarded IDs (from inserted objects)
+ NSMutableArray discardedIDs = new NSMutableArray();
+
+ // refault all objects
+ EOGlobalID id;
+ Object o;
+ Enumeration e = ids.objectEnumerator();
+ while (e.hasMoreElements()) {
+ id = (EOGlobalID) e.nextElement();
+ o = objectForGlobalID(id);
+
+ // some objects may have been manually discarded
+ if (o != null) {
+ // don't refault newly inserted objects
+ if (insertedObjects.indexOfIdenticalObject(o) == NSArray.NotFound) {
+ refaultObject(o, id, this);
+ } else {
+ // discard inserted objects
+ forgetObject(o);
+ discardedIDs.add(id);
+ }
+ invalidatedObjectsBuffer.add(o);
+ }
+ invalidatedIDsBuffer.add(id);
+ }
+ ids.removeAll(discardedIDs);
+
+ // call to parent store (should call this after posting instead?)
+ isInvalidating = true;
+ parentStore.invalidateObjectsWithGlobalIDs(ids);
+ isInvalidating = false;
+ }
+
+ /**
+ * Turns the objects with the specified ids into faults, so that they will be
+ * fetched the next time they are accessed, and forwards the call to the parent
+ * object store.
+ */
+ public void invalidateObjectsWithGlobalIDs(List anArray) {
+ // register change so processRecentChanges is called
+ willChange();
+
+ // call to parent to invalidate objects
+ parentStore.invalidateObjectsWithGlobalIDs(anArray);
+
+ Object o;
+ EOGlobalID id;
+ Iterator it = anArray.iterator();
+ while (it.hasNext()) {
+ id = (EOGlobalID) it.next();
+ if (id != null) {
+ o = objectForGlobalID(id);
+ if (o != null) {
+ Object result = notifyDelegate("editingContextShouldInvalidateObject",
+ new Class[] { EOEditingContext.class, Object.class, EOGlobalID.class },
+ new Object[] { this, o, id });
+ if ((result == null) || (Boolean.TRUE.equals(result))) {
+ // refault the object
+ refaultObject(o, id, this);
+ invalidatedObjectsBuffer.add(o);
+ invalidatedIDsBuffer.add(id);
+ }
+ }
+ } else {
+ throw new WotonomyException("Attempted to invalidate a null global id: " + anArray);
+ }
+ }
+
+ }
+
+ /*
+ * public boolean invalidatesObjectsWhenFinalized ( ) { throw new
+ * net.wotonomy.util.WotonomyException("Not implemented yet."); }
+ *
+ * public boolean invalidatesObjectsWhenFreed ( ) { throw new
+ * net.wotonomy.util.WotonomyException("Not implemented yet."); }
+ */
+ /**
+ * Returns whether the object referenced by the specified id is locked. This
+ * implementation simply forwards the call to the parent object store.
+ */
+ public boolean isObjectLockedWithGlobalID(EOGlobalID aGlobalID, EOEditingContext aContext) {
+ return parentStore.isObjectLockedWithGlobalID(aGlobalID, aContext);
+ }
+
+ /*
+ * public void lock () { throw new
+ * net.wotonomy.util.WotonomyException("Not implemented yet."); }
+ */
+ /**
+ * Locks the specified object in this editing context by calling
+ * lockObjectWithGlobalID on the parent store.
+ */
+ public void lockObject(Object anObject) {
+ parentStore.lockObjectWithGlobalID(globalIDForObject(anObject), this);
+ }
+
+ /**
+ * Locks the object referenced by the specified id in the specified editing
+ * context. This implementation simply forwards the call to the parent object
+ * store.
+ */
+ public void lockObjectWithGlobalID(EOGlobalID aGlobalID, EOEditingContext aContext) {
+ parentStore.lockObjectWithGlobalID(aGlobalID, aContext);
+ }
+
+ /**
+ * Returns whether this editing context attempts to lock objects when they are
+ * first modified.
+ */
+ public boolean locksObjectsBeforeFirstModification() {
+ return lockBeforeModify;
+ }
+
+ /**
+ * Returns the message handler for this editing context, or null if no message
+ * handler has been set.
+ */
+ public Object messageHandler() {
+ if (messageHandler == null)
+ return null;
+ return messageHandler.get();
+ }
+
+ /**
+ * Returns the object registered in this editing context for the specified id,
+ * or null if that id is not registered.
+ */
+ public Object objectForGlobalID(EOGlobalID aGlobalID) {
+ return registrar.objectForGlobalID(aGlobalID);
+ }
+
+ /**
+ * Returns a read-only 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 aRelationshipKey, EOEditingContext aContext) {
//System.out.println( "EOEditingContext.objectsForSourceGlobalID: "
//+ aGlobalID + " : " + aRelationshipKey );
-
- NSArray result = null;
-
-if ( aContext == this )
-{
- throw new WotonomyException( "Assert failed: calling objectsForSourceGlobalID on ourself." );
-}
- Object source = registrar.objectForGlobalID( aGlobalID );
-
- // if not registered in our context
- if ( source == null )
- {
- // get the object registered into our context
- result = parentStore.objectsForSourceGlobalID(
- aGlobalID, aRelationshipKey, this );
- }
- else // source is registered in our context
- {
- // get existing value
- Object value;
- if ( source instanceof EOKeyValueCoding )
- {
- value = ((EOKeyValueCoding)source).storedValueForKey(
- aRelationshipKey );
- }
- else // handle directly
- {
- value = EOKeyValueCodingSupport.storedValueForKey(
- source, aRelationshipKey );
- }
-
- // if we don't have a valid value on our object
- if ( ( value == null )
- || ( ( value instanceof ArrayFault )
- && ( !((ArrayFault)value).isFetched() ) ) )
- {
- // do the same as if the source was null
- result = parentStore.objectsForSourceGlobalID(
- aGlobalID, aRelationshipKey, this );
-
- // set our value since we have it
- if ( source instanceof EOKeyValueCoding )
- {
- ((EOKeyValueCoding)source).takeStoredValueForKey(
- result, aRelationshipKey );
- }
- else // handle directly
- {
- EOKeyValueCodingSupport.takeStoredValueForKey(
- source, result, aRelationshipKey );
- }
- }
- else
- if ( ( value instanceof ArrayFault )
- && ( !((ArrayFault)value).isFetched() ) )
- {
- // do the same as if the source was null
- result = parentStore.objectsForSourceGlobalID(
- aGlobalID, aRelationshipKey, this );
- }
- else
- if ( value instanceof NSArray )
- {
- result = (NSArray) value;
- }
- else // not NSArray
- if ( value instanceof Collection )
- {
- // convert to NSArray
- result = new NSArray( (Collection) value );
- }
- else
- {
- throw new WotonomyException(
- "Relationship key did not return a collection: "
- + aGlobalID + " : " + aRelationshipKey );
- }
- }
-
- // if our context is not the specified context
- if ( aContext != this )
- {
- result = (NSArray) clone( this, result, aContext );
- }
-
- return result;
- }
-
- /**
- * Returns a read-only List of objects the meet the criteria of
- * the supplied specification. This method simply calls
- * objectsWithFetchSpecification on this editing context
- * with this editing context as the parameter.
- */
- public NSArray objectsWithFetchSpecification (
- EOFetchSpecification aFetchSpec )
- {
- return objectsWithFetchSpecification( aFetchSpec, this );
- }
-
- /**
- * Returns a read-only List of objects the meet the criteria of
- * the supplied specification. Faults are not allowed in the array.
- * If any objects are already fetched, they should not be
- * refetched. All objects should belong to the specified editing context.
- * This implementation forwards the call to the parent object
- * store, which will register each object in the specified editing
- * context only if it does not already exist.
- */
- public NSArray objectsWithFetchSpecification (
- EOFetchSpecification aFetchSpec,
- EOEditingContext aContext)
- {
- if ( aContext == this )
- {
- Object result = notifyDelegate(
- "editingContextShouldFetchObjects",
- new Class[] { EOEditingContext.class, EOFetchSpecification.class },
- new Object[] { aContext, aFetchSpec } );
- if ( result instanceof NSArray ) return (NSArray) result;
- }
- return parentStore.objectsWithFetchSpecification( aFetchSpec, aContext );
- }
-
- /**
- * Returns the parent object store for this editing context.
- * The result will not be null.
- */
- public EOObjectStore parentObjectStore ()
- {
- return parentStore;
- }
-
- /**
- * Updates the inserted, updated, and deleted objects lists,
- * and posts notifications about which objects have been changed.
- * This method is called at the end of an event loop in which
- * objects were modified. This method is additionally called
- * by saveChanges() so that any changes in the same event loop
- * will be processed correctly before calling to the parent
- * object store.
- * This implementation updates those lists immediately, but
- * only posts notifications when this method is called.
- */
- public void processRecentChanges ()
- { // System.out.println( "EOEditingContext.processRecentChanges: " + invalidatedObjectsBuffer );
-
- /*
- * This implementation actually updates those lists immediately,
- * but keeps a separate buffer of changes in the current event
- * loop for the purposes of posting a notification.
- * NOTE: to reenable buffering, uncomment lines from this method
- * body and reenable the RecentChangesObserver in the constructor.
- */
-
- // broadcast ObjectsChangedInStoreNotification
- // for the benefit of child editing contexts
-
- boolean postStoreInfo =
- ( insertedObjectsBuffer.size() +
- updatedObjectsBuffer.size() +
- deletedIDsBuffer.size() +
- invalidatedIDsBuffer.size() > 0 );
-
- NSMutableDictionary storeInfo = new NSMutableDictionary();
- if ( postStoreInfo )
- {
- storeInfo.setObjectForKey(
- globalIDsForObjects( insertedObjectsBuffer ),
- // globalIDsForObjects( insertedObjects ),
- EOObjectStore.InsertedKey );
- storeInfo.setObjectForKey(
- globalIDsForObjects( updatedObjectsBuffer ),
- // globalIDsForObjects( updatedObjects ),
- EOObjectStore.UpdatedKey );
- storeInfo.setObjectForKey(
- new NSArray( (Collection) deletedIDsBuffer ),
- // globalIDsForObjects( deletedObjects ),
- EOObjectStore.DeletedKey );
- storeInfo.setObjectForKey(
- new NSArray( (Collection) invalidatedIDsBuffer ),
- EOObjectStore.InvalidatedKey );
- }
-
- // broadcast ObjectsChangedInEditingContextNotification
- // for the benefit of attached display groups
-
- boolean postContextInfo =
- ( insertedObjectsBuffer.size() +
- updatedObjectsBuffer.size() +
- deletedObjectsBuffer.size() +
- invalidatedObjectsBuffer.size() > 0 );
-
- NSMutableDictionary contextInfo = new NSMutableDictionary();
-
- if ( postContextInfo )
- {
-
- contextInfo.setObjectForKey(
- new NSArray( (Collection) insertedObjectsBuffer ),
- // new NSArray( (Collection) insertedObjects ),
- EOObjectStore.InsertedKey );
- contextInfo.setObjectForKey(
- new NSArray( (Collection) updatedObjectsBuffer ),
- // new NSArray( (Collection) updatedObjects ),
- EOObjectStore.UpdatedKey );
- contextInfo.setObjectForKey(
- new NSArray( (Collection) deletedObjectsBuffer ),
- // new NSArray( (Collection) deletedObjects ),
- EOObjectStore.DeletedKey );
- contextInfo.setObjectForKey(
- new NSArray( (Collection) invalidatedObjectsBuffer ),
- EOObjectStore.InvalidatedKey );
- }
-
- // update the current snapshots
-
- Object o;
- Iterator it;
- it = insertedObjectsBuffer.iterator();
- while ( it.hasNext() )
- {
- o = it.next();
- registrar.setCurrentSnapshot( o, takeSnapshot( o ) );
- }
- it = updatedObjectsBuffer.iterator();
- while ( it.hasNext() )
- {
- o = it.next();
- registrar.setCurrentSnapshot( o, takeSnapshot( o ) );
- }
-
- // clear buffers
-
- insertedObjectsBuffer.removeAllObjects();
- updatedObjectsBuffer.removeAllObjects();
- deletedObjectsBuffer.removeAllObjects();
- deletedIDsBuffer.removeAllObjects();
- invalidatedObjectsBuffer.removeAllObjects();
- invalidatedIDsBuffer.removeAllObjects();
-
- // post notifications (does order matter?)
-
- if ( postStoreInfo )
- {
- NSNotificationCenter.defaultCenter().postNotification(
- new NSNotification(
- ObjectsChangedInStoreNotification, this, storeInfo ) );
- }
-
- if ( postContextInfo )
- {
- NSNotificationCenter.defaultCenter().postNotification(
- new NSNotification(
- ObjectsChangedInEditingContextNotification, this, contextInfo ) );
- }
-
- }
-
- /**
- * Returns whether this editing context propagates deletes
- * immediately after the event that triggered the delete.
- * Otherwise, propagation occurs only before commit.
- */
- public boolean propagatesDeletesAtEndOfEvent ()
- {
- return propagateDeletesAfterEvent;
- }
-
- /**
- * Registers the specified object in this editing context
- * for the specified id. This method is called by an object
- * store when fetching objects for a display group, or when
- * objects are inserted into a display group.
- * This implementation will re-register the object under the
- * new id if it is already registered under a different id.
- */
- public void recordObject (
- Object anObject,
- EOGlobalID aGlobalID )
- {
- // find state for re-registration
- boolean inserted = false;
- boolean updated = false;
- boolean deleted = false;
-
- // is the object already registered?
- EOGlobalID existingID = globalIDForObject( anObject );
- if ( existingID != null )
- {
- // remember object state
- int index;
- index = insertedObjects.indexOfIdenticalObject( anObject );
- if ( index != NSArray.NotFound ) inserted = true;
- index = updatedObjects.indexOfIdenticalObject( anObject );
- if ( index != NSArray.NotFound ) updated = true;
- index = deletedObjects.indexOfIdenticalObject( anObject );
- if ( index != NSArray.NotFound ) deleted = true;
- // forget the object
- forgetObject( anObject );
- }
-
- // is the global id already in use?
- Object existingObject = objectForGlobalID( aGlobalID );
- if ( existingObject != null )
- {
- // forget it (don't worry about state?)
- forgetObject( existingObject );
- }
-
- registrar.registerObject( anObject, aGlobalID );
-
- // restore state if necessary
- if ( inserted ) insertedObjects.addObject( anObject );
- if ( updated ) updatedObjects.addObject( anObject );
- if ( deleted ) deletedObjects.addObject( anObject );
- }
-
- /**
- * Undoes the last undo operation.
- */
- public void redo ()
- {
- //TODO: not supported yet
- throw new UnsupportedOperationException("Not implemented yet.");
- }
-
- /**
- * Refaults this editing context, turning all unmodified
- * objects into faults. This implementation calls
- * editingContextWillSaveChanges() on all editors, and
- * then calls refaultObjects().
- */
- public void refault ()
- {
- fireWillSaveChanges();
- refaultObjects();
- }
-
- /**
- * Refaults the specified object, turning it into a fault
- * for the specified global id in the specified context.
- */
- public void refaultObject (
- Object anObject,
- EOGlobalID aGlobalID,
- EOEditingContext aContext)
- {
- aContext.registrar.setCurrentSnapshot( anObject, null );
-
- ignoreChanges = true;
- parentStore.refaultObject( anObject, aGlobalID, aContext );
- ignoreChanges = false;
-
+
+ NSArray result = null;
+
+ if (aContext == this) {
+ throw new WotonomyException("Assert failed: calling objectsForSourceGlobalID on ourself.");
+ }
+ Object source = registrar.objectForGlobalID(aGlobalID);
+
+ // if not registered in our context
+ if (source == null) {
+ // get the object registered into our context
+ result = parentStore.objectsForSourceGlobalID(aGlobalID, aRelationshipKey, this);
+ } else // source is registered in our context
+ {
+ // get existing value
+ Object value;
+ if (source instanceof EOKeyValueCoding) {
+ value = ((EOKeyValueCoding) source).storedValueForKey(aRelationshipKey);
+ } else // handle directly
+ {
+ value = EOKeyValueCodingSupport.storedValueForKey(source, aRelationshipKey);
+ }
+
+ // if we don't have a valid value on our object
+ if ((value == null) || ((value instanceof ArrayFault) && (!((ArrayFault) value).isFetched()))) {
+ // do the same as if the source was null
+ result = parentStore.objectsForSourceGlobalID(aGlobalID, aRelationshipKey, this);
+
+ // set our value since we have it
+ if (source instanceof EOKeyValueCoding) {
+ ((EOKeyValueCoding) source).takeStoredValueForKey(result, aRelationshipKey);
+ } else // handle directly
+ {
+ EOKeyValueCodingSupport.takeStoredValueForKey(source, result, aRelationshipKey);
+ }
+ } else if ((value instanceof ArrayFault) && (!((ArrayFault) value).isFetched())) {
+ // do the same as if the source was null
+ result = parentStore.objectsForSourceGlobalID(aGlobalID, aRelationshipKey, this);
+ } else if (value instanceof NSArray) {
+ result = (NSArray) value;
+ } else // not NSArray
+ if (value instanceof Collection) {
+ // convert to NSArray
+ result = new NSArray((Collection) value);
+ } else {
+ throw new WotonomyException(
+ "Relationship key did not return a collection: " + aGlobalID + " : " + aRelationshipKey);
+ }
+ }
+
+ // if our context is not the specified context
+ if (aContext != this) {
+ result = (NSArray) clone(this, result, aContext);
+ }
+
+ return result;
+ }
+
+ /**
+ * Returns a read-only List of objects the meet the criteria of the supplied
+ * specification. This method simply calls objectsWithFetchSpecification on this
+ * editing context with this editing context as the parameter.
+ */
+ public NSArray objectsWithFetchSpecification(EOFetchSpecification aFetchSpec) {
+ return objectsWithFetchSpecification(aFetchSpec, this);
+ }
+
+ /**
+ * Returns a read-only List of objects the meet the criteria of the supplied
+ * specification. Faults are not allowed in the array. If any objects are
+ * already fetched, they should not be refetched. All objects should belong to
+ * the specified editing context. This implementation forwards the call to the
+ * parent object store, which will register each object in the specified editing
+ * context only if it does not already exist.
+ */
+ public NSArray objectsWithFetchSpecification(EOFetchSpecification aFetchSpec, EOEditingContext aContext) {
+ if (aContext == this) {
+ Object result = notifyDelegate("editingContextShouldFetchObjects",
+ new Class[] { EOEditingContext.class, EOFetchSpecification.class },
+ new Object[] { aContext, aFetchSpec });
+ if (result instanceof NSArray)
+ return (NSArray) result;
+ }
+ return parentStore.objectsWithFetchSpecification(aFetchSpec, aContext);
+ }
+
+ /**
+ * Returns the parent object store for this editing context. The result will not
+ * be null.
+ */
+ public EOObjectStore parentObjectStore() {
+ return parentStore;
+ }
+
+ /**
+ * Updates the inserted, updated, and deleted objects lists, and posts
+ * notifications about which objects have been changed. This method is called at
+ * the end of an event loop in which objects were modified. This method is
+ * additionally called by saveChanges() so that any changes in the same event
+ * loop will be processed correctly before calling to the parent object store.
+ * This implementation updates those lists immediately, but only posts
+ * notifications when this method is called.
+ */
+ public void processRecentChanges() { // System.out.println( "EOEditingContext.processRecentChanges: " +
+ // invalidatedObjectsBuffer );
+
+ /*
+ * This implementation actually updates those lists immediately, but keeps a
+ * separate buffer of changes in the current event loop for the purposes of
+ * posting a notification. NOTE: to reenable buffering, uncomment lines from
+ * this method body and reenable the RecentChangesObserver in the constructor.
+ */
+
+ // broadcast ObjectsChangedInStoreNotification
+ // for the benefit of child editing contexts
+
+ boolean postStoreInfo = (insertedObjectsBuffer.size() + updatedObjectsBuffer.size() + deletedIDsBuffer.size()
+ + invalidatedIDsBuffer.size() > 0);
+
+ NSMutableDictionary storeInfo = new NSMutableDictionary();
+ if (postStoreInfo) {
+ storeInfo.setObjectForKey(globalIDsForObjects(insertedObjectsBuffer),
+ // globalIDsForObjects( insertedObjects ),
+ EOObjectStore.InsertedKey);
+ storeInfo.setObjectForKey(globalIDsForObjects(updatedObjectsBuffer),
+ // globalIDsForObjects( updatedObjects ),
+ EOObjectStore.UpdatedKey);
+ storeInfo.setObjectForKey(new NSArray((Collection) deletedIDsBuffer),
+ // globalIDsForObjects( deletedObjects ),
+ EOObjectStore.DeletedKey);
+ storeInfo.setObjectForKey(new NSArray((Collection) invalidatedIDsBuffer), EOObjectStore.InvalidatedKey);
+ }
+
+ // broadcast ObjectsChangedInEditingContextNotification
+ // for the benefit of attached display groups
+
+ boolean postContextInfo = (insertedObjectsBuffer.size() + updatedObjectsBuffer.size()
+ + deletedObjectsBuffer.size() + invalidatedObjectsBuffer.size() > 0);
+
+ NSMutableDictionary contextInfo = new NSMutableDictionary();
+
+ if (postContextInfo) {
+
+ contextInfo.setObjectForKey(new NSArray((Collection) insertedObjectsBuffer),
+ // new NSArray( (Collection) insertedObjects ),
+ EOObjectStore.InsertedKey);
+ contextInfo.setObjectForKey(new NSArray((Collection) updatedObjectsBuffer),
+ // new NSArray( (Collection) updatedObjects ),
+ EOObjectStore.UpdatedKey);
+ contextInfo.setObjectForKey(new NSArray((Collection) deletedObjectsBuffer),
+ // new NSArray( (Collection) deletedObjects ),
+ EOObjectStore.DeletedKey);
+ contextInfo.setObjectForKey(new NSArray((Collection) invalidatedObjectsBuffer),
+ EOObjectStore.InvalidatedKey);
+ }
+
+ // update the current snapshots
+
+ Object o;
+ Iterator it;
+ it = insertedObjectsBuffer.iterator();
+ while (it.hasNext()) {
+ o = it.next();
+ registrar.setCurrentSnapshot(o, takeSnapshot(o));
+ }
+ it = updatedObjectsBuffer.iterator();
+ while (it.hasNext()) {
+ o = it.next();
+ registrar.setCurrentSnapshot(o, takeSnapshot(o));
+ }
+
+ // clear buffers
+
+ insertedObjectsBuffer.removeAllObjects();
+ updatedObjectsBuffer.removeAllObjects();
+ deletedObjectsBuffer.removeAllObjects();
+ deletedIDsBuffer.removeAllObjects();
+ invalidatedObjectsBuffer.removeAllObjects();
+ invalidatedIDsBuffer.removeAllObjects();
+
+ // post notifications (does order matter?)
+
+ if (postStoreInfo) {
+ NSNotificationCenter.defaultCenter()
+ .postNotification(new NSNotification(ObjectsChangedInStoreNotification, this, storeInfo));
+ }
+
+ if (postContextInfo) {
+ NSNotificationCenter.defaultCenter().postNotification(
+ new NSNotification(ObjectsChangedInEditingContextNotification, this, contextInfo));
+ }
+
+ }
+
+ /**
+ * Returns whether this editing context propagates deletes immediately after the
+ * event that triggered the delete. Otherwise, propagation occurs only before
+ * commit.
+ */
+ public boolean propagatesDeletesAtEndOfEvent() {
+ return propagateDeletesAfterEvent;
+ }
+
+ /**
+ * Registers the specified object in this editing context for the specified id.
+ * This method is called by an object store when fetching objects for a display
+ * group, or when objects are inserted into a display group. This implementation
+ * will re-register the object under the new id if it is already registered
+ * under a different id.
+ */
+ public void recordObject(Object anObject, EOGlobalID aGlobalID) {
+ // find state for re-registration
+ boolean inserted = false;
+ boolean updated = false;
+ boolean deleted = false;
+
+ // is the object already registered?
+ EOGlobalID existingID = globalIDForObject(anObject);
+ if (existingID != null) {
+ // remember object state
+ int index;
+ index = insertedObjects.indexOfIdenticalObject(anObject);
+ if (index != NSArray.NotFound)
+ inserted = true;
+ index = updatedObjects.indexOfIdenticalObject(anObject);
+ if (index != NSArray.NotFound)
+ updated = true;
+ index = deletedObjects.indexOfIdenticalObject(anObject);
+ if (index != NSArray.NotFound)
+ deleted = true;
+ // forget the object
+ forgetObject(anObject);
+ }
+
+ // is the global id already in use?
+ Object existingObject = objectForGlobalID(aGlobalID);
+ if (existingObject != null) {
+ // forget it (don't worry about state?)
+ forgetObject(existingObject);
+ }
+
+ registrar.registerObject(anObject, aGlobalID);
+
+ // restore state if necessary
+ if (inserted)
+ insertedObjects.addObject(anObject);
+ if (updated)
+ updatedObjects.addObject(anObject);
+ if (deleted)
+ deletedObjects.addObject(anObject);
+ }
+
+ /**
+ * Undoes the last undo operation.
+ */
+ public void redo() {
+ // TODO: not supported yet
+ throw new UnsupportedOperationException("Not implemented yet.");
+ }
+
+ /**
+ * Refaults this editing context, turning all unmodified objects into faults.
+ * This implementation calls editingContextWillSaveChanges() on all editors, and
+ * then calls refaultObjects().
+ */
+ public void refault() {
+ fireWillSaveChanges();
+ refaultObjects();
+ }
+
+ /**
+ * Refaults the specified object, turning it into a fault for the specified
+ * global id in the specified context.
+ */
+ public void refaultObject(Object anObject, EOGlobalID aGlobalID, EOEditingContext aContext) {
+ aContext.registrar.setCurrentSnapshot(anObject, null);
+
+ ignoreChanges = true;
+ parentStore.refaultObject(anObject, aGlobalID, aContext);
+ ignoreChanges = false;
+
// remove from updated objects if necessary
- int i = updatedObjects.indexOfIdenticalObject( anObject );
- if ( i != NSArray.NotFound )
- {
- updatedObjects.removeObjectAtIndex( i );
- }
-
- // add to invalidated notification queue
- invalidatedObjectsBuffer.addObject( anObject );
- invalidatedIDsBuffer.addObject( aGlobalID );
- }
-
- /**
- * Turns all unmodified objects into faults, calling
- * processRecentChanges() and then refaultObject() for
- * each unmodified object.
- */
- public void refaultObjects ()
- {
- // is this call really needed?
- // processRecentChanges();
-
- Object o;
- EOGlobalID id;
- Iterator it = registeredObjects().iterator();
- while ( it.hasNext() )
- {
- o = it.next();
- if ( ( updatedObjects.indexOfIdenticalObject( o ) == NSArray.NotFound )
- && ( insertedObjects.indexOfIdenticalObject( o ) == NSArray.NotFound )
- && ( deletedObjects.indexOfIdenticalObject( o ) == NSArray.NotFound ) )
- {
- id = globalIDForObject( o );
- refaultObject( o, id, this );
- }
- }
- }
-
- /**
- * Calls editingContextWillSaveChanges() on all editors,
- * and then calls invalidateAllObjects().
- */
- public void refetch ()
- {
- fireWillSaveChanges();
- invalidateAllObjects();
- }
-
- /**
- * Returns a read-only List of all objects registered in this
- * editing context.
- */
- public NSArray registeredObjects ()
- {
- return registrar.registeredObjects();
- }
-
- /**
- * Unregisters the specified editor with this editing context.
- */
- public void removeEditor ( Object anObject )
- {
- if ( anObject == null ) return;
-
- Object o;
- Iterator i = editorSet.iterator();
- while ( i.hasNext() )
- {
- o = ((WeakReference)i.next()).get();
- if ( ( o == null ) || ( o == anObject ) )
- {
- i.remove();
- }
- }
- }
-
- /**
- * Unregisters all objects from this editing context,
- * and resets the fetch timestamp.
- */
- public void reset ()
- {
- Iterator it = registeredObjects().iterator();
- while ( it.hasNext() )
- {
- forgetObject( it.next() );
- }
- fetchTimestamp = 0; //FIXME: reset timestamp properly
- }
-
- /**
- * Reverts the objects in this editing context to
- * their original state.
- * Calls editingContextWillSaveChanges on all editors,
- * discards all inserted objects, restores deleted
- * objects, and applies the fetch snapshot to all
- * registered objects.
- */
- public void revert ()
- {
- willChange();
- fireWillSaveChanges();
-
- Iterator it;
-
- // forget inserted objects
- it = new NSArray( insertedObjects ).iterator();
- while ( it.hasNext() )
- {
- forgetObject( it.next() );
- }
-
- EOGlobalID id;
- Object o;
- byte[] snapshot;
-
- // re-initialize updated objects
- it = new NSArray( updatedObjects ).iterator();
- while ( it.hasNext() )
- {
- o = it.next();
- snapshot = (byte[]) registrar.getCommitSnapshot( o );
- if ( snapshot != null )
- {
- applySnapshot( snapshot, o );
- }
- registrar.setCommitSnapshot( o, null );
- updatedObjectsBuffer.addObject( o );
- }
-
- // re-initialize deleted objects
- it = new NSArray( deletedObjects ).iterator();
- while ( it.hasNext() )
- {
- o = it.next();
- snapshot = (byte[]) registrar.getCommitSnapshot( o );
- if ( snapshot != null )
- {
- applySnapshot( snapshot, o );
- }
- registrar.setCommitSnapshot( o, null );
- updatedObjectsBuffer.addObject( o );
- }
-
- // reset lists
- insertedObjects.removeAllObjects(); // unneccessary?
- deletedObjects.removeAllObjects();
- updatedObjects.removeAllObjects();
-
- // post notification
- processRecentChanges();
- }
-
- /**
- * Returns the root object store, which is the parent
- * of all parent object stores of this editing context.
- */
- public EOObjectStore rootObjectStore ()
- {
- EOObjectStore parent = parentObjectStore();
- while ( parent instanceof EOEditingContext )
- {
- parent = ((EOEditingContext)parent).parentObjectStore();
- }
- return parent;
- }
-
- /**
- * Calls editingContextWillSaveChanges on all editors,
- * and commits all changes in this editing context to
- * the parent editing context by calling
- * saveChangesInEditingContext to the parent.
- * Then posts EditingContextDidSaveChangeNotification.
- */
- public void saveChanges ()
- {
+ int i = updatedObjects.indexOfIdenticalObject(anObject);
+ if (i != NSArray.NotFound) {
+ updatedObjects.removeObjectAtIndex(i);
+ }
+
+ // add to invalidated notification queue
+ invalidatedObjectsBuffer.addObject(anObject);
+ invalidatedIDsBuffer.addObject(aGlobalID);
+ }
+
+ /**
+ * Turns all unmodified objects into faults, calling processRecentChanges() and
+ * then refaultObject() for each unmodified object.
+ */
+ public void refaultObjects() {
+ // is this call really needed?
+ // processRecentChanges();
+
+ Object o;
+ EOGlobalID id;
+ Iterator it = registeredObjects().iterator();
+ while (it.hasNext()) {
+ o = it.next();
+ if ((updatedObjects.indexOfIdenticalObject(o) == NSArray.NotFound)
+ && (insertedObjects.indexOfIdenticalObject(o) == NSArray.NotFound)
+ && (deletedObjects.indexOfIdenticalObject(o) == NSArray.NotFound)) {
+ id = globalIDForObject(o);
+ refaultObject(o, id, this);
+ }
+ }
+ }
+
+ /**
+ * Calls editingContextWillSaveChanges() on all editors, and then calls
+ * invalidateAllObjects().
+ */
+ public void refetch() {
+ fireWillSaveChanges();
+ invalidateAllObjects();
+ }
+
+ /**
+ * Returns a read-only List of all objects registered in this editing context.
+ */
+ public NSArray registeredObjects() {
+ return registrar.registeredObjects();
+ }
+
+ /**
+ * Unregisters the specified editor with this editing context.
+ */
+ public void removeEditor(Object anObject) {
+ if (anObject == null)
+ return;
+
+ Object o;
+ Iterator i = editorSet.iterator();
+ while (i.hasNext()) {
+ o = ((WeakReference) i.next()).get();
+ if ((o == null) || (o == anObject)) {
+ i.remove();
+ }
+ }
+ }
+
+ /**
+ * Unregisters all objects from this editing context, and resets the fetch
+ * timestamp.
+ */
+ public void reset() {
+ Iterator it = registeredObjects().iterator();
+ while (it.hasNext()) {
+ forgetObject(it.next());
+ }
+ fetchTimestamp = 0; // FIXME: reset timestamp properly
+ }
+
+ /**
+ * Reverts the objects in this editing context to their original state. Calls
+ * editingContextWillSaveChanges on all editors, discards all inserted objects,
+ * restores deleted objects, and applies the fetch snapshot to all registered
+ * objects.
+ */
+ public void revert() {
+ willChange();
+ fireWillSaveChanges();
+
+ Iterator it;
+
+ // forget inserted objects
+ it = new NSArray(insertedObjects).iterator();
+ while (it.hasNext()) {
+ forgetObject(it.next());
+ }
+
+ EOGlobalID id;
+ Object o;
+ byte[] snapshot;
+
+ // re-initialize updated objects
+ it = new NSArray(updatedObjects).iterator();
+ while (it.hasNext()) {
+ o = it.next();
+ snapshot = (byte[]) registrar.getCommitSnapshot(o);
+ if (snapshot != null) {
+ applySnapshot(snapshot, o);
+ }
+ registrar.setCommitSnapshot(o, null);
+ updatedObjectsBuffer.addObject(o);
+ }
+
+ // re-initialize deleted objects
+ it = new NSArray(deletedObjects).iterator();
+ while (it.hasNext()) {
+ o = it.next();
+ snapshot = (byte[]) registrar.getCommitSnapshot(o);
+ if (snapshot != null) {
+ applySnapshot(snapshot, o);
+ }
+ registrar.setCommitSnapshot(o, null);
+ updatedObjectsBuffer.addObject(o);
+ }
+
+ // reset lists
+ insertedObjects.removeAllObjects(); // unneccessary?
+ deletedObjects.removeAllObjects();
+ updatedObjects.removeAllObjects();
+
+ // post notification
+ processRecentChanges();
+ }
+
+ /**
+ * Returns the root object store, which is the parent of all parent object
+ * stores of this editing context.
+ */
+ public EOObjectStore rootObjectStore() {
+ EOObjectStore parent = parentObjectStore();
+ while (parent instanceof EOEditingContext) {
+ parent = ((EOEditingContext) parent).parentObjectStore();
+ }
+ return parent;
+ }
+
+ /**
+ * Calls editingContextWillSaveChanges on all editors, and commits all changes
+ * in this editing context to the parent editing context by calling
+ * saveChangesInEditingContext to the parent. Then posts
+ * EditingContextDidSaveChangeNotification.
+ */
+ public void saveChanges() {
//System.out.println( "EOEditingContext.saveChanges: " + this );
- willChange();
-
- // process any changes
- processRecentChanges();
-
- // set up user info for notification to be posted.
- NSMutableDictionary userInfo = new NSMutableDictionary();
- userInfo.setObjectForKey(
- new NSArray( (Collection) insertedObjects ),
- EOObjectStore.InsertedKey );
- userInfo.setObjectForKey(
- new NSArray( (Collection) updatedObjects ),
- EOObjectStore.UpdatedKey );
- userInfo.setObjectForKey(
- new NSArray( (Collection) deletedObjects ),
- EOObjectStore.DeletedKey );
-
- // notify the editors
- fireWillSaveChanges();
-
- // notify the delegate
- notifyDelegate(
- "editingContextWillSaveChanges",
- new Class[] { EOEditingContext.class },
- new Object[] { this } );
-
- // needed for notification handling
- isInvalidating = true;
- try
- {
- // ask parent to save us
- parentStore.saveChangesInEditingContext( this );
- }
- catch ( RuntimeException e )
- {
- // unset save flag and rethrow
- isInvalidating = false;
- throw e;
- }
- isInvalidating = false;
-
- // no exceptions: proceed!
-
- Object o, key;
- Iterator it;
-
- // update the committed snapshots
- it = insertedObjects.iterator();
- while ( it.hasNext() )
- {
- o = it.next();
- registrar.setCommitSnapshot( o, null );
- registrar.setCurrentSnapshot( o, null );
- }
- it = updatedObjects.iterator();
- while ( it.hasNext() )
- {
- o = it.next();
- registrar.setCommitSnapshot( o, null );
- registrar.setCurrentSnapshot( o, null );
- }
-
- // clear the lists
- updatedObjects.removeAllObjects();
- insertedObjects.removeAllObjects();
- it = new NSArray( deletedObjects() ).iterator();
- while ( it.hasNext() )
- { // parent is doing this as well?
- forgetObject( it.next() );
- }
-
- // post notification
- NSNotificationCenter.defaultCenter().postNotification(
- new NSNotification(
- EditingContextDidSaveChangesNotification, this, userInfo ) );
- }
-
- /**
- * Commits all changes in the specified editing context
- * to this one. Called by child editing contexts in
- * their saveChanges() method.
- */
- public void saveChangesInEditingContext (
- EOEditingContext aContext)
- {
- Object o;
- Iterator it;
-
- // process deletes
- List deletes = new NSArray( aContext.deletedObjects() );
- it = deletes.iterator();
- while ( it.hasNext() )
- {
- o = it.next();
- EOGlobalID id = aContext.globalIDForObject( o );
- Object localVersion = objectForGlobalID( id );
- if ( localVersion == null )
- {
- // make a local copy and register it
- localVersion = registerClone( id, aContext, o, this );
- if ( localVersion == null )
- {
- throw new WotonomyException(
- "Deleted object could not be serialized: "
- + id + " : " + o );
- }
- }
- else // we have a local version, copy changes
- {
- copy( aContext, o, this, localVersion );
- // copy marks the object as updated: will be on both lists
- }
- // delete our copy -- marks context as changed
- deleteObject( localVersion );
- }
-
- // process inserts - all inserts are new objects
- List inserts = new NSArray( aContext.insertedObjects() );
- it = inserts.iterator();
- while ( it.hasNext() )
- {
- o = it.next();
- // make a local copy and register it
- EOGlobalID id = aContext.globalIDForObject( o );
- willChange(); // need to mark editing context as changed
- Object localVersion = registerClone( id, aContext, o, this );
- if ( localVersion == null )
- {
- throw new WotonomyException(
- "Inserted object could not be serialized: "
- + o );
- }
- // insert our copy manually so a new id is not generated
- insertedObjects.addObject( localVersion );
- insertedObjectsBuffer.addObject( localVersion );
- }
-
- // process updates
- List updates = new NSArray( aContext.updatedObjects() );
- it = updates.iterator();
- while ( it.hasNext() )
- {
- willChange(); // need to mark editing context as changed
- o = it.next();
- EOGlobalID id = aContext.globalIDForObject( o );
- Object localVersion = objectForGlobalID( id );
- if ( localVersion == null )
- {
- // make a local copy and register it
- localVersion = registerClone( id, aContext, o, this );
- if ( localVersion == null )
- {
- throw new WotonomyException(
- "Updated object could not be serialized: "
- + id + " : " + o );
- }
- if ( id.isTemporary() )
- {
- // mark this object as inserted
- insertedObjects.addObject( localVersion );
- insertedObjectsBuffer.addObject( localVersion );
- }
- else
- {
- // mark this object as updated
- updatedObjects.addObject( localVersion );
-
- // notify of update only if not on deleted list
- if ( deletedObjectsBuffer.indexOfIdenticalObject(
- localVersion ) == NSArray.NotFound )
- {
- updatedObjectsBuffer.addObject( localVersion );
- }
- }
- }
- else // we have a local version, copy changes
- {
- copy( aContext, o, this, localVersion );
- // copy marks the object as updated
- }
- }
-
- }
-
- /**
- * Sets the delegate for this editing context.
- * Note: this implementation retains only a
- * weak reference to the specified object.
- */
- public void setDelegate (
- Object anObject )
- {
- if ( anObject == null ) delegate = null;
- delegate = new WeakReference( anObject );
- }
-
- /**
- * Sets the fetch timestamp for this editing context.
- */
- public void setFetchTimestamp (
- double aDouble )
- {
- fetchTimestamp = aDouble;
- }
-/*
- public void setInvalidatesObjectsWhenFinalized (
- boolean invalidatesObjects )
- {
- throw new net.wotonomy.util.WotonomyException("Not implemented yet.");
- }
-
- public void setInvalidatesObjectsWhenFreed (
- boolean invalidatesObjects )
- {
- throw new net.wotonomy.util.WotonomyException("Not implemented yet.");
- }
-*/
- /**
- * Sets whether this editing context attempts to
- * lock objects when they are first modified.
- * Default is false.
- */
- public void setLocksObjectsBeforeFirstModification (
- boolean locksObjects )
- {
- lockBeforeModify = locksObjects;
- }
-
- /**
- * Sets the message handler for this editing context.
- * Note: this implementation retains only a
- * weak reference to the specified object.
- */
- public void setMessageHandler (
- Object anObject )
- {
- if ( anObject == null ) messageHandler = null;
- messageHandler = new WeakReference( anObject );
- }
-
- /**
- * Sets whether this editing context propagates deletes
- * immediately after the event that triggered the delete.
- * Otherwise, propagation occurs only before commit.
- * Default is true.
- */
- public void setPropagatesDeletesAtEndOfEvent (
- boolean propagatesDeletes )
- {
- propagateDeletesAfterEvent = propagatesDeletes;
- }
-/*
- public void setSharedEditingContext (
- EOSharedEditingContext aSharedEditingContext )
- {
- throw new net.wotonomy.util.WotonomyException("Not implemented yet.");
- }
-*/
- /**
- * Sets whether validation is stopped after the
- * first error occurs. Otherwise, validation will
- * continue for all other objects.
- * Default is true.
- */
- public void setStopsValidationAfterFirstError (
- boolean stopsValidation )
- {
- stopValidationAfterError = stopsValidation;
- }
-
- /**
- * Sets the undo manager to be used for this context.
- * Note: This is currently javax.swing.undo.UndoManager,
- * until we have a implementation of NSUndoManager.
- */
-/*
- public void setUndoManager (
- UndoManager anUndoManager )
- {
- undoManager = anUndoManager;
- }
-*/
-/*
- public EOSharedEditingContext sharedEditingContext ()
- {
- throw new net.wotonomy.util.WotonomyException("Not implemented yet.");
- }
-*/
- /**
- * Returns whether validation is stopped after the
- * first error occurs. Otherwise, validation will
- * continue for all other objects.
- */
- public boolean stopsValidationAfterFirstError ()
- {
- return stopValidationAfterError;
- }
-
- /**
- * Reverts the last change on the undo stack.
- */
- public void undo ()
- {
- //TODO: not supported yet
- throw new UnsupportedOperationException("Not implemented yet.");
- }
-/*
- public NSUndoManager undoManager ()
- {
- throw new net.wotonomy.util.WotonomyException("Not implemented yet.");
- }
-*/
-/**
- public void unlock ()
- {
- throw new net.wotonomy.util.WotonomyException("Not implemented yet.");
- }
-*/
- /**
- * Returns a read-only list of all objects marked as modified,
- * but not inserted or deleted, in this editing context.
- */
- public NSArray updatedObjects ()
- {
- return updatedObjectsProxy;
- }
-
- /**
- * Notify editors of changes.
- */
- private void fireWillSaveChanges()
- {
- Object o = null;
- Iterator i = editors().iterator();
- while ( i.hasNext() )
- {
- try
- {
- o = i.next();
- NSSelector.invoke( "editingContextWillSaveChanges",
- new Class[] { EOEditingContext.class }, o, this );
- }
- catch ( NoSuchMethodException e )
- {
- // ignore: not implemented
- }
- catch ( Exception exc )
- {
- // log to standard error
- System.err.println( "Error while notifying editor of pending save: " + o );
- exc.printStackTrace();
- }
- }
- }
-
- /**
- * Handles notifications from parent store, looking for
- * InvalidatedAllObjectsInStoreNotification and
- * ObjectsChangedInStoreNotification.
- * The former causes all objects in this store to be
- * invalidated.
- * The latter refaults the invalidated ids, merges changes
- * from the updated ids, and forgets the deleted ids, then
- * posts a ObjectsChangedInStoreNotification.
- * Note: This method is not in the public specification.
- */
- public void handleNotification( NSNotification aNotification )
- { // System.out.println( "EOEditingContext: " + this + " : " + aNotification );
-
- willChange();
- if ( InvalidatedAllObjectsInStoreNotification
- .equals( aNotification.name() ) )
- {
- refaultObjects();
-
- // relay notification
- NSNotificationCenter.defaultCenter().postNotification(
- new NSNotification(
- InvalidatedAllObjectsInStoreNotification, this ) );
- }
- else
- if ( EOGlobalID.GlobalIDChangedNotification
- .equals( aNotification.name() ) )
- {
- NSDictionary userInfo = aNotification.userInfo();
-
- // if any keys in userInfo are registered ids,
- // re-register with new permanent values.
-
- Object o;
- EOGlobalID id;
- Enumeration e = userInfo.keyEnumerator();
- while ( e.hasMoreElements() )
- {
- id = (EOGlobalID) e.nextElement();
- o = objectForGlobalID( id );
- if ( o != null )
- {
- // record object is assumed to handle key updates
- recordObject( o, (EOGlobalID) userInfo.objectForKey( id ) );
- }
- }
- }
- else
- if ( EOObjectStore.ObjectsChangedInStoreNotification
- .equals( aNotification.name() ) )
- {
- //System.out.println( "EOEditingContext.handleNotification: " + aNotification + " : " + this );
- // post so child contexts are notified
- willChange();
-
- Object o;
- EOGlobalID id;
- Enumeration e;
- NSDictionary userInfo = aNotification.userInfo();
-
- // inserted objects are ignored
-
- // existing deleted objects are removed
- NSArray deletes = (NSArray) userInfo.objectForKey(
- EOObjectStore.DeletedKey );
- e = deletes.objectEnumerator();
- while ( e.hasMoreElements() )
- {
- id = (EOGlobalID) e.nextElement();
- o = objectForGlobalID( id );
- if ( o != null )
- {
- //System.out.println( "EOEditingContext: deleted: " + id );
- forgetObject( o );
- deletedObjectsBuffer.addObject( o );
- deletedIDsBuffer.addObject( id );
- }
- }
-
- // existing updated objects are merged
- NSArray updates = (NSArray) userInfo.objectForKey(
- EOObjectStore.UpdatedKey );
- e = updates.objectEnumerator();
- while ( e.hasMoreElements() )
- {
- id = (EOGlobalID) e.nextElement();
- o = objectForGlobalID( id );
- if ( o != null )
- {
- //System.out.println( "EOEditingContext: updated: " + id );
- if ( updatedObjects // only update if unchanged
- .indexOfIdenticalObject( o ) == NSArray.NotFound )
- {
- refaultObject( o, id, this );
- updatedObjectsBuffer.addObject( o );
- }
- else
- {
- // notify user and/or merge
- handleUpdateConflict( id, o );
- }
- }
- }
-
- // existing invalidated objects are refaulted
- NSArray invalidates = (NSArray) userInfo.objectForKey(
- EOObjectStore.InvalidatedKey );
- e = invalidates.objectEnumerator();
- while ( e.hasMoreElements() )
- {
- id = (EOGlobalID) e.nextElement();
- o = objectForGlobalID( id );
- if ( o != null )
- {
- if ( updatedObjects // only invalidate if unchanged
- .indexOfIdenticalObject( o ) == NSArray.NotFound )
- {
- refaultObject( o, id, this );
- }
- else
- {
- // notify user and/or merge
- handleUpdateConflict( id, o );
- }
- if ( invalidatedObjectsBuffer
- .indexOfIdenticalObject( o ) == NSArray.NotFound )
- {
- invalidatedObjectsBuffer.addObject( o );
- }
- if ( invalidatedIDsBuffer
- .indexOfIdenticalObject( id ) == NSArray.NotFound )
- {
- invalidatedIDsBuffer.addObject( id );
- }
- }
- }
-
- }
- }
-
- /**
- * Called by handleNotification to resolve the case where we have
- * received notification that another user or context has updated
- * an object that is currently marked as edited in this context.
- * This implementation first asks the delegate if it should merge.
- * If true or no delegate, the changes are merged. True or false,
- * the delegate is then sent editingContextDidMergeChanges.
- */
- private void handleUpdateConflict( EOGlobalID anID, Object anObject )
- {
- // if we're causing the invalidation, ignore
- // (this is probably better handled by the caller...)
- if ( isInvalidating )
- {
- ignoreChanges = true;
- parentStore.refaultObject( anObject, anID, this );
- ignoreChanges = false;
- return;
- }
-
- Boolean result = (Boolean) notifyDelegate(
- "editingContextShouldMergeChangesForObject",
- new Class[] { EOEditingContext.class, Object.class },
- new Object[] { this, anObject } );
-
- if ( ( result == null ) || ( Boolean.TRUE.equals( result ) ) )
- {
- // do merge
- mergeExternalChanges( anID, anObject );
- }
- else // Boolean.FALSE
- {
- // do nothing: don't lose the user's changes
- }
-
- // notify merge did happen
- notifyDelegate(
- "editingContextDidMergeChanges",
- new Class[] { EOEditingContext.class },
- new Object[] { this } );
- }
-
- /**
- * For the currently modified object with the specified global id,
- * this method merges changes with the updated version in the parent
- * object store. This implementation looks at the fetch snapshot
- * to determine which changes where made by the user, fetches the
- * updated version of the object, and then determine what external
- * changes were made. If the changes do not overlap, the original
- * changes are applied to the updated version. If there is a conflict,
- * notifies the user of the conflict.
- */
- private boolean mergeExternalChanges( EOGlobalID anID, Object anObject )
- {
- try
- {
- Iterator i;
- Object key;
-
- // get fetch snapshot
- Map fetchSnapshot = committedSnapshotForObject( anObject );
-
- // get current snapshot
- Map currentSnapshot = currentEventSnapshotForObject( anObject );
-
- // diff against fetch snapshot
- Map currentDiff = new HashMap();
- i = currentSnapshot.keySet().iterator();
- while ( i.hasNext() )
- {
- key = i.next();
- if ( ! currentSnapshot.get( key ).equals( fetchSnapshot.get( key ) ) )
- {
- currentDiff.put( key, currentSnapshot.get( key ) );
- }
- }
-
- // refault
- ignoreChanges = true;
- parentStore.refaultObject( anObject, anID, this );
- ignoreChanges = false;
-
- // get updated snapshot
- Map updatedSnapshot = convertSnapshotToDictionary( takeSnapshot( anObject ) );
-
- // diff against fetch snapshot
- Map updatedDiff = new HashMap();
- i = updatedSnapshot.keySet().iterator();
- while ( i.hasNext() )
- {
- key = i.next();
- if ( ! updatedSnapshot.get( key ).equals( fetchSnapshot.get( key ) ) )
- {
- updatedDiff.put( key, updatedSnapshot.get( key ) );
- }
- }
-
- // determine if there's a conflict
- boolean proceed = true;
- Set updatedKeys = updatedDiff.keySet();
- i = currentDiff.keySet().iterator();
- while ( i.hasNext() )
- {
- if ( updatedKeys.contains( i.next() ) )
- {
- proceed = false;
- break;
- }
- }
-
- // if no conflicts, apply original diff to current object and exit
- if ( proceed )
- {
- KeyValueCodingUtilities.takeStoredValuesFromDictionary( anObject, currentDiff );
- return true; // exit!
- }
- }
- catch ( Exception exc )
- {
- // log error to standard out
- exc.printStackTrace();
- }
-
- // notify user we're unable to merge
- notifyMessageHandler( MessageChangeConflict + anObject );
- return false;
- }
-
- /**
- * Sends the specified message to the message handler.
- */
- private void notifyMessageHandler( String aMessage )
- {
- Object handler = null;
- try
- {
- handler = messageHandler();
- if ( handler == null ) return;
- NSSelector.invoke( "editingContextPresentErrorMessage",
- new Class[] { EOEditingContext.class, String.class },
- handler, this, aMessage );
- }
- catch ( NoSuchMethodException e )
- {
- // ignore: not implemented
- }
- catch ( Exception exc )
- {
- // log to standard error
- System.err.println(
- "Error while notifying message handler: " +
- handler + " : " + aMessage );
- exc.printStackTrace();
- }
- }
-
- /**
- * Sends the specified message to the delegate.
- * Returns the return value of the method,
- * or null if no return value or no delegate
- * or no implementation.
- */
- private Object notifyDelegate(
- String aMethodName, Class[] types, Object[] params )
- {
- try
- {
- Object delegate = delegate();
- if ( delegate == null ) return null;
- return NSSelector.invoke(
- aMethodName, types, delegate, params );
- }
- catch ( NoSuchMethodException e )
- {
- // ignore: not implemented
- }
- catch ( Exception exc )
- {
- // log to standard error
- System.err.println(
- "Error while messaging delegate: " +
- delegate + " : " + aMethodName );
- exc.printStackTrace();
- }
-
- return null;
- }
-
- // interface EOObserving
-
- /**
- * Implementation of the EOObserving interface.
- * Called before objects are modified.
- */
- public void objectWillChange (
- Object anObject )
- {
- if ( ignoreChanges ) return;
+ willChange();
+
+ // process any changes
+ processRecentChanges();
+
+ // set up user info for notification to be posted.
+ NSMutableDictionary userInfo = new NSMutableDictionary();
+ userInfo.setObjectForKey(new NSArray((Collection) insertedObjects), EOObjectStore.InsertedKey);
+ userInfo.setObjectForKey(new NSArray((Collection) updatedObjects), EOObjectStore.UpdatedKey);
+ userInfo.setObjectForKey(new NSArray((Collection) deletedObjects), EOObjectStore.DeletedKey);
+
+ // notify the editors
+ fireWillSaveChanges();
+
+ // notify the delegate
+ notifyDelegate("editingContextWillSaveChanges", new Class[] { EOEditingContext.class }, new Object[] { this });
+
+ // needed for notification handling
+ isInvalidating = true;
+ try {
+ // ask parent to save us
+ parentStore.saveChangesInEditingContext(this);
+ } catch (RuntimeException e) {
+ // unset save flag and rethrow
+ isInvalidating = false;
+ throw e;
+ }
+ isInvalidating = false;
+
+ // no exceptions: proceed!
+
+ Object o, key;
+ Iterator it;
+
+ // update the committed snapshots
+ it = insertedObjects.iterator();
+ while (it.hasNext()) {
+ o = it.next();
+ registrar.setCommitSnapshot(o, null);
+ registrar.setCurrentSnapshot(o, null);
+ }
+ it = updatedObjects.iterator();
+ while (it.hasNext()) {
+ o = it.next();
+ registrar.setCommitSnapshot(o, null);
+ registrar.setCurrentSnapshot(o, null);
+ }
+
+ // clear the lists
+ updatedObjects.removeAllObjects();
+ insertedObjects.removeAllObjects();
+ it = new NSArray(deletedObjects()).iterator();
+ while (it.hasNext()) { // parent is doing this as well?
+ forgetObject(it.next());
+ }
+
+ // post notification
+ NSNotificationCenter.defaultCenter()
+ .postNotification(new NSNotification(EditingContextDidSaveChangesNotification, this, userInfo));
+ }
+
+ /**
+ * Commits all changes in the specified editing context to this one. Called by
+ * child editing contexts in their saveChanges() method.
+ */
+ public void saveChangesInEditingContext(EOEditingContext aContext) {
+ Object o;
+ Iterator it;
+
+ // process deletes
+ List deletes = new NSArray(aContext.deletedObjects());
+ it = deletes.iterator();
+ while (it.hasNext()) {
+ o = it.next();
+ EOGlobalID id = aContext.globalIDForObject(o);
+ Object localVersion = objectForGlobalID(id);
+ if (localVersion == null) {
+ // make a local copy and register it
+ localVersion = registerClone(id, aContext, o, this);
+ if (localVersion == null) {
+ throw new WotonomyException("Deleted object could not be serialized: " + id + " : " + o);
+ }
+ } else // we have a local version, copy changes
+ {
+ copy(aContext, o, this, localVersion);
+ // copy marks the object as updated: will be on both lists
+ }
+ // delete our copy -- marks context as changed
+ deleteObject(localVersion);
+ }
+
+ // process inserts - all inserts are new objects
+ List inserts = new NSArray(aContext.insertedObjects());
+ it = inserts.iterator();
+ while (it.hasNext()) {
+ o = it.next();
+ // make a local copy and register it
+ EOGlobalID id = aContext.globalIDForObject(o);
+ willChange(); // need to mark editing context as changed
+ Object localVersion = registerClone(id, aContext, o, this);
+ if (localVersion == null) {
+ throw new WotonomyException("Inserted object could not be serialized: " + o);
+ }
+ // insert our copy manually so a new id is not generated
+ insertedObjects.addObject(localVersion);
+ insertedObjectsBuffer.addObject(localVersion);
+ }
+
+ // process updates
+ List updates = new NSArray(aContext.updatedObjects());
+ it = updates.iterator();
+ while (it.hasNext()) {
+ willChange(); // need to mark editing context as changed
+ o = it.next();
+ EOGlobalID id = aContext.globalIDForObject(o);
+ Object localVersion = objectForGlobalID(id);
+ if (localVersion == null) {
+ // make a local copy and register it
+ localVersion = registerClone(id, aContext, o, this);
+ if (localVersion == null) {
+ throw new WotonomyException("Updated object could not be serialized: " + id + " : " + o);
+ }
+ if (id.isTemporary()) {
+ // mark this object as inserted
+ insertedObjects.addObject(localVersion);
+ insertedObjectsBuffer.addObject(localVersion);
+ } else {
+ // mark this object as updated
+ updatedObjects.addObject(localVersion);
+
+ // notify of update only if not on deleted list
+ if (deletedObjectsBuffer.indexOfIdenticalObject(localVersion) == NSArray.NotFound) {
+ updatedObjectsBuffer.addObject(localVersion);
+ }
+ }
+ } else // we have a local version, copy changes
+ {
+ copy(aContext, o, this, localVersion);
+ // copy marks the object as updated
+ }
+ }
+
+ }
+
+ /**
+ * Sets the delegate for this editing context. Note: this implementation retains
+ * only a weak reference to the specified object.
+ */
+ public void setDelegate(Object anObject) {
+ if (anObject == null)
+ delegate = null;
+ delegate = new WeakReference(anObject);
+ }
+
+ /**
+ * Sets the fetch timestamp for this editing context.
+ */
+ public void setFetchTimestamp(double aDouble) {
+ fetchTimestamp = aDouble;
+ }
+
+ /*
+ * public void setInvalidatesObjectsWhenFinalized ( boolean invalidatesObjects )
+ * { throw new net.wotonomy.util.WotonomyException("Not implemented yet."); }
+ *
+ * public void setInvalidatesObjectsWhenFreed ( boolean invalidatesObjects ) {
+ * throw new net.wotonomy.util.WotonomyException("Not implemented yet."); }
+ */
+ /**
+ * Sets whether this editing context attempts to lock objects when they are
+ * first modified. Default is false.
+ */
+ public void setLocksObjectsBeforeFirstModification(boolean locksObjects) {
+ lockBeforeModify = locksObjects;
+ }
+
+ /**
+ * Sets the message handler for this editing context. Note: this implementation
+ * retains only a weak reference to the specified object.
+ */
+ public void setMessageHandler(Object anObject) {
+ if (anObject == null)
+ messageHandler = null;
+ messageHandler = new WeakReference(anObject);
+ }
+
+ /**
+ * Sets whether this editing context propagates deletes immediately after the
+ * event that triggered the delete. Otherwise, propagation occurs only before
+ * commit. Default is true.
+ */
+ public void setPropagatesDeletesAtEndOfEvent(boolean propagatesDeletes) {
+ propagateDeletesAfterEvent = propagatesDeletes;
+ }
+
+ /*
+ * public void setSharedEditingContext ( EOSharedEditingContext
+ * aSharedEditingContext ) { throw new
+ * net.wotonomy.util.WotonomyException("Not implemented yet."); }
+ */
+ /**
+ * Sets whether validation is stopped after the first error occurs. Otherwise,
+ * validation will continue for all other objects. Default is true.
+ */
+ public void setStopsValidationAfterFirstError(boolean stopsValidation) {
+ stopValidationAfterError = stopsValidation;
+ }
+
+ /**
+ * Sets the undo manager to be used for this context. Note: This is currently
+ * javax.swing.undo.UndoManager, until we have a implementation of
+ * NSUndoManager.
+ */
+ /*
+ * public void setUndoManager ( UndoManager anUndoManager ) { undoManager =
+ * anUndoManager; }
+ */
+ /*
+ * public EOSharedEditingContext sharedEditingContext () { throw new
+ * net.wotonomy.util.WotonomyException("Not implemented yet."); }
+ */
+ /**
+ * Returns whether validation is stopped after the first error occurs.
+ * Otherwise, validation will continue for all other objects.
+ */
+ public boolean stopsValidationAfterFirstError() {
+ return stopValidationAfterError;
+ }
+
+ /**
+ * Reverts the last change on the undo stack.
+ */
+ public void undo() {
+ // TODO: not supported yet
+ throw new UnsupportedOperationException("Not implemented yet.");
+ }
+
+ /*
+ * public NSUndoManager undoManager () { throw new
+ * net.wotonomy.util.WotonomyException("Not implemented yet."); }
+ */
+ /**
+ * public void unlock () { throw new net.wotonomy.util.WotonomyException("Not
+ * implemented yet."); }
+ */
+ /**
+ * Returns a read-only list of all objects marked as modified, but not inserted
+ * or deleted, in this editing context.
+ */
+ public NSArray updatedObjects() {
+ return updatedObjectsProxy;
+ }
+
+ /**
+ * Notify editors of changes.
+ */
+ private void fireWillSaveChanges() {
+ Object o = null;
+ Iterator i = editors().iterator();
+ while (i.hasNext()) {
+ try {
+ o = i.next();
+ NSSelector.invoke("editingContextWillSaveChanges", new Class[] { EOEditingContext.class }, o, this);
+ } catch (NoSuchMethodException e) {
+ // ignore: not implemented
+ } catch (Exception exc) {
+ // log to standard error
+ System.err.println("Error while notifying editor of pending save: " + o);
+ exc.printStackTrace();
+ }
+ }
+ }
+
+ /**
+ * Handles notifications from parent store, looking for
+ * InvalidatedAllObjectsInStoreNotification and
+ * ObjectsChangedInStoreNotification. The former causes all objects in this
+ * store to be invalidated. The latter refaults the invalidated ids, merges
+ * changes from the updated ids, and forgets the deleted ids, then posts a
+ * ObjectsChangedInStoreNotification. Note: This method is not in the public
+ * specification.
+ */
+ public void handleNotification(NSNotification aNotification) { // System.out.println( "EOEditingContext: " + this +
+ // " : " + aNotification );
+
+ willChange();
+ if (InvalidatedAllObjectsInStoreNotification.equals(aNotification.name())) {
+ refaultObjects();
+
+ // relay notification
+ NSNotificationCenter.defaultCenter()
+ .postNotification(new NSNotification(InvalidatedAllObjectsInStoreNotification, this));
+ } else if (EOGlobalID.GlobalIDChangedNotification.equals(aNotification.name())) {
+ NSDictionary userInfo = aNotification.userInfo();
+
+ // if any keys in userInfo are registered ids,
+ // re-register with new permanent values.
+
+ Object o;
+ EOGlobalID id;
+ Enumeration e = userInfo.keyEnumerator();
+ while (e.hasMoreElements()) {
+ id = (EOGlobalID) e.nextElement();
+ o = objectForGlobalID(id);
+ if (o != null) {
+ // record object is assumed to handle key updates
+ recordObject(o, (EOGlobalID) userInfo.objectForKey(id));
+ }
+ }
+ } else if (EOObjectStore.ObjectsChangedInStoreNotification.equals(aNotification.name())) {
+ // System.out.println( "EOEditingContext.handleNotification: " + aNotification +
+ // " : " + this );
+ // post so child contexts are notified
+ willChange();
+
+ Object o;
+ EOGlobalID id;
+ Enumeration e;
+ NSDictionary userInfo = aNotification.userInfo();
+
+ // inserted objects are ignored
+
+ // existing deleted objects are removed
+ NSArray deletes = (NSArray) userInfo.objectForKey(EOObjectStore.DeletedKey);
+ e = deletes.objectEnumerator();
+ while (e.hasMoreElements()) {
+ id = (EOGlobalID) e.nextElement();
+ o = objectForGlobalID(id);
+ if (o != null) {
+ // System.out.println( "EOEditingContext: deleted: " + id );
+ forgetObject(o);
+ deletedObjectsBuffer.addObject(o);
+ deletedIDsBuffer.addObject(id);
+ }
+ }
+
+ // existing updated objects are merged
+ NSArray updates = (NSArray) userInfo.objectForKey(EOObjectStore.UpdatedKey);
+ e = updates.objectEnumerator();
+ while (e.hasMoreElements()) {
+ id = (EOGlobalID) e.nextElement();
+ o = objectForGlobalID(id);
+ if (o != null) {
+ // System.out.println( "EOEditingContext: updated: " + id );
+ if (updatedObjects // only update if unchanged
+ .indexOfIdenticalObject(o) == NSArray.NotFound) {
+ refaultObject(o, id, this);
+ updatedObjectsBuffer.addObject(o);
+ } else {
+ // notify user and/or merge
+ handleUpdateConflict(id, o);
+ }
+ }
+ }
+
+ // existing invalidated objects are refaulted
+ NSArray invalidates = (NSArray) userInfo.objectForKey(EOObjectStore.InvalidatedKey);
+ e = invalidates.objectEnumerator();
+ while (e.hasMoreElements()) {
+ id = (EOGlobalID) e.nextElement();
+ o = objectForGlobalID(id);
+ if (o != null) {
+ if (updatedObjects // only invalidate if unchanged
+ .indexOfIdenticalObject(o) == NSArray.NotFound) {
+ refaultObject(o, id, this);
+ } else {
+ // notify user and/or merge
+ handleUpdateConflict(id, o);
+ }
+ if (invalidatedObjectsBuffer.indexOfIdenticalObject(o) == NSArray.NotFound) {
+ invalidatedObjectsBuffer.addObject(o);
+ }
+ if (invalidatedIDsBuffer.indexOfIdenticalObject(id) == NSArray.NotFound) {
+ invalidatedIDsBuffer.addObject(id);
+ }
+ }
+ }
+
+ }
+ }
+
+ /**
+ * Called by handleNotification to resolve the case where we have received
+ * notification that another user or context has updated an object that is
+ * currently marked as edited in this context. This implementation first asks
+ * the delegate if it should merge. If true or no delegate, the changes are
+ * merged. True or false, the delegate is then sent
+ * editingContextDidMergeChanges.
+ */
+ private void handleUpdateConflict(EOGlobalID anID, Object anObject) {
+ // if we're causing the invalidation, ignore
+ // (this is probably better handled by the caller...)
+ if (isInvalidating) {
+ ignoreChanges = true;
+ parentStore.refaultObject(anObject, anID, this);
+ ignoreChanges = false;
+ return;
+ }
+
+ Boolean result = (Boolean) notifyDelegate("editingContextShouldMergeChangesForObject",
+ new Class[] { EOEditingContext.class, Object.class }, new Object[] { this, anObject });
+
+ if ((result == null) || (Boolean.TRUE.equals(result))) {
+ // do merge
+ mergeExternalChanges(anID, anObject);
+ } else // Boolean.FALSE
+ {
+ // do nothing: don't lose the user's changes
+ }
+
+ // notify merge did happen
+ notifyDelegate("editingContextDidMergeChanges", new Class[] { EOEditingContext.class }, new Object[] { this });
+ }
+
+ /**
+ * For the currently modified object with the specified global id, this method
+ * merges changes with the updated version in the parent object store. This
+ * implementation looks at the fetch snapshot to determine which changes where
+ * made by the user, fetches the updated version of the object, and then
+ * determine what external changes were made. If the changes do not overlap, the
+ * original changes are applied to the updated version. If there is a conflict,
+ * notifies the user of the conflict.
+ */
+ private boolean mergeExternalChanges(EOGlobalID anID, Object anObject) {
+ try {
+ Iterator i;
+ Object key;
+
+ // get fetch snapshot
+ Map fetchSnapshot = committedSnapshotForObject(anObject);
+
+ // get current snapshot
+ Map currentSnapshot = currentEventSnapshotForObject(anObject);
+
+ // diff against fetch snapshot
+ Map currentDiff = new HashMap();
+ i = currentSnapshot.keySet().iterator();
+ while (i.hasNext()) {
+ key = i.next();
+ if (!currentSnapshot.get(key).equals(fetchSnapshot.get(key))) {
+ currentDiff.put(key, currentSnapshot.get(key));
+ }
+ }
+
+ // refault
+ ignoreChanges = true;
+ parentStore.refaultObject(anObject, anID, this);
+ ignoreChanges = false;
+
+ // get updated snapshot
+ Map updatedSnapshot = convertSnapshotToDictionary(takeSnapshot(anObject));
+
+ // diff against fetch snapshot
+ Map updatedDiff = new HashMap();
+ i = updatedSnapshot.keySet().iterator();
+ while (i.hasNext()) {
+ key = i.next();
+ if (!updatedSnapshot.get(key).equals(fetchSnapshot.get(key))) {
+ updatedDiff.put(key, updatedSnapshot.get(key));
+ }
+ }
+
+ // determine if there's a conflict
+ boolean proceed = true;
+ Set updatedKeys = updatedDiff.keySet();
+ i = currentDiff.keySet().iterator();
+ while (i.hasNext()) {
+ if (updatedKeys.contains(i.next())) {
+ proceed = false;
+ break;
+ }
+ }
+
+ // if no conflicts, apply original diff to current object and exit
+ if (proceed) {
+ KeyValueCodingUtilities.takeStoredValuesFromDictionary(anObject, currentDiff);
+ return true; // exit!
+ }
+ } catch (Exception exc) {
+ // log error to standard out
+ exc.printStackTrace();
+ }
+
+ // notify user we're unable to merge
+ notifyMessageHandler(MessageChangeConflict + anObject);
+ return false;
+ }
+
+ /**
+ * Sends the specified message to the message handler.
+ */
+ private void notifyMessageHandler(String aMessage) {
+ Object handler = null;
+ try {
+ handler = messageHandler();
+ if (handler == null)
+ return;
+ NSSelector.invoke("editingContextPresentErrorMessage", new Class[] { EOEditingContext.class, String.class },
+ handler, this, aMessage);
+ } catch (NoSuchMethodException e) {
+ // ignore: not implemented
+ } catch (Exception exc) {
+ // log to standard error
+ System.err.println("Error while notifying message handler: " + handler + " : " + aMessage);
+ exc.printStackTrace();
+ }
+ }
+
+ /**
+ * Sends the specified message to the delegate. Returns the return value of the
+ * method, or null if no return value or no delegate or no implementation.
+ */
+ private Object notifyDelegate(String aMethodName, Class[] types, Object[] params) {
+ try {
+ Object delegate = delegate();
+ if (delegate == null)
+ return null;
+ return NSSelector.invoke(aMethodName, types, delegate, params);
+ } catch (NoSuchMethodException e) {
+ // ignore: not implemented
+ } catch (Exception exc) {
+ // log to standard error
+ System.err.println("Error while messaging delegate: " + delegate + " : " + aMethodName);
+ exc.printStackTrace();
+ }
+
+ return null;
+ }
+
+ // interface EOObserving
+
+ /**
+ * Implementation of the EOObserving interface. Called before objects are
+ * modified.
+ */
+ public void objectWillChange(Object anObject) {
+ if (ignoreChanges)
+ return;
//NSNotificationCenter.defaultCenter().postNotification( "objectWillChange", this, new NSDictionary( "object", anObject ) );
//new RuntimeException().printStackTrace();
- willChange();
-
- // mark as updated if not marked already
- int i = updatedObjects.indexOfIdenticalObject( anObject );
- if ( i == NSArray.NotFound )
- {
- // don't mark inserted objects as updated
- i = insertedObjects.indexOfIdenticalObject( anObject );
- if ( i == NSArray.NotFound )
- {
- i = deletedObjects.indexOfIdenticalObject( anObject );
- if ( i == NSArray.NotFound )
- {
- // add object
- updatedObjects.addObject( anObject );
-
- // record revert snapshot
- registrar.setCommitSnapshot( anObject, takeSnapshot( anObject ) );
- }
- }
- }
-
- // add to buffer
- if ( updatedObjectsBuffer.indexOfIdenticalObject( anObject )
- == NSArray.NotFound )
- {
- updatedObjectsBuffer.addObject( anObject );
- }
- }
-
- // static methods
-
- public static double defaultFetchTimestampLag ()
- {
- return defaultFetchTimestampLag;
- }
-
- /**
- * Returns the default parent object store for all
- * object stores created with the parameterless
- * constructor.
- */
- public static EOObjectStore defaultParentObjectStore ()
- {
- return defaultParentObjectStore;
- }
+ willChange();
+
+ // mark as updated if not marked already
+ int i = updatedObjects.indexOfIdenticalObject(anObject);
+ if (i == NSArray.NotFound) {
+ // don't mark inserted objects as updated
+ i = insertedObjects.indexOfIdenticalObject(anObject);
+ if (i == NSArray.NotFound) {
+ i = deletedObjects.indexOfIdenticalObject(anObject);
+ if (i == NSArray.NotFound) {
+ // add object
+ updatedObjects.addObject(anObject);
+
+ // record revert snapshot
+ registrar.setCommitSnapshot(anObject, takeSnapshot(anObject));
+ }
+ }
+ }
-/*
- public static Object initObjectWithCoder (
- Object anObject,
- NSCoder aCoder )
- {
- throw new net.wotonomy.util.WotonomyException("Not implemented yet.");
- }
-*/
+ // add to buffer
+ if (updatedObjectsBuffer.indexOfIdenticalObject(anObject) == NSArray.NotFound) {
+ updatedObjectsBuffer.addObject(anObject);
+ }
+ }
- /**
- * Returns whether editing contexts are configured to retain strong
- * references to their registered objects. If false, editing contexts
- * will only retain weak references to their registered objects.
- */
- public static boolean instancesRetainRegisteredObjects()
- {
- return retainsRegisteredObjects;
- }
-
- /**
- * Sets the global default fetch timestamp lag.
- */
- public static void setDefaultFetchTimestampLag (
- double aDouble )
- {
- defaultFetchTimestampLag = aDouble;
- }
-
- /**
- * Sets the global default parent object store,
- * used for the parameterless constructor.
- */
- public static void setDefaultParentObjectStore (
- EOObjectStore anObjectStore )
- {
- defaultParentObjectStore = anObjectStore;
- }
-
- public static void setInstancesRetainRegisteredObjects (
- boolean retainsObjects )
- {
- retainsRegisteredObjects = retainsObjects;
- }
+ // static methods
-/*
- public static void setSubstitutionEditingContext (
- EOEditingContext aContext)
- {
- throw new net.wotonomy.util.WotonomyException("Not implemented yet.");
- }
-
- public static void setUsesContextRelativeEncoding (
- boolean usesRelativeEncoding )
- {
- throw new net.wotonomy.util.WotonomyException("Not implemented yet.");
- }
-
- public static EOEditingContext substitutionEditingContext ()
- {
- throw new net.wotonomy.util.WotonomyException("Not implemented yet.");
- }
-
- public static boolean usesContextRelativeEncoding ()
- {
- throw new net.wotonomy.util.WotonomyException("Not implemented yet.");
- }
-*/
+ public static double defaultFetchTimestampLag() {
+ return defaultFetchTimestampLag;
+ }
+
+ /**
+ * Returns the default parent object store for all object stores created with
+ * the parameterless constructor.
+ */
+ public static EOObjectStore defaultParentObjectStore() {
+ return defaultParentObjectStore;
+ }
+
+ /*
+ * public static Object initObjectWithCoder ( Object anObject, NSCoder aCoder )
+ * { throw new net.wotonomy.util.WotonomyException("Not implemented yet."); }
+ */
+
+ /**
+ * Returns whether editing contexts are configured to retain strong references
+ * to their registered objects. If false, editing contexts will only retain weak
+ * references to their registered objects.
+ */
+ public static boolean instancesRetainRegisteredObjects() {
+ return retainsRegisteredObjects;
+ }
+
+ /**
+ * Sets the global default fetch timestamp lag.
+ */
+ public static void setDefaultFetchTimestampLag(double aDouble) {
+ defaultFetchTimestampLag = aDouble;
+ }
+
+ /**
+ * Sets the global default parent object store, used for the parameterless
+ * constructor.
+ */
+ public static void setDefaultParentObjectStore(EOObjectStore anObjectStore) {
+ defaultParentObjectStore = anObjectStore;
+ }
+
+ public static void setInstancesRetainRegisteredObjects(boolean retainsObjects) {
+ retainsRegisteredObjects = retainsObjects;
+ }
+
+ /*
+ * public static void setSubstitutionEditingContext ( EOEditingContext aContext)
+ * { throw new net.wotonomy.util.WotonomyException("Not implemented yet."); }
+ *
+ * public static void setUsesContextRelativeEncoding ( boolean
+ * usesRelativeEncoding ) { throw new
+ * net.wotonomy.util.WotonomyException("Not implemented yet."); }
+ *
+ * public static EOEditingContext substitutionEditingContext () { throw new
+ * net.wotonomy.util.WotonomyException("Not implemented yet."); }
+ *
+ * public static boolean usesContextRelativeEncoding () { throw new
+ * net.wotonomy.util.WotonomyException("Not implemented yet."); }
+ */
+
+ public String toString() {
+ return "[EOEditingContext@" + Integer.toHexString(System.identityHashCode(this)) + ":" + " inserted="
+ + idsForObjects(insertedObjects) + " updated=" + idsForObjects(updatedObjects) + " deleted="
+ + idsForObjects(deletedObjects) + " registered=" + registrar.registeredGlobalIDs() + " ]";
+ }
+
+ private List idsForObjects(List objects) {
+ List result = new LinkedList();
+ Iterator i = objects.iterator();
+ while (i.hasNext())
+ result.add(globalIDForObject(i.next()));
+ return result;
+ }
- public String toString()
- {
- return "[EOEditingContext@"+Integer.toHexString(System.identityHashCode(this))+":"+
- " inserted="+idsForObjects(insertedObjects)+
- " updated="+idsForObjects(updatedObjects)+
- " deleted="+idsForObjects(deletedObjects)+
- " registered="+registrar.registeredGlobalIDs()+" ]";
- }
- private List idsForObjects( List objects )
- {
- List result = new LinkedList();
- Iterator i = objects.iterator();
- while ( i.hasNext() ) result.add( globalIDForObject( i.next() ) );
- return result;
- }
-
- // snapshots
-
- /**
- * Returns a NSDictionary containing only the mutable properties
- * for the specified object and deep clones of their values.
- * Nulls are represented by NSNull.nullValue().
- */
- private byte[] takeSnapshot( Object anObject )
- { // System.out.println( "takeSnapshot: " + anObject );
- return KeyValueCodingUtilities.freeze( anObject, this, anObject, true );
- }
-
- /**
- * Applies the map of properties and values to the
- * specified object. Null values for properties must
- * be represented by the NSNull.nullValue().
- * Posts a willChange event before applying changes.
- */
- private void applySnapshot( byte[] aSnapshot, Object anObject )
- {
- // must clone snapshot to avoid changing existing snapshot
- NSDictionary values = convertSnapshotToDictionary( aSnapshot );
+ // snapshots
+
+ /**
+ * Returns a NSDictionary containing only the mutable properties for the
+ * specified object and deep clones of their values. Nulls are represented by
+ * NSNull.nullValue().
+ */
+ private byte[] takeSnapshot(Object anObject) { // System.out.println( "takeSnapshot: " + anObject );
+ return KeyValueCodingUtilities.freeze(anObject, this, anObject, true);
+ }
+
+ /**
+ * Applies the map of properties and values to the specified object. Null values
+ * for properties must be represented by the NSNull.nullValue(). Posts a
+ * willChange event before applying changes.
+ */
+ private void applySnapshot(byte[] aSnapshot, Object anObject) {
+ // must clone snapshot to avoid changing existing snapshot
+ NSDictionary values = convertSnapshotToDictionary(aSnapshot);
//System.out.println( "applySnapshot: " + aSnapshot + " : " + values );
//ignoreChanges = true;
- willChange();
- KeyValueCodingUtilities.takeStoredValuesFromDictionary( anObject, values );
+ willChange();
+ KeyValueCodingUtilities.takeStoredValuesFromDictionary(anObject, values);
//ignoreChanges = false;
- }
-
- /**
- * Snapshots are stored internally in binary format,
- * but exposed to the user as NSDictionaries.
- */
- private NSDictionary convertSnapshotToDictionary( byte[] aSnapshot )
- {
- // get the object
- Object clone = KeyValueCodingUtilities.thaw( aSnapshot, this, true );
- // get all keys for this object
- EOClassDescription classDesc =
- EOClassDescription.classDescriptionForClass( clone.getClass() );
- List keys = new LinkedList();
- keys.addAll( classDesc.attributeKeys() );
- keys.addAll( classDesc.toOneRelationshipKeys() );
- keys.addAll( classDesc.toManyRelationshipKeys() );
-
- return KeyValueCodingUtilities.valuesForKeys( clone, keys );
- }
- /**
- * Creates a deep clone of the specified object.
- * (Object.clone() only creates a shallow clone.)
- * Returns null if operation fails.
- */
- static private Object clone(
- EOEditingContext aSourceContext,
- Object aSource,
- EOEditingContext aDestinationContext )
- { // System.out.println( "clone: " + aSource );
- return KeyValueCodingUtilities.clone(
- aSourceContext, aSource, aDestinationContext );
- }
-
- /**
- * Creates a deep clone of the specified object.
- * but does not transpose references. This allows
- * us to register an object before transposing
- * references so that child objects will be able
- * to resolve references to their parent.
- * After recording the object, we copy the source
- * object into the clone, which does transpose
- * and resolve properly.
- * Returns null if operation fails.
- */
- static private Object registerClone(
- EOGlobalID aGlobalID,
- EOEditingContext aSourceContext,
- Object aSource,
- EOEditingContext aDestinationContext )
- {
- // while we'd like to just transpose/clone at the same time
- // we must record a clone without transposing: this
- // avoids a endless loop if the object graph has a cycle
- Object clone = KeyValueCodingUtilities.thaw(
- KeyValueCodingUtilities.freeze(
- aSource, aSourceContext, aSource, false ),
- aDestinationContext, false );
-
- aDestinationContext.recordObject( clone, aGlobalID );
-
- // need to copy to transpose references into this context
- // while preserving the same instance of the object
- aDestinationContext.ignoreChanges = true;
- copy( aSourceContext, aSource, aDestinationContext, clone );
- aDestinationContext.ignoreChanges = false;
-
- // return our clone
- return clone;
- }
-
- /**
- * Copies values from one object to another.
- * Returns the destination object, or throws exception
- * if operation fails.
- */
- static private Object copy(
- EOEditingContext aSourceContext,
- Object aSource,
- EOEditingContext aDestinationContext,
- Object aDestination )
- { // System.out.println( "copy: " );
- EOObserverCenter.notifyObserversObjectWillChange( aDestination );
- KeyValueCodingUtilities.copy( aSourceContext, aSource, aDestinationContext, aDestination );
- return aDestination;
- }
-
- // process recent changes
-
- /**
- * Called to notify observers of changes.
- * Also calls runLater().
- */
- private void willChange()
- {
- EOObserverCenter.notifyObserversObjectWillChange( this );
- runLater();
- }
-
- /**
- * Called to ensure that processRecentChanges
- * will be called on the next event loop.
- */
- private void runLater()
- {
- if ( ! willRunLater )
- {
+ }
+
+ /**
+ * Snapshots are stored internally in binary format, but exposed to the user as
+ * NSDictionaries.
+ */
+ private NSDictionary convertSnapshotToDictionary(byte[] aSnapshot) {
+ // get the object
+ Object clone = KeyValueCodingUtilities.thaw(aSnapshot, this, true);
+ // get all keys for this object
+ EOClassDescription classDesc = EOClassDescription.classDescriptionForClass(clone.getClass());
+ List keys = new LinkedList();
+ keys.addAll(classDesc.attributeKeys());
+ keys.addAll(classDesc.toOneRelationshipKeys());
+ keys.addAll(classDesc.toManyRelationshipKeys());
+
+ return KeyValueCodingUtilities.valuesForKeys(clone, keys);
+ }
+
+ /**
+ * Creates a deep clone of the specified object. (Object.clone() only creates a
+ * shallow clone.) Returns null if operation fails.
+ */
+ static private Object clone(EOEditingContext aSourceContext, Object aSource, EOEditingContext aDestinationContext) { // System.out.println(
+ // "clone:
+ // "
+ // +
+ // aSource
+ // );
+ return KeyValueCodingUtilities.clone(aSourceContext, aSource, aDestinationContext);
+ }
+
+ /**
+ * Creates a deep clone of the specified object. but does not transpose
+ * references. This allows us to register an object before transposing
+ * references so that child objects will be able to resolve references to their
+ * parent. After recording the object, we copy the source object into the clone,
+ * which does transpose and resolve properly. Returns null if operation fails.
+ */
+ static private Object registerClone(EOGlobalID aGlobalID, EOEditingContext aSourceContext, Object aSource,
+ EOEditingContext aDestinationContext) {
+ // while we'd like to just transpose/clone at the same time
+ // we must record a clone without transposing: this
+ // avoids a endless loop if the object graph has a cycle
+ Object clone = KeyValueCodingUtilities.thaw(
+ KeyValueCodingUtilities.freeze(aSource, aSourceContext, aSource, false), aDestinationContext, false);
+
+ aDestinationContext.recordObject(clone, aGlobalID);
+
+ // need to copy to transpose references into this context
+ // while preserving the same instance of the object
+ aDestinationContext.ignoreChanges = true;
+ copy(aSourceContext, aSource, aDestinationContext, clone);
+ aDestinationContext.ignoreChanges = false;
+
+ // return our clone
+ return clone;
+ }
+
+ /**
+ * Copies values from one object to another. Returns the destination object, or
+ * throws exception if operation fails.
+ */
+ static private Object copy(EOEditingContext aSourceContext, Object aSource, EOEditingContext aDestinationContext,
+ Object aDestination) { // System.out.println( "copy: " );
+ EOObserverCenter.notifyObserversObjectWillChange(aDestination);
+ KeyValueCodingUtilities.copy(aSourceContext, aSource, aDestinationContext, aDestination);
+ return aDestination;
+ }
+
+ // process recent changes
+
+ /**
+ * Called to notify observers of changes. Also calls runLater().
+ */
+ private void willChange() {
+ EOObserverCenter.notifyObserversObjectWillChange(this);
+ runLater();
+ }
+
+ /**
+ * Called to ensure that processRecentChanges will be called on the next event
+ * loop.
+ */
+ private void runLater() {
+ if (!willRunLater) {
willRunLater = true;
- NSRunLoop.currentRunLoop().performSelectorWithOrder(
- runLaterSelector, this, null, EditingContextFlushChangesRunLoopOrdering, null );
+ NSRunLoop.currentRunLoop().performSelectorWithOrder(runLaterSelector, this, null,
+ EditingContextFlushChangesRunLoopOrdering, null);
}
}
-
+
/**
- * This method is called by the event queue run loop
- * and calls processRecentChanges.
- * NOTE: This method is not part of the specification.
- */
- public void flushRecentChanges( Object anObject )
- {
+ * This method is called by the event queue run loop and calls
+ * processRecentChanges. NOTE: This method is not part of the specification.
+ */
+ public void flushRecentChanges(Object anObject) {
//System.out.println( "EODelayedObserverQueue: running" );
- processRecentChanges();
+ processRecentChanges();
willRunLater = false;
}
-
- // inner classes
-
- /**
- * Gatekeeper for all access to registered objects.
- */
- static private class Registrar
- {
- EOEditingContext context;
- NSMutableDictionary IDsToObjects;
- NSMutableDictionary objectsToIDs;
- NSMutableDictionary objectsToCommitSnapshots;
- NSMutableDictionary objectsToCurrentSnapshots;
-
- ReferenceKey comparisonKey; //FIXME not thread safe!
-
- public Registrar( EOEditingContext aContext )
- {
- context = aContext;
- IDsToObjects = new NSMutableDictionary();
- objectsToIDs = new NSMutableDictionary();
- objectsToCommitSnapshots = new NSMutableDictionary();
- objectsToCurrentSnapshots = new NSMutableDictionary();
- comparisonKey = new ReferenceKey();
- }
-
- public NSArray registeredObjects()
- {
- return IDsToObjects.allValues();
- }
-
- public NSArray registeredGlobalIDs()
- {
- return IDsToObjects.allKeys();
- }
-
- public Object objectForGlobalID( EOGlobalID aGlobalID )
- {
- return IDsToObjects.objectForKey( aGlobalID );
- }
-
- public EOGlobalID globalIDForObject( Object anObject )
- {
- comparisonKey.set( anObject );
- return (EOGlobalID) objectsToIDs.objectForKey( comparisonKey );
- }
-
- public byte[] getCommitSnapshot( Object anObject )
- {
- comparisonKey.set( anObject );
- return (byte[]) objectsToCommitSnapshots.objectForKey( comparisonKey );
- }
-
- public void setCommitSnapshot( Object anObject, byte[] aSnapshot )
- {
- if ( aSnapshot == null )
- {
- comparisonKey.set( anObject );
- objectsToCommitSnapshots.removeObjectForKey( comparisonKey );
- }
- else
- {
- objectsToCommitSnapshots.setObjectForKey(
- aSnapshot, new ReferenceKey( anObject ) );
- }
- }
-
- public byte[] getCurrentSnapshot( Object anObject )
- {
- comparisonKey.set( anObject );
- return (byte[]) objectsToCurrentSnapshots.objectForKey( comparisonKey );
- }
-
- public void setCurrentSnapshot( Object anObject, byte[] aSnapshot )
- {
- if ( aSnapshot == null )
- {
- comparisonKey.set( anObject );
- objectsToCurrentSnapshots.removeObjectForKey( comparisonKey );
- }
- else
- {
- objectsToCurrentSnapshots.setObjectForKey(
- aSnapshot, new ReferenceKey( anObject ) );
- }
- }
-
- public void registerObject( Object anObject, EOGlobalID aGlobalID )
- {
- IDsToObjects.setObjectForKey( anObject, aGlobalID );
- objectsToIDs.setObjectForKey( aGlobalID, new ReferenceKey( anObject ) );
- EOObserverCenter.addObserver( context, anObject );
- }
-
- public void forgetObject( Object anObject )
- {
- comparisonKey.set( anObject );
- Object id = objectsToIDs.objectForKey( comparisonKey );
- IDsToObjects.removeObjectForKey( id );
- objectsToIDs.removeObjectForKey( comparisonKey );
- EOObserverCenter.removeObserver( context, anObject );
- }
-
- public void disposeSnapshots( Object anObject )
- {
- setCommitSnapshot( anObject, null );
- setCurrentSnapshot( anObject, null );
- }
-
- }
-
- /**
- * Registrar that uses only WeakReferences.
- * Used if retainsRegisteredObjects is false.
- */
- static private class WeakRegistrar extends Registrar
- {
- private WeakReferenceKey weakComparisonKey; //FIXME not thread safe!
-
- public WeakRegistrar( EOEditingContext aContext )
- {
- super( aContext );
- weakComparisonKey = new WeakReferenceKey();
- }
-
- public NSArray registeredObjects()
- {
- Object object;
- WeakReferenceKey weakKey;
- NSMutableArray result = new NSMutableArray();
- Enumeration e = new NSArray( objectsToIDs.allKeys() ).objectEnumerator();
- while ( e.hasMoreElements() )
- {
- weakKey = (WeakReferenceKey) e.nextElement();
- object = weakKey.get();
- if ( object != null )
- {
- result.addObject( object );
- }
- else
- {
- // object has been released: perform cleanup
- disposeObject( null, weakKey );
- }
- }
- return result;
- }
-
- public Object objectForGlobalID( EOGlobalID aGlobalID )
- {
- WeakReference ref = (WeakReference) super.objectForGlobalID( aGlobalID );
- if ( ref == null ) return null;
- Object result = ref.get();
- if ( result == null )
- {
- // clean up manually
- IDsToObjects.removeObjectForKey( aGlobalID );
- Iterator i = new LinkedList( objectsToIDs.allKeysForObject( ref ) ).iterator();
- while ( i.hasNext() )
- {
- objectsToIDs.removeObjectForKey( i.next() );
- }
- disposeSnapshots( aGlobalID );
- }
- return result;
- }
-
- public byte[] getCommitSnapshot( Object anObject )
- {
- weakComparisonKey.set( anObject );
- return (byte[]) objectsToCommitSnapshots.objectForKey( weakComparisonKey );
- }
-
- public void setCommitSnapshot( Object anObject, byte[] aSnapshot )
- {
- if ( aSnapshot == null )
- {
- weakComparisonKey.set( anObject );
- objectsToCommitSnapshots.removeObjectForKey( weakComparisonKey );
- }
- else
- {
- objectsToCommitSnapshots.setObjectForKey(
- aSnapshot, new WeakReferenceKey( anObject ) );
- }
- }
-
- public byte[] getCurrentSnapshot( Object anObject )
- {
- weakComparisonKey.set( anObject );
- return (byte[]) objectsToCurrentSnapshots.objectForKey( weakComparisonKey );
- }
-
- public void setCurrentSnapshot( Object anObject, byte[] aSnapshot )
- {
- if ( aSnapshot == null )
- {
- weakComparisonKey.set( anObject );
- objectsToCurrentSnapshots.removeObjectForKey( weakComparisonKey );
- }
- else
- {
- objectsToCurrentSnapshots.setObjectForKey(
- aSnapshot, new WeakReferenceKey( anObject ) );
- }
- }
-
- public void registerObject( Object anObject, EOGlobalID aGlobalID )
- { // new net.wotonomy.ui.swing.ReferenceInspector( anObject );
- IDsToObjects.setObjectForKey( new WeakReference( anObject ), aGlobalID );
- objectsToIDs.setObjectForKey( aGlobalID, new WeakReferenceKey( anObject ) );
- EOObserverCenter.addObserver( context, anObject );
- }
-
- public void forgetObject( Object anObject )
- {
- disposeObject( anObject, null );
- }
-
- // must specify one or the other
- private void disposeObject( Object anObject, WeakReferenceKey key )
- {
- if ( key == null ) key = new WeakReferenceKey( anObject );
- EOGlobalID id = (EOGlobalID) objectsToIDs.objectForKey( key );
- if ( id != null ) IDsToObjects.removeObjectForKey( id );
- objectsToIDs.removeObjectForKey( key );
- disposeSnapshots( id );
- if ( anObject != null )
- {
- EOObserverCenter.removeObserver( context, anObject );
- }
- }
- }
-
- /**
- * Private class used to force a hashmap to
- * perform key comparisons by reference.
- */
- static private class ReferenceKey
- {
- int hashCode;
- Object referent;
-
- public ReferenceKey()
- {
- referent = null;
- hashCode = -1;
- }
-
- public ReferenceKey( Object anObject )
- {
- set( anObject );
- }
-
- public Object get()
- {
- return referent;
- }
-
- public void set( Object anObject )
- {
- referent = anObject;
- hashCode = anObject.hashCode();
- }
-
- /**
- * Returns the actual key's hash code.
- */
- public int hashCode()
- {
- return hashCode;
- }
-
- /**
- * Compares by reference.
- */
- public boolean equals( Object anObject )
- {
- if ( anObject == this ) return true;
- if ( anObject instanceof ReferenceKey )
- {
- return ((ReferenceKey)anObject).get() == referent;
- }
- return false;
- }
- }
-
- /**
- * Private class used to force a hashmap to
- * perform key comparisons by reference.
- */
- static private class WeakReferenceKey extends ReferenceKey
- {
- public WeakReferenceKey()
- {
- super();
- }
-
- public WeakReferenceKey( Object anObject )
- {
- super( anObject );
- }
-
- public Object get()
- {
- return ((WeakReference)referent).get();
- }
-
- public void set( Object anObject )
- {
- referent = new WeakReference( anObject );
- hashCode = anObject.hashCode();
- }
-
- /**
- * Compares by reference.
- */
- public boolean equals( Object anObject )
- {
- if ( anObject == this ) return true;
- if ( anObject instanceof ReferenceKey )
- {
- return ((ReferenceKey)anObject).get() == get();
- }
- return false;
- }
- }
-
- /**
- * Key combining an object with a string.
- * Object is compared by reference.
- */
- static private class CompoundKey
- {
- private Object object;
- private String string;
- private int hashCode;
-
- /**
- * Creates compound key.
- * Neither name nor object may be null.
- */
- public CompoundKey (
- Object anObject, String aString )
- {
- object = anObject;
- string = aString;
- hashCode = object.hashCode() + string.hashCode();
- }
-
- public int hashCode()
- {
- return hashCode;
- }
-
- public boolean equals( Object anObject )
- {
- if ( anObject instanceof CompoundKey )
- {
- CompoundKey key = (CompoundKey) anObject;
- return ( ( key.object == object ) && ( key.string.equals( string ) ) );
- }
- return false;
- }
-
- public String toString()
- {
- return "[CompoundKey:"+object+":"+string+"]";
- }
- }
-
- /**
- * Used by EditingContext to delegate behavior to another class.
- * Note that EditingContext doesn't require its delegates to implement
- * this interface: rather, this interface defines the methods that
- * EditingContext will attempt to invoke dynamically on its delegate.
- * The delegate may choose to implement only a subset of the methods
- * on the interface.
- */
- public interface Delegate
- {
- /**
- * Called after the editing context has completed merge operations
- * on one or more objects after receiving an ObjectChangedInStore
- * notification.
- */
- void editingContextDidMergeChanges(
- EOEditingContext anEditingContext );
-
- /**
- * Called by objectsWithFetchSpecification.
- * If null, the editing context will pass the fetch specification
- * on to its parent store, as normal. Otherwise, the context
- * will use the returned array to service the request.
- */
- NSArray editingContextShouldFetchObjects(
- EOEditingContext anEditingContext,
- EOFetchSpecification fetchSpecification );
-
- /**
- * Called to determine whether an object should be invalidated.
- * Return false to prevent the object from being invalidated.
- * Default is true.
- */
- boolean editingContextShouldInvalidateObject(
- EOEditingContext anEditingContext,
- Object anObject,
- EOGlobalID aGlobalID );
-
- /**
- * Called to determine whether the editing context should attempt
- * to merge changes in the specified object that the parent store
- * says has changed via an ObjectChangedInStore notification.
- * Default is true. Return false if you wish to handle the merge
- * yourself, by extracting the values in the object now and comparing
- * them to the values when editingContextDidMergeChanges is called.
- */
- boolean editingContextShouldMergeChangesForObject(
- EOEditingContext anEditingContext,
- Object anObject );
-
- /**
- * Returns whether the editing context should ask its message handler
- * to display a message. Return false if the delegate will display the error.
- * Default is true.
- */
- boolean editingContextShouldPresentException(
- EOEditingContext anEditingContext,
- Throwable exception );
-
- /**
- * Returns whether the editing context should undo the most
- * recent set of changes that resulted in a validation failure.
- * Default is true.
- */
- boolean editingContextShouldUndoUserActionsAfterFailure(
- EOEditingContext anEditingContext );
-
- /**
- * Returns whether the editing context should validate the
- * most recent set of changes. Default is true.
- */
- boolean editingContextShouldValidateChanges(
- EOEditingContext anEditingContext );
-
- /**
- * Called before the editing context saves its changes
- * to the parent object store.
- */
- void editingContextWillSaveChanges(
- EOEditingContext anEditingContext );
-
- }
-
- /**
- * Editors register themselves with the editing context
- * so that they may receive notification before the context
- * commits changes. This is useful for associations whose
- * components do not immediately commit their changes to
- * the object they are editing.
- */
- public interface Editor
- {
- /**
- * Called before the editing context saves its changes
- * to the parent object store.
- */
- void editingContextWillSaveChanges(
- EOEditingContext anEditingContext );
-
- /**
- * Called to determine whether this editor has changes
- * that have not been committed to the object in the context.
- */
- boolean editorHasChangesForEditingContext(
- EOEditingContext anEditingContext );
-
- }
-
- /**
- * Used by EditingContext to delegate messaging handling to another class,
- * typically the display group that has the currently active association.
- * Note that EditingContext doesn't require its message handlers to implement
- * this interface: rather, this interface defines the methods that
- * EditingContext will attempt to invoke dynamically on its delegate.
- * The delegate may choose to implement only a subset of the methods
- * on the interface.
- */
- public interface MessageHandler
- {
- /**
- * Called to display a message for an error that occurred
- * in the specified editing context.
- */
- void editingContextPresentErrorMessage(
- EOEditingContext anEditingContext,
- String aMessage );
-
- /**
- * Called by the specified object store to determine whether
- * fetching should continue, where count is the current count
- * and limit is the limit as specified by the fetch specification.
- * Default is false.
- */
- boolean editingContextShouldContinueFetching(
- EOEditingContext anEditingContext,
- int count,
- int limit,
- EOObjectStore anObjectStore );
-
- }
-
+
+ // inner classes
+
+ /**
+ * Gatekeeper for all access to registered objects.
+ */
+ static private class Registrar {
+ EOEditingContext context;
+ NSMutableDictionary IDsToObjects;
+ NSMutableDictionary objectsToIDs;
+ NSMutableDictionary objectsToCommitSnapshots;
+ NSMutableDictionary objectsToCurrentSnapshots;
+
+ ReferenceKey comparisonKey; // FIXME not thread safe!
+
+ public Registrar(EOEditingContext aContext) {
+ context = aContext;
+ IDsToObjects = new NSMutableDictionary();
+ objectsToIDs = new NSMutableDictionary();
+ objectsToCommitSnapshots = new NSMutableDictionary();
+ objectsToCurrentSnapshots = new NSMutableDictionary();
+ comparisonKey = new ReferenceKey();
+ }
+
+ public NSArray registeredObjects() {
+ return IDsToObjects.allValues();
+ }
+
+ public NSArray registeredGlobalIDs() {
+ return IDsToObjects.allKeys();
+ }
+
+ public Object objectForGlobalID(EOGlobalID aGlobalID) {
+ return IDsToObjects.objectForKey(aGlobalID);
+ }
+
+ public EOGlobalID globalIDForObject(Object anObject) {
+ comparisonKey.set(anObject);
+ return (EOGlobalID) objectsToIDs.objectForKey(comparisonKey);
+ }
+
+ public byte[] getCommitSnapshot(Object anObject) {
+ comparisonKey.set(anObject);
+ return (byte[]) objectsToCommitSnapshots.objectForKey(comparisonKey);
+ }
+
+ public void setCommitSnapshot(Object anObject, byte[] aSnapshot) {
+ if (aSnapshot == null) {
+ comparisonKey.set(anObject);
+ objectsToCommitSnapshots.removeObjectForKey(comparisonKey);
+ } else {
+ objectsToCommitSnapshots.setObjectForKey(aSnapshot, new ReferenceKey(anObject));
+ }
+ }
+
+ public byte[] getCurrentSnapshot(Object anObject) {
+ comparisonKey.set(anObject);
+ return (byte[]) objectsToCurrentSnapshots.objectForKey(comparisonKey);
+ }
+
+ public void setCurrentSnapshot(Object anObject, byte[] aSnapshot) {
+ if (aSnapshot == null) {
+ comparisonKey.set(anObject);
+ objectsToCurrentSnapshots.removeObjectForKey(comparisonKey);
+ } else {
+ objectsToCurrentSnapshots.setObjectForKey(aSnapshot, new ReferenceKey(anObject));
+ }
+ }
+
+ public void registerObject(Object anObject, EOGlobalID aGlobalID) {
+ IDsToObjects.setObjectForKey(anObject, aGlobalID);
+ objectsToIDs.setObjectForKey(aGlobalID, new ReferenceKey(anObject));
+ EOObserverCenter.addObserver(context, anObject);
+ }
+
+ public void forgetObject(Object anObject) {
+ comparisonKey.set(anObject);
+ Object id = objectsToIDs.objectForKey(comparisonKey);
+ IDsToObjects.removeObjectForKey(id);
+ objectsToIDs.removeObjectForKey(comparisonKey);
+ EOObserverCenter.removeObserver(context, anObject);
+ }
+
+ public void disposeSnapshots(Object anObject) {
+ setCommitSnapshot(anObject, null);
+ setCurrentSnapshot(anObject, null);
+ }
+
+ }
+
+ /**
+ * Registrar that uses only WeakReferences. Used if retainsRegisteredObjects is
+ * false.
+ */
+ static private class WeakRegistrar extends Registrar {
+ private WeakReferenceKey weakComparisonKey; // FIXME not thread safe!
+
+ public WeakRegistrar(EOEditingContext aContext) {
+ super(aContext);
+ weakComparisonKey = new WeakReferenceKey();
+ }
+
+ public NSArray registeredObjects() {
+ Object object;
+ WeakReferenceKey weakKey;
+ NSMutableArray result = new NSMutableArray();
+ Enumeration e = new NSArray(objectsToIDs.allKeys()).objectEnumerator();
+ while (e.hasMoreElements()) {
+ weakKey = (WeakReferenceKey) e.nextElement();
+ object = weakKey.get();
+ if (object != null) {
+ result.addObject(object);
+ } else {
+ // object has been released: perform cleanup
+ disposeObject(null, weakKey);
+ }
+ }
+ return result;
+ }
+
+ public Object objectForGlobalID(EOGlobalID aGlobalID) {
+ WeakReference ref = (WeakReference) super.objectForGlobalID(aGlobalID);
+ if (ref == null)
+ return null;
+ Object result = ref.get();
+ if (result == null) {
+ // clean up manually
+ IDsToObjects.removeObjectForKey(aGlobalID);
+ Iterator i = new LinkedList(objectsToIDs.allKeysForObject(ref)).iterator();
+ while (i.hasNext()) {
+ objectsToIDs.removeObjectForKey(i.next());
+ }
+ disposeSnapshots(aGlobalID);
+ }
+ return result;
+ }
+
+ public byte[] getCommitSnapshot(Object anObject) {
+ weakComparisonKey.set(anObject);
+ return (byte[]) objectsToCommitSnapshots.objectForKey(weakComparisonKey);
+ }
+
+ public void setCommitSnapshot(Object anObject, byte[] aSnapshot) {
+ if (aSnapshot == null) {
+ weakComparisonKey.set(anObject);
+ objectsToCommitSnapshots.removeObjectForKey(weakComparisonKey);
+ } else {
+ objectsToCommitSnapshots.setObjectForKey(aSnapshot, new WeakReferenceKey(anObject));
+ }
+ }
+
+ public byte[] getCurrentSnapshot(Object anObject) {
+ weakComparisonKey.set(anObject);
+ return (byte[]) objectsToCurrentSnapshots.objectForKey(weakComparisonKey);
+ }
+
+ public void setCurrentSnapshot(Object anObject, byte[] aSnapshot) {
+ if (aSnapshot == null) {
+ weakComparisonKey.set(anObject);
+ objectsToCurrentSnapshots.removeObjectForKey(weakComparisonKey);
+ } else {
+ objectsToCurrentSnapshots.setObjectForKey(aSnapshot, new WeakReferenceKey(anObject));
+ }
+ }
+
+ public void registerObject(Object anObject, EOGlobalID aGlobalID) { // new
+ // net.wotonomy.ui.swing.ReferenceInspector(
+ // anObject );
+ IDsToObjects.setObjectForKey(new WeakReference(anObject), aGlobalID);
+ objectsToIDs.setObjectForKey(aGlobalID, new WeakReferenceKey(anObject));
+ EOObserverCenter.addObserver(context, anObject);
+ }
+
+ public void forgetObject(Object anObject) {
+ disposeObject(anObject, null);
+ }
+
+ // must specify one or the other
+ private void disposeObject(Object anObject, WeakReferenceKey key) {
+ if (key == null)
+ key = new WeakReferenceKey(anObject);
+ EOGlobalID id = (EOGlobalID) objectsToIDs.objectForKey(key);
+ if (id != null)
+ IDsToObjects.removeObjectForKey(id);
+ objectsToIDs.removeObjectForKey(key);
+ disposeSnapshots(id);
+ if (anObject != null) {
+ EOObserverCenter.removeObserver(context, anObject);
+ }
+ }
+ }
+
+ /**
+ * Private class used to force a hashmap to perform key comparisons by
+ * reference.
+ */
+ static private class ReferenceKey {
+ int hashCode;
+ Object referent;
+
+ public ReferenceKey() {
+ referent = null;
+ hashCode = -1;
+ }
+
+ public ReferenceKey(Object anObject) {
+ set(anObject);
+ }
+
+ public Object get() {
+ return referent;
+ }
+
+ public void set(Object anObject) {
+ referent = anObject;
+ hashCode = anObject.hashCode();
+ }
+
+ /**
+ * Returns the actual key's hash code.
+ */
+ public int hashCode() {
+ return hashCode;
+ }
+
+ /**
+ * Compares by reference.
+ */
+ public boolean equals(Object anObject) {
+ if (anObject == this)
+ return true;
+ if (anObject instanceof ReferenceKey) {
+ return ((ReferenceKey) anObject).get() == referent;
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Private class used to force a hashmap to perform key comparisons by
+ * reference.
+ */
+ static private class WeakReferenceKey extends ReferenceKey {
+ public WeakReferenceKey() {
+ super();
+ }
+
+ public WeakReferenceKey(Object anObject) {
+ super(anObject);
+ }
+
+ public Object get() {
+ return ((WeakReference) referent).get();
+ }
+
+ public void set(Object anObject) {
+ referent = new WeakReference(anObject);
+ hashCode = anObject.hashCode();
+ }
+
+ /**
+ * Compares by reference.
+ */
+ public boolean equals(Object anObject) {
+ if (anObject == this)
+ return true;
+ if (anObject instanceof ReferenceKey) {
+ return ((ReferenceKey) anObject).get() == get();
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Key combining an object with a string. Object is compared by reference.
+ */
+ static private class CompoundKey {
+ private Object object;
+ private String string;
+ private int hashCode;
+
+ /**
+ * Creates compound key. Neither name nor object may be null.
+ */
+ public CompoundKey(Object anObject, String aString) {
+ object = anObject;
+ string = aString;
+ hashCode = object.hashCode() + string.hashCode();
+ }
+
+ public int hashCode() {
+ return hashCode;
+ }
+
+ public boolean equals(Object anObject) {
+ if (anObject instanceof CompoundKey) {
+ CompoundKey key = (CompoundKey) anObject;
+ return ((key.object == object) && (key.string.equals(string)));
+ }
+ return false;
+ }
+
+ public String toString() {
+ return "[CompoundKey:" + object + ":" + string + "]";
+ }
+ }
+
+ /**
+ * Used by EditingContext to delegate behavior to another class. Note that
+ * EditingContext doesn't require its delegates to implement this interface:
+ * rather, this interface defines the methods that EditingContext will attempt
+ * to invoke dynamically on its delegate. The delegate may choose to implement
+ * only a subset of the methods on the interface.
+ */
+ public interface Delegate {
+ /**
+ * Called after the editing context has completed merge operations on one or
+ * more objects after receiving an ObjectChangedInStore notification.
+ */
+ void editingContextDidMergeChanges(EOEditingContext anEditingContext);
+
+ /**
+ * Called by objectsWithFetchSpecification. If null, the editing context will
+ * pass the fetch specification on to its parent store, as normal. Otherwise,
+ * the context will use the returned array to service the request.
+ */
+ NSArray editingContextShouldFetchObjects(EOEditingContext anEditingContext,
+ EOFetchSpecification fetchSpecification);
+
+ /**
+ * Called to determine whether an object should be invalidated. Return false to
+ * prevent the object from being invalidated. Default is true.
+ */
+ boolean editingContextShouldInvalidateObject(EOEditingContext anEditingContext, Object anObject,
+ EOGlobalID aGlobalID);
+
+ /**
+ * Called to determine whether the editing context should attempt to merge
+ * changes in the specified object that the parent store says has changed via an
+ * ObjectChangedInStore notification. Default is true. Return false if you wish
+ * to handle the merge yourself, by extracting the values in the object now and
+ * comparing them to the values when editingContextDidMergeChanges is called.
+ */
+ boolean editingContextShouldMergeChangesForObject(EOEditingContext anEditingContext, Object anObject);
+
+ /**
+ * Returns whether the editing context should ask its message handler to display
+ * a message. Return false if the delegate will display the error. Default is
+ * true.
+ */
+ boolean editingContextShouldPresentException(EOEditingContext anEditingContext, Throwable exception);
+
+ /**
+ * Returns whether the editing context should undo the most recent set of
+ * changes that resulted in a validation failure. Default is true.
+ */
+ boolean editingContextShouldUndoUserActionsAfterFailure(EOEditingContext anEditingContext);
+
+ /**
+ * Returns whether the editing context should validate the most recent set of
+ * changes. Default is true.
+ */
+ boolean editingContextShouldValidateChanges(EOEditingContext anEditingContext);
+
+ /**
+ * Called before the editing context saves its changes to the parent object
+ * store.
+ */
+ void editingContextWillSaveChanges(EOEditingContext anEditingContext);
+
+ }
+
+ /**
+ * Editors register themselves with the editing context so that they may receive
+ * notification before the context commits changes. This is useful for
+ * associations whose components do not immediately commit their changes to the
+ * object they are editing.
+ */
+ public interface Editor {
+ /**
+ * Called before the editing context saves its changes to the parent object
+ * store.
+ */
+ void editingContextWillSaveChanges(EOEditingContext anEditingContext);
+
+ /**
+ * Called to determine whether this editor has changes that have not been
+ * committed to the object in the context.
+ */
+ boolean editorHasChangesForEditingContext(EOEditingContext anEditingContext);
+
+ }
+
+ /**
+ * Used by EditingContext to delegate messaging handling to another class,
+ * typically the display group that has the currently active association. Note
+ * that EditingContext doesn't require its message handlers to implement this
+ * interface: rather, this interface defines the methods that EditingContext
+ * will attempt to invoke dynamically on its delegate. The delegate may choose
+ * to implement only a subset of the methods on the interface.
+ */
+ public interface MessageHandler {
+ /**
+ * Called to display a message for an error that occurred in the specified
+ * editing context.
+ */
+ void editingContextPresentErrorMessage(EOEditingContext anEditingContext, String aMessage);
+
+ /**
+ * Called by the specified object store to determine whether fetching should
+ * continue, where count is the current count and limit is the limit as
+ * specified by the fetch specification. Default is false.
+ */
+ boolean editingContextShouldContinueFetching(EOEditingContext anEditingContext, int count, int limit,
+ EOObjectStore anObjectStore);
+
+ }
+
}
/*
- * $Log$
- * Revision 1.2 2006/02/16 16:47:14 cgruber
- * Move some classes in to "internal" packages and re-work imports, etc.
+ * $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.
+ * 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.1 2006/02/16 13:19:57 cgruber Check in all sources in
+ * eclipse-friendly maven-enabled packages.
*
- * Revision 1.86 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.86 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.85 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.85 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.84 2003/08/06 23:07:52 chochos
- * general code cleanup (mostly, removing unused imports)
+ * Revision 1.84 2003/08/06 23:07:52 chochos general code cleanup (mostly,
+ * removing unused imports)
*
- * Revision 1.83 2003/02/13 15:24:33 mpowers
- * hasChanges is now derived, not tracked.
- * refaultObject now more consistently removes object from updated list.
+ * Revision 1.83 2003/02/13 15:24:33 mpowers hasChanges is now derived, not
+ * tracked. refaultObject now more consistently removes object from updated
+ * list.
*
- * Revision 1.82 2002/12/16 15:46:00 mpowers
- * Major refactoring to implement setInstancesRetainRegisteredObjects().
+ * Revision 1.82 2002/12/16 15:46:00 mpowers Major refactoring to implement
+ * setInstancesRetainRegisteredObjects().
*
- * Revision 1.81 2002/11/18 22:10:58 mpowers
- * Now resetting hasChanges flag on reset.
+ * Revision 1.81 2002/11/18 22:10:58 mpowers Now resetting hasChanges flag on
+ * reset.
*
- * Revision 1.80 2002/10/24 21:15:33 mpowers
- * New implementations of NSArray and subclasses.
+ * Revision 1.80 2002/10/24 21:15:33 mpowers New implementations of NSArray and
+ * subclasses.
*
- * Revision 1.79 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.79 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.78 2002/06/21 21:44:33 mpowers
- * No longer marking deleted objects as updated (thanks to dwang).
+ * Revision 1.78 2002/06/21 21:44:33 mpowers No longer marking deleted objects
+ * as updated (thanks to dwang).
*
- * Revision 1.77 2002/05/20 15:10:17 mpowers
- * No longer refaulting if delegate does not wish to handle the merge.
+ * Revision 1.77 2002/05/20 15:10:17 mpowers No longer refaulting if delegate
+ * does not wish to handle the merge.
*
- * Revision 1.76 2002/03/26 21:46:06 mpowers
- * Contributing EditingContext as a java-friendly convenience.
+ * Revision 1.76 2002/03/26 21:46:06 mpowers Contributing EditingContext as a
+ * java-friendly convenience.
*
- * Revision 1.75 2002/03/06 16:14:57 mpowers
- * More attempts at ignoring update conflicts that come from ourself.
+ * Revision 1.75 2002/03/06 16:14:57 mpowers More attempts at ignoring update
+ * conflicts that come from ourself.
*
- * Revision 1.74 2002/02/21 21:57:50 mpowers
- * Implemented default merge behavior.
+ * Revision 1.74 2002/02/21 21:57:50 mpowers Implemented default merge behavior.
*
- * Revision 1.73 2002/02/20 16:46:54 mpowers
- * Implemented better support for EOEditingContext.Delegate.
+ * Revision 1.73 2002/02/20 16:46:54 mpowers Implemented better support for
+ * EOEditingContext.Delegate.
*
- * Revision 1.70 2002/02/19 22:26:05 mpowers
- * Implemented EOEditingContext.MessageHandler support.
+ * Revision 1.70 2002/02/19 22:26:05 mpowers Implemented
+ * EOEditingContext.MessageHandler support.
*
- * Revision 1.69 2002/02/19 16:33:42 mpowers
- * Implemented support for EditingContext.Editor
+ * Revision 1.69 2002/02/19 16:33:42 mpowers Implemented support for
+ * EditingContext.Editor
*
- * Revision 1.68 2002/02/13 22:00:34 mpowers
- * Fixed: invalidateAllObjects tries to invalidate inserted objects,
- * typically causing class cast exceptions involving EOTemporaryGlobalID.
+ * Revision 1.68 2002/02/13 22:00:34 mpowers Fixed: invalidateAllObjects tries
+ * to invalidate inserted objects, typically causing class cast exceptions
+ * involving EOTemporaryGlobalID.
*
- * Revision 1.67 2002/02/06 21:15:35 mpowers
- * No longer refaulting a dirty object when we receive an invalidation notif.
+ * Revision 1.67 2002/02/06 21:15:35 mpowers No longer refaulting a dirty object
+ * when we receive an invalidation notif.
*
- * Revision 1.66 2002/01/08 19:31:03 mpowers
- * refaultObject now correctly refaults the object.
+ * Revision 1.66 2002/01/08 19:31:03 mpowers refaultObject now correctly
+ * refaults the object.
*
- * Revision 1.65 2001/12/20 18:56:15 mpowers
- * Refinements to snapshotting and calling processRecentChanges.
+ * Revision 1.65 2001/12/20 18:56:15 mpowers Refinements to snapshotting and
+ * calling processRecentChanges.
*
- * Revision 1.64 2001/12/10 15:11:41 mpowers
- * Now only tracking a commit snapshot after an object has been modified.
+ * Revision 1.64 2001/12/10 15:11:41 mpowers Now only tracking a commit snapshot
+ * after an object has been modified.
*
- * Revision 1.63 2001/11/14 00:08:10 mpowers
- * Now marking context changed when objects are inserted or deleted
- * and when child contexts save their changes into this context.
+ * Revision 1.63 2001/11/14 00:08:10 mpowers Now marking context changed when
+ * objects are inserted or deleted and when child contexts save their changes
+ * into this context.
*
- * Revision 1.62 2001/11/07 14:49:31 mpowers
- * invalidateAllObjects now handles objects manually discarded in the course
- * of invalidation.
+ * Revision 1.62 2001/11/07 14:49:31 mpowers invalidateAllObjects now handles
+ * objects manually discarded in the course of invalidation.
*
- * Revision 1.61 2001/10/26 20:02:49 mpowers
- * No longer using NSNotificationQueue: all notifications are posted immed.
+ * Revision 1.61 2001/10/26 20:02:49 mpowers No longer using
+ * NSNotificationQueue: all notifications are posted immed.
*
- * Revision 1.60 2001/10/26 18:37:50 mpowers
- * Now using NSRunLoop to correctly flush recent changes before delayed
- * observers and AWT events.
+ * Revision 1.60 2001/10/26 18:37:50 mpowers Now using NSRunLoop to correctly
+ * flush recent changes before delayed observers and AWT events.
*
- * Revision 1.59 2001/10/23 22:29:59 mpowers
- * Now posting notifications immediately.
- * Recent changes are flushed at ObserverPrioritySixth, soon to change.
+ * Revision 1.59 2001/10/23 22:29:59 mpowers Now posting notifications
+ * immediately. Recent changes are flushed at ObserverPrioritySixth, soon to
+ * change.
*
- * Revision 1.58 2001/09/10 14:16:51 mpowers
- * EditingContexts now relay InvalidatedAllObjectsInStore notifications.
+ * Revision 1.58 2001/09/10 14:16:51 mpowers EditingContexts now relay
+ * InvalidatedAllObjectsInStore notifications.
*
- * Revision 1.57 2001/06/18 14:11:15 mpowers
- * Inserting a deleted object simply cancels the delete operation.
+ * Revision 1.57 2001/06/18 14:11:15 mpowers Inserting a deleted object simply
+ * cancels the delete operation.
*
- * Revision 1.56 2001/06/07 22:07:59 mpowers
- * Now handling delete notifications before update notifications.
- * Eliminated the case where deleted objects were also being put
- * on the updated list when notifying child contexts.
+ * Revision 1.56 2001/06/07 22:07:59 mpowers Now handling delete notifications
+ * before update notifications. Eliminated the case where deleted objects were
+ * also being put on the updated list when notifying child contexts.
*
- * Revision 1.55 2001/05/18 21:04:33 mpowers
- * Reimplemented EditingContext.initializeObject.
+ * Revision 1.55 2001/05/18 21:04:33 mpowers Reimplemented
+ * EditingContext.initializeObject.
*
- * Revision 1.54 2001/05/05 23:05:42 mpowers
- * Implemented Array Faults.
+ * Revision 1.54 2001/05/05 23:05:42 mpowers Implemented Array Faults.
*
- * Revision 1.53 2001/05/05 15:00:06 mpowers
- * Tested load-on-demand: still works.
- * Now using registerClone for consistency.
- * Editing context is temporarily posting notification on objectWillChange.
+ * Revision 1.53 2001/05/05 15:00:06 mpowers Tested load-on-demand: still works.
+ * Now using registerClone for consistency. Editing context is temporarily
+ * posting notification on objectWillChange.
*
- * Revision 1.52 2001/05/05 14:11:48 mpowers
- * Implemented registerClone.
+ * Revision 1.52 2001/05/05 14:11:48 mpowers Implemented registerClone.
*
- * Revision 1.51 2001/05/04 16:57:56 mpowers
- * Now correctly transposing references to editing contexts when
- * cloning/copying between editing contexts.
+ * Revision 1.51 2001/05/04 16:57:56 mpowers Now correctly transposing
+ * references to editing contexts when cloning/copying between editing contexts.
*
- * Revision 1.50 2001/05/02 17:58:41 mpowers
- * Removed debugging code, added comments.
+ * Revision 1.50 2001/05/02 17:58:41 mpowers Removed debugging code, added
+ * comments.
*
- * Revision 1.49 2001/05/02 15:47:40 mpowers
- * Fixed the pernicious problem with reverts: recordObject was recording
- * a snapshot of the clone before the transposition-copy happened,
- * so the revert object would lose all of its transposed relationships.
+ * Revision 1.49 2001/05/02 15:47:40 mpowers Fixed the pernicious problem with
+ * reverts: recordObject was recording a snapshot of the clone before the
+ * transposition-copy happened, so the revert object would lose all of its
+ * transposed relationships.
*
- * Revision 1.48 2001/05/02 12:39:05 mpowers
- * Fixed a nasty problem with transpose-cloning and faultForGlobalID.
- * Now we're forced to create a deep clone, registered it, and then
- * transpose it after it has been registered.
+ * Revision 1.48 2001/05/02 12:39:05 mpowers Fixed a nasty problem with
+ * transpose-cloning and faultForGlobalID. Now we're forced to create a deep
+ * clone, registered it, and then transpose it after it has been registered.
*
- * Revision 1.47 2001/04/30 13:15:24 mpowers
- * Child contexts re-initializing objects invalidated in parent now
- * propery transpose relationships.
+ * Revision 1.47 2001/04/30 13:15:24 mpowers Child contexts re-initializing
+ * objects invalidated in parent now propery transpose relationships.
*
- * Revision 1.46 2001/04/29 22:02:45 mpowers
- * Work on id transposing between editing contexts.
+ * Revision 1.46 2001/04/29 22:02:45 mpowers Work on id transposing between
+ * editing contexts.
*
- * Revision 1.45 2001/04/29 02:29:31 mpowers
- * Debugging relationship faulting.
+ * Revision 1.45 2001/04/29 02:29:31 mpowers Debugging relationship faulting.
*
- * Revision 1.44 2001/04/28 16:18:44 mpowers
- * Implementing relationships.
+ * Revision 1.44 2001/04/28 16:18:44 mpowers Implementing relationships.
*
- * Revision 1.43 2001/04/28 14:12:23 mpowers
- * Refactored cloning/copying into KeyValueCodingUtilities.
+ * Revision 1.43 2001/04/28 14:12:23 mpowers Refactored cloning/copying into
+ * KeyValueCodingUtilities.
*
- * Revision 1.42 2001/04/27 00:27:11 mpowers
- * Provided description to not-implemented exceptions.
+ * Revision 1.42 2001/04/27 00:27:11 mpowers Provided description to
+ * not-implemented exceptions.
*
- * Revision 1.41 2001/04/26 01:16:44 mpowers
- * Major bug fix so we no longer accumulate objects in the all objects
- * list with every InvalidateAllObjectsInStore.
+ * Revision 1.41 2001/04/26 01:16:44 mpowers Major bug fix so we no longer
+ * accumulate objects in the all objects list with every
+ * InvalidateAllObjectsInStore.
*
- * Revision 1.40 2001/04/21 23:07:49 mpowers
- * Now only broadcasts notifications if there's actually a change.
+ * Revision 1.40 2001/04/21 23:07:49 mpowers Now only broadcasts notifications
+ * if there's actually a change.
*
- * Revision 1.39 2001/04/13 16:33:11 mpowers
- * Corrected the refaulting behavior.
+ * Revision 1.39 2001/04/13 16:33:11 mpowers Corrected the refaulting behavior.
*
- * Revision 1.38 2001/04/09 21:42:10 mpowers
- * Debugging and optimizing notifications.
+ * Revision 1.38 2001/04/09 21:42:10 mpowers Debugging and optimizing
+ * notifications.
*
- * Revision 1.37 2001/04/08 20:59:47 mpowers
- * objectsForFetchSpecification now relies on faultForGlobalID.
+ * Revision 1.37 2001/04/08 20:59:47 mpowers objectsForFetchSpecification now
+ * relies on faultForGlobalID.
*
- * Revision 1.36 2001/04/03 20:36:01 mpowers
- * Fixed refaulting/reverting/invalidating to be self-consistent.
+ * Revision 1.36 2001/04/03 20:36:01 mpowers Fixed
+ * refaulting/reverting/invalidating to be self-consistent.
*
- * Revision 1.35 2001/03/29 03:29:49 mpowers
- * Now using KeyValueCoding and Support instead of Introspector.
+ * Revision 1.35 2001/03/29 03:29:49 mpowers Now using KeyValueCoding and
+ * Support instead of Introspector.
*
- * Revision 1.34 2001/03/28 14:06:29 mpowers
- * Implemented snapshots. Revert now uses snapshots.
+ * Revision 1.34 2001/03/28 14:06:29 mpowers Implemented snapshots. Revert now
+ * uses snapshots.
*
- * Revision 1.33 2001/03/20 23:20:33 mpowers
- * invalidating all objects now sets the dirty flag to false.
+ * Revision 1.33 2001/03/20 23:20:33 mpowers invalidating all objects now sets
+ * the dirty flag to false.
*
- * Revision 1.32 2001/03/19 21:44:36 mpowers
- * Reverts reinitialize for now.
+ * Revision 1.32 2001/03/19 21:44:36 mpowers Reverts reinitialize for now.
* Testing for inserted objects instead of temp id when invalidating object.
*
- * Revision 1.31 2001/03/15 21:10:26 mpowers
- * Implemented global id re-registration for newly saved inserts.
+ * Revision 1.31 2001/03/15 21:10:26 mpowers Implemented global id
+ * re-registration for newly saved inserts.
*
- * Revision 1.30 2001/03/13 21:41:34 mpowers
- * Broadcasting willChange for any change to hasChanges.
- * Fixed major bug with inserted objects treated as updated objects
- * in child display groups.
+ * Revision 1.30 2001/03/13 21:41:34 mpowers Broadcasting willChange for any
+ * change to hasChanges. Fixed major bug with inserted objects treated as
+ * updated objects in child display groups.
*
- * Revision 1.29 2001/03/09 22:10:30 mpowers
- * Fine tuned initializeObject.
+ * Revision 1.29 2001/03/09 22:10:30 mpowers Fine tuned initializeObject.
*
- * Revision 1.28 2001/03/06 23:23:55 mpowers
- * objectForGlobalID now returns null if not found.
- * objectsForFetchSpecification again does things the old way, for now.
+ * Revision 1.28 2001/03/06 23:23:55 mpowers objectForGlobalID now returns null
+ * if not found. objectsForFetchSpecification again does things the old way, for
+ * now.
*
- * Revision 1.27 2001/03/02 16:31:45 mpowers
- * Trying to better handle fetches from child contexts.
- * No longer trying to invalidate temporary objects.
+ * Revision 1.27 2001/03/02 16:31:45 mpowers Trying to better handle fetches
+ * from child contexts. No longer trying to invalidate temporary objects.
*
- * Revision 1.26 2001/02/28 16:25:19 mpowers
- * Now calling globalIDForObject internally.
+ * Revision 1.26 2001/02/28 16:25:19 mpowers Now calling globalIDForObject
+ * internally.
*
- * Revision 1.25 2001/02/27 17:36:55 mpowers
- * Objects inserted from child now preserve the existing temporary id.
+ * Revision 1.25 2001/02/27 17:36:55 mpowers Objects inserted from child now
+ * preserve the existing temporary id.
*
- * Revision 1.24 2001/02/27 02:11:17 mpowers
- * Now throwing exception when cloning fails.
- * Removed debugging printlns.
+ * Revision 1.24 2001/02/27 02:11:17 mpowers Now throwing exception when cloning
+ * fails. Removed debugging printlns.
*
- * Revision 1.23 2001/02/26 22:41:51 mpowers
- * Implemented null placeholder classes.
- * Duplicator now uses NSNull.
- * No longer catching base exception class.
+ * Revision 1.23 2001/02/26 22:41:51 mpowers Implemented null placeholder
+ * classes. Duplicator now uses NSNull. No longer catching base exception class.
*
- * Revision 1.22 2001/02/26 21:18:45 mpowers
- * Now marking edited objects from child contexts that were not already
- * recorded in parent as changed in saveChangesInEditingContext.
+ * Revision 1.22 2001/02/26 21:18:45 mpowers Now marking edited objects from
+ * child contexts that were not already recorded in parent as changed in
+ * saveChangesInEditingContext.
*
- * Revision 1.21 2001/02/26 15:53:22 mpowers
- * Fine-tuning notification firing.
+ * Revision 1.21 2001/02/26 15:53:22 mpowers Fine-tuning notification firing.
* Child display groups now update properly after parent save or invalidate.
*
- * Revision 1.20 2001/02/24 17:03:22 mpowers
- * Implemented the notification queue, and changed editing context to use it.
+ * Revision 1.20 2001/02/24 17:03:22 mpowers Implemented the notification queue,
+ * and changed editing context to use it.
*
- * Revision 1.19 2001/02/23 23:44:15 mpowers
- * Fine-tuning notification handling.
+ * Revision 1.19 2001/02/23 23:44:15 mpowers Fine-tuning notification handling.
*
- * Revision 1.18 2001/02/22 22:56:57 mpowers
- * Only refaulting edited objects on parent store invalidateAll.
+ * Revision 1.18 2001/02/22 22:56:57 mpowers Only refaulting edited objects on
+ * parent store invalidateAll.
*
- * Revision 1.17 2001/02/22 20:54:39 mpowers
- * Implemented notification handling.
+ * Revision 1.17 2001/02/22 20:54:39 mpowers Implemented notification handling.
*
- * Revision 1.16 2001/02/21 22:10:55 mpowers
- * Editing context is now posting appropriate notifications.
+ * Revision 1.16 2001/02/21 22:10:55 mpowers Editing context is now posting
+ * appropriate notifications.
*
- * Revision 1.15 2001/02/21 21:17:32 mpowers
- * Now retaining a reference to the recent changes observer.
- * Better documented need to retain reference.
- * Started implementing notifications.
+ * Revision 1.15 2001/02/21 21:17:32 mpowers Now retaining a reference to the
+ * recent changes observer. Better documented need to retain reference. Started
+ * implementing notifications.
*
- * Revision 1.14 2001/02/20 17:24:22 mpowers
- * Now using reference keys in objectToID.
+ * Revision 1.14 2001/02/20 17:24:22 mpowers Now using reference keys in
+ * objectToID.
*
- * Revision 1.13 2001/02/20 16:45:36 mpowers
- * Child data sources now accept a data source instead of an editing context
- * for more flexibility. Child data sources now forward relationship
- * methods to parent source.
+ * Revision 1.13 2001/02/20 16:45:36 mpowers Child data sources now accept a
+ * data source instead of an editing context for more flexibility. Child data
+ * sources now forward relationship methods to parent source.
*
- * Revision 1.12 2001/02/16 22:51:29 mpowers
- * Now deep-cloning objects passed between editing contexts.
+ * Revision 1.12 2001/02/16 22:51:29 mpowers Now deep-cloning objects passed
+ * between editing contexts.
*
- * Revision 1.11 2001/02/16 18:34:19 mpowers
- * Implementing nested contexts.
+ * Revision 1.11 2001/02/16 18:34:19 mpowers Implementing nested contexts.
*
- * Revision 1.9 2001/02/15 21:13:30 mpowers
- * First draft implementation is complete. Now on to debugging.
+ * Revision 1.9 2001/02/15 21:13:30 mpowers First draft implementation is
+ * complete. Now on to debugging.
*
- * Revision 1.7 2001/02/13 23:24:29 mpowers
- * Implementing more of editing context.
+ * Revision 1.7 2001/02/13 23:24:29 mpowers Implementing more of editing
+ * context.
*
- * Revision 1.4 2001/02/09 22:09:34 mpowers
- * Completed implementation of EOObjectStore.
+ * Revision 1.4 2001/02/09 22:09:34 mpowers Completed implementation of
+ * EOObjectStore.
*
- * Revision 1.3 2001/02/06 15:24:11 mpowers
- * Widened parameters on abstract method to fix build.
+ * Revision 1.3 2001/02/06 15:24:11 mpowers Widened parameters on abstract
+ * method to fix build.
*
- * Revision 1.2 2001/02/05 03:45:37 mpowers
- * Starting work on EOEditingContext.
+ * Revision 1.2 2001/02/05 03:45:37 mpowers Starting work on EOEditingContext.
*
- * Revision 1.1.1.1 2000/12/21 15:46:42 mpowers
- * Contributing wotonomy.
+ * Revision 1.1.1.1 2000/12/21 15:46:42 mpowers Contributing wotonomy.
*
*/
-
-