package org.maachang.bdb ;

import java.io.IOException;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import com.sleepycat.je.Cursor;
import com.sleepycat.je.CursorConfig;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseConfig;
import com.sleepycat.je.Environment;
import com.sleepycat.je.LockMode;
import com.sleepycat.je.OperationStatus;

/**
 * Berkeley DB wrapper実装.
 *  
 * @version 2010/01/02
 * @author  masahito suzuki
 * @since   bdb 1.0.0
 */
class BdbImpl implements Bdb {
    
    /** カーソルコンフィグ **/
    protected static final CursorConfig CURSOR_CONF = new CursorConfig().setReadUncommitted( true ) ;
    
    /** １キーで重複を禁止. **/
    private static final boolean _NOT_SORT_KEY = true ;
    
    /** BdbManager. **/
    private final AtomicOBJ<BdbManager> man = new AtomicOBJ<BdbManager>() ;
    
    /** Bdb-database. **/
    private final AtomicOBJ<Database> db = new AtomicOBJ<Database>() ;
    
    /** Bdb登録名 **/
    private final AtomicOBJ<String> name = new AtomicOBJ<String>() ;
    
    /** クローズフラグ **/
    private final AtomicBL closeFlag = new AtomicBL( true ) ;
    
    /** 読み込み／書き込み用ロック. **/
    private final ReentrantReadWriteLock sync = new ReentrantReadWriteLock() ;
    private final Lock rsync = sync.readLock() ;
    private final Lock wsync = sync.writeLock() ;
    
    /** コンストラクタ. **/
    private BdbImpl(){}
    
    /**
     * コンストラクタ.
     * @param mode [true]の場合、Bdbマネージャに条件を追加しません.
     * @param bdbName 対象名を設定します.
     * @param manager BdbManagerを設定します.
     * @exception Exception 例外.
     */
    protected BdbImpl( boolean mode,String bdbName,BdbManager manager ) throws Exception {
        if( bdbName == null || ( bdbName = bdbName.trim() ).length() <= 0 || manager == null ) {
            throw new IllegalArgumentException( "引数は不正です" ) ;
        }
        if( manager.isDestroy() ) {
            throw new IOException( "BdbManagerは既にクローズしています" ) ;
        }
        Database db = null ;
        try {
            Environment env = manager.env.get() ;
            DatabaseConfig dbConfig = new DatabaseConfig() ;
            dbConfig.setTransactional(false) ;// トランザクションなし.
            dbConfig.setAllowCreate(true) ;// 初期起動.
            dbConfig.setSortedDuplicates(!_NOT_SORT_KEY) ;// falseの場合は、１キーで重複させない.
            dbConfig.setDeferredWrite( BdbManager._DEFERRED_WRITE ) ;// 遅延書き込み.
            db = env.openDatabase(null,bdbName,dbConfig) ;
            this.db.set( db ) ;
            this.man.set( manager ) ;
            this.name.set( bdbName ) ;
            this.closeFlag.set( false ) ;
            if( !mode ) {
                manager.manager.put( bdbName,this ) ;
            }
        } catch( Exception e ) {
            if( db != null ) {
                try {
                    db.close() ;
                } catch( Exception ee ) {
                }
            }
            throw e ;
        }
    }
    
    /**
     * オブジェクトをクローズ.
     * @exception Exception 例外.
     */
    public void close() throws Exception {
        if( !this.closeFlag.get() ) {
            this.closeFlag.set( true ) ;
            Database db = this.db.get() ;
            this.db.set( null ) ;
            String name = this.name.get() ;
            this.name.set( null ) ;
            BdbManager man = this.man.get() ;
            this.man.set( null ) ;
            wsync.lock() ;
            try {
                try {
                    db.close() ;
                } catch( Exception e ) {
                }
                if( name != null && man != null && !man.isDestroy() ) {
                    man.manager.remove( name ) ;
                }
            } finally {
                wsync.unlock() ;
            }
        }
    }
    
    /**
     * Berkeley DB データベースオブジェクトを取得.
     * @return Database Berkeley DB データベースオブジェクトが返されます.
     * @exception Exception 例外.
     */
    public Database database() throws Exception {
        if( this.closeFlag.get() ) {
            throw new IOException( "オブジェクトは既にクローズしています" ) ;
        }
        return this.db.get() ;
    }
    
