Coverage Report - net.wotonomy.control.KeyValueCodingUtilities
 
Classes in this File Line Coverage Branch Coverage Complexity
KeyValueCodingUtilities
0% 
0% 
2.385
 
 1  
 /*
 2  
 Wotonomy: OpenStep design patterns for pure Java applications.
 3  
 Copyright (C) 2001 Intersect Software Corporation
 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.io.ByteArrayInputStream;
 22  
 import java.io.ByteArrayOutputStream;
 23  
 import java.io.IOException;
 24  
 import java.io.InputStream;
 25  
 import java.io.ObjectInputStream;
 26  
 import java.io.ObjectOutputStream;
 27  
 import java.io.ObjectStreamClass;
 28  
 import java.io.OutputStream;
 29  
 import java.io.Serializable;
 30  
 import java.util.Iterator;
 31  
 import java.util.LinkedList;
 32  
 import java.util.List;
 33  
 import java.util.Map;
 34  
 
 35  
 import net.wotonomy.foundation.NSDictionary;
 36  
 import net.wotonomy.foundation.NSMutableDictionary;
 37  
 import net.wotonomy.foundation.internal.Duplicator;
 38  
 import net.wotonomy.foundation.internal.WotonomyException;
 39  
 
 40  
 /**
 41  
 * KeyValueCodingUtilities implements what 
 42  
 * EOKeyValueCodingSupport leaves out.  Importantly,
 43  
 * this class implements the deep clone and deep copy 
 44  
 * operations that are essential to the functioning of 
 45  
 * nested editing contexts.
 46  
 *
 47  
 * @author michael@mpowers.net
 48  
 * @author $Author: cgruber $
 49  
 * @version $Revision: 900 $
 50  
 */
 51  0
 public class KeyValueCodingUtilities
 52  
 {
 53  
     /**
 54  
     * Returns a Map of the specified keys to their values,
 55  
     * each of which is obtained by calling valueForKey
 56  
     * on the specified object if it implements EOKeyValueCoding,
 57  
     * and otherwise falling back on EOKeyValueCodingSupport.
 58  
     * Null values must be represented by NSNull.nullValue().
 59  
     */
 60  
     static public NSDictionary valuesForKeys( 
 61  
         Object anObject, List aKeyList )
 62  
     {
 63  0
         return valuesForKeys( anObject, aKeyList, false );
 64  
     }
 65  
     
 66  
     /**
 67  
     * Returns a Map of the specified keys to their values,
 68  
     * each of which is obtained by calling storedValueForKey
 69  
     * on the specified object if it implements EOKeyValueCoding,
 70  
     * and otherwise falling back on EOKeyValueCodingSupport.
 71  
     * Null values must be represented by NSNull.nullValue().
 72  
     */
 73  
     static public NSDictionary storedValuesForKeys( 
 74  
         Object anObject, List aKeyList )
 75  
     {
 76  0
         return valuesForKeys( anObject, aKeyList, true );
 77  
     }
 78  
     
 79  
     /**
 80  
     * Called by valuesForKeys and storedValuesForKeys.
 81  
     * This uses storedValueForKey if isStored is true,
 82  
     * otherwise uses valueForKey.
 83  
     */
 84  
     static private NSDictionary valuesForKeys( 
 85  
         Object anObject, List aKeyList, boolean isStored )
 86  
     {
 87  
         EOKeyValueCoding coding;
 88  0
         if ( anObject instanceof EOKeyValueCoding )
 89  
         {
 90  0
             coding = (EOKeyValueCoding) anObject;
 91  0
         }
 92  
         else
 93  
         {
 94  0
             coding = null;   
 95  
         }
 96  
         
 97  
         String key;
 98  
         Object value;
 99  0
         NSMutableDictionary result = new NSMutableDictionary();
 100  0
         Iterator it = aKeyList.iterator();
 101  0
         while ( it.hasNext() )
 102  
         {
 103  
             //TODO: get rid of this try/catch - exceptions should be fatal (?) 
 104  
             try
 105  
             {
 106  0
                 key = it.next().toString();            
 107  0
                 if ( coding != null )
 108  
                 {
 109  0
                     if ( isStored )
 110  0
                         value = coding.storedValueForKey( key );
 111  
                     else
 112  0
                         value = coding.valueForKey( key );
 113  0
                 }
 114  
                 else
 115  
                 {
 116  0
                     if ( isStored )
 117  0
                         value = EOKeyValueCodingSupport.storedValueForKey( anObject, key );
 118  
                     else
 119  0
                         value = EOKeyValueCodingSupport.valueForKey( anObject, key );
 120  
                 }
 121  0
                 if ( value == null )
 122  
                 {
 123  0
                     value = EONullValue.nullValue();   
 124  
                 }
 125  0
                 result.setObjectForKey( value, key );
 126  
             }
 127  0
             catch ( RuntimeException exc )
 128  
             {
 129  0
                 System.out.println( 
 130  0
                 "KeyValueCodingUtilities.valuesForKeys: " 
 131  0
                 + isStored + " : " + exc );   
 132  0
             }
 133  0
         }
 134  0
         return result;
 135  
     }
 136  
     
 137  
     /**
 138  
     * Takes the keys from the specified Map as properties
 139  
     * and applies the corresponding values, each of which
 140  
     * might be set by calling takeValueForKey on the 
 141  
     * specified object if it implements EOKeyValueCoding,
 142  
     * and otherwise falling back on EOKeyValueCodingSupport.
 143  
     * Null values must be represented by NSNull.nullValue().
 144  
     */
 145  
     static public void takeValuesFromDictionary( 
 146  
         Object anObject, Map aMap )
 147  
     {
 148  0
         takeStoredValuesFromDictionary( anObject, aMap, false );
 149  0
     }
 150  
 
 151  
     /**
 152  
     * Takes the keys from the specified Map as properties
 153  
     * and applies the corresponding values, each of which
 154  
     * might be set by calling takeStoredValueForKey on the 
 155  
     * specified object if it implements EOKeyValueCoding,
 156  
     * and otherwise falling back on EOKeyValueCodingSupport.
 157  
     * Null values must be represented by NSNull.nullValue().
 158  
     */
 159  
     static public void takeStoredValuesFromDictionary( 
 160  
         Object anObject, Map aMap )
 161  
     {
 162  0
         takeStoredValuesFromDictionary( anObject, aMap, true );    
 163  0
     }
 164  
     
 165  
     /**
 166  
     * Called by takeValuesFromDictionary and takeStoredValuesFromDictionary.
 167  
     * This uses takeStoredValueForKey if isStored is true,
 168  
     * otherwise uses takeValueForKey.
 169  
     */
 170  
     static private void takeStoredValuesFromDictionary( 
 171  
         Object anObject, Map aMap, boolean isStored )
 172  
     {
 173  
         EOKeyValueCoding coding;
 174  0
         if ( anObject instanceof EOKeyValueCoding )
 175  
         {
 176  0
             coding = (EOKeyValueCoding) anObject;
 177  0
         }
 178  
         else
 179  
         {
 180  0
             coding = null;   
 181  
         }
 182  
         
 183  
         String key;
 184  
         Object value;
 185  0
         NSMutableDictionary result = new NSMutableDictionary();
 186  0
         Iterator it = aMap.keySet().iterator();
 187  0
         while ( it.hasNext() )
 188  
         {
 189  
             //TODO: get rid of this try/catch - exceptions should be fatal (?) 
 190  
             try
 191  
             {
 192  0
                 key = it.next().toString();
 193  0
                 value = aMap.get( key );
 194  0
                 if ( value instanceof EONullValue ) 
 195  
                 // can't use == nullValue() because of cloning/serialization
 196  
                 {
 197  0
                     value = null;   
 198  
                 }
 199  0
                 if ( coding != null )
 200  
                 {
 201  0
                     if ( isStored )
 202  0
                         coding.takeStoredValueForKey( value, key );
 203  
                     else
 204  0
                         coding.takeValueForKey( value, key );
 205  0
                 }
 206  
                 else
 207  
                 {
 208  0
                     if ( isStored )
 209  0
                         EOKeyValueCodingSupport.takeStoredValueForKey( 
 210  0
                             anObject, value, key );
 211  
                     else
 212  0
                         EOKeyValueCodingSupport.takeValueForKey( 
 213  0
                             anObject, value, key );
 214  
                 }
 215  
             }
 216  0
             catch ( WotonomyException exc )
 217  
             {
 218  0
                 System.out.println( 
 219  0
                     "KeyValueCodingUtilities.takeStoredValuesFromDictionary: " 
 220  0
                     + isStored + " : " + exc );
 221  0
             }
 222  0
         }
 223  0
     }
 224  
 
 225  
     /**
 226  
     * Creates a deep clone of the specified object.
 227  
     * (Object.clone() only creates a shallow clone.)
 228  
     * Returns null if operation fails.
 229  
     */ 
 230  
     static public Object clone( Object aSource )
 231  
     {
 232  0
         return Duplicator.deepClone( aSource );
 233  
     }
 234  
 
 235  
     /**
 236  
     * Creates a deep clone of the specified object,
 237  
     * registered in the specified source editing context,
 238  
     * transposing it into the specified destination
 239  
     * editing context.
 240  
     * Returns null if operation fails.
 241  
     */ 
 242  
     static public Object clone( 
 243  
         EOEditingContext aSourceContext, Object aSource,
 244  
         EOEditingContext aDestinationContext )
 245  
     {
 246  0
         return clone( aSourceContext, aSource, aDestinationContext, aSource );    
 247  
     }
 248  
     
 249  
     /**
 250  
     * Called by clone and copy.
 251  
     * The specified root object will not be replaced
 252  
     * by an object in the destination editing context:
 253  
     * this should be the same as the source object for
 254  
     * cloning, but should be null for copying.
 255  
     * Returns null if operation fails.
 256  
     */ 
 257  
     static private Object clone( 
 258  
         EOEditingContext aSourceContext, Object aSource,
 259  
         EOEditingContext aDestinationContext,
 260  
         Object aRootObject )
 261  
     {
 262  
 
 263  
 //System.out.println();
 264  
 //System.out.println( "clone: " + aSourceContext );
 265  
 //System.out.println( "     : " + aSource );
 266  
 //System.out.println( "     : " + aDestinationContext );
 267  
 //System.out.println();
 268  
 
 269  
         // the only known way to deep copy in
 270  
         // java without native code is serialization
 271  
         
 272  0
         return thaw( 
 273  0
             freeze( aSource, aSourceContext, aRootObject, true ), 
 274  0
             aDestinationContext, true );
 275  
     }
 276  
     
 277  
     /**
 278  
     * Serializes an object to a byte array containing
 279  
     * GlobalIDMarkers in place of references to other objects 
 280  
     * registered in the specified context.
 281  
     * The specified root object will be serialized, 
 282  
     * even if it is registered in the specified context:
 283  
     * this is typically the root object you're trying to
 284  
     * serialize.
 285  
     * Package access, as this method is used by editing
 286  
     * context for snapshots.
 287  
     */
 288  
     static public byte[] freeze( 
 289  
         Object anObject, EOEditingContext aContext, Object aRootObject, boolean transpose )
 290  
     {
 291  
         try
 292  
         {
 293  
 //long t = System.currentTimeMillis();            
 294  0
             ByteArrayOutputStream byteOutput =
 295  0
                 new ByteArrayOutputStream();// CloneBufferSize );
 296  
             ObjectOutputStream objectOutput;
 297  0
             if ( transpose )
 298  
             {
 299  0
                 objectOutput =
 300  0
                     new TransposingContextObjectOutputStream( 
 301  0
                         byteOutput, aContext, aRootObject );
 302  0
             }
 303  
             else
 304  
             {
 305  0
                 objectOutput =
 306  0
                     new ContextObjectOutputStream( 
 307  0
                         byteOutput, aContext );
 308  
             }
 309  
                 
 310  0
             objectOutput.writeObject( anObject );
 311  0
             objectOutput.flush();
 312  0
             objectOutput.close();
 313  
 
 314  0
             return byteOutput.toByteArray();
 315  
 
 316  
 // profiling
 317  
 /*
 318  
 byte[] result = byteOutput.toByteArray();
 319  
 long size = result.length;
 320  
 long time = ( System.currentTimeMillis() - t );
 321  
 maxSize = Math.max( size, maxSize );
 322  
 minSize = Math.min( size, minSize );
 323  
 totSize += size;
 324  
 maxTime = Math.max( time, maxTime );
 325  
 minTime = Math.min( time, minTime );
 326  
 totTime += time;
 327  
 nTime++;
 328  
 System.out.println( "freeze: size = [ " + size + " : " + minSize + " : " + ( (float)totSize / (float)nTime ) + " : " + maxSize 
 329  
 + " ]  time = [ " + time + " : " + minTime + " : " + ( (float)totTime / (float)nTime ) + " : " + maxTime + " ]" );
 330  
 return result;            
 331  
 */
 332  
 // end profiling
 333  
 
 334  
         } 
 335  0
         catch ( Exception exc )
 336  
         {
 337  0
             throw new WotonomyException( exc );
 338  
         }
 339  
     }
 340  
     
 341  
 //static long maxTime, minTime, totTime, nTime, maxSize, minSize, totSize;    
 342  
 //static long maxTimeThaw, minTimeThaw, totTimeThaw, nTimeThaw;
 343  
 
 344  
     /**
 345  
     * De-serializes an object from the specified byte
 346  
     * array, replacing GlobalIDMarkers with reference
 347  
     * to objects registered in the specified editing
 348  
     * context.
 349  
     * Package access, as this method is used by editing
 350  
     * context for snapshots.
 351  
     */
 352  
     static public Object thaw( 
 353  
         byte[] aByteArray, EOEditingContext aContext, boolean transpose )
 354  
     {
 355  0
         return thaw( aByteArray, aContext, null, transpose );
 356  
     }
 357  
 
 358  
     /**
 359  
     * De-serializes an object from the specified byte
 360  
     * array, replacing GlobalIDMarkers with reference
 361  
     * to objects registered in the specified editing
 362  
     * context.
 363  
     * Package access, as this method is used by editing
 364  
     * context for snapshots.
 365  
     */
 366  
     static public Object thaw( 
 367  
         byte[] aByteArray, EOEditingContext aContext, ClassLoader aLoader, boolean transpose )
 368  
     {
 369  
         try
 370  
         {
 371  
 //long t = System.currentTimeMillis();            
 372  0
             ByteArrayInputStream byteInput =
 373  0
                 new ByteArrayInputStream( aByteArray );
 374  
             ObjectInputStream objectInput;
 375  0
             if ( transpose )
 376  
             {
 377  0
                 objectInput =
 378  0
                     new TransposingContextObjectInputStream( 
 379  0
                         byteInput, aContext, aLoader );
 380  0
             }
 381  
             else
 382  
             {
 383  0
                 objectInput =
 384  0
                     new ContextObjectInputStream( 
 385  0
                         byteInput, aContext, aLoader );
 386  
             }
 387  
                     
 388  0
             return objectInput.readObject();
 389  
 // profiling
 390  
 /*
 391  
 Object result = objectInput.readObject();
 392  
 long timeThaw = ( System.currentTimeMillis() - t );
 393  
 maxTimeThaw = Math.max( timeThaw, maxTimeThaw );
 394  
 minTimeThaw = Math.min( timeThaw, minTimeThaw );
 395  
 totTimeThaw += timeThaw;
 396  
 nTimeThaw++;
 397  
 System.out.println( "thaw: size = " + aByteArray.length + ", time = [ " + timeThaw + " : " + minTimeThaw + " : " + ( (float)totTimeThaw / (float)nTimeThaw ) + " : " + maxTimeThaw + " ]" );
 398  
 return result;
 399  
 */
 400  
 // end profiling
 401  
         } 
 402  0
         catch ( Exception exc )
 403  
         {
 404  0
             throw new WotonomyException( exc );
 405  
         }
 406  
     }
 407  
 
 408  
     /**
 409  
     * Copies values from one object registered in the
 410  
     * specified origin context to the specified destination
 411  
     * object 
 412  
     * The values themselves are cloned, so this is a deep copy.
 413  
     * Returns the destination object, or throws exception
 414  
     * if operation fails.
 415  
     */ 
 416  
     static public Object copy( Object aSource, Object aDestination )
 417  
     {
 418  0
         NSDictionary values = (NSDictionary) 
 419  0
             clone( valuesForKeys( aSource, 
 420  0
                 EOClassDescription.classDescriptionForClass( 
 421  0
                     aSource.getClass() ).attributeKeys() ) );
 422  
 
 423  0
         takeStoredValuesFromDictionary( aDestination, values );        
 424  0
         return aDestination;
 425  
     }
 426  
     
 427  
     /**
 428  
     * Copies values from one object registered in the
 429  
     * specified origin context to the specified destination
 430  
     * object 
 431  
     * The values themselves are cloned, so this is a deep copy.
 432  
     * Returns the destination object, or throws exception
 433  
     * if operation fails.
 434  
     */ 
 435  
     static public Object copy( 
 436  
         EOEditingContext aSourceContext, Object aSource, 
 437  
         EOEditingContext aDestinationContext, Object aDestination )
 438  
     {
 439  
         // get all keys for this object
 440  0
         EOClassDescription classDesc =
 441  0
             EOClassDescription.classDescriptionForClass( aSource.getClass() );            
 442  0
         List keys = new LinkedList();
 443  0
         keys.addAll( classDesc.attributeKeys() ); 
 444  0
         keys.addAll( classDesc.toOneRelationshipKeys() ); 
 445  0
         keys.addAll( classDesc.toManyRelationshipKeys() ); 
 446  
 
 447  
         // transpose all objects registered in source context
 448  0
         NSDictionary values = storedValuesForKeys( aSource, keys );
 449  0
         values = (NSDictionary)
 450  0
             clone( aSourceContext, values, aDestinationContext, null );
 451  
             
 452  
         // apply to destination object
 453  0
         takeStoredValuesFromDictionary( aDestination, values );        
 454  0
         return aDestination;
 455  
     }
 456  
     
 457  
     // inner classes
 458  
     
 459  
     /**
 460  
     * An ObjectOutputStream that serializes objects with references
 461  
     * to an editing context.  The specified context will not be
 462  
     * serialized but referenced, so that a ContextObjectInputStream
 463  
     * can replace the reference with another editing context.
 464  
     */
 465  
     static private class ContextObjectOutputStream extends ObjectOutputStream
 466  
     {
 467  0
         private EditingContextMarker marker = new EditingContextMarker();
 468  
         protected EOEditingContext editingContext;
 469  
         
 470  
         /**
 471  
         * Specifies the output stream to wrap,
 472  
         * and the source context that should be 
 473  
         * referenced but not serialized.
 474  
         */
 475  
         public ContextObjectOutputStream( 
 476  
             OutputStream anOutputStream,
 477  
             EOEditingContext aContext )
 478  
         throws IOException
 479  
         {
 480  0
             super( anOutputStream );
 481  0
             editingContext = aContext;
 482  
             try
 483  
             {
 484  0
                 enableReplaceObject(true);
 485  
             }
 486  0
             catch ( Exception exc )
 487  
             {
 488  0
                 exc.printStackTrace();   
 489  0
             }
 490  0
         }
 491  
         
 492  
         protected Object replaceObject(Object anObject) throws IOException
 493  
         {
 494  
 //            if ( anObject == editingContext ) return marker;
 495  
 //FIXME: this should be more strict as above
 496  0
             if ( anObject instanceof EOEditingContext ) return marker;
 497  0
             return anObject;
 498  
         }
 499  
 
 500  
     }
 501  
     
 502  
     /**
 503  
     * A ContextObjectOutputStream that replaces any objects registered
 504  
     * in the source editing context with markers to be used in
 505  
     * ContextObjectInputStream.
 506  
     */
 507  
     static private class TransposingContextObjectOutputStream 
 508  
         extends ContextObjectOutputStream
 509  
     {
 510  
         protected Object rootObject;
 511  
         
 512  
         /**
 513  
         * Specifies the output stream to wrap,
 514  
         * the source context containing objects that
 515  
         * should be replaced if found, 
 516  
         * and the object which should not be re-registered,
 517  
         * which is typically the object being cloned, but
 518  
         * may be null.
 519  
         */
 520  
         public TransposingContextObjectOutputStream( 
 521  
             OutputStream anOutputStream,
 522  
             EOEditingContext aContext, 
 523  
             Object anObject )
 524  
         throws IOException
 525  
         {
 526  0
             super( anOutputStream, aContext );
 527  0
             rootObject = anObject;
 528  0
         }
 529  
         
 530  
         protected Object replaceObject(Object anObject) throws IOException
 531  
         {
 532  0
             if ( anObject == rootObject ) return anObject;
 533  0
             if ( editingContext != null )
 534  
             {
 535  0
                 EOGlobalID id = editingContext.globalIDForObject( anObject );
 536  0
                 if ( id != null ) 
 537  
                 {
 538  0
                     Object result = new GlobalIDMarker( id );
 539  
     //System.out.println( "KeyValueCodingUtilities.replaceObject: returning: " + result );
 540  0
                     return result;
 541  
                 }
 542  
             }
 543  0
             return super.replaceObject( anObject );
 544  
         }
 545  
 
 546  
     }
 547  
     
 548  
     /**
 549  
     * A marker class so references to objects registered in editing 
 550  
     * contexts get transposed rather than cloned.
 551  
     */
 552  
     static private class GlobalIDMarker implements Serializable
 553  
     {
 554  
         private EOGlobalID id;
 555  
         
 556  0
         public GlobalIDMarker( EOGlobalID anID )
 557  0
         {
 558  0
             id = anID;   
 559  0
         }
 560  
         
 561  
         public EOGlobalID getID() 
 562  
         {
 563  0
             return id;   
 564  
         }
 565  
         
 566  
         public String toString()
 567  
         {
 568  0
             return "[GlobalIDMarker:"+id+"]";   
 569  
         }
 570  
     }
 571  
     
 572  
     /**
 573  
     * A marker class so references an object's editing context
 574  
     * gets transposed rather than cloned.
 575  
     */
 576  0
     static private class EditingContextMarker implements Serializable
 577  
     {
 578  
         // just a marker class - no implementation necessary
 579  
     }
 580  
     
 581  
     /**
 582  
     * An ObjectInputStream that replaces any markers from
 583  
     * ContextObjectOutputStream with objects registered
 584  
     * in the destination editing context.
 585  
     */
 586  
     static private class ContextObjectInputStream extends ObjectInputStream
 587  
     {
 588  
         protected EOEditingContext editingContext;
 589  
         protected ClassLoader classLoader;
 590  
         
 591  
         /**
 592  
         * Specifies the output stream to wrap,
 593  
         * the source context containing objects that
 594  
         * should be to replace any markers.
 595  
         * The class loader may be null.
 596  
         */
 597  
         public ContextObjectInputStream( 
 598  
             InputStream anInputStream,
 599  
             EOEditingContext aContext,
 600  
             ClassLoader aClassLoader )
 601  
         throws IOException
 602  
         {
 603  0
             super( anInputStream );
 604  0
             editingContext = aContext;
 605  0
             classLoader = aClassLoader;
 606  0
             if ( classLoader == null )
 607  
             {
 608  0
                 classLoader = 
 609  0
                     KeyValueCodingUtilities.class.getClassLoader();
 610  
             }
 611  
             try
 612  
             {
 613  0
                 enableResolveObject(true);
 614  
             }
 615  0
             catch ( Exception exc )
 616  
             {
 617  0
                 exc.printStackTrace();   
 618  0
             }
 619  0
         }
 620  
         
 621  
         protected Object resolveObject(Object anObject) throws IOException
 622  
         {
 623  0
             if ( anObject instanceof EditingContextMarker )
 624  
             {
 625  0
                 return editingContext;
 626  
             }
 627  0
             return anObject;
 628  
         }
 629  
         
 630  
         protected Class resolveClass(ObjectStreamClass v)
 631  
              throws IOException, ClassNotFoundException 
 632  
         {
 633  0
             return classLoader.loadClass( v.getName() );
 634  
         }
 635  
     }
 636  
     
 637  
     /**
 638  
     * A ContextObjectInputStream that replaces any markers from
 639  
     * TransposingContextObjectOutputStream with objects registered
 640  
     * in the destination editing context.
 641  
     */
 642  0
     static private class TransposingContextObjectInputStream 
 643  
         extends ContextObjectInputStream
 644  
     {
 645  
         /**
 646  
         * Specifies the output stream to wrap,
 647  
         * the source context containing objects that
 648  
         * should be to replace any markers.
 649  
         */
 650  
         public TransposingContextObjectInputStream( 
 651  
             InputStream anInputStream,
 652  
             EOEditingContext aContext,
 653  
             ClassLoader aClassLoader )
 654  
         throws IOException
 655  
         {
 656  0
             super( anInputStream, aContext, aClassLoader );
 657  0
         }
 658  
         
 659  
         protected Object resolveObject(Object anObject) throws IOException
 660  
         {
 661  0
             if ( anObject instanceof GlobalIDMarker )
 662  
             {
 663  0
                 return editingContext.faultForGlobalID( 
 664  0
                     ((GlobalIDMarker)anObject).getID(), editingContext );
 665  
             }
 666  0
             return super.resolveObject( anObject );
 667  
         }
 668  
     }
 669  
     
 670  
 }
 671  
 
 672  
 /*
 673  
  * $Log$
 674  
  * Revision 1.3  2006/02/18 22:46:44  cgruber
 675  
  * Add Surrogate map from .util into control's internal package, and fix imports.
 676  
  *
 677  
  * Revision 1.2  2006/02/16 16:47:14  cgruber
 678  
  * Move some classes in to "internal" packages and re-work imports, etc.
 679  
  *
 680  
  * Also use UnsupportedOperationExceptions where appropriate, instead of WotonomyExceptions.
 681  
  *
 682  
  * Revision 1.1  2006/02/16 13:19:57  cgruber
 683  
  * Check in all sources in eclipse-friendly maven-enabled packages.
 684  
  *
 685  
  * Revision 1.15  2003/01/21 22:30:10  mpowers
 686  
  * thaw() now allows you to pass in a class loader.
 687  
  *
 688  
  * Revision 1.14  2002/05/15 13:46:35  mpowers
 689  
  * Exposed freeze and thaw as public.
 690  
  *
 691  
  * Revision 1.13  2001/08/22 19:25:13  mpowers
 692  
  * Added (and commented out) profiling code for freeze.
 693  
  *
 694  
  * Revision 1.12  2001/05/06 18:27:10  mpowers
 695  
  * More broadly catching editing contexts for now.
 696  
  *
 697  
  * Revision 1.11  2001/05/05 13:18:49  mpowers
 698  
  * Fixed: transposing output stream was not returning the object to replace.
 699  
  *
 700  
  * Revision 1.10  2001/05/04 16:57:56  mpowers
 701  
  * Now correctly transposing references to editing contexts when
 702  
  * cloning/copying between editing contexts.
 703  
  *
 704  
  * Revision 1.9  2001/05/04 14:42:58  mpowers
 705  
  * Now getting stored values in KeyValueCoding.
 706  
  * MasterDetail now marks dirty based on whether it's an attribute
 707  
  * or relation.
 708  
  * Implemented editing context marker.
 709  
  *
 710  
  * Revision 1.8  2001/05/02 15:47:40  mpowers
 711  
  * Fixed the pernicious problem with reverts: recordObject was recording
 712  
  * a snapshot of the clone before the transposition-copy happened,
 713  
  * so the revert object would lose all of its transposed relationships.
 714  
  *
 715  
  * Revision 1.7  2001/04/30 12:33:17  mpowers
 716  
  * Fixed problem with use of EONullValue.nullValue(), which can't be used
 717  
  * when we're serializably duplicating objects.
 718  
  *
 719  
  * Revision 1.6  2001/04/30 02:14:25  mpowers
 720  
  * Copying should call takeStoredValueForKeys.
 721  
  *
 722  
  * Revision 1.5  2001/04/29 22:02:45  mpowers
 723  
  * Work on id transposing between editing contexts.
 724  
  *
 725  
  * Revision 1.4  2001/04/29 02:29:31  mpowers
 726  
  * Debugging relationship faulting.
 727  
  *
 728  
  * Revision 1.3  2001/04/28 16:18:44  mpowers
 729  
  * Implementing relationships.
 730  
  *
 731  
  * Revision 1.2  2001/04/28 14:12:23  mpowers
 732  
  * Refactored cloning/copying into KeyValueCodingUtilities.
 733  
  *
 734  
  * Revision 1.1  2001/04/27 23:41:12  mpowers
 735  
  * Contributing file for KeyValueCodingUtilities.
 736  
  *
 737  
  *
 738  
  */
 739  
     
 740