/*
* Copyright (C) 2004 TiongHiang Lee
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Email: thlee@onemindsoft.org
*/
package org.onemind.commons.java.datastructure;
import java.util.*;
import java.util.logging.Logger;
/**
* A nametable stack contains key-value mapping that has a scope.
* A new scope can be opened for putting new mappings and all the
* mappings added in this scope can be wiped out easily with a
* closeScope command. In effect this is like a stack of Maps,
* hence the name NametableStack.
*
* NOTE: the implementation use a map and list to achieve the behaviour.
* @author TiongHiang Lee (thlee@onemindsoft.org)
* @version $Id: NametableStack.java,v 1.7 2005/06/22 22:57:37 thlee Exp $ $Name: $
*/
public class NametableStack
{
/** the logger * */
private static final Logger _logger = Logger.getLogger(NametableStack.class.getName());
/** the map * */
private Nametable _nametable;
/** the list of maps * */
private ArrayList _list = new ArrayList();
/**
* The local nametable defines a scope where local variables mask out the global
* variables, but the global variable can still be accessed. This is useful for
* implementing function context
*
* @author TiongHiang Lee (thlee@onemindsoft.org)
*/
private static class LocalNametable implements Nametable
{
/** the local variables **/
private final Map _locals = new HashMap();
/** the global map **/
private final Nametable _global;
/** the scope **/
private final int _scope;
/**
* Constructor
* @param global the global
* @param scope the scope # where this local nametabe is openned
*/
private LocalNametable(Nametable global, int scope)
{
_global = global;
_scope = scope;
}
/**
* Get the global
* @return the global
*/
private Nametable getGlobal()
{
return _global;
}
/**
* {@inheritDoc}
*/
public boolean containsName(String key)
{
return _locals.containsKey(key) || _global.containsName(key);
}
/**
* {@inheritDoc}
*/
public void declare(String name, Object value)
{
_locals.put(name, value);
}
/**
* {@inheritDoc}
*/
public Object assign(String name, Object value)
{
if (_locals.containsKey(name))
{
return _locals.put(name, value);
} else
{
//TODO: determine whether to allow local scope to reassign global
return _global.assign(name, value);
}
}
/**
* {@inheritDoc}
*/
public Object access(String name)
{
if (_locals.containsKey(name))
{
return _locals.get(name);
} else
{
return _global.access(name);
}
}
/**
* {@inheritDoc}
*/
public void undeclare(String name)
{
if (_locals.containsKey(name))
{
_locals.remove(name);
} else
{
//TODO: determine whether to allow local scope to undeclare global
_global.undeclare(name);
}
}
public String toString(){
StringBuffer sb = new StringBuffer();
sb.append("Scope=");
sb.append(_scope);
sb.append("\n");
sb.append("Locals=" + _locals + "\n");
sb.append("Global=" + _global + "\n");
return sb.toString();
}
public Map asMap(){
Map m = new HashMap(_global.asMap());
m.putAll(_locals);
return Collections.unmodifiableMap(m);
}
}
/**
* {@inheritDoc}
*/
public NametableStack()
{
this(new HashMap());
}
/**
* {@inheritDoc}
* @param m the initial mapping
*/
public NametableStack(Map m)
{
_nametable = new SimpleNametable(m);
}
/**
* Open a new scope for mappings. Return an integer that represents a scope id
* @return the new scope
*/
public int newScope()
{
return _list.size();
}
public int newLocalScope()
{
_nametable = new LocalNametable(_nametable, _list.size());
return _list.size();
}
public void closeLocalScope(int i)
{
if (_nametable instanceof LocalNametable)
{
LocalNametable nt = (LocalNametable) _nametable;
if (nt._scope != i)
{
throw new IllegalArgumentException("Local scope " + i + " not matched");
} else
{
for (int k = _list.size(); k > i; k--)
{
_list.remove(k-1);
}
_nametable = nt.getGlobal();
}
} else
{
throw new IllegalStateException("Cannot find scope " + i);
}
}
/**
* Close a scope
* @param l the scope id
*/
public void closeScope(int l)
{
if (_nametable instanceof LocalNametable)
{
if (l < ((LocalNametable) _nametable)._scope)
{
throw new IllegalStateException("Encounter unclosed local scope");
}
}
int n = _list.size();
if (l > n)
{
throw new IllegalArgumentException("The scope has been closed");
} else if (l < n)
{
int diff = n - l;
for (int i = 0; i < diff; i++)
{
_nametable.undeclare((String)_list.remove(n - i - 1));
}
}
}
/**
* Declare name value pair
* @param name
* @param value
* @return
*/
public void declare(String name, Object value)
{
_nametable.declare(name, value);
_list.add(name);
}
/**
* Assign name/value pair
* @param name
* @param value
* @return
*/
public Object assign(String name, Object value)
{
return _nametable.assign(name, value);
}
/**
* Resolve the value associated with key name
* @param name the key
* @return the value associated with key
*/
public Object access(String name)
{
return _nametable.access(name);
}
/**
* Whether the map contains key name
* @param name the key
* @return true if map contains key name
*/
public boolean containsName(String name)
{
return _nametable.containsName(name);
}
/**
* Return map representation of the nametable stack
* @return the map
*/
public Map asMap()
{
return _nametable.asMap();
}
/**
* {@inheritDoc}
*/
public String toString()
{
return _nametable.toString();
}
}