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
**  20Oct98     subtle       added serialisation
**  25Oct98     subtle       fixed a small bug in count()
**  02Nov98     subtle       moved to key.util package
**
*/

package key.util;

import java.io.*;
import java.util.Enumeration;
import java.util.NoSuchElementException;

/**
  * Implementation of a doubly linked list
  *
  * @version 1.2, 02Nov98
  * @author Paul Mclachlan
  *
 */
public class LinkedList implements java.io.Serializable
{
	private static final long serialVersionUID = -377921300625278844L;
	private Element First;
	private Element Last;
	private int Count;
	
	private static int totalLists;
	
    /**
	  * creates the queue
     */
	public LinkedList()
	{
		init();
	}
	
	private void init()
	{
		totalLists++;
		
		First = null;
		Last = null;
		Count = 0;
	}
	
	private void readObject( ObjectInputStream from )
		throws IOException,StreamCorruptedException,ClassNotFoundException
	{
		init();
		
		int count;
		count = from.readInt();
		
		while( count-- > 0 )
			append( from.readObject() );
	}
	
	private void writeObject( ObjectOutputStream to )
		throws IOException
	{
		to.writeInt( Count );
		
		for( Enumeration e = elements(); e.hasMoreElements(); )
			to.writeObject( e.nextElement() );
	}
	
	/**
	  * Adds another object to the queue
	  *
	  * @param store the object to be added
	 */
	public final synchronized void append( Object store )
	{
		Element latest = new Element( store );
		
		if( Last != null )
		{
			latest.setPrevious( Last );
			Last.setNext( latest );
		}
		else
			First = latest;
		
		Last = latest;
		
		Count++;
	}
	
	public final synchronized void appendCycle( Object store, int max )
	{
		append( store );
		
		while( Count > max && First != null )
		{
				//  remove lines from the front until
				//  we have at most max lines.
			removeLinkedListElement( First );
		}
	}
	
	/**
	  * Adds another object to the queue
	  *
	  * @param store the object to be added
	 */
	public final synchronized void prepend( Object store )
	{
		Element latest = new Element( store );
		
		if( First != null )
		{
			latest.setNext( First );
			First.setPrevious( latest );
		}
		else
			Last = latest;
		
		First = latest;
		
		Count++;
	}
	
	/**
	  * Returns the number of objects in the queue
	  * @return the number of objects in the queue
	 */
	public final int count()
	{
		return( Count );
	}

	public final Object getFirstElement()
	{
		if( First != null )
			return( First.item() );
		else
			return( null );
	}

	public final Object getLastElement()
	{
		if( Last != null )
			return( Last.item() );
		else
			return( null );
	}
	
	public final Enumeration elements()
	{
		return( new Enumerator( First ) );
	}

	public final boolean contains( Object o )
	{
		for( Enumeration e = this.elements(); e.hasMoreElements(); )
			if( e.nextElement() == o )
				return true;
		
		return false;
	}

	public final boolean containsEqual( Object o )
	{
		for( Enumeration e = this.elements(); e.hasMoreElements(); )
			if( e.nextElement().equals( o ) )
				return true;
		
		return false;
	}

	public final void replaceEqual( Object o )
	{
		Element lle = First;
		
		while( lle != null )
		{
			if( lle.stored.equals( o ) )
			{
				lle.stored = o;
				return;
			}
			
			lle = lle.next;
		}
	}
	
	public final Object getElementAt( int c )
	{
		int i = 0;
		for( Enumeration e = this.elements(); e.hasMoreElements(); i++ )
		{
			Object o = e.nextElement();
			if( i == c )
				return o;
		}
		
		return( null );
	}

	private final synchronized void removeLinkedListElement( Element s )
	{
		if( s == First )
		{
			if( s == Last )
			{	//  only this element in the list
				First = null;
				Last = null;
			}
			else
			{	//  the first element in the list
				First = s.getNext();
				First.setPrevious( null );
			}
		}
		else
		{
			if( s == Last )
			{	//  last element in the list
				Last = s.getPrevious();
				Last.setNext( null );
			}
			else
			{	//  middle element in the list
				s.getPrevious().setNext( s.getNext() );
				s.getNext().setPrevious( s.getPrevious() );
			}
		}
		Count--;
		return;
	}
	
	public final synchronized void removeElementAt( int c )
	{
		Element s=First;
		int i = 0;
		while( s != null )
		{
			if( i == c )
			{
				removeLinkedListElement( s );
				return;
			}
			
			s = s.getNext();
			i++;
		}
	}

	/**
	  *  No action if o is not in the list
	 */
	public final synchronized void remove( Object o )
	{
		Element s=First;
		while( s != null )
		{
			if( s.item() == o )
			{
				removeLinkedListElement( s );
				return;
			}
			
			s = s.getNext();
		}
	}
	
	/**
	  *  Call if you wish to use the .equals() method
	  *  to compare objects.
	 */
	public final synchronized void removeEqual( Object o )
	{
		Element s=First;
		while( s != null )
		{
			if( s.item().equals( o ) )
			{
				removeLinkedListElement( s );
				return;
			}
			
			s = s.getNext();
		}
	}
	
	/**
	  * Erase all elements in this list
	 */
	public final synchronized void clear()
	{	//  ahh, the wonders of a garbage collector
		First = null;
		Last = null;
		Count = 0;
	}

	public final Iterator iterator()
	{
		return( new Iterator() );
	}

