// **********************************************************************
//
// Copyright (c) 2003-2007 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 >= 2)
        {
            _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 >= 2)
        {
            _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 >= 2)
        {
            _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 >= 2)
        {
            _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 >= 2)
        {
            _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();
            
            //
            // When we have a custom-comparison function, Berkeley DB returns
            // the key on-disk (when it finds one). We disable this behavior:
            // (ref Oracle SR 5925672.992)
            //
            dbKey.setPartial(true);

            //
            // 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 >= 2)
            {
                _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.getSearchKey(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();
}
