key0-96/
key0-96/doc/key/
key0-96/doc/key/credits/
key0-96/doc/key/developers/
key0-96/doc/key/developers/resources/
key0-96/setup/caves/
key0-96/setup/help/
key0-96/setup/ruins/
key0-96/src/
key0-96/src/commands/
key0-96/src/events/
key0-96/src/hack/
key0-96/src/sql/
key0-96/src/swing/
key0-96/src/talker/forest/
key0-96/src/talker/objects/
key0-96/src/terminals/
/*
**               j###t  ########## ####   ####
**              j###t   ########## ####   ####
**             j###T               "###L J###"
**          ######P'    ##########  #########
**          ######k,    ##########   T######T
**          ####~###L   ####
**          #### q###L  ##########   .#####
**          ####  \###L ##########   #####"
**
**  $Id$
**
**  Class History
**
**  Date        Name         Description
**  ---------|------------|-----------------------------------------------
**  07Sep98     subtle       created
**
*/

package key;

import key.io.KeyOutputStream;
import key.io.KeyInputStream;

import java.net.*;
import java.io.*;
import java.util.*;
import java.lang.*;
import java.lang.reflect.*;

public final class Factory
{
	public static void storeObject( Object object, File to )
	{
		storeObject( object, to, false );
	}
	
	public static Object loadObject( File from )
	{
		ObjectInputStream ds = null;
		Object result = null;
		InputStream fis = null;
		
		try
		{
			fis = new FileInputStream( from );
			
			if( Main.COMPRESS_DISK_FILES )
				fis = new java.util.zip.GZIPInputStream( fis );
			
			ds = new KeyInputStream( fis );
		}
		catch( IOException e )
		{
			Log.error( "while loading file '" + from.getPath() + "'", e );
			return( null );
		}
		
		try
		{
			result = ds.readObject();
		}
		catch( java.io.InvalidClassException e )
		{
			if( Key.isRunning() )
				Log.error( "while loading file '" + from.getPath() + "'", e );
			else
			{
				e.printStackTrace();
				Log.fatal( "Key", e.toString() + " while loading from file '" + from.getPath() + "'" );
			}
		}
		catch( ClassNotFoundException e )
		{
			Log.error( "while loading from file '" + from.getName() + "'", e );
			return( null );
		}
		catch( IOException e )
		{
			Log.error( "while loading from file '" + from.getName() + "'", e );
			return( null );
		}
		finally
		{
			try
			{
				ds.close();
				fis.close();
			}
			catch( IOException except )
			{
			}
		}
		
			//  this is done by the Factory on construction, but
			//  here on load, obviously
		if( result instanceof Atom )
			Factory.incrementImplicitReferenceCounts( (Atom) result );
		
		return( result );
	}
	
	public static void storeObject( Object object, File to, boolean distinct )
	{
		File temp = null;
		
		try
		{
			temp = new File( to.getPath() + tempFileExtension );
			
			{
				//  streamed code (is RandomAccessFile faster?)
				OutputStream fos = new FileOutputStream( temp );
				
				if( Main.COMPRESS_DISK_FILES )
					fos = new java.util.zip.GZIPOutputStream( fos );
				
				KeyOutputStream oos = new KeyOutputStream( fos );
				
				if( distinct )
					oos.doSwapping();
				
				oos.writeObject( object );
				oos.close();
				fos.close();
			}
			
			to.delete();
			temp.renameTo( to );
		}
		catch( IOException e )
		{
			if( temp != null )
				temp.delete();
			
			throw new UnexpectedResult( e.toString() + " while trying to saveObject to file '" + to.getName() + "'" );
		}
	}
	
	public static Atom makeAtom( Class cl, Object key )
	{
		Atom a;
		a = createAtom( cl );
		a.setKey( key );
		
		a.constructed();
		
		return( a );
	}
	
	public static Atom makeAtom( Class cl )
	{
		Atom a;
		a = createAtom( cl );
		a.constructed();
		return( a );
	}
	
	private static Atom createAtom( Class cl )
	{
		Atom a;
		
		try
		{
			a = (Atom) cl.newInstance();
		}
		catch( Exception e )
		{
			e.printStackTrace( System.out );
			throw new UnexpectedResult( e );
		}
		
		postCreateProcess( a );
		
		return( a );
	}
	
	public static void postCreateProcess( Atom a )
	{
		processFields( a.getClass(), a, CONSTRUCTER );
	}
	
	public static void decrementImplicitReferenceCounts( Atom a )
	{
		processFields( a.getClass(), a, DECREMENTER );
	}
	
	public static void incrementImplicitReferenceCounts( Atom a )
	{
		processFields( a.getClass(), a, INCREMENTER );
	}
	
