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
**  ---------|------------|-----------------------------------------------
**  19Aug98     subtle       start of recorded history
**
*/

package key;

import key.primitive.*;
import java.util.Hashtable;
import java.io.*;

/**
  *  A wrapper for a class, since we can't extend it.
  *
  *  If class is made un-final, change KeyInputStream to use instanceof
  *  instead of o.getClass() == Type.class
 */
public final class Type
implements Symbol, Serializable, key.io.Replaceable
{
	private static final long serialVersionUID = -3520340202727157134L;
	private static Hashtable registeredTypeNames = new Hashtable();
	private static Hashtable registeredTypeClasses = new Hashtable();
	
	Class type;
	String name;
	String className;
	
	public Type( Class c, String n )
	{
		type = c;
		name = n;
		className = null;
	}
	
	protected Type( String cn, String n )
	{
		className = cn;
		name = n;
		type = null;
	}
	
	private void writeObject( ObjectOutputStream oos ) throws IOException
	{
		if( className != null )
			oos.writeObject( className );
		else if( type != null )
			oos.writeObject( type.getName() );
		else
			oos.writeObject( null );
	}
	
	private void readObject( ObjectInputStream ois ) throws IOException
	{
		try
		{
			className = (String) ois.readObject();
		}
		catch( ClassNotFoundException e )
		{
			throw new UnexpectedResult( e );
		}
	}
	
	private final void resolve()
	{
		if( type == null && className != null )
		{
			try
			{
				type = Class.forName( className );

				if( type == null )
					Log.error( "key.Type: Class.forName( " + className + " ) returned null" );
				else
				{
					className = null;
					registeredTypeClasses.put( type, this );
				}
			}
			catch( java.lang.ClassNotFoundException e )
			{
				Log.error( "key.Type", e );
				throw new UnexpectedResult( e.toString() );
			}
		}
	}
	
	public String getName()
	{
		return( name );
	}
	
	public Object getKey()
	{
		return( name );
	}
	
	public final void setKey( Object key )
	{
		name = (String) key;
	}

	public final Object newInstance() throws InstantiationException,
	                                         IllegalAccessException
	{
		resolve();
		return( type.newInstance() );
	}

	public final boolean isInterface()
	{
		resolve();
		return( type.isInterface() );
	}

	public final Object clone() throws CloneNotSupportedException
	{
		throw new CloneNotSupportedException();
	}

	/**
	  * returns true if Class 'is' is the same as class 't', or is
	  * a specialised version of it (or implements interface t)
	 */
	public boolean isA( Type t )
	{
		resolve();
		t.resolve();
		
		if( t == null )
			return( false );
		
		return( isA( type, t.type ) );
	}

	/**
	  * returns true if Class 'is' is the same as class 't', or is
	  * a specialised version of it (or implements interface t)
	 */
	public static boolean isA( Class is, Class t )
	{
		return( t.isAssignableFrom( is ) );
		/* --  JDK 1.0.2 recursive implementation
		if( t == null || is==null )
			return( false );
		
		if( is == t )
			return( true );
		else
		{
			if( t.isInterface() )
			{
				Class[] interfaces = is.getInterfaces();

				for( int i = 0; i < interfaces.length; i++ )
				{
					if( interfaces[i] == t )
						return( true );
				}
			}
			return( isA( is.getSuperclass(), t ) );
		}
		*/
	}
	
	/**
	  *  One day, this will be able to accept things like:
	  *  'subtle:myRoom', because I've uploaded the appropriate
	  *  class name.  As it is, this only works for *registered*
	  *  types.  One day we'll support loading them from a
	  *  players spot.
	 */
	public static Type forName( String s ) throws ClassNotFoundException
	{
		Type t = (Type) registeredTypeNames.get( s );
		if( t != null )
			return( t );
		else
			throw new ClassNotFoundException( s + " not found" );
	}
	
	public static Type typeOf( Object o )
	{
		return( typeFor( o.getClass() ) );
	}

	public static Type typeFor( Class c )
	{
		Type t = (Type) registeredTypeClasses.get( c );
		
		if( t == null )
		{
			String cn = c.getName();
			
			if( c.isArray() )
			{
					//  it's an array:  we could
					//  be more clever about how we
					//  determined the local name, but
					//  blah.
				return( new Type( cn, cn ) );
			}
			
			t = (Type) registeredTypeNames.get( cn );
			
			if( t == null )
			{
				t = newType( c, cn );
				//Log.debug( "Type", "found new type '" + cn + "'" );
			}
			else
			{
					//  manually resolve this type
				t.type = c;
				t.className = null;
			}
		}
		
		return( t );
	}
	
	/**
	  *  Never use newType when you mean forName().  It's possible
	  *  we'll prevent newType being executed by non-priviledged
	  *  people
	 */
	public static Type newType( String className, String localName )
	{
		boolean regnow = false;
		
		Type t;
		
		t = (Type) registeredTypeNames.get( className );
		
		if( t != null )
		{
			t.name = localName;
			registeredTypeNames.put( localName, t );
		}
		else
		{
			t = new Type( className, localName );
			
			registeredTypeNames.put( localName, t );
			registeredTypeNames.put( className, t );
		}
		
		return( t );
	}
	
	/**
	  *  Never use newType when you mean forName().  It's possible
	  *  we'll prevent newType being executed by non-priviledged
	  *  people
	 */
	public static Type newType( Class c, String localName )
	{
		Type t = new Type( c, localName );
		
		registeredTypeNames.put( localName, t );
		registeredTypeNames.put( c.getName(), t );
		registeredTypeClasses.put( c, t );
		
		return( t );
	}
	
	public final Object getReplacement()
	{
		try
		{
			if( className != null )
				return( Type.forName( className ) );
		}
		catch( Exception e )
		{
			Log.error( "while resolving a null Type", e );
		}
		
		return( null );
	}
	
		//  a list of all the types that are needed by the program code
		//  keep in mind that if you use this in a static class, they
		//  may not have been initialised yet.  I wrote some really nice
		//  code in the C++ version that forced initialisation in a certain
		//  order, but it appears I'm not good enough at Java yet. - subtle
	
	public static final Type STRING = newType( "java.lang.String", "string" );
	public static final Type INTEGER = newType( "java.lang.Integer", "integer" );
	public static final Type INT = newType( Integer.TYPE, "int" );
	public static final Type REFERENCE = newType( "key.Reference", "reference" );
	public static final Type BOOLEAN = newType( "java.lang.Boolean", "boolean" );
	public static final Type ATOM = newType( "key.Atom", "atom" );
	public static final Type GENDER = newType( "key.primitive.Gender", "gender" );
	public static final Type STORABLE = newType( "key.Storable", "storable" );
	public static final Type COMMAND = newType( "key.Command", "command" );
	public static final Type COMMANDLIST = newType( "key.CommandList", "commandList" );
	public static final Type DATETIME = newType( "key.primitive.DateTime", "dateTime" );
	public static final Type DURATION = newType( "key.primitive.Duration", "duration" );
	public static final Type PASSWORD = newType( "key.primitive.Password", "password" );
	public static final Type TIMESTATISTICS = newType( "key.TimeStatistics", "timeStatistics" );
	public static final Type CONNECTIONSTATISTICS = newType( "key.ConnectionStatistics", "connectionStatistics" );
	public static final Type CLAN = newType( "key.Clan", "clan" );
	public static final Type FRIENDS = newType( "key.Friends", "friends" );
	public static final Type ROOM = newType( "key.Room", "room" );
	public static final Type PUBLICROOM = newType( "key.PublicRoom", "publicRoom" );
	public static final Type EXIT = newType( "key.Exit", "exit" );
	public static final Type MOVEMENT = newType( "key.effect.Movement", "movement" );
	public static final Type BLOCKING = newType( "key.effect.Blocking", "blocking" );
	public static final Type CONNECTION = newType( "key.effect.Connection", "connection" );
	public static final Type SHOUT = newType( "key.effect.Shout", "shouts" );
	//public static final Type OBJECT_DIRECT = newType( "key.effect.ObjectDirect", "objdirect" );
	//public static final Type OBJECT_ROOM = newType( "key.effect.ObjectRoom", "objects" );
	public static final Type CONTAINER = newType( "key.Container", "container" );
	public static final Type INTERACTIVECONNECTION = newType( "key.InteractiveConnection", "interactiveConnection" );
	public static final Type PLAYER = newType( "key.Player", "player" );
	public static final Type PARAGRAPH = newType( "key.Paragraph", "paragraph" );
	public static final Type LANDSCAPE = newType( "key.Landscape", "landscape" );
	public static final Type LETTER = newType( "key.Letter", "letter" );
	public static final Type MESSAGEBOX = newType( "key.MessageBox", "messageBox" );
	public static final Type IMPLICATIONS = newType( "key.Implications", "implications" );
	public static final Type GROUP = newType( "key.Group", "group" );
	public static final Type DAEMON = newType( "key.Daemon", "daemon" );
	public static final Type SCAPE = newType( "key.Scape", "scape" );
	public static final Type RANK = newType( "key.Rank", "rank" );
	public static final Type EMAILADDRESS = newType( "key.EmailAddress", "emailAddress" );
	public static final Type WEBPAGE = newType( "key.Webpage", "webpage" );
	public static final Type INFORMLIST = newType( "key.InformList", "informList" );
	public static final Type SUBNET = newType( "key.Subnet", "subnet" );

	public static final Type SHORTCUTCOLLECTION = newType( "key.collections.ShortcutCollection", "shortcutCollection" );
	public static final Type NUMBEREDCOLLECTION = newType( "key.collections.NumberedCollection", "numberedCollection" );
	public static final Type SITECOLLECTION = newType( "key.collections.SiteCollection", "siteCollection" );
	public static final Type NETWORKCOLLECTION = newType( "key.collections.NetworkCollection", "networkCollection" );
	public static final Type STRINGKEYCOLLECTION = newType( "key.collections.StringKeyCollection", "stringKeyCollection" );
	public static final Type ALIASCOLLECTION = newType( "key.collections.AliasCollection", "aliasCollection" );
	public static final Type SITE = newType( "key.Site", "site" );
	public static final Type TARGETS = newType( "key.Targets", "targets" );
	public static final Type TARGETABLE = newType( "key.Targetable", "targetable" );
	public static final Type SCREEN = newType( "key.Screen", "screen" );
	public static final Type STRINGSET = newType( "key.StringSet", "StringSet" );
	public static final Type CONFIGURATION = newType( "key.config.Configuration", "configuration" );
	public static final Type MEMO = newType( "key.Memo", "memo" );
	public static final Type CLANACCEPT = newType( "key.commands.clan.Accept", "accept" );
	public static final Type CLANADD = newType( "key.commands.clan.Add", "add" );
	public static final Type CLANBASERANK = newType( "key.commands.clan.BaseRank", "baseRank" );
	public static final Type CLANMOTD = newType( "key.commands.clan.ClanMotd", "ClanMOTD" );
	public static final Type CLANDISSOLVE = newType( "key.commands.clan.Dissolve", "dissolve" );
	public static final Type CLANEDITMOTD = newType( "key.commands.clan.EditMotd", "editMOTD" );
	public static final Type CLANIMPLIES = newType( "key.commands.clan.Implies", "implies" );
	public static final Type CLANMAKE = newType( "key.commands.clan.Make", "make" );
	public static final Type CLANNOTIMPLIES = newType( "key.commands.clan.NotImplies", "notImplies" );
	public static final Type CLANREFCLAN = newType( "key.commands.clan.RefClan", "refClan" );
	public static final Type CLANREFCLANLAND = newType( "key.commands.clan.RefClanLand", "refClanLand" );
	public static final Type CLANREFRANK = newType( "key.commands.clan.RefRank", "refRank" );
	public static final Type CLANREVOKE = newType( "key.commands.clan.Revoke", "revoke" );
	public static final Type CLANSETHALL = newType( "key.commands.clan.SetHall", "setHall" );
	public static final Type CLANTAKE = newType( "key.commands.clan.Take", "take" );
	public static final Type THING = newType( "key.Thing", "thing" );
	public static final Type CHANNEL = newType( "key.Channel", "channel" );
	//public static final Type STAFF_CHANNEL = newType( "key.StaffChannel", "staffChannel" );
	//public static final Type ADMIN_CHANNEL = newType( "key.AdminChannel", "adminChannel" );
	
	public static final Type COUNCIL = newType( "key.talker.forest.Council", "council" );
	public static final Type ADVISOR = newType( "key.talker.forest.Advisor", "advisor" );
	public static final Type ACTION = newType( "key.Action", "action" );
}