/*
** j###t ########## #### ####
** j###t ########## #### ####
** j###T "###L J###"
** ######P' ########## #########
** ######k, ########## T######T
** ####~###L ####
** #### q###L ########## .#####
** #### \###L ########## #####"
**
** Class History
**
** Date Name Description
** ---------|------------|-----------------------------------------------
** 21Jun97 subtle modified loadFrom so that the order of the
** elements in the linked list is preserved.
** 22Jun97 subtle added a constructor to clone the contents of
** another collection.
**
*/
package key.collections;
import key.*;
import key.util.Trie;
import key.util.LinkedList;
import key.util.EmptyEnumeration;
import java.util.Enumeration;
import java.io.IOException;
import java.io.DataInput;
import java.io.DataOutput;
import java.util.NoSuchElementException;
/**
* The shortcut container was created so that you could 'shortcut'
* the contents of a container on the string. There are limitations
* in the implementation - it uses Trie's to provide the shortcut,
* so only [a-z] characters may be used. (No punctuation)
*
* Obviously, everything in the collection must be adequately (and
* uniquely named).
*/
public final class ShortcutCollection implements Collection,LatentlyCached
{
private static final long serialVersionUID = 2190566778863697841L;
private transient Trie theTrie;
private LinkedList theList;
private LinkedList concealList;
private boolean concealing=false;
public ShortcutCollection()
{
theList = null;
concealList = null;
}
public ShortcutCollection( Collection c )
{
this();
for( Enumeration e = c.elements(); e.hasMoreElements(); )
{
try
{
link( (Symbol) e.nextElement() );
}
catch( BadKeyException ex )
{
}
catch( NonUniqueKeyException ex )
{
}
}
}
/**
* Add this object to the list of objects
* @param p the player to add to the list
* @exception NonUniqueKeyException if there is already a player in with this name
* @exception BadKeyException if this players name is malformed somehow
*/
public synchronized void link( Symbol a ) throws NonUniqueKeyException,BadKeyException
{
ensureList();
theList.prepend( a );
linkToSecondary( a );
}
public synchronized void conceal( Symbol a )
{
ensureList();
ensureConcealList();
if( theList.containsEqual( a ) )
{
theList.removeEqual( a );
concealList.prepend( a );
}
else
throw new UnexpectedResult( "Attempting to conceal a symbol which isn't in the linked trie" );
}
public synchronized void reveal( Symbol a )
{
ensureList();
ensureConcealList();
if( concealList.containsEqual( a ) )
{
concealList.removeEqual( a );
theList.prepend( a );
if( concealList.count() == 0 )
concealList = null;
}
else
throw new UnexpectedResult( "Attempting to conceal a symbol which isn't an alias" );
}
public void concealable( boolean t )
{
concealing = true;
}
protected synchronized void linkToSecondary( Symbol a ) throws NonUniqueKeyException,BadKeyException
{
if( theTrie != null )
{
try
{
theTrie.insert( (String) a.getKey(), a );
}
catch( ClassCastException e )
{
System.out.println( e.toString() );
System.out.println( "during SHTCUT: " + a.toString() );
throw e;
}
}
}
protected synchronized void unlinkFromSecondary( Symbol a ) throws NonUniqueKeyException,BadKeyException
{
if( theTrie != null )
{
try
{
theTrie.remove( (String) a.getKey() );
}
catch( ClassCastException e )
{
}
}
}
/**
* Adds this atom to the database in such a
* way that it can be matched, but not seen
* by iterating through the elements.
*/
public synchronized void partialLink( Symbol a ) throws NonUniqueKeyException,BadKeyException
{
ensureTrie();
linkToSecondary( a );
ensureConcealList();
concealList.prepend( a );
}
/**
* Take this player out of the list of players
* @param p the player to remove from the list
* @exception NoSuchElementException if the player is not in the list
* @exception BadKeyException if the players name is malformed somehow
*/
public synchronized void unlink( Symbol a ) throws NoSuchElementException,BadKeyException
{
try
{
unlinkFromSecondary( a );
if( theList != null )
{
theList.removeEqual( a );
if( theList.count() == 0 )
theList = null;
}
if( concealList != null )
{
concealList.removeEqual( a );
if( concealList.count() == 0 )
concealList = null;
}
}
catch( NonUniqueKeyException e )
{
throw new java.util.NoSuchElementException( e.getKey() );
}
}
public boolean contains( Symbol o )
{
return( (theList != null && theList.containsEqual( o )) || (concealList != null && concealList.containsEqual( o ) ) );
}
private synchronized final void ensureList()
{
if( theList == null )
theList = new LinkedList();
}
private synchronized final void ensureConcealList()
{
if( concealList == null )
concealList = new LinkedList();
}
public synchronized void ensureTrie()
{
if( theTrie == null )
{
theTrie = new Trie();
//Log.debug( this, "creating trie for " + count() + " elements" );
if( theList != null )
linkToSecondary( theList );
if( concealList != null )
linkToSecondary( concealList );
//Key.getLatencyCache().addToCache( this );
}
// it was called because the trie was used
// keep it in the latent cache for another round
changed = true;
}
/**
* Sorts the elements in this linked trie
*/
public synchronized void sort()
{
if( theList != null )
{
ensureTrie();
LinkedList sorted = theTrie.getSortedList();
for( Enumeration e = sorted.elements(); e.hasMoreElements(); )
{
Object o = e.nextElement();
if( theList.containsEqual( o ) )
{
theList.removeEqual( o );
theList.append( o );
}
}
}
}
protected synchronized void linkToSecondary( LinkedList ll )
{
// add all the elements of the list
// into the Trie, retrospectively
LinkedList.Iterator l = ll.iterator();
while( l.isValid() )
{
Symbol a = (Symbol) l.element();
if( a instanceof Reference )
{
if( !((Reference)a).isValid() )
{
l.remove();
continue;
}
}
try
{
linkToSecondary( a );
l.next();
}
catch( NonUniqueKeyException t )
{
Log.debug( this, "while creating Trie: " + t.toString() + " (offending element removed)" );
l.remove();
}
catch( BadKeyException t )
{
Log.debug( this, "while creating Trie: " + t.toString() + " (offending element removed)" );
l.remove();
}
}
}
/**
* Returns the atom matched, or, possibly, an instance of
* a Trie object that contains all the matching atoms.
* <p>
* A null is returned if no matches were found at all. The
* match string is searched until the end of the string or
* a non-alphabetical character is found.
*
* @param match the start or whole string to match from
* @return An atom object, referring to the sole match, or a Trie
*/
public Object get( Object key )
{
String match;
try
{
match = (String) key;
}
catch( ClassCastException e )
{
throw new UnexpectedResult( "non string key passed to shortcut collection" );
}
ensureTrie();
Object o = theTrie.search( match );
//System.out.println( " get in shortcutCollection returned " + o );
return( o );
}
public Object getTrieFor( String match )
{
ensureTrie();
return( theTrie.getTrieFor( match ) );
}
public Symbol getExact( String match )
{
ensureTrie();
if( match.length() > 0 )
if( Character.isDigit( match.charAt( 0 ) ) )
return( (Symbol) get( match ) );
return( (Symbol) theTrie.searchExact( match ) );
}
public Symbol getElementAt( int c )
{
if( theList != null )
return( (Symbol) theList.getElementAt( c ) );
else
return( null );
}
/**
* aha!, but I reserve the right to make this function more efficient
* this way ;p~ (ie, it isn't very, atm)
*/
public synchronized void removeElementAt( int c ) throws NonUniqueKeyException,NoSuchElementException,BadKeyException
{
if( theList != null )
unlink( (Symbol) theList.getElementAt( c ) );
}
public Enumeration elements()
{
if( theList != null )
return( theList.elements() );
else
return( new EmptyEnumeration() );
}
public int count()
{
if( theList != null )
return( theList.count() );
else
return( 0 );
}
/**
* boolean used to indicate modified() state of the
* class (LatentlyCached)
*/
private transient boolean changed;
public synchronized void deallocate()
{
/*
if( count() != 0 && theTrie != null )
Log.debug( this, "trie deallocated for " + count() + " elements" );
*/
theTrie = null;
}
public boolean modified()
{
return( changed );
}
public void resetModify()
{
changed = false;
}
}