	public static void distinctSyncFields( Atom a )
	{
		processFields( a.getClass(), a, DISTINCT_SYNCER );
	}
	
	public static void nonTemporaryFields( Atom a )
	{
		processFields( a.getClass(), a, NON_TEMPORARY );
	}
	
	public static void partDeleteFields( Atom a )
	{
		processFields( a.getClass(), a, PART_DELETER );
	}
	
	/**
	  *  Makes sure that the owner of this atom also
	  *  owns all of this atom's final atomic fields.
	 */
	public static void syncOwnerFields( Atom a )
	{
		processFields( a.getClass(), a, SYNC_OWNER );
	}
	
	public static void syncOwnerRecursiveFields( Atom a )
	{
		processFields( a.getClass(), a, SYNC_OWNER_R );
	}
	
	private final static void processFields( Class start, Atom parent, FieldScanner fs )
		throws SecurityException
	{
		{
			Field[] fields;
			
			fields = start.getDeclaredFields();
			
			for( int i = 0; i < fields.length; i++ )
			{
				Field f = fields[i];
				
				if( Atom.class.isAssignableFrom( f.getType() ) )
				{
						//  this is an atom
					try
					{
						int mod = f.getModifiers();
						
						if( !Modifier.isTransient( mod ) && !Modifier.isStatic( mod ) )
						{
								//  The field must be final, then, since if
								//  re-assignments are made we would have to update
								//  reference counts here, there, and everywhere, and
								//  that can't be guaranteed.
							if( !Modifier.isFinal( mod ) )
							{
									//  replace with exception
								System.err.println( "ERROR: Atomic Field " + f.toString() + " is not final or transient." );
							}
							
							Atom sub = (Atom) f.get( parent );
							
							if( sub == null )
							{
									//  TODO: replace with exception
								Log.error( "WARNING: Atomic Field " + f.toString() + " has a value of null, which isn't useful" );
								continue;
							}
							
							fs.processField( f, parent, sub );
						}
					}
					catch( Exception e )
					{
						Log.error( "Factory", e );
						throw new UnexpectedResult( e );  //  this line temporary
					}
				}
			}
		}
		
		{
			Class root;
			
			root = start.getSuperclass();
			
			if( root != Atom.class && root != null )
			{
				processFields( root, parent, fs );
			}
		}
	}

	public static final OnConstruct CONSTRUCTER = new OnConstruct();
	public static final DecrementImplicitRefs DECREMENTER = new DecrementImplicitRefs();
	public static final IncrementImplicitRefs INCREMENTER = new IncrementImplicitRefs();
	public static final DistinctSyncer DISTINCT_SYNCER = new DistinctSyncer();
	public static final NonTemporary NON_TEMPORARY = new NonTemporary();
	public static final PartDeleter PART_DELETER = new PartDeleter();
	public static final SyncOwner SYNC_OWNER = new SyncOwner();
	public static final SyncOwnerRecursive SYNC_OWNER_R = new SyncOwnerRecursive();
	
	public static final String tempFileExtension = ".tmp";
}

interface FieldScanner
{
	public void processField( Field f, Atom parent, Atom sub );
}

class OnConstruct implements FieldScanner
{
	public void processField( Field f, Atom parent, Atom sub )
	{
		sub.owner = parent.owner;
		sub.setKey( f.getName() );
		sub.setParent( parent, AtomicElement.PARENT_TYPE );
		sub.addReference( parent );
	}
}

class DecrementImplicitRefs implements FieldScanner
{
	public void processField( Field f, Atom parent, Atom sub )
	{
		sub.removeReference( parent );
	}
}

/**
  *  the opposite of decrementImplicitRefs, but not to be used for
  *  creation, only for loading.  Typically called from Key.loadObject
 */
class IncrementImplicitRefs implements FieldScanner
{
	public void processField( Field f, Atom parent, Atom sub )
	{
		sub.addReference( parent );
	}
}

class DistinctSyncer implements FieldScanner
{
	public void processField( Field f, Atom parent, Atom sub )
	{
		Registry.instance.syncDistinct( sub.index, sub.timestamp );
	}
}

class NonTemporary implements FieldScanner
{
	public void processField( Field f, Atom parent, Atom sub )
	{
		sub.stopBeingTemporary();
	}
}

class PartDeleter implements FieldScanner
{
	public void processField( Field f, Atom parent, Atom sub )
	{
		Registry.instance.delete( sub );
	}
}

class SyncOwner implements FieldScanner
{
	public void processField( Field f, Atom parent, Atom sub )
	{
		sub.setOwner( parent.getOwner() );
	}
}

class SyncOwnerRecursive implements FieldScanner
{
	public void processField( Field f, Atom parent, Atom sub )
	{
		sub.setRecursiveOwner( parent.getOwner() );
	}
}