    /**
     * データ追加.
     * @param o 追加条件を設定します.
     * @exception Exception 例外.
     */
    public void put( BdbKeyValue o ) throws Exception {
        if( o == null || !o.isKeyValue() ) {
            throw new IllegalArgumentException( "引数は不正です" ) ;
        }
        if( this.closeFlag.get() ) {
            throw new IOException( "オブジェクトは既にクローズしています" ) ;
        }
        OperationStatus state ;
        Database db = this.db.get() ;
        wsync.lock() ;
        try {
            state = db.put( null,o.keyEntry(),o.valueEntry() ) ;
        } finally {
            wsync.unlock() ;
        }
        if( OperationStatus.SUCCESS != state ) {
            throw new IOException( "データ追加に失敗しました:" + state ) ;
        }
    }
    
    protected void putNoLock( BdbKeyValue o ) throws Exception {
        if( o == null || !o.isKeyValue() ) {
            throw new IllegalArgumentException( "引数は不正です" ) ;
        }
        if( this.closeFlag.get() ) {
            throw new IOException( "オブジェクトは既にクローズしています" ) ;
        }
        OperationStatus state ;
        Database db = this.db.get() ;
        state = db.put( null,o.keyEntry(),o.valueEntry() ) ;
        if( OperationStatus.SUCCESS != state ) {
            throw new IOException( "データ追加に失敗しました:" + state ) ;
        }
    }
    
    /**
     * データ部分書き換え.
     * @param o 追加条件を設定します.
     * @param offset 対象のオフセットを設定します.
     * @param length 対象の長さを設定します.
     * @exception Exception 例外.
     */
    public void put( BdbKeyValue o,int offset,int length ) throws Exception {
        if( o == null || !o.isKeyValue() ) {
            throw new IllegalArgumentException( "引数は不正です" ) ;
        }
        if( this.closeFlag.get() ) {
            throw new IOException( "オブジェクトは既にクローズしています" ) ;
        }
        if( offset < 0 || length <= 0 ) {
            throw new IOException( "オフセット値、長さが不正です" ) ;
        }
        OperationStatus state ;
        Database db = this.db.get() ;
        o.valueEntry().setPartial( offset,length,true ) ;
        wsync.lock() ;
        try {
            state = db.put( null,o.keyEntry(),o.valueEntry() ) ;
        } finally {
            wsync.unlock() ;
            o.valueEntry().setPartial( 0,0,false ) ;
        }
        if( OperationStatus.SUCCESS != state ) {
            throw new IOException( "データ追加に失敗しました:" + state ) ;
        }
    }
    
    protected void putNoLock( BdbKeyValue o,int offset,int length ) throws Exception {
        if( o == null || !o.isKeyValue() ) {
            throw new IllegalArgumentException( "引数は不正です" ) ;
        }
        if( this.closeFlag.get() ) {
            throw new IOException( "オブジェクトは既にクローズしています" ) ;
        }
        if( offset < 0 || length <= 0 ) {
            throw new IOException( "オフセット値、長さが不正です" ) ;
        }
        OperationStatus state ;
        Database db = this.db.get() ;
        o.valueEntry().setPartial( offset,length,true ) ;
        try {
            state = db.put( null,o.keyEntry(),o.valueEntry() ) ;
        } finally {
            o.valueEntry().setPartial( 0,0,false ) ;
        }
        if( OperationStatus.SUCCESS != state ) {
            throw new IOException( "データ追加に失敗しました:" + state ) ;
        }
    }
    
    /**
     * データ削除.
     * @param o 削除条件を設定します.
     * @return boolean [true]の場合、データが削除できました.
     * @exception Exception 例外.
     */
    public boolean remove( BdbKeyValue o ) throws Exception {
        if( o == null || !o.isKey() ) {
            throw new IllegalArgumentException( "引数は不正です" ) ;
        }
        if( this.closeFlag.get() ) {
            throw new IOException( "オブジェクトは既にクローズしています" ) ;
        }
        OperationStatus state ;
        Database db = this.db.get() ;
        wsync.lock() ;
        try {
            state = db.delete( null,o.keyEntry() ) ;
        } finally {
            wsync.unlock() ;
        }
        if( OperationStatus.SUCCESS != state ) {
            return false ;
        }
        return true ;
    }
    
