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