	/* TEST SUITE
	final void dump()
	{
	    if( First != null )
			System.out.println( "First: " + First.toString() );
		else
			System.out.println( "First: null" );
		if( Last != null )
			System.out.println( "Last: " + Last.toString() );
		else
			System.out.println( "Last: null" );

		System.out.println( "----elements----" );
		Element t;
		t = First;
		while( t != null )
		{
			System.out.println( t.toString() + "---> " + t.item().toString() );
			t = t.getNext();
		}
	}

	public static void main( String args[] )
	{
		LinkedList main = new LinkedList();
		String entry;
		int count=0;

		do
		{
			System.out.println( "\nLinkedList contains " + main.count() + " elements" );
			entry = input( "append,prepend,dump: " );
			if( entry.length() == 0 )
			{
			}
			else if( entry.equals( "dump" ) )
			{
				System.out.println( "\nDUMPING:\n--------" );
				main.dump();
			}
			else if( entry.equals( "quit" ) )
			{
			}
			else if( entry.startsWith( "append " ) )
			{
				entry = entry.substring( 7 );
				System.out.println( "Appending " + entry + "..." );
				main.append( entry );
			}
			else if( entry.startsWith( "prepend " ) )
			{
				entry = entry.substring( 7 );
				System.out.println( "Prepending " + entry + "..." );
				main.prepend( entry );
			}
			else
			{
				System.out.println( "Unknown command '" + entry + "'" );
			}
		} while( !entry.equals( "quit" ) );
	}
	
	public static String input( String prompt )
	{
		StringBuffer buildInput = new StringBuffer();
		char b=' ';

		System.out.print( prompt );
		System.out.flush();

		do
		{
			try
			{
				b = (char) System.in.read();
			}
			catch( IOException e )
			{
					System.out.println( e.toString() );
					System.exit( 1 );
			}
			if( b != '\n' && b != 0 )
				buildInput.append( b );
		} while( b != '\n' && b != 0 );

		return( new String( buildInput ) );
	}
	*/
	
	public static int getTotalLists()
	{
		return( totalLists );
	}

	protected void finalize() throws Throwable
	{
		super.finalize();
		
		totalLists--;
	}
	
	private final static class Enumerator implements Enumeration
	{
		Element c;

		public Enumerator( Element first )
		{
			c = first;
		}

		public boolean hasMoreElements()
		{
			return( c != null );
		}

		public Object nextElement()
		{
			if( c != null )
			{
				Element result = c;
				c = c.getNext();
				return( result.item() );
			}
			else
				throw new NoSuchElementException( "iterating past end of linked list" );
		}
	}
	
	private final static class Element
	{
		Object stored;
		Element next;
		Element prev;
		
		/**
		  * Creates a new queue element to hold
		  * this object
		 */
		public Element( Object store )
		{
			stored = store;
			next = null;
			prev = null;
		}

		/**
		  * Sets the value of 'next' to the given value
		  @param next the next element in the queue
		 */
		public void setNext( Element Next )
		{
			next = Next;
		}

		public Element getNext()
		{
			return( next );
		}

		public void setPrevious( Element Previous )
		{
			prev = Previous;
		}

		public Element getPrevious()
		{
			return( prev );
		}

		public Object item()
		{
			return( stored );
		}

		public String toString()
		{
			if( prev != null )
			{
				if( next  != null)
					return(  String.valueOf( super.hashCode() ) + ": previous=" + prev.hashCode() + "  next=" + next.hashCode() );
				else
					return(  String.valueOf( super.hashCode() ) + ": previous=" + prev.hashCode() + "  next=null" );
			}
			else
			{
				if( next  != null)
					return( String.valueOf( super.hashCode() ) + ": previous=null  next=" + next.hashCode() );
				else
					return( String.valueOf( super.hashCode() ) + ": previous=null  next=null" );
			}
		}
	}
	
	public final class Iterator
	{
		Element c;

		public Iterator()
		{
			c = LinkedList.this.First;
		}

		public Object element()
		{
			return( c.item() );
		}

		public void next()
		{
			c = c.getNext();
		}

		public void previous()
		{
			c = c.getPrevious();
		}

		public boolean isFirst()
		{
			return( LinkedList.this.First == c );
		}

		/**
		  * Use this to iterate through the linked list.  eg:
		  *
		  * for( Iterator l = list.iterator(); l.isValid(); l.next() )
		  *    loop();
		 */
		public boolean isValid()
		{
			return( c != null );
		}

		public boolean isLast()
		{
			return( LinkedList.this.Last == c );
		}

		public void last()
		{
			c = LinkedList.this.Last;
		}

		public void first()
		{
			c = LinkedList.this.First;
		}

		public void insertBefore( Object o )
		{
			Element lle = new Element( o );

			lle.setPrevious( c.getPrevious() );
			lle.setNext( c );

			if( isFirst() )
				LinkedList.this.First = lle;
			
			if( c.getPrevious() != null )
				c.getPrevious().setNext( lle );
			
			c.setPrevious( lle );

			LinkedList.this.Count++;
		}

		public void insertAfter( Object o )
		{
			Element lle = new Element( o );

			lle.setNext( c.getNext() );
			lle.setPrevious( c );

			if( isLast() )
				LinkedList.this.Last = lle;

			if( c.getNext() != null )
				c.getNext().setPrevious( lle );
			
			c.setNext( lle );
			
			LinkedList.this.Count++;
		}

		public void remove()
		{
			if( isFirst() )
				LinkedList.this.First = c.getNext();
			if( isLast() )
				LinkedList.this.Last = c.getPrevious();

			if( c.getNext() != null )
				c.getNext().setPrevious( c.getPrevious() );

			if( c.getPrevious() != null )
				c.getPrevious().setNext( c.getNext() );

			LinkedList.this.Count--;
			
			c = c.getNext();
		}
	}
}