    protected boolean removeNoLock( BdbKeyValue o ) throws Exception {
        if( o == null || !o.isKey() ) {
            throw new IllegalArgumentException( "引数は不正です" ) ;
        }
        if( this.closeFlag.get() ) {
            throw new IOException( "オブジェクトは既にクローズしています" ) ;
        }
        OperationStatus state ;
        Database db = this.db.get() ;
        state = db.delete( null,o.keyEntry() ) ;
        if( OperationStatus.SUCCESS != state ) {
            return false ;
        }
        return true ;
    }
    
    /**
     * データ取得.
     * @param o 取得条件を設定します.
     * @return boolean [true]の場合、データが取得できました.
     * @exception Exception 例外.
     */
    public boolean get( BdbKeyValue o ) throws Exception {
        if( o == null || !o.isKey() ) {
            throw new IllegalArgumentException( "引数は不正です" ) ;
        }
        if( this.closeFlag.get() ) {
            throw new IOException( "オブジェクトは既にクローズしています" ) ;
        }
        OperationStatus state ;
        Database db = this.db.get() ;
        o.setValue( null ) ;
        rsync.lock() ;
        try {
            state = db.get( null,o.keyEntry(),o.valueEntry(),LockMode.READ_UNCOMMITTED ) ;
        } finally {
            rsync.unlock() ;
        }
        if( OperationStatus.SUCCESS != state ) {
            return false ;
        }
        return true ;
    }
    
    protected boolean getNoLock( BdbKeyValue o ) throws Exception {
        if( o == null || !o.isKey() ) {
            throw new IllegalArgumentException( "引数は不正です" ) ;
        }
        if( this.closeFlag.get() ) {
            throw new IOException( "オブジェクトは既にクローズしています" ) ;
        }
        OperationStatus state ;
        Database db = this.db.get() ;
        o.setValue( null ) ;
        state = db.get( null,o.keyEntry(),o.valueEntry(),LockMode.READ_UNCOMMITTED ) ;
        if( OperationStatus.SUCCESS != state ) {
            return false ;
        }
        return true ;
    }
    
    /**
     * データ部分取得.
     * @param o 取得条件を設定します.
     * @param offset 対象のオフセットを設定します.
     * @param length 対象の長さを設定します.
     * @return boolean [true]の場合、データが取得できました.
     * @exception Exception 例外.
     */
    public boolean get( BdbKeyValue o,int offset,int length ) throws Exception {
        if( o == null || !o.isKey() ) {
            throw new IllegalArgumentException( "引数は不正です" ) ;
        }
        if( offset < 0 || length <= 0 ) {
            return false ;
        }
        if( this.closeFlag.get() ) {
            throw new IOException( "オブジェクトは既にクローズしています" ) ;
        }
        OperationStatus state ;
        Database db = this.db.get() ;
        o.setValue( null ) ;
        o.valueEntry().setPartial( offset,length,true ) ;
        rsync.lock() ;
        try {
            state = db.get( null,o.keyEntry(),o.valueEntry(),LockMode.READ_UNCOMMITTED ) ;
        } finally {
            rsync.unlock() ;
            o.valueEntry().setPartial( 0,0,false ) ;
        }
        if( OperationStatus.SUCCESS != state ) {
            return false ;
        }
        return true ;
    }
    
    protected boolean getNoLock( BdbKeyValue o,int offset,int length ) throws Exception {
        if( o == null || !o.isKey() ) {
            throw new IllegalArgumentException( "引数は不正です" ) ;
        }
        if( offset < 0 || length <= 0 ) {
            return false ;
        }
        if( this.closeFlag.get() ) {
            throw new IOException( "オブジェクトは既にクローズしています" ) ;
        }
        OperationStatus state ;
        Database db = this.db.get() ;
        o.setValue( null ) ;
        o.valueEntry().setPartial( offset,length,true ) ;
        try {
            state = db.get( null,o.keyEntry(),o.valueEntry(),LockMode.READ_UNCOMMITTED ) ;
        } finally {
            o.valueEntry().setPartial( 0,0,false ) ;
        }
        if( OperationStatus.SUCCESS != state ) {
            return false ;
        }
        return true ;
    }
    
