// **********************************************************************
//
// Copyright (c) 2003-2006 ZeroC, Inc. All rights reserved.
//
// This copy of Ice is licensed to you under the terms described in the
// ICE_LICENSE file included in this distribution.
//
// **********************************************************************

package Freeze;

public abstract class Map extends java.util.AbstractMap 
    implements java.util.SortedMap, KeyCodec
{
    public abstract byte[] encodeValue(Object o, Ice.Communicator communicator);
    public abstract Object decodeValue(byte[] b, Ice.Communicator communicator);

    public
    Map(Connection connection, String dbName, String key, String value, 
	boolean createDb, java.util.Comparator comparator)
    {
	_connection = (ConnectionI) connection;
	_comparator = (comparator == null) ? null : new Comparator(comparator);
		
	_errorPrefix = "Freeze DB DbEnv(\"" + _connection.envName() + "\") Db(\"" + dbName + "\"): ";
	_trace = _connection.trace();
	
	init(null, dbName, key, value, createDb, null);
    }

    protected
    Map(Connection connection, String dbName, java.util.Comparator comparator)
    {
	_connection = (ConnectionI) connection;
	_comparator = (comparator == null) ? null : new Comparator(comparator);

	_errorPrefix = "Freeze DB DbEnv(\"" + _connection.envName() + "\") Db(\"" + dbName + "\"): ";
	_trace = _connection.trace();
    }

    protected void
    init(Freeze.Map.Index[] indices, String dbName, 
	 String key, String value, boolean createDb, java.util.Map indexComparators)
    {
	_db = Freeze.SharedDb.get(_connection, dbName, key, 
				  value, indices, createDb, _comparator,
				  indexComparators);
	_token = _connection.registerMap(this);

	if(indices != null)
	{
	    for(int i = 0; i < indices.length; ++i)
	    {
		_indexMap.put(indices[i].name(), indices[i]);
	    }
	}
    }

    public void
    close()
    {
	close(false);
    }

    //
    // SortedMap methods
    //

    public java.util.Comparator
    comparator()
    {
	if(_comparator == null)
	{
	    return null;
	}
	else
	{
	    //
	    // Return's the user's comparator, not the DB comparator.
	    //
	    return _comparator.comparator();
	}
    }

    public Object firstKey()
    {
	return firstKey(null, null);
    }
    
    public Object lastKey()
    {
	return lastKey(null, null);
    }

    Object firstKey(Object fromKey, Object toKey)
    {
	byte[] fk = fromKey == null ? null :
	    encodeKey(fromKey, _connection.communicator());

	byte[] k = getFirstOrLastKey(_db.db(), _db.dbName(), fk, true);
	if(k == null)
	{
	    throw new NoSuchElementException();
	}
	else
	{
	    Object key = decodeKey(k, _connection.communicator());
	    if(toKey != null && comparator().compare(key, toKey) >= 0)
	    {
		throw new NoSuchElementException();
	    }
	    return key;
	}
    }

    Object lastKey(Object fromKey, Object toKey)
    {
	byte[] tk = toKey == null ? null :
	    encodeKey(toKey, _connection.communicator());

	byte[] k = getFirstOrLastKey(_db.db(), _db.dbName(), tk, false);
	if(k == null)
	{
	    throw new NoSuchElementException();
	}
	else
	{
	    Object key = decodeKey(k, _connection.communicator());
	    if(fromKey != null && comparator().compare(fromKey, key) > 0)
	    {
		throw new NoSuchElementException();
	    }
	    return key;
	}
    }

    public java.util.SortedMap headMap(Object toKey)
    {
	if(toKey == null)
	{
	    throw new NullPointerException();
	}
	if(_comparator == null)
	{
	    throw new UnsupportedOperationException();
	}

	return new SubMap(this, null, toKey);
    }
    
    public java.util.SortedMap tailMap(Object fromKey)
    {
	if(fromKey == null)
	{
	    throw new NullPointerException();
	}
	if(_comparator == null)
	{
	    throw new UnsupportedOperationException();
	}

	return new SubMap(this, fromKey, null);
    } 
   
    public java.util.SortedMap subMap(Object fromKey, Object toKey)
    {
	if(fromKey == null || toKey == null )
	{
	    throw new NullPointerException();
	}
	if(_comparator == null)
	{
	    throw new UnsupportedOperationException();
	}
	return new SubMap(this, fromKey, toKey);
    }

    
    //
    // Additional non-standard xxMapForIndex methods
    //
    public java.util.SortedMap headMapForIndex(String indexName, Object toKey)
    {
	if(toKey == null)
	{
	    throw new NullPointerException();
	}
	
	Map.Index index = (Map.Index)_indexMap.get(indexName);
	if(index == null)
	{
	    throw new IllegalArgumentException("Can't find index '" + indexName + "'");
	}
	else if(index.comparator() == null)
	{
	    throw new IllegalArgumentException("Index '" + indexName + "' has no user-defined comparator");
	}
	return new SubMap(index, null, toKey);
    }
    
    public java.util.SortedMap tailMapForIndex(String indexName, Object fromKey)
    {
	if(fromKey == null)
	{
	    throw new NullPointerException();
	}
	Map.Index index = (Map.Index)_indexMap.get(indexName);
	if(index == null)
	{
	    throw new IllegalArgumentException("Can't find index '" + indexName + "'");
	}
	else if(index.comparator() == null)
	{
	    throw new IllegalArgumentException("Index '" + indexName + "' has no user-defined comparator");
	}
	return new SubMap(index, fromKey, null);
    } 
   
    public java.util.SortedMap subMapForIndex(String indexName, Object fromKey, Object toKey)
    {
	if(fromKey == null || toKey == null )
	{
	    throw new NullPointerException();
	}
	Map.Index index = (Map.Index)_indexMap.get(indexName);
	if(index == null)
	{
	    throw new IllegalArgumentException("Can't find index '" + indexName + "'");
	}
	else if(index.comparator() == null)
	{
	    throw new IllegalArgumentException("Index '" + indexName + "' has no user-defined comparator");
	}
	return new SubMap(index, fromKey, toKey);
    }

    public java.util.SortedMap mapForIndex(String indexName)
    {
	Map.Index index = (Map.Index)_indexMap.get(indexName);
	if(index == null)
	{
	    throw new IllegalArgumentException("Can't find index '" + indexName + "'");
	}
	else if(index.comparator() == null)
	{
	    throw new IllegalArgumentException("Index '" + indexName + "' has no user-defined comparator");
	}
	return new SubMap(index, null, null);
    }
    
    //
    // Plain Map methods
    //
    public int
    size()
    {
	if(_db == null)
	{
	    DatabaseException ex = new DatabaseException();
	    ex.message = _errorPrefix + "\"" + _db.dbName() + "\" has been closed";
	    throw ex;
	}

	//
	// The number of records cannot be cached and then adjusted by
	// the member functions since the map would no longer work in
	// the presence of transactions - if a record is added (and
	// the size adjusted) and the transaction aborted then the
	// cached map size() would be incorrect.
	//
	
	//
	// TODO: DB_FAST_STAT doesn't seem to do what the
	// documentation says...
	//
	try
	{
	    com.sleepycat.db.StatsConfig config = new com.sleepycat.db.StatsConfig();
	    //
	    // TODO: DB_FAST_STAT doesn't seem to do what the
	    // documentation says...
	    //      
	    //config.setFast(true);
	    com.sleepycat.db.BtreeStats s = (com.sleepycat.db.BtreeStats)_db.db().getStats(null, config);
	    return s.getNumData();
	}
	catch(com.sleepycat.db.DatabaseException e)
	{
	    DatabaseException ex = new DatabaseException();
	    ex.initCause(e);
	    ex.message = _errorPrefix + "Db.stat: " + e.getMessage();
	    throw ex;
	}
    }

    public boolean
    containsValue(Object value)
    {
	for(;;)
	{
	    EntryIterator p = null;
	    try
	    {
		p = (EntryIterator)entrySet().iterator();

		if(value == null)
		{
		    while(p.hasNext())
		    {
			Entry e = (Entry)p.next();
			if(e.getValue() == null)
			{
			    return true;
			}
		    }
		} 
		else 
		{
		    while(p.hasNext()) 
		    {
			Entry e = (Entry)p.next();
			if(value.equals(e.getValue()))
			{
			    return true;
			}
		    }
		}
		return false;
	    }
	    catch(DeadlockException e)
	    {
		if(_connection.dbTxn() != null)
		{
		    throw e;
		}
		else
		{
		    if(_connection.deadlockWarning())
		    {
			_connection.communicator().getLogger().warning("Deadlock in Freeze.Map.containsValue while " +
								       "iterating over Db \"" + _db.dbName()  +
								       "\"; retrying...");
		    }

		    //
		    // Try again
		    //
		}
	    }
	    finally
	    {
		if(p != null)
		{
		    p.close();
		}
	    }
	}
    }

    public boolean
    containsKey(Object key)
    {
	if(_db == null)
	{
	    DatabaseException ex = new DatabaseException();
	    ex.message = _errorPrefix + "\"" + _db.dbName() + "\" has been closed";
	    throw ex;
	}

	byte[] k = encodeKey(key, _connection.communicator());

	com.sleepycat.db.DatabaseEntry dbKey = new com.sleepycat.db.DatabaseEntry(k);
	com.sleepycat.db.DatabaseEntry dbValue = new com.sleepycat.db.DatabaseEntry();
	dbValue.setPartial(true);

	if(_trace >= 1)
	{
	    _connection.communicator().getLogger().trace(
		"Freeze.Map", "checking key in Db \"" + _db.dbName() + "\"");
	}

	for(;;)
	{
	    try
	    {
		return _db.db().get(_connection.dbTxn(), dbKey, dbValue, null)
		    == com.sleepycat.db.OperationStatus.SUCCESS;
	    }
	    catch(com.sleepycat.db.DeadlockException e)
	    {
		if(_connection.dbTxn() != null)
		{
		    DeadlockException ex = new DeadlockException();
		    ex.initCause(e);
		    ex.message = _errorPrefix + "Db.get: " + e.getMessage();
		    throw ex;
		}
		else
		{
		    if(_connection.deadlockWarning())
		    {
			_connection.communicator().getLogger().warning(
			    "Deadlock in Freeze.Map.containsKey while " +
			    "reading Db \"" + _db.dbName() +
			    "\"; retrying...");
		    }
		    //
		    // Try again
		    //
		}
	    }
	    catch(com.sleepycat.db.DatabaseException e)
	    {
		DatabaseException ex = new DatabaseException();
		ex.initCause(e);
		ex.message = _errorPrefix + "Db.get: " + e.getMessage();
		throw ex;
	    }
	}
    }

    public Object
    get(Object key)
    {
	byte[] k = encodeKey(key, _connection.communicator());
	com.sleepycat.db.DatabaseEntry dbKey = new com.sleepycat.db.DatabaseEntry(k);
	byte[] v = getImpl(dbKey);
	if(v == null)
	{
	    return null;
	}
	else
	{
	    return decodeValue(v, _connection.communicator());
	}
    }

    public Object
    put(Object key, Object value)
    {
	byte[] k = encodeKey(key, _connection.communicator());
	com.sleepycat.db.DatabaseEntry dbKey = new com.sleepycat.db.DatabaseEntry(k);
	byte[] v = getImpl(dbKey);
	Object o = null;
	if(v != null)
	{
	    o = decodeValue(v, _connection.communicator());
	}
	putImpl(dbKey, value);
	return o;
    }

    public Object
    remove(Object key)
    {
	byte[] k = encodeKey(key, _connection.communicator());
	com.sleepycat.db.DatabaseEntry dbKey = new com.sleepycat.db.DatabaseEntry(k);
	byte[] v = getImpl(dbKey);
	
	if(v != null && removeImpl(dbKey))
	{
	    return decodeValue(v, _connection.communicator());
	}
	else
	{
	    return null;
	}
    }

    //
    // Proprietary API calls. These are much faster than the
    // corresponding Java collections API methods since the unwanted
    // reads are avoided.
    //
    public void
    fastPut(Object key, Object value)
    {
	byte[] k = encodeKey(key, _connection.communicator());
	com.sleepycat.db.DatabaseEntry dbKey = new com.sleepycat.db.DatabaseEntry(k);
	putImpl(dbKey, value);
    }

    //
    // Returns true if the record was removed, false otherwise.
    //
    public boolean
    fastRemove(Object key)
    {
	byte[] k = encodeKey(key, _connection.communicator());
	com.sleepycat.db.DatabaseEntry dbKey = new com.sleepycat.db.DatabaseEntry(k);
	return removeImpl(dbKey);
    }

    public void
    clear()
    {
	if(_db == null)
	{
	    DatabaseException ex = new DatabaseException();
	    ex.message = _errorPrefix + "\"" + _db.dbName() + "\" has been closed";
	    throw ex;
	}

	com.sleepycat.db.Transaction txn = _connection.dbTxn();

	for(;;)
	{
	    try
	    {
		_db.db().truncate(txn, false);
		break;
	    }
	    catch(com.sleepycat.db.DeadlockException e)
	    {
		if(txn != null)
		{
		    DeadlockException ex = new DeadlockException();
		    ex.initCause(e);
		    ex.message = _errorPrefix + "Db.truncate: " + e.getMessage();
		    throw ex;
		}
		else
		{
		    if(_connection.deadlockWarning())
		    {
			_connection.communicator().getLogger().warning("Deadlock in Freeze.Map.clear on Db \"" +
								       _db.dbName() + "\"; retrying...");
		    }
		    
		    //
		    // Try again
		    //
		}
	    }
	    catch(com.sleepycat.db.DatabaseException e)
	    {
		DatabaseException ex = new DatabaseException();
		ex.initCause(e);
		ex.message = _errorPrefix + "Db.truncate: " + e.getMessage();
		throw ex;
	    }
	}
    }
	
    public java.util.Set
    entrySet()
    {
        if(_entrySet == null)
	{
            _entrySet = new java.util.AbstractSet()
	    {
		public java.util.Iterator
		iterator()
		{
		    return new EntryIteratorImpl(null, null, null, false, false);
		}
		
		public boolean
		contains(Object o)
		{
		    if(!(o instanceof Map.Entry))
		    {
			return false;
		    }
		    Map.Entry entry = (Map.Entry)o;
		    Object value = entry.getValue();
		    
		    byte[] v = getImpl(entry.getDbKey());
		    return v != null && valEquals(decodeValue(v, _connection.communicator()), value);
		}
		
		public boolean
		remove(Object o)
		{
		    if(!(o instanceof Map.Entry))
		    {
			return false;
		    }
		    Map.Entry entry = (Map.Entry)o;
		    Object value = entry.getValue();

		    byte[] v = getImpl(entry.getDbKey());
		    if(v != null && valEquals(decodeValue(v, _connection.communicator()), value))
		    {
			return removeImpl(entry.getDbKey());
		    }
		    return false;
		}
		
		public int
		size()
		{
		    return Map.this.size();
		}
		
		public void
		clear()
		{
		    Map.this.clear();
		}
            };
        }

        return _entrySet;
    }

    public void
    closeAllIterators()
    {
	closeAllIteratorsExcept(null, false);
    }
    
    void
    closeAllIteratorsExcept(Object except, boolean finalizing)
    {
	synchronized(_iteratorList)
	{
	    java.util.Iterator p = _iteratorList.iterator();

	    while(p.hasNext())
	    {
		Object obj = p.next();
		if(obj != except)
		{
		    ((EntryIteratorImpl)obj).close(finalizing);
		}
	    }
	}
    }

    protected void
    finalize()
    {
	close(true);
    }

    //
    // The synchronization is only needed when finalizing is true
    //
    void 
    close(boolean finalizing)
    {
	synchronized(_connection)
	{
	    if(_db != null)
	    {
		closeAllIteratorsExcept(null, finalizing);
		try
		{
		    _db.close();
		}
		finally
		{
		    _db = null;
		    _connection.unregisterMap(_token);
		    _token = null;
		}
	    }
	}
    }

    EntryIterator 
    createIterator(Index index, Object fromKey, Object toKey)
    {
	KeyCodec codec = index == null ? (KeyCodec)this : (KeyCodec)index;

	Ice.Communicator communicator = _connection.getCommunicator();

	return new EntryIteratorImpl(index, 
				 fromKey == null ? null : codec.encodeKey(fromKey, communicator),
				 toKey == null ? null : codec.encodeKey(toKey, communicator),
				 false, true);
    }
	
    ConnectionI connection()
    {
	return _connection;
    }

    private static boolean
    valEquals(Object o1, Object o2)
    {
        return (o1 == null ? o2 == null : o1.equals(o2));
    }

    private byte[]
    getFirstOrLastKey(com.sleepycat.db.Database db, String dbName, byte[] key, boolean first)
    {
	if(db == null)
	{
	    DatabaseException ex = new DatabaseException();
	    ex.message = _errorPrefix + "\"" + dbName + "\" has been closed";
	    throw ex;
	}
	
	if(_trace >= 1)
	{
	    _connection.communicator().getLogger().trace(
		"Freeze.Map", "Searching db \"" + dbName + "\"");
	}
	
	com.sleepycat.db.DatabaseEntry dbKey = key == null ?
	    new com.sleepycat.db.DatabaseEntry():
	    new com.sleepycat.db.DatabaseEntry(key);

	com.sleepycat.db.DatabaseEntry dbValue = new com.sleepycat.db.DatabaseEntry();
	dbValue.setPartial(true); // not interested in value

	try
	{
	    for(;;)
	    {
		com.sleepycat.db.Cursor dbc = null;
		try
		{
		    dbc = db.openCursor(_connection.dbTxn(), null);
		    com.sleepycat.db.OperationStatus status;

		    if(key == null)
		    {
			status = first ?
			    dbc.getFirst(dbKey, dbValue, null) : dbc.getLast(dbKey, dbValue, null);
		    }
		    else if(first)
		    {
			status = dbc.getSearchKeyRange(dbKey, dbValue, null);
		    }
		    else
		    {
			status = dbc.getSearchKeyRange(dbKey, dbValue, null);

			if(status == com.sleepycat.db.OperationStatus.SUCCESS)
			{
			    //
			    // goto the previous pair, which must be < key
			    //
			    status = dbc.getPrevNoDup(dbKey, dbValue, null);
			}
			else if(status == com.sleepycat.db.OperationStatus.NOTFOUND)
			{
			    //
			    // All keys < desired toKey, so we pick the largest of
			    // all, the last one
			    //
			    status = dbc.getLast(dbKey, dbValue, null);
			}
		    }
		   
		    if(status == com.sleepycat.db.OperationStatus.SUCCESS)
		    {
			return dbKey.getData();
		    }
		    else
		    {
			return null;
		    }
		}
		catch(com.sleepycat.db.DeadlockException dx)
		{
		    if(_connection.dbTxn() != null)
		    {
			DeadlockException ex = new DeadlockException();
			ex.initCause(dx);
			ex.message = _errorPrefix + "Dbc.getXXX: " + dx.getMessage();
			throw ex;
		    }
		    else
		    {
			if(_connection.deadlockWarning())
			{
			    _connection.communicator().getLogger().warning(
				"Deadlock in Freeze.Map while searching \"" + db.getDatabaseName() +
				"\"; retrying...");
			}
			
			//
			// Retry
			//
		    }
		}
		finally
		{
		    if(dbc != null)
		    {
			try
			{
			    dbc.close();
			}
			catch(com.sleepycat.db.DeadlockException dx)
			{
			    //
			    // Ignored
			    //
			}
		    }
		}
	    }
	}
	catch(com.sleepycat.db.DatabaseException dx)
	{
	    DatabaseException ex = new DatabaseException();
	    ex.initCause(dx);
	    ex.message = _errorPrefix + "Db.openCursor/Dbc.getXXX: " + dx.getMessage();
	    throw ex; 
	}
    }

    private byte[]
    getImpl(com.sleepycat.db.DatabaseEntry dbKey)
    {
	if(_db == null)
	{
	    DatabaseException ex = new DatabaseException();
	    ex.message = _errorPrefix + "\"" + _db.dbName() + "\" has been closed";
	    throw ex;
	}

	com.sleepycat.db.DatabaseEntry dbValue = new com.sleepycat.db.DatabaseEntry();

	if(_trace >= 1)
	{
	    _connection.communicator().getLogger().trace("Freeze.Map", "reading value from Db \"" + _db.dbName() +
							 "\"");
	}

	for(;;)
	{
	    try
	    {
		com.sleepycat.db.OperationStatus rc = _db.db().get(_connection.dbTxn(), dbKey, dbValue, null);
		if(rc == com.sleepycat.db.OperationStatus.SUCCESS)
		{
		    return dbValue.getData();
		}
		else
		{
		    return null;
		}
	    }
	    catch(com.sleepycat.db.DeadlockException e)
	    {
		if(_connection.dbTxn() != null)
		{
		    DeadlockException ex = new DeadlockException();
		    ex.initCause(e);
		    ex.message = _errorPrefix + "Db.get: " + e.getMessage();
		    throw ex;
		}
		else
		{
		    if(_connection.deadlockWarning())
		    {
			_connection.communicator().getLogger().warning("Deadlock in Freeze.Map.getImpl while " +
								       "reading Db \"" + _db.dbName() +
								       "\"; retrying...");
		    }

		    //
		    // Try again
		    //
		}
	    }
	    catch(com.sleepycat.db.DatabaseException e)
	    {
		DatabaseException ex = new DatabaseException();
		ex.initCause(e);
		ex.message = _errorPrefix + "Db.get: " + e.getMessage();
		throw ex;
	    }
	}
    }

    private void
    putImpl(com.sleepycat.db.DatabaseEntry dbKey, Object value)
    {
	if(_db == null)
	{
	    DatabaseException ex = new DatabaseException();
	    ex.message = _errorPrefix + "\"" + _db.dbName() + "\" has been closed";
	    throw ex;
	}

	byte[] v = encodeValue(value, _connection.communicator());	
	com.sleepycat.db.DatabaseEntry dbValue = new com.sleepycat.db.DatabaseEntry(v);
	
	if(_trace >= 1)
	{
	    _connection.communicator().getLogger().trace("Freeze.Map", "writing value in Db \"" + _db.dbName() + "\"");
	}

	com.sleepycat.db.Transaction txn = _connection.dbTxn();
	if(txn == null)
	{
	    closeAllIterators();
	}

	for(;;)
	{
	    try
	    {
		_db.db().put(txn, dbKey, dbValue);
		break;
	    }
	    catch(com.sleepycat.db.DeadlockException e)
	    {
		if(txn != null)
		{
		    DeadlockException ex = new DeadlockException();
		    ex.initCause(e);
		    ex.message = _errorPrefix + "Db.put: " + e.getMessage();
		    throw ex;
		}
		else
		{
		    if(_connection.deadlockWarning())
		    {
			_connection.communicator().getLogger().warning("Deadlock in Freeze.Map.putImpl while " +
								       "writing into Db \"" + _db.dbName() +
								       "\"; retrying...");
		    }

		    //
		    // Try again
		    //
		}
	    }
	    catch(com.sleepycat.db.DatabaseException e)
	    {
		DatabaseException ex = new DatabaseException();
		ex.initCause(e);
		ex.message = _errorPrefix + "Db.put: " + e.getMessage();
		throw ex;
	    }
	}
    }

    private boolean
    removeImpl(com.sleepycat.db.DatabaseEntry dbKey)
    {
	if(_db == null)
	{
	    DatabaseException ex = new DatabaseException();
	    ex.message = _errorPrefix + "\"" + _db.dbName() + "\" has been closed";
	    throw ex;
	}

	if(_trace >= 1)
	{
	    _connection.communicator().getLogger().trace("Freeze.Map", "deleting value from Db \"" + _db.dbName() +
							 "\"");
	}

	com.sleepycat.db.Transaction txn = _connection.dbTxn();
	if(txn == null)
	{
	    closeAllIterators();
	}

	for(;;)
	{
	    try
	    {
		com.sleepycat.db.OperationStatus rc = _db.db().delete(txn, dbKey);
		return (rc == com.sleepycat.db.OperationStatus.SUCCESS);
	    }
	    catch(com.sleepycat.db.DeadlockException e)
	    {
		if(txn != null)
		{
		    DeadlockException ex = new DeadlockException();
		    ex.initCause(e);
		    ex.message = _errorPrefix + "Db.del: " + e.getMessage();
		    throw ex;
		}
		else
		{
		    if(_connection.deadlockWarning())
		    {
			_connection.communicator().getLogger().warning("Deadlock in Freeze.Map.removeImpl while " +
								       "writing into Db \"" + _db.dbName() +
								       "\"; retrying...");
		    }

		    //
		    // Try again
		    //
		}
	    }
	    catch(com.sleepycat.db.DatabaseException e)
	    {
		DatabaseException ex = new DatabaseException();
		ex.initCause(e);
		ex.message = _errorPrefix + "Db.del: " + e.getMessage();
		throw ex;
	    }
	}
    }

    private class Comparator implements java.util.Comparator
    {
	Comparator(java.util.Comparator comparator)
	{
	    _comparator = comparator;
	}
	
	public java.util.Comparator comparator()
	{
	    return _comparator;
	}
	
	public int compare(Object o1, Object o2)
	{
	    byte[] d1 = (byte[])o1;
	    byte[] d2 = (byte[])o2;

	    Ice.Communicator communicator = _connection.communicator();

	    return _comparator.compare(decodeKey(d1, communicator),
				       decodeKey(d2, communicator));
	}

	//
	// The user-supplied comparator
	//
	private final java.util.Comparator _comparator;
    }

    public abstract class Index 
	implements com.sleepycat.db.SecondaryKeyCreator, java.util.Comparator, KeyCodec
    {
	//
	// Implementation details
	//
	public boolean
	createSecondaryKey(com.sleepycat.db.SecondaryDatabase secondary,
			   com.sleepycat.db.DatabaseEntry key,
			   com.sleepycat.db.DatabaseEntry value,
			   com.sleepycat.db.DatabaseEntry result)
	    throws com.sleepycat.db.DatabaseException
	{
	    Ice.Communicator communicator = _connection.getCommunicator();   
	    byte[] secondaryKey = marshalKey(value.getData());
	    assert(secondaryKey != null);
	   
	    result.setData(secondaryKey);
	    result.setSize(secondaryKey.length);
	    return true;
	}
	
	com.sleepycat.db.SecondaryDatabase
	db()
	{
	    return _db;
	}
	
	String name()
	{
	    return _name;
	}

	protected Index(String name)
	{
	    _name = name;
	}

	void
	associate(String dbName, com.sleepycat.db.Database db, 
		  com.sleepycat.db.Transaction txn, boolean createDb,
		  java.util.Comparator comparator)
	    throws com.sleepycat.db.DatabaseException, java.io.FileNotFoundException
	{
	    _dbName = dbName + "." + _name;
	    _comparator = comparator;

	    assert(txn != null);
	    assert(_db == null);

	    com.sleepycat.db.SecondaryConfig config = new com.sleepycat.db.SecondaryConfig();
	    config.setAllowCreate(createDb);
	    config.setAllowPopulate(true); // We always populate empty indices
	    config.setSortedDuplicates(true);
	    config.setType(com.sleepycat.db.DatabaseType.BTREE);
	    if(_comparator != null)
	    {
		config.setBtreeComparator(this);
	    }
	    config.setKeyCreator(this);

	    _db = _connection.dbEnv().getEnv().openSecondaryDatabase(txn, _dbName, null, db, config);
	}
	
	void init(Index from)
	{
	    assert(_name.equals(from._name));
	    assert(_db == null);

	    _dbName = from._dbName;
	    _db = from._db;
	    _comparator = from._comparator;
	}

	java.util.Comparator comparator()
	{
	    return _comparator;
	}
	
	Map parent()
	{
	    return Map.this;
	}

	Object firstKey(Object fromKey, Object toKey)
	{
	    byte[] fk = fromKey == null ? null :
		encodeKey(fromKey, _connection.communicator());
	    
	    byte[] k = getFirstOrLastKey(_db, _dbName, fk, true);
	    if(k == null)
	    {
		throw new NoSuchElementException();
	    }
	    else
	    {
		Object key = decodeKey(k, _connection.communicator());
		if(toKey != null && _comparator.compare(key, toKey) >= 0)
		{
		    throw new NoSuchElementException();
		}
		return key;
	    }
	}

	Object lastKey(Object fromKey, Object toKey)
	{
	    byte[] tk = toKey == null ? null :
		encodeKey(toKey, _connection.communicator());
	    
	    byte[] k = getFirstOrLastKey(_db, _dbName, tk, false);
	    if(k == null)
	    {
		throw new NoSuchElementException();
	    }
	    else
	    {
		Object key = decodeKey(k, _connection.communicator());
		if(fromKey != null && _comparator.compare(fromKey, key) > 0)
		{
		    throw new NoSuchElementException();
		}
		return key;
	    }
	}

	void close()
	{
	    //
	    // close() is called by SharedDb only on the "main" index
	    // (the one that was associated)
	    //

	    if(_db != null)
	    {
		try
		{
		    _db.close();
		}
		catch(com.sleepycat.db.DatabaseException dx)
		{
		    DatabaseException ex = new DatabaseException();
		    ex.initCause(dx);
		    ex.message = _errorPrefix + "Db.close for index \"" + _dbName + "\": " + dx.getMessage();
		    throw ex;
		}
		_db = null;
	    }
	}
    
	public EntryIterator 
	untypedFind(Object key, boolean onlyDups)
	{
	    byte[] k = encodeKey(key, _connection.communicator());
	    return new EntryIteratorImpl(this, k, null, onlyDups, false);
	}

	public int
	untypedCount(Object key)
	{
	    byte[] k = encodeKey(key, _connection.communicator());

	    com.sleepycat.db.DatabaseEntry dbKey = new com.sleepycat.db.DatabaseEntry(k);
	    com.sleepycat.db.DatabaseEntry dbValue = new com.sleepycat.db.DatabaseEntry();
	    //
	    // dlen is 0, so we should not retrieve any value 
	    // 
	    dbValue.setPartial(true);
	    
	    try
	    {
		for(;;)
		{
		    com.sleepycat.db.Cursor dbc = null;
		    try
		    {
			dbc = _db.openCursor(null, null);   
			if(dbc.getSearchKey(dbKey, dbValue, null) == com.sleepycat.db.OperationStatus.SUCCESS)
			{
			    return dbc.count();
			}
			else
			{
			    return 0;
			}
		    }
		    catch(com.sleepycat.db.DeadlockException dx)
		    {
			if(_connection.deadlockWarning())
			{
			    _connection.communicator().getLogger().warning(
				"Deadlock in Freeze.Map.Index.untypedCount while iterating over index \"" + _dbName +
				"\"; retrying...");
			}
			
			//
			// Retry
			//
		    }
		    finally
		    {
			if(dbc != null)
			{
			    try
			    {
				dbc.close();
			    }
			    catch(com.sleepycat.db.DeadlockException dx)
			    {
				//
				// Ignored
				//
			    }
			}
		    }
		}
	    }
	    catch(com.sleepycat.db.DatabaseException dx)
	    {
		DatabaseException ex = new DatabaseException();
		ex.initCause(dx);
		ex.message = _errorPrefix + "Db.cursor for index \"" + _dbName + "\": " + dx.getMessage();
		throw ex; 
	    }
	} 
	
	boolean containsKey(Object key)
	{
	    byte[] k = encodeKey(key, _connection.communicator());

	    com.sleepycat.db.DatabaseEntry dbKey = new com.sleepycat.db.DatabaseEntry(k);
	    com.sleepycat.db.DatabaseEntry dbValue = new com.sleepycat.db.DatabaseEntry();
	    dbValue.setPartial(true);

	    if(_trace >= 1)
	    {
		_connection.communicator().getLogger().trace(
		    "Freeze.Map.Index", "checking key in Db \"" + _dbName + "\"");
	    }
	    
	    for(;;)
	    {
		try
		{
		    return _db.get(_connection.dbTxn(), dbKey, dbValue, null)
			== com.sleepycat.db.OperationStatus.SUCCESS;
		}
		catch(com.sleepycat.db.DeadlockException e)
		{
		    if(_connection.dbTxn() != null)
		    {
			DeadlockException ex = new DeadlockException();
			ex.initCause(e);
			ex.message = _errorPrefix + "Db.get: " + e.getMessage();
			throw ex;
		    }
		    else
		    {
			if(_connection.deadlockWarning())
			{
			    _connection.communicator().getLogger().warning(
				"Deadlock in Freeze.Map.Index.containsKey while " +
				"reading Db \"" + _dbName + "\"; retrying...");
			}
			//
			// Try again
			//
		    }
		}
		catch(com.sleepycat.db.DatabaseException e)
		{
		    DatabaseException ex = new DatabaseException();
		    ex.initCause(e);
		    ex.message = _errorPrefix + "Db.get: " + e.getMessage();
		    throw ex;
		}
	    }
	}
	
	//
	// Extracts the index key from this value
	//
	public abstract Object extractKey(Object value);

	protected byte[] marshalKey(byte[] value)
	{
	    Object decodedValue = decodeValue(value, _connection.communicator());
	    return encodeKey(extractKey(decodedValue), _connection.communicator());
	}

	//
	// The user-supplied comparator
	//
	protected java.util.Comparator _comparator;

	private String _name;
	private String _dbName;
	private com.sleepycat.db.SecondaryDatabase _db;
    }

    /**
     *
     * The entry iterator allows clients to explicitly close the iterator 
     * and free resources allocated for the iterator as soon as possible.
     *
     **/
    public interface EntryIterator extends java.util.Iterator
    {
	void close();
	void destroy(); // an alias for close
    }

    class EntryIteratorImpl implements EntryIterator
    {
        EntryIteratorImpl(Index index, byte[] fromKey, byte[] toKey, 
			  boolean onlyFromKeyDups, boolean skipDups)
        {
	    _index = index;
	    _fromKey = fromKey;
	    _toKey = toKey;
	    _onlyFromKeyDups = onlyFromKeyDups;
	    _skipDups = skipDups;

	    try
	    {
		com.sleepycat.db.Transaction txn = _connection.dbTxn();
		if(txn == null)
		{
		    //
		    // Start transaction
		    //
		    txn = _connection.dbEnv().getEnv().beginTransaction(null, null);
		    _txn = txn;

		    if(_connection.txTrace() >= 1)
		    {
			String txnId = Long.toHexString((_txn.getId() & 0x7FFFFFFF) + 0x80000000L); 

			_connection.communicator().getLogger().trace("Freeze.Map", _errorPrefix +
								     "started transaction " + txnId + " for cursor");
		    }
		}
		
		//
		// Open cursor with this transaction
		//
		if(index == null)
		{
		    _cursor = _db.db().openCursor(txn, null);
		}
		else
		{
		    _cursor = index.db().openSecondaryCursor(txn, null);
		}
	    }
	    catch(com.sleepycat.db.DeadlockException dx)
	    {
		dead();
		DeadlockException ex = new DeadlockException();
		ex.initCause(dx);
		ex.message = _errorPrefix + "EntryIterator constructor: " + dx.getMessage();
		throw ex;
	    }
	    catch(com.sleepycat.db.DatabaseException dx)
	    {
		dead();
		DatabaseException ex = new DatabaseException();
		ex.initCause(dx);
		ex.message = _errorPrefix + "EntryIterator constructor: " + dx.getMessage();
		throw ex;
	    }

	    synchronized(_iteratorList)
	    {
		_iteratorList.addFirst(this);
		java.util.Iterator p = _iteratorList.iterator();
		p.next();
		_iteratorListToken = p;
	    }
        }

        public boolean
        hasNext()
        {
	    if(_current == null || _current == _lastReturned)
	    {
		com.sleepycat.db.DatabaseEntry dbKey = new com.sleepycat.db.DatabaseEntry();
		com.sleepycat.db.DatabaseEntry dbValue = new com.sleepycat.db.DatabaseEntry();
		com.sleepycat.db.DatabaseEntry dbIKey = new com.sleepycat.db.DatabaseEntry();
		com.sleepycat.db.OperationStatus status = null;

		try
		{
		    if(_index != null)
		    {
			com.sleepycat.db.SecondaryCursor c = (com.sleepycat.db.SecondaryCursor)_cursor;
			if(_current == null)
			{
			    //
			    // First key
			    //
			    if(_fromKey != null)
			    {
				dbIKey.setData(_fromKey);
				status = c.getSearchKeyRange(dbIKey, dbKey, dbValue, null);
			    }
			    else
			    {
				status = c.getFirst(dbIKey, dbKey, dbValue, null);
			    }
			}
			else
			{   
			    if(_onlyFromKeyDups)
			    {
				status = c.getNextDup(dbIKey, dbKey, dbValue, null);
			    }
			    else if(_skipDups)
			    {
				status = c.getNextNoDup(dbIKey, dbKey, dbValue, null);
			    }
			    else
			    {
				status = c.getNext(dbIKey, dbKey, dbValue, null);
			    }
			}
		    }
		    else
		    {
			if(_current == null && _fromKey != null)
			{
			    dbKey.setData(_fromKey);
			    status = _cursor.getSearchKey(dbKey, dbValue, null);
			}
			else
			{
			    status = _cursor.getNext(dbKey, dbValue, null);
			}
		    }
		}
		catch(com.sleepycat.db.DeadlockException dx)
		{
		    dead();
		    DeadlockException ex = new DeadlockException();
		    ex.initCause(dx);
		    ex.message = _errorPrefix + "Dbc.get: " + dx.getMessage();
		    throw ex;
		}
		catch(com.sleepycat.db.DatabaseException dx)
		{
		    dead();
		    DatabaseException ex = new DatabaseException();
		    ex.initCause(dx);
		    ex.message = _errorPrefix + "Dbc.get: " + dx.getMessage();
		    throw ex;
		}
		
		if(status == com.sleepycat.db.OperationStatus.SUCCESS)
		{
		    //
		    // Verify it's < _toKey
		    //
		    boolean inRange = true;
		    if(_toKey != null)
		    {
			if(_index != null)
			{
			    inRange = _index.compare(dbIKey.getData(), _toKey) < 0;
			}
			else
			{
			    inRange = _comparator.compare(dbKey.getData(), _toKey) < 0;
			}
		    }
		    
		    if(inRange)
		    {
			_current = new Entry(this, Map.this, _connection.communicator(), dbKey, 
					     dbValue.getData(), dbIKey.getData());
			return true;
		    }
		}
		return false;
	    }
	    else
	    {
		return true;
	    }
        }
	
        public Object
        next()
        {
	    if(hasNext())
	    {
		_lastReturned = _current;
		return _lastReturned;
	    }
	    else
	    {
		throw new java.util.NoSuchElementException();
	    }
        }

        public void
        remove()
        {
	    if(_txn != null)
	    {
		closeAllIteratorsExcept(this, false);
	    }

	    //
	    // Removes the last object returned by next()
	    //
	    if(_lastReturned == null)
	    {
		throw new IllegalStateException();
	    }
	    
	    if(_lastReturned == _current)
	    {
		try
		{
		    if(_cursor.delete() == com.sleepycat.db.OperationStatus.KEYEMPTY)
		    {
			throw new IllegalStateException();
		    }
		}
		catch(com.sleepycat.db.DeadlockException e)
		{
		    dead();
		    DeadlockException ex = new DeadlockException();
		    ex.initCause(e);
		    ex.message = _errorPrefix + "Dbc.del: " + e.getMessage();
		    throw ex;
		}
		catch(com.sleepycat.db.DatabaseException e)
		{ 
		    DatabaseException ex = new DatabaseException();
		    ex.initCause(e);
		    ex.message = _errorPrefix + "Dbc.del: " + e.getMessage();
		    throw ex;
		}
	    }
	    else
	    {
		//
		// Duplicate the cursor and move the _lastReturned 
		// element to delete it (using the duplicate)
		//
		
		//
		// This works only for non-index iterators
		//
		if(_index != null)
		{
		    throw new UnsupportedOperationException();
		}

		com.sleepycat.db.Cursor clone = null;

		try
		{
		    clone = _cursor.dup(true);

		    //
		    // Not interested in data
		    //
		    com.sleepycat.db.DatabaseEntry dbValue = new com.sleepycat.db.DatabaseEntry();
		    dbValue.setPartial(true);
		    
		    com.sleepycat.db.OperationStatus rc = clone.getSearchKey(_lastReturned.getDbKey(), dbValue, null);

		    if(rc == com.sleepycat.db.OperationStatus.NOTFOUND)
		    {
			throw new IllegalStateException();
		    }
		    if(clone.delete() == com.sleepycat.db.OperationStatus.KEYEMPTY)
		    {
			throw new IllegalStateException();
		    }
		}
		catch(com.sleepycat.db.DeadlockException e)
		{
		    dead();
		    DeadlockException ex = new DeadlockException();
		    ex.initCause(e);
		    ex.message = _errorPrefix + "EntryIterator.remove: " + e.getMessage();
		    throw ex;
		}
		catch(com.sleepycat.db.DatabaseException e)
		{ 
		    DatabaseException ex = new DatabaseException();
		    ex.initCause(e);
		    ex.message = _errorPrefix + "EntryIterator.remove: " + e.getMessage();
		    throw ex;
		}
		finally
		{
		    if(clone != null)
		    {
			closeCursor(clone);
		    }
		}
	    }
        }

        //
        // Extra operations.
        //
        public void
        close()
        {
	    close(false);
	}

	//
	// The synchronized is needed because this method can be called 
	// concurrently by Connection, Map and Map.EntryIterator finalizers.
	//
	synchronized void
	close(boolean finalizing)
	{
	    if(finalizing && (_cursor != null || _txn != null) && _connection.closeInFinalizeWarning())
	    {
		_connection.communicator().getLogger().warning(
		    "finalize() closing a live iterator on Map \"" + _db.dbName() + "\"; the application " +
		     "should have closed it earlier by calling Map.EntryIterator.close(), " +
		     "Map.closeAllIterators(), Map.close(), Connection.close(), or (if also " +
		     "leaking a transaction) Transaction.commit() or Transaction.rollback()");
	    }
	 
	    if(_iteratorListToken != null)
	    {
		synchronized(_iteratorList)
		{
		    _iteratorListToken.remove();
		    _iteratorListToken = null;
		}
	    }
   
	    if(_cursor != null)	
	    {
		com.sleepycat.db.Cursor cursor = _cursor;
		_cursor = null;
		closeCursor(cursor);
	    }
	    
	    if(_txn != null)
	    {
		String txnId = null;

		try
		{
		    if(_connection.txTrace() >= 1)
		    {
			txnId = Long.toHexString((_txn.getId() & 0x7FFFFFFF) + 0x80000000L); 
		    }

		    _txn.commit();

		    if(_connection.txTrace() >= 1)
		    {
			_connection.communicator().getLogger().trace("Freeze.Map", _errorPrefix +
								     "committed transaction " + txnId);
		    }
		}
		catch(com.sleepycat.db.DeadlockException e)
		{
		    if(_connection.txTrace() >= 1)
		    {
			_connection.communicator().getLogger().trace("Freeze.Map", _errorPrefix +
								     "failed to commit transaction " + txnId + ": " +
								     e.getMessage());
		    }

		    DeadlockException ex = new DeadlockException();
		    ex.initCause(e);
		    ex.message = _errorPrefix + "DbTxn.commit: " + e.getMessage();
		    throw ex;
		}
		catch(com.sleepycat.db.DatabaseException e)
		{
		    if(_connection.txTrace() >= 1)
		    {
			_connection.communicator().getLogger().trace("Freeze.Map", _errorPrefix +
								     "failed to commit transaction " + txnId + ": " +
								     e.getMessage());
		    }

		    DatabaseException ex = new DatabaseException();
		    ex.initCause(e);
		    ex.message = _errorPrefix + "DbTxn.commit: " + e.getMessage();
		    throw ex;
		}
		finally
		{
		    _txn = null;
		}
	    }   
	}
	
	//
	// An alias for close()
	//
	public void
	destroy()
	{
	    close();
	}

	protected void
        finalize()
        {
            close(true);
        }

	void
	setValue(Map.Entry entry, Object value)
	{
	    if(_index != null)
	    {
		throw new UnsupportedOperationException(
		    _errorPrefix + "Cannot set an iterator retrieved through an index");
	    }

	    if(_txn != null)
	    {
		closeAllIteratorsExcept(this, false);
	    }

	    //
	    // Are we trying to update the current value?
	    //
	    if(_current == entry)
	    {
		//
		// Yes, update it directly
		//
		byte[] v = encodeValue(value, _connection.communicator());
		com.sleepycat.db.DatabaseEntry dbValue = new com.sleepycat.db.DatabaseEntry(v);
		
		try
		{
		    _cursor.putCurrent(dbValue);
		}
		catch(com.sleepycat.db.DeadlockException e)
		{
		    dead();
		    DeadlockException ex = new DeadlockException();
		    ex.initCause(e);
		    ex.message = _errorPrefix + "Dbc.put: " + e.getMessage();
		    throw ex;
		}
		catch(com.sleepycat.db.DatabaseException e)
		{ 
		    DatabaseException ex = new DatabaseException();
		    ex.initCause(e);
		    ex.message = _errorPrefix + "Dbc.put: " + e.getMessage();
		    throw ex;
		}
	    }
	    else
	    {
		//
		// Duplicate the cursor and move the entry
		// element to update it (using the duplicate cursor)
		//
		
		com.sleepycat.db.Cursor clone = null;

		try
		{
		    clone = _cursor.dup(true);

		    //
		    // Not interested in data
		    //
		    com.sleepycat.db.DatabaseEntry dummy = new com.sleepycat.db.DatabaseEntry();
		    dummy.setPartial(true);
		    
		    com.sleepycat.db.OperationStatus rc = clone.getSearchKey(entry.getDbKey(), dummy, null);

		    if(rc == com.sleepycat.db.OperationStatus.NOTFOUND)
		    {
			NotFoundException ex = new NotFoundException();
			ex.message = _errorPrefix + "Dbc.get: DB_NOTFOUND";
			throw ex;
		    }
		   
		    byte[] v = encodeValue(value, _connection.communicator());
		    com.sleepycat.db.DatabaseEntry dbValue = new com.sleepycat.db.DatabaseEntry(v);
		    clone.putCurrent(dbValue);
		}
		catch(com.sleepycat.db.DeadlockException e)
		{
		    dead();
		    DeadlockException ex = new DeadlockException();
		    ex.initCause(e);
		    ex.message = _errorPrefix + "EntryIterator.setValue: " + e.getMessage();
		    throw ex;
		}
		catch(com.sleepycat.db.DatabaseException e)
		{ 
		    DatabaseException ex = new DatabaseException();
		    ex.initCause(e);
		    ex.message = _errorPrefix + "EntryIterator.setValue: " + e.getMessage();
		    throw ex;
		}
		finally
		{
		    if(clone != null)
		    {
			closeCursor(clone);
		    }
		}
	    }
	}

	private void
	closeCursor(com.sleepycat.db.Cursor cursor)
	{
	    try
	    {
		cursor.close();
	    }
	    catch(com.sleepycat.db.DeadlockException e)
	    {
		dead();
		DeadlockException ex = new DeadlockException();
		ex.initCause(e);
		ex.message = _errorPrefix + "Dbc.close: " + e.getMessage();
		throw ex;
	    }
	    catch(com.sleepycat.db.DatabaseException e)
	    { 
		DatabaseException ex = new DatabaseException();
		ex.initCause(e);
		ex.message = _errorPrefix + "Dbc.close: " + e.getMessage();
		throw ex;
	    }
	}

	private void
	dead()
	{
	    if(_cursor != null)	
	    {
		com.sleepycat.db.Cursor cursor = _cursor;
		_cursor = null;
		closeCursor(cursor);
	    }

	    if(_txn != null)
	    {
		String txnId = null;

		try
		{
		    if(_connection.txTrace() >= 1)
		    {
			txnId = Long.toHexString((_txn.getId() & 0x7FFFFFFF) + 0x80000000L); 
		    }

		    _txn.abort();

		    if(_connection.txTrace() >= 1)
		    {
			_connection.communicator().getLogger().trace("Freeze.Map", _errorPrefix +
								     "rolled back transaction " + txnId);
		    }
		}
		catch(com.sleepycat.db.DeadlockException e)
		{
		    if(_connection.txTrace() >= 1)
		    {
			_connection.communicator().getLogger().trace("Freeze.Map", _errorPrefix +
								     "failed to roll back transaction " + txnId +
								     ": " + e.getMessage());
		    }

		    DeadlockException ex = new DeadlockException();
		    ex.initCause(e);
		    ex.message = _errorPrefix + "DbTxn.abort: " + e.getMessage();
		    throw ex;
		}
		catch(com.sleepycat.db.DatabaseException e)
		{
		    if(_connection.txTrace() >= 1)
		    {
			_connection.communicator().getLogger().trace("Freeze.Map", _errorPrefix +
								     "failed to roll back transaction " +
								     txnId + ": " + e.getMessage());
		    }

		    DatabaseException ex = new DatabaseException();
		    ex.initCause(e);
		    ex.message = _errorPrefix + "DbTxn.abort: " + e.getMessage();
		    throw ex;
		}
		finally
		{
		    _txn = null;
		}
	    }
	}

	private com.sleepycat.db.Transaction _txn;
        private com.sleepycat.db.Cursor _cursor;
        private Entry _current;
        private Entry _lastReturned;
	private java.util.Iterator _iteratorListToken;
	
	private final Index _index;
	private final byte[] _fromKey;
	private final byte[] _toKey;
	private final boolean _onlyFromKeyDups;
	private final boolean _skipDups;
    }

    static class Entry implements java.util.Map.Entry 
    {
        public
        Entry(EntryIteratorImpl iterator, Map map, Ice.Communicator communicator, 
	      com.sleepycat.db.DatabaseEntry dbKey, byte[] valueBytes, byte[] indexBytes)
        {
            _iterator = iterator;
	    _map = map;
	    _communicator = communicator;
            _dbKey = dbKey;
	    _valueBytes = valueBytes;
	    _indexBytes = indexBytes;
        }

	public Object
	getKey()
	{
            if(!_haveKey)
            {
                assert(_dbKey != null);
                _key = _map.decodeKey(_dbKey.getData(), _communicator);
                _haveKey = true;
            }
	    return _key;
	}

	public Object
	getValue()
	{
            if(!_haveValue)
            {
                assert(_valueBytes != null);
                _value = _map.decodeValue(_valueBytes, _communicator);
                _haveValue = true;
		//
		// Not needed anymore
		//
		_valueBytes = null;
            }
	    return _value;
	}

	public byte[]
	getIndexBytes()
	{
	    return _indexBytes;
	}

	public Object
	setValue(Object value)
	{
	    Object old = getValue();
	    _iterator.setValue(this, value);
	    _value = value;
            _haveValue = true;
	    return old;
	}

	public boolean
	equals(Object o)
	{
	    if(!(o instanceof Map.Entry))
	    {
		return false;
	    }
	    Map.Entry e = (Map.Entry)o;
	    return eq(getKey(), e.getKey()) && eq(getValue(), e.getValue());
	}

	public int
	hashCode()
	{
	    return ((getKey()   == null) ? 0 : getKey().hashCode()) ^
	           ((getValue() == null) ? 0 : getValue().hashCode());
	}

	public String
	toString()
	{
	    return getKey() + "=" + getValue();
	}

	com.sleepycat.db.DatabaseEntry
        getDbKey()
	{
	    return _dbKey;
	}

	private /*static*/ boolean
	eq(Object o1, Object o2)
	{
	    return (o1 == null ? o2 == null : o1.equals(o2));
	}

	private EntryIteratorImpl _iterator;
	private Map _map;
	private Ice.Communicator _communicator;
	private com.sleepycat.db.DatabaseEntry _dbKey;
	private byte[] _valueBytes;
	private byte[] _indexBytes;
	private Object _key;
        private boolean _haveKey = false;
	private Object _value;
        private boolean _haveValue = false;
    }

    public static class Patcher implements IceInternal.Patcher
    {
        public
        Patcher(String type)
        {
            this.type = type;
        }

        public void
        patch(Ice.Object v)
        {
            value = v;
        }

        public String
        type()
        {
            return this.type;
        }

        public Ice.Object
        value()
        {
            return this.value;
        }

        public String type;
        public Ice.Object value;
    }
    
    protected ConnectionI _connection;
    private final Comparator _comparator;

    protected java.util.Iterator _token;
    protected SharedDb _db;
    protected String _errorPrefix;
    protected int _trace;

    private java.util.Set _entrySet;
    private LinkedList _iteratorList = new LinkedList();
    private java.util.Map _indexMap = new java.util.HashMap();
}
