A
download Duplicator.java
Language: Java
License: LGPL
Copyright: (C) 2001 Michael Powers
LOC: 149
Project Info
wotonomy
Server: SourceForge
Type: cvs
...wotonomy\net\wotonomy\util\
   BrowserLauncher.java
   Duplicator.java
   Introspector.java
   IntrospectorException.java
   ...gPropertyException.java
   NetworkClassLoader.java
   ...PrimitiveException.java
   package.html
   PropertyComparator.java
   PropertyListParser.java
   QueueMap.java
   Surrogate.java
   URLResourceReader.java
   ValueConverter.java
   WotonomyException.java

/*
Wotonomy: OpenStep design patterns for pure Java applications.
Copyright (C) 2001 Michael Powers

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, see http://www.gnu.org
*/

package net.wotonomy.util;

import net.wotonomy.foundation.*;
import java.io.*;
import java.util.*; //collections

/**
* Duplicator makes use of Introspector to duplicate objects,
* either by shallow copy, deep copy, or by copying properties
* from one object to apply to another object.  You may find this
* class useful because java.lang.Object.clone() only supports
* shallow copying.
*
* @author michael@mpowers.net
* @author $Author: mpowers $
* @version $Revision: 1.11 $
*/

public class Duplicator
{
    /**
    * Used to represent null values for properties in the
    * maps returned by readProperties and cloneProperties
    * and in the parameter to writeProperties.
    * This actually references the NSNull instance.
    */
    public static final Object NULL = NSNull.nullValue();
    private static NSSelector clone = new NSSelector( "clone" );

    /**
    * Returns a list of properties for the specified class
    * that are both readable and writable.
    */
    static public List editablePropertiesForObject( 
        Object anObject )
    {
        List readProperties = new ArrayList();
        String[] read = Introspector.getReadPropertiesForObject( anObject );
        for ( int i = 0; i < read.length; i++ )
        {
            readProperties.add( read[i] );
        }

        List properties = new ArrayList();
        String[] write = Introspector.getWritePropertiesForObject( anObject );
        for ( int i = 0; i < write.length; i++ )
        {
            properties.add( write[i] );
        }
        
        // only use properties on both lists: read/write
        properties.retainAll( readProperties );

        return properties;
    }
    
    /**
    * Returns a Map containing only the mutable properties 
    * for the specified object and their values.
    * Any null values for properties will be represented with
    * the NULL object.
    */
    static public Map readPropertiesForObject( 
        Object anObject )
    {
        NSMutableDictionary result = new NSMutableDictionary();
        
        String key;
        Object value;
        Iterator it = editablePropertiesForObject( anObject ).iterator();
        while ( it.hasNext() )
        {
            key = it.next().toString();
            value = Introspector.get( anObject, key );
            if ( value == null ) value = NULL;
            result.setObjectForKey( value, key );
        }
        return result;        
    }
    
    /**
    * Returns a Map containing only the mutable properties 
    * for the specified object and deep clones of their values.
    * Nulls are represented by the NULL object.
    */
    static public Map clonePropertiesForObject( 
        Object anObject )
    {
        Object key, value;
        Map result = readPropertiesForObject( anObject );
        Iterator it = result.keySet().iterator();
        while ( it.hasNext() )
        {
            key = it.next(); 
            value = result.get( key );            
            value = deepClone( value );
            result.put( key, value );
        }
        return result;
    }
    
    /**
    * Applies the map of properties and values to the
    * specified object.  Null values for properties must
    * be represented by the NULL object.
    */
    static public void writePropertiesForObject( 
        Map aMap, Object anObject )
    {
        String key;
        Object value;
        Iterator it = aMap.keySet().iterator();
        while ( it.hasNext() )
        {
            key = it.next().toString();
            value = aMap.get( key );
            if ( NULL.equals( value ) ) value = null;
            Introspector.set( anObject, key, value );
        }
    }    
    