    /**
     * 情報を取得して削除.
     * @param o 取得条件を設定します.
     * @return boolean [true]の場合、正常に処理されました.
     * @exception Exception 例外.
     */
    public boolean getToRemove( BdbKeyValue o ) throws Exception {
        if( o == null || !o.isKey() ) {
            throw new IllegalArgumentException( "引数は不正です" ) ;
        }
        if( this.closeFlag.get() ) {
            throw new IOException( "オブジェクトは既にクローズしています" ) ;
        }
        Database db = this.db.get() ;
        Cursor cursor = null ;
        wsync.lock() ;
        try {
            cursor = db.openCursor( null,CURSOR_CONF ) ;
            OperationStatus state ;
            state = cursor.getSearchKey( o.keyEntry(),o.valueEntry(),LockMode.READ_UNCOMMITTED ) ;
            if( OperationStatus.SUCCESS != state ) {
                return false ;
            }
            cursor.delete() ;
        } finally {
            if( cursor != null ) {
                try {
                    cursor.close() ;
                } catch( Exception e ) {
                }
            }
            wsync.unlock() ;
        }
        return true ;
    }
    
    protected boolean getToRemoveNoLock( BdbKeyValue o ) throws Exception {
        if( o == null || !o.isKey() ) {
            throw new IllegalArgumentException( "引数は不正です" ) ;
        }
        if( this.closeFlag.get() ) {
            throw new IOException( "オブジェクトは既にクローズしています" ) ;
        }
        Database db = this.db.get() ;
        Cursor cursor = null ;
        try {
            cursor = db.openCursor( null,CURSOR_CONF ) ;
            OperationStatus state ;
            state = cursor.getSearchKey( o.keyEntry(),o.valueEntry(),LockMode.READ_UNCOMMITTED ) ;
            if( OperationStatus.SUCCESS != state ) {
                return false ;
            }
            cursor.delete() ;
        } finally {
            if( cursor != null ) {
                try {
                    cursor.close() ;
                } catch( Exception e ) {
                }
            }
        }
        return true ;
    }
    
    /**
     * データ存在確認.
     * @param o チェック条件を設定します.
     * @return boolean [true]の場合、キー情報は存在します.
     * @exception Exception 例外.
     */
    public boolean containsKey( BdbKeyValue o ) throws Exception {
        if( o == null || !o.isKey() ) {
            throw new IllegalArgumentException( "引数は不正です" ) ;
        }
        if( this.closeFlag.get() ) {
            throw new IOException( "オブジェクトは既にクローズしています" ) ;
        }
        OperationStatus state ;
        Database db = this.db.get() ;
        o.valueEntry().setPartial( 0,0,true ) ;
        rsync.lock() ;
        try {
            state = db.get( null,o.keyEntry(),o.valueEntry(),LockMode.READ_UNCOMMITTED ) ;
        } finally {
            rsync.unlock() ;
        }
        o.setValue( null ) ;
        if( OperationStatus.SUCCESS != state ) {
            return false ;
        }
        return true ;
    }
    
    protected boolean containsKeyNoLock( BdbKeyValue o ) throws Exception {
        if( o == null || !o.isKey() ) {
            throw new IllegalArgumentException( "引数は不正です" ) ;
        }
        if( this.closeFlag.get() ) {
            throw new IOException( "オブジェクトは既にクローズしています" ) ;
        }
        OperationStatus state ;
        Database db = this.db.get() ;
        o.valueEntry().setPartial( 0,0,true ) ;
        state = db.get( null,o.keyEntry(),o.valueEntry(),LockMode.READ_UNCOMMITTED ) ;
        o.setValue( null ) ;
        if( OperationStatus.SUCCESS != state ) {
            return false ;
        }
        return true ;
    }
    
    /**
     * キーイテレータを取得.
     * @return Iterator<BdbKeyValue> キーイテレータが返されます.
     * @exception Exception 例外.
     */
    public Iterator<byte[]> keys() throws Exception {
        return keys( null ) ;
    }
    
    /**
     * キーイテレータを取得.
     * @param o 取得に対するオブジェクトを設定します.
     * @return Iterator<BdbKeyValue> キーイテレータが返されます.
     * @exception Exception 例外.
     */
    public Iterator<byte[]> keys( BdbKeyValue o ) throws Exception {
        if( this.closeFlag.get() ) {
            throw new IOException( "オブジェクトは既にクローズしています" ) ;
        }
        return new BdbKeyIterator( this.rsync,this.wsync,this.db.get(),o ) ;
    }
    
