| Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||||||
| EOEditingContext |
|
| 2.43609022556391;2.436 |
| 1 | /* |
|
| 2 | Wotonomy: OpenStep design patterns for pure Java applications. |
|
| 3 | Copyright (C) 2001 Michael Powers |
|
| 4 | ||
| 5 | This library is free software; you can redistribute it and/or |
|
| 6 | modify it under the terms of the GNU Lesser General Public |
|
| 7 | License as published by the Free Software Foundation; either |
|
| 8 | version 2.1 of the License, or (at your option) any later version. |
|
| 9 | ||
| 10 | This library is distributed in the hope that it will be useful, |
|
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
| 13 | Lesser General Public License for more details. |
|
| 14 | ||
| 15 | You should have received a copy of the GNU Lesser General Public |
|
| 16 | License along with this library; if not, see http://www.gnu.org |
|
| 17 | */ |
|
| 18 | ||
| 19 | package net.wotonomy.control; |
|
| 20 | ||
| 21 | import java.lang.ref.WeakReference; |
|
| 22 | import java.util.Collection; |
|
| 23 | import java.util.Enumeration; |
|
| 24 | import java.util.HashMap; |
|
| 25 | import java.util.Iterator; |
|
| 26 | import java.util.LinkedList; |
|
| 27 | import java.util.List; |
|
| 28 | import java.util.Map; |
|
| 29 | import java.util.Set; |
|
| 30 | ||
| 31 | import net.wotonomy.foundation.NSArray; |
|
| 32 | import net.wotonomy.foundation.NSDictionary; |
|
| 33 | import net.wotonomy.foundation.NSMutableArray; |
|
| 34 | import net.wotonomy.foundation.NSMutableDictionary; |
|
| 35 | import net.wotonomy.foundation.NSNotification; |
|
| 36 | import net.wotonomy.foundation.NSNotificationCenter; |
|
| 37 | import net.wotonomy.foundation.NSRunLoop; |
|
| 38 | import net.wotonomy.foundation.NSSelector; |
|
| 39 | import net.wotonomy.foundation.internal.WotonomyException; |
|
| 40 | ||
| 41 | // swing dependency for undo manager |
|
| 42 | //import javax.swing.undo.UndoManager; |
|
| 43 | ||
| 44 | /** |
|
| 45 | * EOEditingContext provides transactional support for |
|
| 46 | * fetching, editing, and committing changes made on a |
|
| 47 | * collection of objects to a parent object store. <br><br> |
|
| 48 | * |
|
| 49 | * EOEditingContext is itself a subclass of EOObjectStore, |
|
| 50 | * and this means that EOEditingContexts can use other |
|
| 51 | * EOEditingContexts as their parent. However, there |
|
| 52 | * still must exist an EOObjectStore as the root of the |
|
| 53 | * editing hierarchy that can maintain persistent state. |
|
| 54 | * |
|
| 55 | * @author michael@mpowers.net |
|
| 56 | * @author $Author: cgruber $ |
|
| 57 | * @version $Revision: 894 $ |
|
| 58 | */ |
|
| 59 | 0 | public class EOEditingContext |
| 60 | extends EOObjectStore |
|
| 61 | implements EOObserving |
|
| 62 | { |
|
| 63 | /** |
|
| 64 | * Key for the NSNotification posted after this editing context |
|
| 65 | * saves changes. Object of the notification will be this editing |
|
| 66 | * context, and user info will contain InsertedKey, UpdatedKey, |
|
| 67 | * and DeletedKey (keys are defined in EOObjectStore). |
|
| 68 | */ |
|
| 69 | public static final String |
|
| 70 | EditingContextDidSaveChangesNotification = |
|
| 71 | "EOEditingContextDidSaveChangesNotification"; |
|
| 72 | ||
| 73 | /** |
|
| 74 | * Key for the NSNotification posted after this editing context |
|
| 75 | * observes changes. Object of the notification will be this editing |
|
| 76 | * context, and user info will contain InsertedKey, UpdatedKey, InvalidatedKey, |
|
| 77 | * and DeletedKey (keys are defined in EOObjectStore), however |
|
| 78 | * the objects in the corresponding Lists will be the actual |
|
| 79 | * objects, not their ids. |
|
| 80 | */ |
|
| 81 | public static final String |
|
| 82 | ObjectsChangedInEditingContextNotification = |
|
| 83 | "EOObjectsChangedInEditingContextNotification"; |
|
| 84 | ||
| 85 | /** |
|
| 86 | * The default run loop ordering processes recent changes |
|
| 87 | * before delayed observers are notified and before dispatching |
|
| 88 | * the AWT event queue. |
|
| 89 | */ |
|
| 90 | public static int |
|
| 91 | 0 | EditingContextFlushChangesRunLoopOrdering = 300000; |
| 92 | ||
| 93 | 0 | private static NSSelector runLaterSelector = |
| 94 | 0 | new NSSelector( "flushRecentChanges", |
| 95 | 0 | new Class[] { Object.class } ); |
| 96 | ||
| 97 | 0 | private static EOObjectStore defaultParentObjectStore = null; |
| 98 | 0 | private static double defaultFetchTimestampLag = 0; |
| 99 | 0 | private static boolean retainsRegisteredObjects = true; |
| 100 | ||
| 101 | private EOObjectStore parentStore; |
|
| 102 | private WeakReference delegate; |
|
| 103 | private WeakReference messageHandler; |
|
| 104 | private List editorSet; |
|
| 105 | private double fetchTimestamp; |
|
| 106 | private boolean lockBeforeModify; |
|
| 107 | private boolean propagateDeletesAfterEvent; |
|
| 108 | private boolean stopValidationAfterError; |
|
| 109 | private NSMutableArray insertedObjects; |
|
| 110 | private NSMutableArray insertedObjectsBuffer; |
|
| 111 | private NSArray insertedObjectsProxy; |
|
| 112 | private NSMutableArray updatedObjects; |
|
| 113 | private NSMutableArray updatedObjectsBuffer; |
|
| 114 | private NSArray updatedObjectsProxy; |
|
| 115 | private NSMutableArray deletedObjects; |
|
| 116 | private NSMutableArray deletedObjectsBuffer; |
|
| 117 | private NSArray deletedObjectsProxy; |
|
| 118 | private NSMutableArray deletedIDsBuffer; |
|
| 119 | private NSMutableArray invalidatedObjectsBuffer; |
|
| 120 | private NSMutableArray invalidatedIDsBuffer; |
|
| 121 | private Registrar registrar; |
|
| 122 | // private UndoManager undoManager; |
|
| 123 | ||
| 124 | // so we don't have to trouble EOObserverCenter |
|
| 125 | private boolean ignoreChanges; |
|
| 126 | ||
| 127 | // for delayed handling of processRecentChanges |
|
| 128 | private boolean willRunLater; |
|
| 129 | ||
| 130 | // for handling of notifications posted |
|
| 131 | // while we're in the saveChanges method |
|
| 132 | private boolean isInvalidating; |
|
| 133 | ||
| 134 | // for i18n or other customization |
|
| 135 | 0 | static protected String MessageChangeConflict = |
| 136 | 0 | "Another user changed an object you are editing: "; |
| 137 | ||
| 138 | /** |
|
| 139 | * Default constructor creates a new editing context |
|
| 140 | * that uses the default object store. If the default |
|
| 141 | * object store has not been set, an exception is thrown. |
|
| 142 | */ |
|
| 143 | public EOEditingContext() |
|
| 144 | { |
|
| 145 | 0 | this( defaultParentObjectStore() ); |
| 146 | 0 | } |
| 147 | ||
| 148 | /** |
|
| 149 | * Creates a new editing context that uses the specified |
|
| 150 | * object store as its parent object store. |
|
| 151 | */ |
|
| 152 | 0 | public EOEditingContext( EOObjectStore anObjectStore ) |
| 153 | 0 | { |
| 154 | 0 | if ( anObjectStore == null ) |
| 155 | { |
|
| 156 | 0 | throw new IllegalArgumentException( |
| 157 | 0 | "A parent object store must be specified." ); |
| 158 | } |
|
| 159 | ||
| 160 | 0 | parentStore = anObjectStore; |
| 161 | 0 | delegate = null; |
| 162 | 0 | messageHandler = null; |
| 163 | 0 | editorSet = new LinkedList(); |
| 164 | 0 | fetchTimestamp = 0; |
| 165 | 0 | lockBeforeModify = false; |
| 166 | 0 | propagateDeletesAfterEvent = true; |
| 167 | 0 | stopValidationAfterError = true; |
| 168 | 0 | insertedObjects = new NSMutableArray(); |
| 169 | 0 | insertedObjectsBuffer = new NSMutableArray(); |
| 170 | 0 | insertedObjectsProxy = NSArray.arrayBackedByList( insertedObjects ); |
| 171 | 0 | updatedObjects = new NSMutableArray(); |
| 172 | 0 | updatedObjectsBuffer = new NSMutableArray(); |
| 173 | 0 | updatedObjectsProxy = NSArray.arrayBackedByList( updatedObjects ); |
| 174 | 0 | deletedObjects = new NSMutableArray(); |
| 175 | 0 | deletedObjectsBuffer = new NSMutableArray(); |
| 176 | 0 | deletedObjectsProxy = NSArray.arrayBackedByList( deletedObjects ); |
| 177 | 0 | deletedIDsBuffer = new NSMutableArray(); |
| 178 | 0 | invalidatedObjectsBuffer = new NSMutableArray(); |
| 179 | 0 | invalidatedIDsBuffer = new NSMutableArray(); |
| 180 | ||
| 181 | 0 | if ( instancesRetainRegisteredObjects() ) |
| 182 | { |
|
| 183 | 0 | registrar = new Registrar( this ); |
| 184 | 0 | } |
| 185 | else |
|
| 186 | { |
|
| 187 | 0 | registrar = new WeakRegistrar( this ); |
| 188 | } |
|
| 189 | ||
| 190 | 0 | ignoreChanges = false; |
| 191 | 0 | willRunLater = false; |
| 192 | 0 | isInvalidating = false; |
| 193 | ||
| 194 | // create undo manager |
|
| 195 | //TODO: this should be NSUndoManager |
|
| 196 | // undoManager = new UndoManager(); |
|
| 197 | ||
| 198 | // register for notifications |
|
| 199 | 0 | NSSelector handleNotification = |
| 200 | 0 | new NSSelector( "handleNotification", |
| 201 | 0 | new Class[] { NSNotification.class } ); |
| 202 | // any from parent store |
|
| 203 | 0 | NSNotificationCenter.defaultCenter().addObserver( |
| 204 | 0 | this, handleNotification, null, parentStore ); |
| 205 | // global id change from any |
|
| 206 | 0 | NSNotificationCenter.defaultCenter().addObserver( |
| 207 | 0 | this, handleNotification, EOGlobalID.GlobalIDChangedNotification, null ); |
| 208 | //new net.wotonomy.ui.swing.NotificationInspector( null, parentStore ); |
|
| 209 | 0 | } |
| 210 | ||
| 211 | /** |
|
| 212 | * Registers the specified object as an editor for this |
|
| 213 | * context. The object is expected to implement |
|
| 214 | * EOEditingContext.Editor. |
|
| 215 | */ |
|
| 216 | public void addEditor ( Object anEditor ) |
|
| 217 | { |
|
| 218 | 0 | if ( anEditor == null ) return; |
| 219 | 0 | editorSet.add( new WeakReference( anEditor ) ); |
| 220 | 0 | } |
| 221 | ||
| 222 | /** |
|
| 223 | * Returns a read-only List of objects associated with the object |
|
| 224 | * with the specified id for the specified property |
|
| 225 | * relationship, or may return a placeholder array that |
|
| 226 | * will defer the fetch until needed (aka an array fault). |
|
| 227 | * All objects must be registered in the specified editing context. |
|
| 228 | * This implementation calls to its parent object store's |
|
| 229 | * implementation if the requested source object is not |
|
| 230 | * registered in this editing context. |
|
| 231 | * The specified relationship key must produce a result of |
|
| 232 | * type Collection for the source object or an exception is thrown. |
|
| 233 | */ |
|
| 234 | public NSArray arrayFaultWithSourceGlobalID ( |
|
| 235 | EOGlobalID aGlobalID, |
|
| 236 | String aRelationshipKey, |
|
| 237 | EOEditingContext aContext ) |
|
| 238 | { |
|
| 239 | 0 | NSArray result = null; |
| 240 | 0 | Object source = registrar.objectForGlobalID( aGlobalID ); |
| 241 | ||
| 242 | // if not registered in our context |
|
| 243 | 0 | if ( source == null ) |
| 244 | { |
|
| 245 | // get the object registered into our context |
|
| 246 | 0 | result = parentStore.arrayFaultWithSourceGlobalID( |
| 247 | 0 | aGlobalID, aRelationshipKey, this ); |
| 248 | 0 | } |
| 249 | else // source is registered in our context |
|
| 250 | { |
|
| 251 | // get existing value |
|
| 252 | Object value; |
|
| 253 | 0 | if ( source instanceof EOKeyValueCoding ) |
| 254 | { |
|
| 255 | 0 | value = ((EOKeyValueCoding)source).storedValueForKey( |
| 256 | 0 | aRelationshipKey ); |
| 257 | 0 | } |
| 258 | else // handle directly |
|
| 259 | { |
|
| 260 | 0 | value = EOKeyValueCodingSupport.storedValueForKey( |
| 261 | 0 | source, aRelationshipKey ); |
| 262 | } |
|
| 263 | ||
| 264 | 0 | if ( value == null ) |
| 265 | { |
|
| 266 | // do the same as if the source was null |
|
| 267 | 0 | result = parentStore.arrayFaultWithSourceGlobalID( |
| 268 | 0 | aGlobalID, aRelationshipKey, this ); |
| 269 | 0 | } |
| 270 | else |
|
| 271 | 0 | if ( value instanceof NSArray ) |
| 272 | { |
|
| 273 | 0 | result = (NSArray) value; |
| 274 | 0 | } |
| 275 | else // not NSArray |
|
| 276 | 0 | if ( value instanceof Collection ) |
| 277 | { |
|
| 278 | // convert to NSArray |
|
| 279 | 0 | result = new NSArray( (Collection) value ); |
| 280 | 0 | } |
| 281 | else |
|
| 282 | { |
|
| 283 | 0 | throw new WotonomyException( |
| 284 | 0 | "Relationship key did not return a collection: " |
| 285 | 0 | + aGlobalID + " : " + aRelationshipKey ); |
| 286 | } |
|
| 287 | } |
|
| 288 | ||
| 289 | // if our context is not the specified context |
|
| 290 | 0 | if ( aContext != this ) |
| 291 | { |
|
| 292 | 0 | result = (NSArray) clone( this, result, aContext ); |
| 293 | } |
|
| 294 | ||
| 295 | 0 | return result; |
| 296 | } |
|
| 297 | ||
| 298 | /** |
|
| 299 | * Returns a snapshot of the specified object as it |
|
| 300 | * existed when it was last read or committed to the |
|
| 301 | * parent object store. |
|
| 302 | */ |
|
| 303 | public NSDictionary committedSnapshotForObject ( |
|
| 304 | Object anObject ) |
|
| 305 | { |
|
| 306 | 0 | byte[] snapshot = (byte[]) |
| 307 | 0 | registrar.getCommitSnapshot( anObject ); |
| 308 | 0 | if ( snapshot == null ) |
| 309 | { |
|
| 310 | // this object not modified: take a current snapshot |
|
| 311 | 0 | snapshot = takeSnapshot( anObject ); |
| 312 | } |
|
| 313 | 0 | return convertSnapshotToDictionary( snapshot ); |
| 314 | } |
|
| 315 | ||
| 316 | /** |
|
| 317 | * Returns a snapshot of the specified object as it |
|
| 318 | * existed before the edits triggered by the current |
|
| 319 | * event loop were processed. |
|
| 320 | */ |
|
| 321 | public NSDictionary currentEventSnapshotForObject ( |
|
| 322 | Object anObject ) |
|
| 323 | { |
|
| 324 | 0 | byte[] result = (byte[]) |
| 325 | 0 | registrar.getCurrentSnapshot( anObject ); |
| 326 | 0 | if ( result == null ) |
| 327 | { |
|
| 328 | 0 | return committedSnapshotForObject( anObject ); |
| 329 | } |
|
| 330 | 0 | return convertSnapshotToDictionary( result ); |
| 331 | } |
|
| 332 | ||
| 333 | /** |
|
| 334 | * Returns the delegate for this editing context, |
|
| 335 | * or null if no delegate has been set. |
|
| 336 | */ |
|
| 337 | public Object delegate () |
|
| 338 | { |
|
| 339 | 0 | if ( delegate == null ) return null; |
| 340 | 0 | return delegate.get(); |
| 341 | } |
|
| 342 | ||
| 343 | /** |
|
| 344 | * Deletes the specified object from this editing context. |
|
| 345 | * The editing context marks the object as deleted and |
|
| 346 | * will notify the parent store when changes are committed. |
|
| 347 | */ |
|
| 348 | public void deleteObject ( |
|
| 349 | Object anObject ) |
|
| 350 | { |
|
| 351 | 0 | willChange(); |
| 352 | ||
| 353 | int i; |
|
| 354 | // remove from added objects if necessary |
|
| 355 | 0 | i = insertedObjects.indexOfIdenticalObject( anObject ); |
| 356 | 0 | if ( i != NSArray.NotFound ) |
| 357 | { |
|
| 358 | 0 | insertedObjects.removeObjectAtIndex( i ); |
| 359 | ||
| 360 | // if in the inserted objects buffer |
|
| 361 | 0 | int index = insertedObjectsBuffer.indexOfIdenticalObject( anObject ); |
| 362 | 0 | if ( index != NSArray.NotFound ) |
| 363 | { |
|
| 364 | // remove from inserted objects buffer |
|
| 365 | 0 | insertedObjectsBuffer.removeObjectAtIndex( index ); |
| 366 | } |
|
| 367 | ||
| 368 | // now forget the object ever existed. |
|
| 369 | 0 | forgetObject( anObject ); |
| 370 | ||
| 371 | // we're done |
|
| 372 | 0 | return; |
| 373 | } |
|
| 374 | else // otherwise add to deleted objects list |
|
| 375 | { |
|
| 376 | 0 | deletedObjects.addObject( anObject ); |
| 377 | } |
|
| 378 | ||
| 379 | // remove from updated objects if necessary |
|
| 380 | 0 | i = updatedObjects.indexOfIdenticalObject( anObject ); |
| 381 | 0 | if ( i != NSArray.NotFound ) |
| 382 | { |
|
| 383 | 0 | updatedObjects.removeObjectAtIndex( i ); |
| 384 | } |
|
| 385 | ||
| 386 | // add to buffer |
|
| 387 | 0 | deletedObjectsBuffer.addObject( anObject ); |
| 388 | 0 | deletedIDsBuffer.addObject( globalIDForObject( anObject ) ); |
| 389 | 0 | } |
| 390 | ||
| 391 | /** |
|
| 392 | * Returns a read-only List of all objects marked as deleted |
|
| 393 | * in this editing context. |
|
| 394 | */ |
|
| 395 | public NSArray deletedObjects () |
|
| 396 | { |
|
| 397 | 0 | return deletedObjectsProxy; |
| 398 | } |
|
| 399 | ||
| 400 | /** |
|
| 401 | * Called by child editing contexts when they no longer |
|
| 402 | * need to track the specified id. |
|
| 403 | * This implementation forwards the call to the parent store. |
|
| 404 | */ |
|
| 405 | public void editingContextDidForgetObjectWithGlobalID ( |
|
| 406 | EOEditingContext aContext, |
|
| 407 | EOGlobalID aGlobalID ) |
|
| 408 | { |
|
| 409 | 0 | parentStore.editingContextDidForgetObjectWithGlobalID( |
| 410 | 0 | aContext, aGlobalID ); |
| 411 | 0 | } |
| 412 | ||
| 413 | /** |
|
| 414 | * Returns a read-only List of registered editors of this |
|
| 415 | * editing context. |
|
| 416 | */ |
|
| 417 | public NSArray editors () |
|
| 418 | { |
|
| 419 | 0 | NSMutableArray result = new NSMutableArray(); |
| 420 | Object o; |
|
| 421 | 0 | Iterator i = editorSet.iterator(); |
| 422 | 0 | while ( i.hasNext() ) |
| 423 | { |
|
| 424 | 0 | o = ((WeakReference)i.next()).get(); |
| 425 | 0 | if ( o != null ) |
| 426 | { |
|
| 427 | 0 | result.addObject( o ); |
| 428 | 0 | } |
| 429 | else |
|
| 430 | { |
|
| 431 | 0 | i.remove(); |
| 432 | } |
|
| 433 | 0 | } |
| 434 | 0 | return result; |
| 435 | } |
|
| 436 | ||
| 437 | /* |
|
| 438 | public static void encodeObjectWithCoder ( |
|
| 439 | Object anObject, |
|
| 440 | NSCoder aCoder ) |
|
| 441 | { |
|
| 442 | throw new net.wotonomy.util.WotonomyException("Not implemented yet."); |
|
| 443 | } |
|
| 444 | */ |
|
| 445 | ||
| 446 | /** |
|
| 447 | * Returns the object for the specified id. |
|
| 448 | * If the object is registered in in this context |
|
| 449 | * but not in the specified context, |
|
| 450 | * this implementation will create a copy of the object |
|
| 451 | * and register it in the specified context. |
|
| 452 | * Otherwise it will forward the call to the parent |
|
| 453 | * object store. |
|
| 454 | */ |
|
| 455 | public /*EOEnterpriseObject*/ Object faultForGlobalID ( |
|
| 456 | EOGlobalID aGlobalID, |
|
| 457 | EOEditingContext aContext ) |
|
| 458 | { |
|
| 459 | 0 | Object result = registrar.objectForGlobalID( aGlobalID ); |
| 460 | ||
| 461 | // if not registered in our context |
|
| 462 | 0 | if ( result == null ) |
| 463 | { |
|
| 464 | // get the object registered into our context |
|
| 465 | 0 | result = parentStore.faultForGlobalID( aGlobalID, this ); |
| 466 | } |
|
| 467 | ||
| 468 | // if our context is not the specified context |
|
| 469 | 0 | if ( aContext != this ) |
| 470 | { |
|
| 471 | 0 | result = registerClone( aGlobalID, this, result, aContext ); |
| 472 | } |
|
| 473 | ||
| 474 | 0 | return result; |
| 475 | } |
|
| 476 | ||
| 477 | /** |
|
| 478 | * Returns a fault representing an object of |
|
| 479 | * the specified entity type with values from |
|
| 480 | * the specified dictionary. |
|
| 481 | * This implementation calls faultForRawRow |
|
| 482 | * on the parent store. |
|
| 483 | */ |
|
| 484 | public Object faultForRawRow ( |
|
| 485 | Map aDictionary, |
|
| 486 | String anEntityName ) |
|
| 487 | { |
|
| 488 | 0 | return parentStore.faultForRawRow( |
| 489 | 0 | aDictionary, anEntityName, this ); |
| 490 | } |
|
| 491 | ||
| 492 | /** |
|
| 493 | * Returns a fault representing an object of |
|
| 494 | * the specified entity type with values from |
|
| 495 | * the specified dictionary. The fault should |
|
| 496 | * belong to the specified editing context. |
|
| 497 | * This implementation forwards the call to |
|
| 498 | * the parent store. |
|
| 499 | */ |
|
| 500 | public /*EOEnterpriseObject*/ Object faultForRawRow ( |
|
| 501 | Map aDictionary, |
|
| 502 | String anEntityName, |
|
| 503 | EOEditingContext aContext ) |
|
| 504 | { |
|
| 505 | 0 | return parentStore.faultForRawRow( |
| 506 | 0 | aDictionary, anEntityName, aContext ); |
| 507 | } |
|
| 508 | ||
| 509 | /** |
|
| 510 | * Returns the fetch timestamp for this editing context. |
|
| 511 | */ |
|
| 512 | public double fetchTimestamp () |
|
| 513 | { |
|
| 514 | 0 | return fetchTimestamp; |
| 515 | } |
|
| 516 | ||
| 517 | /** |
|
| 518 | * Unregisters the specified object from this editing context, |
|
| 519 | * removing all references to it. Use this method to remove |
|
| 520 | * an object from the context without marking it for deletion. |
|
| 521 | */ |
|
| 522 | public void forgetObject ( |
|
| 523 | Object anObject ) |
|
| 524 | { |
|
| 525 | 0 | EOGlobalID id = registrar.globalIDForObject( anObject ); |
| 526 | 0 | if ( id == null ) |
| 527 | { |
|
| 528 | 0 | System.err.println( |
| 529 | 0 | "EOEditingContext.forgetObject: not registered: " + anObject ); |
| 530 | 0 | return; |
| 531 | } |
|
| 532 | ||
| 533 | // unregister object |
|
| 534 | 0 | registrar.forgetObject( anObject ); |
| 535 | ||
| 536 | // remove from all, inserted, updated, and deleted lists |
|
| 537 | int index; |
|
| 538 | 0 | index = updatedObjects.indexOfIdenticalObject( anObject ); |
| 539 | 0 | if ( index != NSArray.NotFound ) |
| 540 | { |
|
| 541 | 0 | updatedObjects.removeObjectAtIndex( index ); |
| 542 | } |
|
| 543 | 0 | index = insertedObjects.indexOfIdenticalObject( anObject ); |
| 544 | 0 | if ( index != NSArray.NotFound ) |
| 545 | { |
|
| 546 | 0 | insertedObjects.removeObjectAtIndex( index ); |
| 547 | 0 | return; |
| 548 | } |
|
| 549 | 0 | index = deletedObjects.indexOfIdenticalObject( anObject ); |
| 550 | 0 | if ( index != NSArray.NotFound ) |
| 551 | { |
|
| 552 | 0 | deletedObjects.removeObjectAtIndex( index ); |
| 553 | 0 | return; |
| 554 | } |
|
| 555 | ||
| 556 | // notify parent context |
|
| 557 | 0 | parentStore.editingContextDidForgetObjectWithGlobalID( this, id ); |
| 558 | 0 | } |
| 559 | ||
| 560 | /** |
|
| 561 | * Returns the id for the specified object, or null |
|
| 562 | * if the object is not registered in this context. |
|
| 563 | */ |
|
| 564 | public EOGlobalID globalIDForObject ( |
|
| 565 | Object anObject ) |
|
| 566 | { |
|
| 567 | 0 | return registrar.globalIDForObject( anObject ); |
| 568 | } |
|
| 569 | ||
| 570 | /** |
|
| 571 | * Returns an array of ids for an array of objects. |
|
| 572 | */ |
|
| 573 | private NSArray globalIDsForObjects( |
|
| 574 | List anObjectList ) |
|
| 575 | { |
|
| 576 | 0 | NSMutableArray result = new NSMutableArray(); |
| 577 | 0 | Iterator it = anObjectList.iterator(); |
| 578 | 0 | while ( it.hasNext() ) |
| 579 | { |
|
| 580 | 0 | result.add( globalIDForObject( it.next() ) ); |
| 581 | 0 | } |
| 582 | 0 | return result; |
| 583 | } |
|
| 584 | ||
| 585 | /** |
|
| 586 | * Returns whether this editing context has changes that |
|
| 587 | * have not yet been committed to the parent object store. |
|
| 588 | */ |
|
| 589 | public boolean hasChanges () |
|
| 590 | { |
|
| 591 | 0 | if ( updatedObjects.count() > 0 ) return true; |
| 592 | 0 | if ( insertedObjects.count() > 0 ) return true; |
| 593 | 0 | if ( deletedObjects.count() > 0 ) return true; |
| 594 | 0 | return false; |
| 595 | } |
|
| 596 | ||
| 597 | /** |
|
| 598 | * Given a newly instantiated object, this method |
|
| 599 | * initializes its properties to values appropriate |
|
| 600 | * for the specified id. The object should already |
|
| 601 | * belong to the specified editing context. |
|
| 602 | * This method is called to populate faults. |
|
| 603 | * This implementation will try to apply the values |
|
| 604 | * from an object with a matching id in this editing |
|
| 605 | * context if possible, calling to the parent object |
|
| 606 | * store only if such an object is not found. |
|
| 607 | */ |
|
| 608 | public void initializeObject ( |
|
| 609 | /*EOEnterpriseObject*/ Object anObject, |
|
| 610 | EOGlobalID aGlobalID, |
|
| 611 | EOEditingContext aContext ) |
|
| 612 | { |
|
| 613 | 0 | Object existingObject = registrar.objectForGlobalID( aGlobalID ); |
| 614 | ||
| 615 | // if not registered in our context |
|
| 616 | 0 | if ( existingObject == null ) |
| 617 | { |
|
| 618 | // get the object registered into our context |
|
| 619 | 0 | existingObject = parentStore.faultForGlobalID( aGlobalID, this ); |
| 620 | } |
|
| 621 | ||
| 622 | 0 | if ( aContext == this ) |
| 623 | { |
|
| 624 | // initialize the object |
|
| 625 | 0 | parentStore.initializeObject( |
| 626 | 0 |