    /**
    * Creates a new copy of the specified object.
    * This implementation tries to call clone(),
    * and failing that, calls newInstance
    * and then calls copy() to transfer the values.
    * @throws WotonomyException if any operation fails.
    */ 
    static public Object clone( 
        Object aSource )
    {
        Object result = null;
        if ( clone.implementedByObject( aSource ) )
        {
            try 
            {
                result = clone.invoke( aSource );
                return result;
            }
            catch ( Exception exc )
            {
                // fall back on newInstance()
            }
        }
        
        Class c = aSource.getClass();
        try 
        {
            result = c.newInstance();
        }
        catch ( Exception exc )
        {
            throw new WotonomyException( exc );
        }
        return copy( aSource, result );        
    }

    /**
    * Creates a deep copy of the specified object.
    * Every object in this objects graph will be
    * duplicated with new instances.
    * @throws WotonomyException if any operation fails.
    */ 
    static public Object deepClone(
        Object aSource )
    {
        // the only known way to deep copy in
        // java without native code is serialization
        
        try
        {
            ByteArrayOutputStream byteOutput =
                new ByteArrayOutputStream();
            ObjectOutputStream objectOutput =
                new ObjectOutputStream( byteOutput );
                
            objectOutput.writeObject( aSource );
            objectOutput.flush();
            objectOutput.close();
            
            ByteArrayInputStream byteInput =
                new ByteArrayInputStream( byteOutput.toByteArray() );
            ObjectInputStream objectInput =
                new ObjectInputStream( byteInput );
            return objectInput.readObject();
        } 
        catch ( Exception exc )
        {
            throw new WotonomyException( "Error cloning object: " + aSource, exc );
        }
    }

    /**
    * Copies values from one object to another.
    * Returns the destination object.
    * @throws WotonomyException if any operation fails.
    */ 
    static public Object copy( 
        Object aSource, Object aDestination )
    {
        try 
        {
            writePropertiesForObject(
                readPropertiesForObject( aSource ), aDestination );
        }
        catch ( RuntimeException exc )
        {
            throw new WotonomyException( exc );
        }
        return aDestination;        
    }
    
    /**
    * Deeply clones the values from one object and applies them
    * to another object.
    * Returns the destination object.
    * @throws WotonomyException if any operation fails.
    */ 
    static public Object deepCopy( 
        Object aSource, Object aDestination )
    {
        try 
        {
            writePropertiesForObject(
                clonePropertiesForObject( aSource ), aDestination );
        }
        catch ( RuntimeException exc )
        {
            throw new WotonomyException( exc );
        }
        return aDestination;        
    }
}

/*
 * $Log: Duplicator.java,v $
 * Revision 1.11  2001/08/22 19:24:26  mpowers
 * Providing a more helpful error message for cloning exceptions.
 *
 * Revision 1.10  2001/03/29 03:30:36  mpowers
 * Refactored duplicator a bit.
 * Disabled MissingPropertyExceptions for now.
 *
 * Revision 1.9  2001/03/28 14:11:23  mpowers
 * Removed debugging printlns.
 *
 * Revision 1.8  2001/03/27 23:25:48  mpowers
 * Basically reverting to the previous version.
 *
 * Revision 1.7  2001/03/06 23:18:13  mpowers
 * Clarified some comments.
 *
 * Revision 1.6  2001/03/01 20:36:35  mpowers
 * Better error handling and better handling of nulls.
 *
 * Revision 1.5  2001/02/27 21:43:40  mpowers
 * Removed NullMarker class in favor of NSNull.
 *
 * Revision 1.4  2001/02/26 22:41:51  mpowers
 * Implemented null placeholder classes.
 * Duplicator now uses NSNull.
 * No longer catching base exception class.
 *
 * Revision 1.3  2001/02/23 21:07:46  mpowers
 * Documented the NULL object.
 *
 * Revision 1.1  2001/02/16 22:51:29  mpowers
 * Now deep-cloning objects passed between editing contexts.
 *
 *
 */

About Koders | Resources | Downloads | Support | Black Duck | Terms of Service | DMCA | Privacy Policy | Contact Us