    /**
     * キー要素イテレータを取得.
     * @return Iterator<BdbKeyValue> キーイテレータが返されます.
     * @exception Exception 例外.
     */
    public Iterator<BdbKeyValue> keyValues() throws Exception {
        return keyValues( null ) ;
    }
    
    /**
     * キー要素イテレータを取得.
     * @param o 取得に対するオブジェクトを設定します.
     * @return Iterator<BdbKeyValue> キーイテレータが返されます.
     * @exception Exception 例外.
     */
    public Iterator<BdbKeyValue> keyValues( BdbKeyValue o ) throws Exception {
        if( this.closeFlag.get() ) {
            throw new IOException( "オブジェクトは既にクローズしています" ) ;
        }
        return new BdbKeyValueIterator( this.rsync,this.wsync,this.db.get(),o ) ;
    }
    
    /**
     * データ数を取得.
     * @return long データ数が返されます.
     * @exception Exception 例外.
     */
    public long size() throws Exception {
        if( this.closeFlag.get() ) {
            throw new IOException( "オブジェクトは既にクローズしています" ) ;
        }
        long ret ;
        Database db = this.db.get() ;
        rsync.lock() ;
        try {
            ret = db.count() ;
        } finally {
            rsync.unlock() ;
        }
        return ret ;
    }
    
    protected long sizeNoLock() throws Exception {
        if( this.closeFlag.get() ) {
            throw new IOException( "オブジェクトは既にクローズしています" ) ;
        }
        long ret ;
        Database db = this.db.get() ;
        ret = db.count() ;
        return ret ;
    }
    
    /**
     * オープン名を取得.
     * @return String オープン名が返されます.
     * @exception Exception 例外.
     */
    public String getName() throws Exception {
        if( this.closeFlag.get() ) {
            throw new IOException( "オブジェクトは既にクローズしています" ) ;
        }
        return this.name.get() ;
    }
    
    /**
     * 読み込み用同期オブジェクトを取得.
     * @return Lock 読み込み用同期オブジェクトが返されます.
     */
    public Lock getRSync() {
        return rsync ;
    }
    
    /**
     * 書き込み用同期オブジェクトを取得.
     * @return Lock 書き込み用同期オブジェクトが返されます.
     */
    public Lock getWSync() {
        return wsync ;
    }
    
    /**
     * BdbKeyValueオブジェクトを取得.
     * <p>※このオブジェクトは、ThreadLocalから取得されているので、Threadセーフです.</p>
     * @return BdbKeyValue BdbKeyValueオブジェクトが返されます.
     */
    public BdbKeyValue getBdbKeyValue() {
        return BdbManager.getBdbKeyValue() ;
    }
    
    /**
     * オブジェクトがクローズされているかチェック.
     * @return boolean [true]の場合、クローズされています.
     */
    public boolean isClose() {
        return closeFlag.get() ;
    }
    
}

/** キーイテレータ. **/
class BdbKeyIterator implements Iterator<byte[]> {
    private static final CursorConfig CONF = BdbImpl.CURSOR_CONF ;
    private Cursor cursor = null ;
    private BdbKeyValue kv = null ;
    private Lock rsync = null ;
    private Lock wsync = null ;
    private boolean flg = false ;
    
    protected BdbKeyIterator( Lock rsync,Lock wsync,Database db ) throws Exception {
        this( rsync,wsync,db,null ) ;
    }
    
    protected BdbKeyIterator( Lock rsync,Lock wsync,Database db,BdbKeyValue o ) throws Exception {
        this.cursor = db.openCursor(null, CONF) ;
        if( o != null ) {
            this.kv = o ;
        }
        else {
            this.kv = new BdbKeyValue() ;
        }
        this.rsync = rsync ;
        this.wsync = wsync ;
    }
    
    protected void finalize() throws Exception {
        close() ;
    }
    
    public void close() {
        if( cursor != null ) {
            rsync.lock() ;
            try {
                cursor.close() ;
            } catch( Exception e ) {
            } finally {
                rsync.unlock() ;
            }
        }
        if( kv != null ) {
            kv.clear() ;
        }
        cursor = null ;
        kv = null ;
        flg = false ;
    }
    
