Coverage Report - net.wotonomy.foundation.internal.Duplicator
 
Classes in this File Line Coverage Branch Coverage Complexity
Duplicator
0% 
0% 
3.375
 
 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.foundation.internal;
 20  
 
 21  
 import net.wotonomy.foundation.*;
 22  
 import net.wotonomy.foundation.internal.Introspector;
 23  
 import net.wotonomy.foundation.internal.WotonomyException;
 24  
 
 25  
 import java.io.*;
 26  
 import java.util.*; //collections
 27  
 
 28  
 /**
 29  
 * Duplicator makes use of Introspector to duplicate objects,
 30  
 * either by shallow copy, deep copy, or by copying properties
 31  
 * from one object to apply to another object.  You may find this
 32  
 * class useful because java.lang.Object.clone() only supports
 33  
 * shallow copying.
 34  
 *
 35  
 * @author michael@mpowers.net
 36  
 * @author $Author: cgruber $
 37  
 * @version $Revision: 895 $
 38  
 */
 39  
 
 40  0
 public class Duplicator
 41  
 {
 42  
     /**
 43  
     * Used to represent null values for properties in the
 44  
     * maps returned by readProperties and cloneProperties
 45  
     * and in the parameter to writeProperties.
 46  
     * This actually references the NSNull instance.
 47  
     */
 48  0
     public static final Object NULL = NSNull.nullValue();
 49  0
     private static NSSelector clone = new NSSelector( "clone" );
 50  
 
 51  
     /**
 52  
     * Returns a list of properties for the specified class
 53  
     * that are both readable and writable.
 54  
     */
 55  
     static public List editablePropertiesForObject( 
 56  
         Object anObject )
 57  
     {
 58  0
         List readProperties = new ArrayList();
 59  0
         String[] read = Introspector.getReadPropertiesForObject( anObject );
 60  0
         for ( int i = 0; i < read.length; i++ )
 61  
         {
 62  0
             readProperties.add( read[i] );
 63  
         }
 64  
 
 65  0
         List properties = new ArrayList();
 66  0
         String[] write = Introspector.getWritePropertiesForObject( anObject );
 67  0
         for ( int i = 0; i < write.length; i++ )
 68  
         {
 69  0
             properties.add( write[i] );
 70  
         }
 71  
         
 72  
         // only use properties on both lists: read/write
 73  0
         properties.retainAll( readProperties );
 74  
 
 75  0
         return properties;
 76  
     }
 77  
     
 78  
     /**
 79  
     * Returns a Map containing only the mutable properties 
 80  
     * for the specified object and their values.
 81  
     * Any null values for properties will be represented with
 82  
     * the NULL object.
 83  
     */
 84  
     static public Map readPropertiesForObject( 
 85  
         Object anObject )
 86  
     {
 87  0
         NSMutableDictionary result = new NSMutableDictionary();
 88  
         
 89  
         String key;
 90  
         Object value;
 91  0
         Iterator it = editablePropertiesForObject( anObject ).iterator();
 92  0
         while ( it.hasNext() )
 93  
         {
 94  0
             key = it.next().toString();
 95  0
             value = Introspector.get( anObject, key );
 96  0
             if ( value == null ) value = NULL;
 97  0
             result.setObjectForKey( value, key );
 98  0
         }
 99  0
         return result;        
 100  
     }
 101  
     
 102  
     /**
 103  
     * Returns a Map containing only the mutable properties 
 104  
     * for the specified object and deep clones of their values.
 105  
     * Nulls are represented by the NULL object.
 106  
     */
 107  
     static public Map clonePropertiesForObject( 
 108  
         Object anObject )
 109  
     {
 110  
         Object key, value;
 111  0
         Map result = readPropertiesForObject( anObject );
 112  0
         Iterator it = result.keySet().iterator();
 113  0
         while ( it.hasNext() )
 114  
         {
 115  0
             key = it.next(); 
 116  0
             value = result.get( key );            
 117  0
             value = deepClone( value );
 118  0
             result.put( key, value );
 119  0
         }
 120  0
         return result;
 121  
     }
 122  
     
 123  
     /**
 124  
     * Applies the map of properties and values to the
 125  
     * specified object.  Null values for properties must
 126  
     * be represented by the NULL object.
 127  
     */
 128  
     static public void writePropertiesForObject( 
 129  
         Map aMap, Object anObject )
 130  
     {
 131  
         String key;
 132  
         Object value;
 133  0
         Iterator it = aMap.keySet().iterator();
 134  0
         while ( it.hasNext() )
 135  
         {
 136  0
             key = it.next().toString();
 137  0
             value = aMap.get( key );
 138  0
             if ( NULL.equals( value ) ) value = null;
 139  0
             Introspector.set( anObject, key, value );
 140  0
         }
 141  0
     }    
 142  
     
 143  
     /**
 144  
     * Creates a new copy of the specified object.
 145  
     * This implementation tries to call clone(),
 146  
     * and failing that, calls newInstance
 147  
     * and then calls copy() to transfer the values.
 148  
     * @throws WotonomyException if any operation fails.
 149  
     */ 
 150  
     static public Object clone( 
 151  
         Object aSource )
 152  
     {
 153  0
         Object result = null;
 154  0
         if ( clone.implementedByObject( aSource ) )
 155  
         {
 156  
             try 
 157  
             {
 158  0
                 result = clone.invoke( aSource );
 159  0
                 return result;
 160  
             }
 161  0
             catch ( Exception exc )
 162  
             {
 163  
                 // fall back on newInstance()
 164  
             }
 165  
         }
 166  
         
 167  0
         Class c = aSource.getClass();
 168  
         try 
 169  
         {
 170  0
             result = c.newInstance();
 171  
         }
 172  0
         catch ( Exception exc )
 173  
         {
 174  0
             throw new WotonomyException( exc );
 175  0
         }
 176  0
         return copy( aSource, result );        
 177  
     }
 178  
 
 179  
     /**
 180  
     * Creates a deep copy of the specified object.
 181  
     * Every object in this objects graph will be
 182  
     * duplicated with new instances.
 183  
     * @throws WotonomyException if any operation fails.
 184  
     */ 
 185  
     static public Object deepClone(
 186  
         Object aSource )
 187  
     {
 188  
         // the only known way to deep copy in
 189  
         // java without native code is serialization
 190  
         
 191  
         try
 192  
         {
 193  0
             ByteArrayOutputStream byteOutput =
 194  0
                 new ByteArrayOutputStream();
 195  0
             ObjectOutputStream objectOutput =
 196  0
                 new ObjectOutputStream( byteOutput );
 197  
                 
 198  0
             objectOutput.writeObject( aSource );
 199  0
             objectOutput.flush();
 200  0
             objectOutput.close();
 201  
             
 202  0
             ByteArrayInputStream byteInput =
 203  0
                 new ByteArrayInputStream( byteOutput.toByteArray() );
 204  0
             ObjectInputStream objectInput =
 205  0
                 new ObjectInputStream( byteInput );
 206  0
             return objectInput.readObject();
 207  
         } 
 208  0
         catch ( Exception exc )
 209  
         {
 210  0
             throw new WotonomyException( "Error cloning object: " + aSource, exc );
 211  
         }
 212  
     }
 213  
 
 214  
     /**
 215  
     * Copies values from one object to another.
 216  
     * Returns the destination object.
 217  
     * @throws WotonomyException if any operation fails.
 218  
     */ 
 219  
     static public Object copy( 
 220  
         Object aSource, Object aDestination )
 221  
     {
 222  
         try 
 223  
         {
 224  0
             writePropertiesForObject(
 225  0
                 readPropertiesForObject( aSource ), aDestination );
 226  
         }
 227  0
         catch ( RuntimeException exc )
 228  
         {
 229  0
             throw new WotonomyException( exc );
 230  0
         }
 231  0
         return aDestination;        
 232  
     }
 233  
     
 234  
     /**
 235  
     * Deeply clones the values from one object and applies them
 236  
     * to another object.
 237  
     * Returns the destination object.
 238  
     * @throws WotonomyException if any operation fails.
 239  
     */ 
 240  
     static public Object deepCopy( 
 241  
         Object aSource, Object aDestination )
 242  
     {
 243  
         try 
 244  
         {
 245  0
             writePropertiesForObject(
 246  0
                 clonePropertiesForObject( aSource ), aDestination );
 247  
         }
 248  0
         catch ( RuntimeException exc )
 249  
         {
 250  0
             throw new WotonomyException( exc );
 251  0
         }
 252  0
         return aDestination;        
 253  
     }
 254  
 }
 255  
 
 256  
 /*
 257  
  * $Log$
 258  
  * Revision 1.1  2006/02/16 16:52:12  cgruber
 259  
  * Add cvsignore crap to find off checking in binary crap.
 260  
  *
 261  
  * Revision 1.1  2006/02/16 13:22:22  cgruber
 262  
  * Check in all sources in eclipse-friendly maven-enabled packages.
 263  
  *
 264  
  * Revision 1.11  2001/08/22 19:24:26  mpowers
 265  
  * Providing a more helpful error message for cloning exceptions.
 266  
  *
 267  
  * Revision 1.10  2001/03/29 03:30:36  mpowers
 268  
  * Refactored duplicator a bit.
 269  
  * Disabled MissingPropertyExceptions for now.
 270  
  *
 271  
  * Revision 1.9  2001/03/28 14:11:23  mpowers
 272  
  * Removed debugging printlns.
 273  
  *
 274  
  * Revision 1.8  2001/03/27 23:25:48  mpowers
 275  
  * Basically reverting to the previous version.
 276  
  *
 277  
  * Revision 1.7  2001/03/06 23:18:13  mpowers
 278  
  * Clarified some comments.
 279  
  *
 280  
  * Revision 1.6  2001/03/01 20:36:35  mpowers
 281  
  * Better error handling and better handling of nulls.
 282  
  *
 283  
  * Revision 1.5  2001/02/27 21:43:40  mpowers
 284  
  * Removed NullMarker class in favor of NSNull.
 285  
  *
 286  
  * Revision 1.4  2001/02/26 22:41:51  mpowers
 287  
  * Implemented null placeholder classes.
 288  
  * Duplicator now uses NSNull.
 289  
  * No longer catching base exception class.
 290  
  *
 291  
  * Revision 1.3  2001/02/23 21:07:46  mpowers
 292  
  * Documented the NULL object.
 293  
  *
 294  
  * Revision 1.1  2001/02/16 22:51:29  mpowers
 295  
  * Now deep-cloning objects passed between editing contexts.
 296  
  *
 297  
  *
 298  
  */
 299