package de.unisiegen.tpml.core.util ;
import java.util.Enumeration ;
import java.util.Iterator ;
import java.util.LinkedList ;
/**
* Abstract implementation of the generic <code>Environment</code> interface.
*
* @author Benedikt Meurer
* @author Christian Fehler
* @version $Rev:277 $
* @param <S> The symbol.
* @param <E> The entry.
* @see de.unisiegen.tpml.core.util.Environment
*/
public abstract class AbstractEnvironment < S , E > implements
Environment < S , E >
{
/**
* The store mappings, mapping symbols to entries.
*
* @param <S> The symbol.
* @param <E> The entry.
*/
protected static class Mapping < S , E >
{
/**
* The symbol.
*/
private S symbol ;
/**
* The entry.
*/
private E entry ;
/**
* Initializes the mapping.
*
* @param pSymbol The symbol.
* @param pEntry The entry.
*/
public Mapping ( S pSymbol , E pEntry )
{
if ( pSymbol == null )
{
throw new NullPointerException ( "symbol is null" ) ; //$NON-NLS-1$
}
if ( pEntry == null )
{
throw new NullPointerException ( "entry is null" ) ; //$NON-NLS-1$
}
this.symbol = pSymbol ;
this.entry = pEntry ;
}
/**
* Returns the entry.
*
* @return The entry.
*/
public E getEntry ( )
{
return this.entry ;
}
/**
* Returns the symbol.
*
* @return The symbol.
*/
public S getSymbol ( )
{
return this.symbol ;
}
}
/**
* The mappings, the symbol/entry pairs, within this environment.
*/
protected LinkedList < Mapping < S , E >> mappings ;
/**
* Default constructor, creates a new empty environment with no mappings.
*/
protected AbstractEnvironment ( )
{
// start with an empty mapping
this.mappings = new LinkedList < Mapping < S , E >> ( ) ;
}
/**
* Creates a new environment, based on the mappings from the specified
* <code>environment</code>.
*
* @param environment another <code>AbstractEnvironment</code> to inherit
* the mappings from.
* @throws NullPointerException if the <code>environment</code> is
* <code>null</code>.
*/
protected AbstractEnvironment ( AbstractEnvironment < S , E > environment )
{
if ( environment == null )
{
throw new NullPointerException ( "environment is null" ) ; //$NON-NLS-1$
}
// copy-on-write for the mappings
this.mappings = environment.mappings ;
}
/**
* {@inheritDoc}
*
* @see Environment#containsSymbol(Object)
*/
public boolean containsSymbol ( S symbol )
{
if ( symbol == null )
{
throw new NullPointerException ( "symbol is null" ) ; //$NON-NLS-1$
}
for ( Mapping < S , E > mapping : this.mappings )
{
if ( mapping.getSymbol ( ).equals ( symbol ) )
{
return true ;
}
}
return false ;
}
/**
* {@inheritDoc}
*
* @see Environment#get(Object)
*/
public E get ( S symbol )
{
if ( symbol == null )
{
throw new NullPointerException ( "symbol is null" ) ; //$NON-NLS-1$
}
for ( Mapping < S , E > mapping : this.mappings )
{
if ( mapping.getSymbol ( ).equals ( symbol ) )
{
return mapping.getEntry ( ) ;
}
}
throw new IllegalArgumentException ( "symbol is invalid" ) ; //$NON-NLS-1$
}
/**
* Adds a new mapping from <code>symbol</code> to <code>entry</code>. Any
* previous mapping for <code>symbol</code> will be removed.
*
* @param symbol the symbol for the new mapping.
* @param entry the entry for the new mapping.
* @see #get(Object)
*/
protected void put ( S symbol , E entry )
{
if ( symbol == null )
{
throw new NullPointerException ( "symbol is null" ) ; //$NON-NLS-1$
}
if ( entry == null )
{
throw new NullPointerException ( "entry is null" ) ; //$NON-NLS-1$
}
// copy-on-write semantics
LinkedList < Mapping < S , E >> newMappings = new LinkedList < Mapping < S , E >> ( ) ;
newMappings.add ( new Mapping < S , E > ( symbol , entry ) ) ;
for ( Mapping < S , E > mapping : this.mappings )
{
if ( ! mapping.getSymbol ( ).equals ( symbol ) )
{
newMappings.add ( mapping ) ;
}
}
this.mappings = newMappings ;
}
/**
* {@inheritDoc}
*
* @see Environment#symbols()
*/
public Enumeration < S > symbols ( )
{
return new Enumeration < S > ( )
{
private Iterator < Mapping < S , E >> iterator = AbstractEnvironment.this.mappings
.iterator ( ) ;
public boolean hasMoreElements ( )
{
return this.iterator.hasNext ( ) ;
}
public S nextElement ( )
{
return this.iterator.next ( ).getSymbol ( ) ;
}
} ;
}
/**
* Returns the string representation for the mutable store. Should be used for
* debugging purpose only.
*
* @return the string representation.
* @see Object#toString()
*/
@ Override
public String toString ( )
{
StringBuilder builder = new StringBuilder ( ) ;
builder.append ( '[' ) ;
for ( Mapping < S , E > mapping : this.mappings )
{
if ( builder.length ( ) > 1 ) builder.append ( ", " ) ; //$NON-NLS-1$
builder.append ( mapping.getSymbol ( ) ) ;
builder.append ( ": " ) ; //$NON-NLS-1$
builder.append ( mapping.getEntry ( ) ) ;
}
builder.append ( ']' ) ;
return builder.toString ( ) ;
}
}