    public boolean hasNext() {
        if( cursor != null ) {
            OperationStatus state ;
            kv.clear() ;
            kv.valueEntry().setPartial( 0,0,true ) ;
            rsync.lock() ;
            try {
                state = cursor.getNext( kv.keyEntry(),kv.valueEntry(),LockMode.READ_UNCOMMITTED ) ;
            } catch( Exception e ) {
                close() ;
                return false ;
            } finally {
                rsync.unlock() ;
            }
            if( OperationStatus.SUCCESS != state ) {
                close() ;
                return false ;
            }
            flg = true ;
            return true ;
        }
        return false ;
    }
    
    public byte[] next() {
        if( cursor != null ) {
            if( flg == false ) {
                OperationStatus state ;
                kv.clear() ;
                kv.valueEntry().setPartial( 0,0,true ) ;
                rsync.lock() ;
                try {
                    state = cursor.getNext( kv.keyEntry(),kv.valueEntry(),LockMode.READ_UNCOMMITTED ) ;
                } catch( Exception e ) {
                    close() ;
                    throw new NoSuchElementException() ;
                } finally {
                    rsync.unlock() ;
                }
                if( OperationStatus.SUCCESS != state ) {
                    close() ;
                    throw new NoSuchElementException() ;
                }
            }
            flg = false ;
            return kv.getKey() ;
        }
        throw new NoSuchElementException() ;
    }
    
    public void remove() {
        if( cursor != null ) {
            wsync.lock() ;
            try {
                cursor.delete() ;
            } catch( Exception e ) {
            } finally {
                wsync.unlock() ;
            }
        }
    }
}

/** キー要素イテレータ. **/
class BdbKeyValueIterator implements Iterator<BdbKeyValue> {
    private static final CursorConfig CONF = BdbImpl.CURSOR_CONF ;
    private Cursor cursor = null ;
    private BdbKeyValue kv = null ;
    private Lock rsync = null ;
    private Lock wsync = null ;
    private boolean flg = false ;
    
    protected BdbKeyValueIterator( Lock rsync,Lock wsync,Database db ) throws Exception {
        this( rsync,wsync,db,null ) ;
    }
    
    protected BdbKeyValueIterator( Lock rsync,Lock wsync,Database db,BdbKeyValue o ) throws Exception {
        this.cursor = db.openCursor(null, CONF) ;
        if( o != null ) {
            this.kv = o ;
        }
        else {
            this.kv = new BdbKeyValue() ;
        }
        this.rsync = rsync ;
        this.wsync = wsync ;
    }
    
    protected void finalize() throws Exception {
        close() ;
    }
    
    public void close() {
        if( cursor != null ) {
            wsync.lock() ;
            try {
                cursor.close() ;
            } catch( Exception e ) {
            } finally {
                wsync.unlock() ;
            }
        }
        if( kv != null ) {
            kv.clear() ;
        }
        cursor = null ;
        kv = null ;
        flg = false ;
    }
    
    public boolean hasNext() {
        if( cursor != null ) {
            OperationStatus state ;
            kv.clear() ;
            rsync.lock() ;
            try {
                state = cursor.getNext( kv.keyEntry(),kv.valueEntry(),LockMode.READ_UNCOMMITTED ) ;
            } catch( Exception e ) {
                close() ;
                return false ;
            } finally {
                rsync.unlock() ;
            }
            if( OperationStatus.SUCCESS != state ) {
                close() ;
                return false ;
            }
            flg = true ;
            return true ;
        }
        return false ;
    }
    
    public BdbKeyValue next() {
        if( cursor != null ) {
            if( flg == false ) {
                OperationStatus state ;
                kv.clear() ;
                rsync.lock() ;
                try {
                    state = cursor.getNext( kv.keyEntry(),kv.valueEntry(),LockMode.READ_UNCOMMITTED ) ;
                } catch( Exception e ) {
                    close() ;
                    throw new NoSuchElementException() ;
                } finally {
                    rsync.unlock() ;
                }
                if( OperationStatus.SUCCESS != state ) {
                    close() ;
                    throw new NoSuchElementException() ;
                }
            }
            flg = false ;
            return kv ;
        }
        throw new NoSuchElementException() ;
    }
    
    public void remove() {
        if( cursor != null ) {
            wsync.lock() ;
            try {
                cursor.delete() ;
            } catch( Exception e ) {
            } finally {
                wsync.unlock() ;
            }
        }
    }
}

