package org.itscool.commons.property;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Hashtable;

/**
 * キーデータ取得クラス<BR>
 * プロパティファイルから特定キーワードに該当する値を取得する。
 * @author 加納
 * @version 1.00A 2001/03/24 新規作成
 * @since jdk1.4.1
 */
public class PropertyFile{
    /** プロパティファイルのコメント行を表すトークン */
    public static final String PROPERTIES_COMMENT_KEY = "#";
    
    /** 検索結果リスト*/
    protected Hashtable dataList;
    private String path;
    private String commentKey;
    
    /**
     * プロパティファイルの初期化を行います
     * @param path プロパティファイル名を含むパス
     * @throws IOException
     */
    public PropertyFile(String path) throws IOException{
        dataList = new Hashtable();
        this.path = path;
        this.commentKey = PROPERTIES_COMMENT_KEY;
        this.load();
    }
    
    /**
     * 初期化データ作成処理
     * @throws Exception, IOException : 検索失敗時、ファイルオープンエラー
     */
    public void load() throws IOException{
        String buff     = commentKey;
        
        FileReader frIn = null;
        try{
            frIn = new FileReader( path );
        }catch( FileNotFoundException fnfe ){
            return;
        }
        
        BufferedReader brIn = new BufferedReader( frIn );
        dataList.clear();
        
        while(buff != null){
            buff = brIn.readLine();
            
            if( buff == null ){
                continue;
            }
            
            if( buff.startsWith( commentKey, 0 ) ){
                continue;
            }
            
            // 検索文字列から"="を検索
            int index = buff.indexOf( "=" );
            if( index == -1 ){
                continue;
            }
            
            // "="以前の文字列を取得
            String key = buff.substring(0,index );
            key = key.trim();
            
            // "="以降の文字列を取得
            String data = buff.substring( index + 1 );
            data = data.trim();
            
            dataList.put(key, data);
        }
        
        frIn.close();
        brIn.close();
    }
    
    /**
     * プロパティ情報を取得します<BR>
     * @param key プロパティ名
     * @return プロパティ情報をString形式で返します
     * @throws IOException
     */
    public String read(String key){
        String data = (String)dataList.get(key);
        return data;
    }
    
    /**
     * 引数dataで指定された文字列情報をkeyで指定されたプロパティに書き込みます
     * @param key プロパティ名
     * @param data プロパティにセットする文字列情報
     * @param append 追加モード。trueが指定された場合は既存のプロパティファイルに追加されます
     * @return プロパティの追加に成功した場合はtrueを返します。このプロパティが既に存在する
     * 場合はfalseを返します
     * @throws IOException
     */
    public boolean write(String key, String data, boolean append)throws IOException{
        if( read(key) != null ){
            //同じキーが存在する場合
            return false;
        }
        if( !append ){
            dataList.clear();
        }
        dataList.put(key, data);
        out(key, data, append);
        return true;
    }
    
    /**
     * 引数dataで指定された文字列情報をkeyで指定されたプロパティに書き込みます
     * @param key プロパティ名
     * @param data プロパティにセットする文字列情報
     * @param append 追加モード。trueが指定された場合は既存のプロパティファイルに追加されます
     * @throws IOException
     */
    protected synchronized void out(String key, String data, boolean append ) throws IOException{
        FileOutputStream out = new FileOutputStream(path, append);
        String line = System.getProperty("line.separator");
        //ファイルの内容を更新
        out.write(key.getBytes());
        out.write((new String(" = ")).getBytes());
        out.write(data.getBytes());
        out.write(line.getBytes());
        out.flush();
        //ファイルを閉じる
        out.close();
        
    }
    
    /**
     * 指定されたプロパティ情報を削除します。
     * @param delKey プロパティ名を指定します
     * @return プロパティの削除に成功した場合はtrueを返します
     * @throws IOException
     */
    public boolean delete(String delKey) throws IOException{
        if( isFile(path) ){
            load();            //ファイルロード
        }else{
            return false;
        }
        if( dataList.get(delKey) != null ){
            dataList.remove(delKey);        //配列から要素を削除
            upFile();        //ファイルを上書き
        }
        return true;
    }
    
    /**
     * このプロパティファイルに登録されているプロパティ情報を
     * 全て削除します
     */
    public void removeAll(){
        if( isFile(path) ){
            delFile(path);
        }
        dataList.clear();
    }
    
    /**
     * BinaryPropertyFileインスタンスをファイルに更新します
     * @throws IOException
     */
    protected void upFile() throws IOException{
        if( dataList.size() == 0 ){
            delFile(this.path);
        }else{
            for( Enumeration enumObj = dataList.keys();
            enumObj.hasMoreElements(); ){
                String key = ( String )enumObj.nextElement();
                String data    = read(key);
                out(key, data, true);
            }
        }
    }
    
    /**
     * 指定されたパスがファイルであるかチェックします
     * @param path ファイルパス
     * @return 指定されたパスがファイルの場合はtrueを返します
     */
    protected boolean isFile(String path){
        File file = new File( path  );
        
        return file.canRead();
        
    }
    
    /**
     * 指定されたファイルを削除します
     * @param path ファイルパス
     */
    protected synchronized void delFile(String path){
        File file = new File( path  );
        file.delete();
    }
    
    /**
     * プロパティの件数を取得します
     * @return プロパティ件数
     */
    public int size(){
        return dataList.size();
    }
    
    /**
     * debug キーとデータのリストを返す。
     * @return String : キーとデータのリスト
     */
    public String toString(){
        String line = System.getProperty("line.separator");
        StringBuffer sb = new StringBuffer();
        for( Enumeration enumObj = dataList.keys();
        enumObj.hasMoreElements(); ){
            String key = ( String )enumObj.nextElement();
            String data    =
                ( String )dataList.get(key);
            sb.append( "[KEY-->" + key + "] [DATA--->" + data );
            sb.append( line);
        }
        return sb.toString();
    }
    
    public Hashtable getDataList() {
        return dataList;
    }
}
