Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||||||
DataObjectStore |
|
| 2.2666666666666666;2.267 |
1 | package net.wotonomy.test; |
|
2 | ||
3 | import java.util.Iterator; |
|
4 | import java.util.LinkedList; |
|
5 | import java.util.List; |
|
6 | import java.util.Map; |
|
7 | ||
8 | import net.wotonomy.control.ArrayFault; |
|
9 | import net.wotonomy.control.EOEditingContext; |
|
10 | import net.wotonomy.control.EOFetchSpecification; |
|
11 | import net.wotonomy.control.EOGlobalID; |
|
12 | import net.wotonomy.control.EOObjectStore; |
|
13 | import net.wotonomy.control.EOObserverCenter; |
|
14 | import net.wotonomy.control.KeyValueCodingUtilities; |
|
15 | import net.wotonomy.datastore.DataKey; |
|
16 | import net.wotonomy.datastore.DataSoup; |
|
17 | import net.wotonomy.datastore.DataView; |
|
18 | import net.wotonomy.datastore.XMLFileSoup; |
|
19 | import net.wotonomy.foundation.NSArray; |
|
20 | import net.wotonomy.foundation.NSMutableArray; |
|
21 | import net.wotonomy.foundation.NSMutableDictionary; |
|
22 | import net.wotonomy.foundation.NSNotification; |
|
23 | import net.wotonomy.foundation.NSNotificationQueue; |
|
24 | import net.wotonomy.foundation.internal.WotonomyException; |
|
25 | ||
26 | /** |
|
27 | * An object store that wraps a datastore for vending test objects. |
|
28 | */ |
|
29 | public class DataObjectStore extends EOObjectStore |
|
30 | { |
|
31 | DataSoup soup; |
|
32 | ||
33 | /** |
|
34 | * Constructor specifies path to datastore. |
|
35 | */ |
|
36 | 0 | public DataObjectStore( String aPath ) |
37 | 0 | { |
38 | 0 | soup = new XMLFileSoup( aPath ); |
39 | 0 | } |
40 | ||
41 | /** |
|
42 | * This implementation returns an appropriately configured array fault. |
|
43 | */ |
|
44 | public NSArray arrayFaultWithSourceGlobalID ( |
|
45 | EOGlobalID aGlobalID, |
|
46 | String aRelationship, |
|
47 | EOEditingContext aContext ) |
|
48 | { |
|
49 | 0 | return new ArrayFault( |
50 | 0 | aGlobalID, aRelationship, aContext ); |
51 | } |
|
52 | ||
53 | /** |
|
54 | * This implementation returns the actual |
|
55 | * object for the specified id. |
|
56 | */ |
|
57 | public Object faultForGlobalID ( |
|
58 | EOGlobalID aGlobalID, |
|
59 | EOEditingContext aContext ) |
|
60 | { |
|
61 | 0 | System.out.println( "DataObjectStore.faultForGlobalID: * reading object * : " + aGlobalID ); |
62 | 0 | Object result = soup.getObjectByKey( |
63 | 0 | ((DataKeyID)aGlobalID).getKey() ); |
64 | ||
65 | 0 | if ( result == null ) return null; |
66 | ||
67 | //! transpose keys to objects |
|
68 | 0 | convertRelationKeysToObjects( aContext, result, aGlobalID ); |
69 | //! |
|
70 | ||
71 | 0 | aContext.recordObject( result, aGlobalID ); |
72 | 0 | return result; |
73 | } |
|
74 | ||
75 | /** |
|
76 | * Returns a fault representing an object of |
|
77 | * the specified entity type with values from |
|
78 | * the specified dictionary. The fault should |
|
79 | * belong to the specified editing context. |
|
80 | */ |
|
81 | public Object faultForRawRow ( |
|
82 | Map aDictionary, |
|
83 | String anEntityName, |
|
84 | EOEditingContext aContext ) |
|
85 | { |
|
86 | //TODO: faults are not yet supported |
|
87 | 0 | throw new WotonomyException( |
88 | 0 | "Faults are not yet supported." ); |
89 | } |
|
90 | ||
91 | /** |
|
92 | * Given a newly instantiated object, this method |
|
93 | * initializes its properties to values appropriate |
|
94 | * for the specified id. The object should belong |
|
95 | * to the specified editing context. |
|
96 | * This method is called to populate faults. |
|
97 | */ |
|
98 | public void initializeObject(Object anObject, EOGlobalID aGlobalID, |
|
99 | EOEditingContext aContext) { |
|
100 | 0 | if (aGlobalID.isTemporary()) { |
101 | // TODO: this should never happen, but it does now until we get |
|
102 | // faults. |
|
103 | ||
104 | // do not reinit an uncommitted object |
|
105 | 0 | return; |
106 | } |
|
107 | ||
108 | 0 | System.out.println("DataObjectStore.initializeObject: * reading object * : " |
109 | 0 | + aGlobalID); |
110 | //net.wotonomy.ui.swing.util.StackTraceInspector.printShortStackTrace(); |
|
111 | 0 | Object original = soup.getObjectByKey(((DataKeyID) aGlobalID).getKey()); |
112 | ||
113 | // ! transpose keys to objects |
|
114 | 0 | convertRelationKeysToObjects(aContext, original, aGlobalID); |
115 | // ! |
|
116 | 0 | EOObserverCenter.notifyObserversObjectWillChange(anObject); |
117 | 0 | KeyValueCodingUtilities.copy(aContext, original, aContext, anObject); |
118 | 0 | } |
119 | ||
120 | /** |
|
121 | * Remove all values from all objects in memory, turning them into faults, |
|
122 | * and posts a notification that all objects have been invalidated. |
|
123 | */ |
|
124 | public void invalidateAllObjects () |
|
125 | { |
|
126 | // does nothing except post notification |
|
127 | ||
128 | 0 | NSNotificationQueue.defaultQueue().enqueueNotification( |
129 | 0 | new NSNotification( |
130 | 0 | InvalidatedAllObjectsInStoreNotification, this ), |
131 | 0 | NSNotificationQueue.PostNow ); |
132 | 0 | } |
133 | ||
134 | /** |
|
135 | * Removes values with the specified ids from memory, |
|
136 | * turning them into faults, and posts a notification |
|
137 | * that those objects have been invalidated. |
|
138 | */ |
|
139 | public void invalidateObjectsWithGlobalIDs ( |
|
140 | List aList ) |
|
141 | { |
|
142 | // does nothing |
|
143 | 0 | } |
144 | ||
145 | /** |
|
146 | * Returns false because locking is not permitted. |
|
147 | */ |
|
148 | public boolean isObjectLockedWithGlobalID ( |
|
149 | EOGlobalID aGlobalID, |
|
150 | EOEditingContext aContext ) |
|
151 | { |
|
152 | 0 | return false; |
153 | } |
|
154 | ||
155 | /** |
|
156 | * Does nothing because locking is not permitted. |
|
157 | */ |
|
158 | public void lockObjectWithGlobalID ( |
|
159 | EOGlobalID aGlobalID, |
|
160 | EOEditingContext aContext ) |
|
161 | { |
|
162 | // does nothing |
|
163 | 0 | } |
164 | ||
165 | /** |
|
166 | * Returns a List of objects associated with the object |
|
167 | * with the specified id for the specified property |
|
168 | * relationship. This method may not return an array fault |
|
169 | * because array faults call this method to fetch on demand. |
|
170 | * All objects must be registered the specified editing context. |
|
171 | * The specified relationship key must produce a result of |
|
172 | * type Collection for the source object or an exception is thrown. |
|
173 | */ |
|
174 | public NSArray objectsForSourceGlobalID ( |
|
175 | EOGlobalID aGlobalID, |
|
176 | String aRelationship, |
|
177 | EOEditingContext aContext ) |
|
178 | { |
|
179 | 0 | System.out.println( "DataObjectStore.objectsForSourceGlobalID: * reading object * : " + aGlobalID ); |
180 | 0 | Object object = soup.getObjectByKey(((DataKeyID)aGlobalID).getKey() ); |
181 | ||
182 | 0 | if ( object == null ) return null; |
183 | ||
184 | Object fault; |
|
185 | EOGlobalID id; |
|
186 | 0 | NSMutableArray result = new NSMutableArray(); |
187 | ||
188 | 0 | Iterator it = ((TestObject)object).getChildList().iterator(); |
189 | 0 | while ( it.hasNext() ) |
190 | { |
|
191 | 0 | id = new DataKeyID((DataKey)it.next()); |
192 | 0 | fault = aContext.faultForGlobalID( id, aContext ); |
193 | ||
194 | // if key still exists |
|
195 | 0 | if ( fault != null ) |
196 | { |
|
197 | //System.out.println( "objectsForSourceGlobalID: found: " + id + " : " + fault ); |
|
198 | 0 | result.add( fault ); |
199 | ||
200 | // for testing purposes |
|
201 | 0 | ((TestObject)fault).setParent( (TestObject) object ); |
202 | 0 | } |
203 | else // key no longer exists |
|
204 | { |
|
205 | // do not add |
|
206 | 0 | System.out.println( "objectsForSourceGlobalID: could not find fault for id: " + id ); |
207 | } |
|
208 | 0 | } |
209 | 0 | return result; |
210 | ||
211 | } |
|
212 | ||
213 | /** |
|
214 | * Returns a List of objects the meet the criteria of |
|
215 | * the supplied specification. |
|
216 | * Each object is registered with the specified editing context. |
|
217 | * If any object is already registered in the specified context, |
|
218 | * it is not refetched and that object should be used in the array. |
|
219 | */ |
|
220 | public NSArray objectsWithFetchSpecification ( |
|
221 | EOFetchSpecification aFetchSpec, |
|
222 | EOEditingContext aContext ) |
|
223 | { |
|
224 | //TODO: fetch specs are not yet supported |
|
225 | ||
226 | 0 | DataView view = soup.queryObjects( null, null ); |
227 | 0 | System.out.println( "DataObjectStore: ** querying all objects **" ); |
228 | ||
229 | // we've changed this implementation so that |
|
230 | // it simply calls faultForGlobalID on the context |
|
231 | // for each id in the result set. |
|
232 | // this way, child contexts inherit parent's state. |
|
233 | // however, it's unclear if the specification allows |
|
234 | // faults in the resulting array. sounds like it doesn't. |
|
235 | 0 | NSMutableArray result = new NSMutableArray(); |
236 | DataKeyID id; |
|
237 | 0 | Iterator it = view.iterator(); |
238 | 0 | while ( it.hasNext() ) |
239 | { |
|
240 | 0 | id = new DataKeyID( view.getKeyForObject( it.next() ) ); |
241 | 0 | result.addObject( aContext.faultForGlobalID( id, aContext ) ); |
242 | 0 | } |
243 | 0 | return result; |
244 | } |
|
245 | ||
246 | /** |
|
247 | * Removes all values from the specified object, |
|
248 | * converting it into a fault for the specified id. |
|
249 | * New or deleted objects should not be refaulted. |
|
250 | */ |
|
251 | public void refaultObject ( |
|
252 | Object anObject, |
|
253 | EOGlobalID aGlobalID, |
|
254 | EOEditingContext aContext ) |
|
255 | { |
|
256 | //TODO: faults are not yet supported |
|
257 | // just re-initialize the object |
|
258 | 0 | initializeObject( anObject, aGlobalID, aContext ); |
259 | 0 | } |
260 | ||
261 | /** |
|
262 | * Writes all changes in the specified editing context |
|
263 | * to the respository. |
|
264 | */ |
|
265 | public void saveChangesInEditingContext ( |
|
266 | EOEditingContext aContext ) |
|
267 | { |
|
268 | Object o; |
|
269 | DataKeyID id; |
|
270 | Iterator it; |
|
271 | ||
272 | // process deletes |
|
273 | 0 | it = aContext.deletedObjects().iterator(); |
274 | 0 | while ( it.hasNext() ) |
275 | { |
|
276 | 0 | o = it.next(); |
277 | 0 | id = (DataKeyID) aContext.globalIDForObject( o ); |
278 | 0 | System.out.println( "DataObjectStore: * deleting object * : " + id ); |
279 | 0 | soup.removeObject( id.getKey() ); |
280 | // remove object from editing context |
|
281 | 0 | aContext.forgetObject( o ); |
282 | 0 | } |
283 | ||
284 | // process inserts |
|
285 | 0 | NSMutableDictionary userInfo = null; |
286 | 0 | it = aContext.insertedObjects().iterator(); |
287 | 0 | while ( it.hasNext() ) |
288 | { |
|
289 | 0 | o = it.next(); |
290 | 0 | EOGlobalID oldId = aContext.globalIDForObject( o ); |
291 | ||
292 | //! transpose objects to keys |
|
293 | 0 | convertRelationObjectsToKeys( aContext, (TestObject) o ); |
294 | 0 | id = new DataKeyID( soup.addObject( o ) ); |
295 | 0 | convertRelationKeysToObjects( aContext, (TestObject) o, oldId ); |
296 | //! |
|
297 | ||
298 | 0 | System.out.println( "DataObjectStore: * adding object * : " + id ); |
299 | ||
300 | // save mapping of old id to new id |
|
301 | 0 | if ( userInfo == null ) |
302 | { |
|
303 | 0 | userInfo = new NSMutableDictionary(); |
304 | } |
|
305 | 0 | userInfo.setObjectForKey( id, oldId ); |
306 | 0 | } |
307 | ||
308 | // broadcast inserted objects' new ids if necessary |
|
309 | 0 | if ( userInfo != null ) |
310 | { |
|
311 | 0 | NSNotificationQueue.defaultQueue().enqueueNotification( |
312 | 0 | new NSNotification( |
313 | 0 | EOGlobalID.GlobalIDChangedNotification, null, userInfo ), |
314 | 0 | NSNotificationQueue.PostNow ); |
315 | } |
|
316 | ||
317 | 0 | System.out.println( aContext.updatedObjects() ); |
318 | ||
319 | // process updates |
|
320 | 0 | it = aContext.updatedObjects().iterator(); |
321 | 0 | while ( it.hasNext() ) |
322 | { |
|
323 | //if ( true ) // test validation error message handling |
|
324 | //throw new RuntimeException( "Update not allowed." ); |
|
325 | 0 | o = it.next(); |
326 | 0 | id = (DataKeyID) aContext.globalIDForObject( o ); |
327 | 0 | System.out.println( "DataObjectStore: * updating object * : " + id ); |
328 | ||
329 | //! transpose objects to keys |
|
330 | 0 | convertRelationObjectsToKeys( aContext, (TestObject) o ); |
331 | 0 | soup.updateObject( id.getKey(), o ); |
332 | 0 | convertRelationKeysToObjects( aContext, (TestObject) o, id ); |
333 | //! |
|
334 | ||
335 | 0 | } |
336 | 0 | } |
337 | ||
338 | private void convertRelationKeysToObjects( |
|
339 | EOEditingContext aContext, Object anObject, EOGlobalID aGlobalID ) |
|
340 | { // System.out.println( "convertRelationKeysToObjects: " + anObject ); |
|
341 | // set editing context for testing |
|
342 | 0 | ((TestObject)anObject).editingContext = aContext; |
343 | ||
344 | Object fault; |
|
345 | DataKeyID id; |
|
346 | 0 | List result = new LinkedList(); |
347 | 0 | Iterator it = ((TestObject)anObject).getChildList().iterator(); |
348 | 0 | while ( it.hasNext() ) |
349 | { |
|
350 | 0 | id = new DataKeyID((DataKey)it.next()); |
351 | 0 | fault = aContext.faultForGlobalID( id, aContext ); |
352 | ||
353 | // if key still exists |
|
354 | 0 | if ( fault != null ) |
355 | { |
|
356 | //System.out.println( "convertRelationObjectsToKeys: found: " + id + " : " + fault ); |
|
357 | 0 | result.add( fault ); |
358 | ||
359 | // for testing purposes |
|
360 | 0 | ((TestObject)fault).setParent( (TestObject) anObject ); |
361 | 0 | } |
362 | else // key no longer exists |
|
363 | { |
|
364 | // do not add |
|
365 | 0 | System.out.println( "convertRelationObjectsToKeys: could not find fault for id: " + id ); |
366 | } |
|
367 | 0 | } |
368 | // this tests loading manually on-demand |
|
369 | // ((TestObject)anObject).setChildList( null ); |
|
370 | // this tests loading immediately |
|
371 | 0 | ((TestObject)anObject).setChildList( result ); |
372 | // this tests loading array faults |
|
373 | // ((TestObject)result).setChildList( null ); |
|
374 | 0 | ((TestObject)anObject).setChildList( |
375 | 0 | aContext.arrayFaultWithSourceGlobalID( |
376 | 0 | aGlobalID, "childList", aContext ) ); |
377 | ||
378 | 0 | } |
379 | ||
380 | private void convertRelationObjectsToKeys( |
|
381 | EOEditingContext aContext, Object anObject ) |
|
382 | { // System.out.println( "convertRelationObjectsToKeys: " + anObject ); |
|
383 | Object o; |
|
384 | DataKeyID id; |
|
385 | 0 | List result = new LinkedList(); |
386 | 0 | Iterator it = ((TestObject)anObject).getChildList().iterator(); |
387 | // for testing purposes |
|
388 | 0 | ((TestObject)anObject).setParent( null ); |
389 | 0 | ((TestObject)anObject).editingContext = null; |
390 | 0 | while ( it.hasNext() ) |
391 | { |
|
392 | 0 | o = it.next(); |
393 | //System.out.println( "convertRelationObjectsToKeys: " + o + " : " + aContext.globalIDForObject( o ) ); |
|
394 | 0 | id = (DataKeyID)aContext.globalIDForObject( o ); |
395 | ||
396 | // if object still exists in context |
|
397 | 0 | if ( id != null ) |
398 | { |
|
399 | 0 | result.add( id.getKey() ); |
400 | 0 | } |
401 | else // object was deleted |
|
402 | { |
|
403 | // do not add |
|
404 | 0 | System.out.println( "convertRelationObjectsToKeys: could not find id for object: " + o ); |
405 | 0 | System.out.println( aContext.registeredObjects() ); |
406 | } |
|
407 | ||
408 | 0 | } |
409 | 0 | ((TestObject)anObject).setChildList( result ); |
410 | 0 | } |
411 | ||
412 | ||
413 | /* |
|
414 | * $Log$ |
|
415 | * Revision 1.1 2006/02/19 16:30:25 cgruber |
|
416 | * Update imports and maven dependencies. |
|
417 | * |
|
418 | * Revision 1.1 2006/02/16 13:18:56 cgruber |
|
419 | * Check in all sources in eclipse-friendly maven-enabled packages. |
|
420 | * |
|
421 | * Revision 1.18 2002/03/11 03:18:39 mpowers |
|
422 | * Now properly handling ObserverChangesLater. |
|
423 | * |
|
424 | * Revision 1.17 2001/10/26 18:39:44 mpowers |
|
425 | * Posting notifications immediately, rather than delayed. |
|
426 | * |
|
427 | * Revision 1.16 2001/05/06 18:27:10 mpowers |
|
428 | * More broadly catching editing contexts for now. |
|
429 | * |
|
430 | * Revision 1.15 2001/05/05 23:05:43 mpowers |
|
431 | * Implemented Array Faults. |
|
432 | * |
|
433 | * Revision 1.14 2001/05/05 15:00:06 mpowers |
|
434 | * Tested load-on-demand: still works. |
|
435 | * Now using registerClone for consistency. |
|
436 | * Editing context is temporarily posting notification on objectWillChange. |
|
437 | * |
|
438 | * Revision 1.13 2001/05/04 23:24:30 mpowers |
|
439 | * Changes to test code. |
|
440 | * |
|
441 | * Revision 1.12 2001/05/04 16:57:56 mpowers |
|
442 | * Now correctly transposing references to editing contexts when |
|
443 | * cloning/copying between editing contexts. |
|
444 | * |
|
445 | * Revision 1.11 2001/05/02 17:33:28 mpowers |
|
446 | * More changes for testing. |
|
447 | * |
|
448 | * Revision 1.10 2001/04/30 13:15:24 mpowers |
|
449 | * Child contexts re-initializing objects invalidated in parent now |
|
450 | * propery transpose relationships. |
|
451 | * |
|
452 | * Revision 1.9 2001/04/29 22:02:45 mpowers |
|
453 | * Work on id transposing between editing contexts. |
|
454 | * |
|
455 | * Revision 1.8 2001/04/29 02:29:31 mpowers |
|
456 | * Debugging relationship faulting. |
|
457 | * |
|
458 | * Revision 1.7 2001/04/28 22:17:51 mpowers |
|
459 | * Revised PropertyDataSource to be EOClassDescription-aware. |
|
460 | * |
|
461 | * Revision 1.6 2001/04/28 16:18:44 mpowers |
|
462 | * Implementing relationships. |
|
463 | * |
|
464 | * Revision 1.5 2001/04/13 16:33:36 mpowers |
|
465 | * Now broadcasting notifications. |
|
466 | * |
|
467 | * Revision 1.4 2001/04/08 21:00:54 mpowers |
|
468 | * Changes to support new objectsForFetchSpecification scheme. |
|
469 | * |
|
470 | * Revision 1.3 2001/03/22 21:37:52 mpowers |
|
471 | * Testing new features. |
|
472 | * |
|
473 | * Revision 1.2 2001/03/15 21:10:41 mpowers |
|
474 | * Implemented global id re-registration for newly saved inserts. |
|
475 | * |
|
476 | * Revision 1.1 2001/03/05 22:12:11 mpowers |
|
477 | * Created the control package for a datastore-specific implementation |
|
478 | * of EOObjectStore. |
|
479 | * |
|
480 | * |
|
481 | */ |
|
482 | } |
|
483 |