using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

using CommonMP.HYSSOP.Interface.HSData;
using CommonMP.HYSSOP.CoreImpl.HSData;
using CommonMP.HYSSOP.CoreImpl.HSTools;

namespace CommonMP.HYSSOP.CoreImpl.HSDBA.FileBase
{
    /// <summary>
    /// CSVファイルDB基底クラス
    /// </summary>
    /// <remarks>
    /// <para>CSVファイルを「テーブル」に見立てて各種操作を提供する。</para>
    /// <para>CSVファイルの1行をレコード、カンマで区切られた値をフィールドとして扱い、
    /// 物理ファイルアクセスや基本的な繰り返し処理、排他制御を実装する。</para>
    /// <para>デザインパターン「テンプレートメソッド」のテンプレートクラスに当たる。
    /// 個別のファイルに依存する箇所(フィールドの数、位置、データ内容のチェック)は
    /// 抽象メソッドに分離し、サブクラスに委譲する。</para>
    /// <para>複数のテーブルの関連は取り扱わないため、使用者側で適切に処理すること。</para>
    /// </remarks>
    /// <example>
    /// <para>基本的な使用法：</para>
    /// <para> HySCSVDBABase</para>
    /// <para> HySStockData csStockData;</para>
    /// <para> string csTMDataFilePath;</para>
    /// <para> McTMDataDBAData csTMData = new McTMDataDBAData(csTMDataFilePath);</para>
    /// <para> csTMData.Lock();                // 最初に必ずロックする</para>
    /// <para> csTMData.Load();                // データの内容を参照・変更する場合、必ずロードする</para>
    /// <para> csTMData.Insert(csStockData);   // INSERT,UPDATE,DELETE,SELECTなどを実行する</para>
    /// <para> csTMData.Commit();              // 変更をファイルに反映する場合、コミットする</para>
    /// <para> csTMData.Unlock();              // 最後に必ずアンロックする</para>
    /// </example>
    /// <remarks><para>history:</para>
    /// <para>[CommonMP][ver 1.0.0][2009/06/30][新規作成]</para>
    /// </remarks>
    public abstract class HySCSVDBABase
    {
        /// <summary>
        /// 保存期限区分のDB値: 一時保管モード
        /// </summary>
        public const string PRESERVE_PERIOD_TEMPORARY = "0";
        /// <summary>
        /// 保存期限区分のDB値: 永久保管モード
        /// </summary>
        public const string PRESERVE_PERIOD_ETERNITY = "1";

        /// <summary>
        /// 一時保存ファイルの拡張子
        /// </summary>
        public const string TMP_FILE_EXTENSION = ".tmp";

        /// <summary>
        /// 一時退避用旧ファイルの拡張子
        /// </summary>
        public const string OLD_FILE_EXTENSION = ".old";

        /// <summary>
        /// データファイルの内容
        /// </summary>
        /// <remarks>
        /// <para>ファイル内の文字列形式をそのまま保持する。</para>
        /// <para>検索処理の便宜上、サブクラスに公開するが、リストの更新
        /// (要素の追加・削除)およびデータの変更は行わないこと。</para>
        /// </remarks>
        protected List<string[]> m_csDataFileContents = null;

        /// <summary>
        /// ロックファイル
        /// </summary>
        private HySDBALockFile m_csLockFile = null;

        /// <summary>
        /// データファイルのパス名
        /// </summary>
        private string m_csDataFilePath = null;

        /// <summary>
        /// フィールド比較のデリゲート
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>HySCommonDBA csDBA = csMatchField(csDBVal, csValueToCompare);</para>
        /// </example>
        /// <param name="csDBVal">DBフィールド値</param>
        /// <param name="csValueToCompare">比較対象値</param>
        /// <returns>true:同一値 false:異なる値</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        private delegate bool IsMatchField(string csDBVal, object csValueToCompare);

        /// <summary>
        /// 自クラス名(ログ出力用)
        /// </summary>
        private const string m_csMyClassName = "HySCSVDBABase";

        /// <summary><para>method outline:</para>
        /// <para>デフォルトコンストラクタ</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para> HySCSVDBABase csDBA = new HySCSVDBABase() </para>
        /// </example>
        /// <param name="">無し</param>
        /// <returns>HySCSVDBABase 生成されたクラスのインスタンス</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        protected HySCSVDBABase()
        {
        }

        /// <summary><para>method outline:</para>
        /// <para>データファイルをロックする</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para> bool bRtn = Lock() </para>
        /// </example>
        /// <param name="">無し</param>
        /// <returns>true:成功、false:失敗</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        public virtual bool Lock()
        {
            AssertUnlocked();

            // サブクラスからデータファイルパス名を取得する
            m_csDataFilePath = GetDataFilePath();

            // データファイルをロックする
            HySDBALockFile csLockFile = new HySDBALockFile();
            if (!csLockFile.Lock(m_csDataFilePath, HySDBALockFile.LockMode.FOR_MODIFY))
            {
                return false; // ロック失敗
            }

            m_csLockFile = csLockFile;
            return true;
        }

        /// <summary><para>method outline:</para>
        /// <para>データファイルをロードする</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para> bool bRtn = Load() </para>
        /// </example>
        /// <param name="">無し</param>
        /// <returns>true:ロード成功、false:ロード失敗</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        public virtual bool Load()
        {
            const string csMyMethodName = "Load";

            AssertLocked();

            // サブクラスからデータファイルパス名を取得する
            m_csDataFilePath = GetDataFilePath();

            // 空の内容リストを作成する
            m_csDataFileContents = new List<string[]>();

            // ファイルがまだ作成されていない場合、処理終了
            HySFile csFile = new HySFile(m_csDataFilePath);
            if (!csFile.Exist())
            {
                return true;
            }

            // ファイルオープン
            if (csFile.Open(HySFile.OPEN_MODE.OPEN, HySFile.READ_WRITE_MODE.READ, HySFile.DIRECTORY_MODE.MK_DIR) != 0)
            {
                HySDBALog.WriteOnline(GetCurrentClassName(), csMyMethodName, "cannot open file",
                    "filepath", m_csDataFilePath);

                m_csDataFileContents = null;    // リストを破棄
                return false;   // オープン失敗
            }

            // 内容を読み込む
            try
            {
                int iFieldNum = GetFieldNum();

                HySString csLine = new HySString();
                while (csFile.ReadText(ref csLine) != 0)
                {
                    // 読み込んだ文字列をカンマで分割し、行を作る
                    string[] csFields = csLine.ToString().Split(',');
                    string[] csRow = new string[iFieldNum];
                    int iValidColumnNum = Math.Min(csFields.Length, iFieldNum);
                    for (int i = 0; i < iValidColumnNum; ++i)
                    {
                        csRow[i] = csFields[i];
                    }
                    for (int i = iValidColumnNum; i < iFieldNum; ++i)
                    {
                        csRow[i] = string.Empty;
                    }

                    // 行をリストに追加する
                    m_csDataFileContents.Add(csRow);
                }
            }
            catch (Exception)
            {
                HySDBALog.WriteOnline(GetCurrentClassName(), csMyMethodName, "exception while reading file",
                    "filepath", m_csDataFilePath);

                m_csDataFileContents = null;    // リストを破棄
                return false;   // ロード失敗
            }
            finally
            {
                csFile.Close();
            }

            return true;
        }

        /// <summary><para>method outline:</para>
        /// <para>データを挿入する</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para> bool bRtn = Insert(csStockData, out iRowIndex) </para>
        /// </example>
        /// <param name="csStockData">DB登録データ</param>
        /// <param name="iRowIndex">挿入された行のインデックス</param>
        /// <returns>true:成功、false:失敗</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        public virtual bool Insert(HySStockData csStockData, out int iRowIndex)
        {
            AssertLoaded();

            iRowIndex = -1;

            // 登録日時を設定する
            csStockData.SetRegisertTime(HySCalendar.CreateNowTime());

            // 登録データを作成する
            string[] csNewRow;
            if (!CreateDBRow(csStockData, out csNewRow))
            {
                return false;
            }

            // INSERT可能か調べる
            if (!CanInsertRow(csNewRow))
            {
                return false;
            }

            // 内部リストに行を追加
            m_csDataFileContents.Add(csNewRow);
            iRowIndex = m_csDataFileContents.Count - 1; // 最終行インデックスを返す

            return true;
        }

        /// <summary><para>method outline:</para>
        /// <para>データを挿入する（複数一括）</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para> bool bRtn = Insert(csStockDataList, out iRowIndexes) </para>
        /// </example>
        /// <param name="csStockDataList">DB登録データのリスト</param>
        /// <param name="iRowIndexes">挿入した行のインデックス配列</param>
        /// <returns>true:成功、false:失敗</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        public virtual bool Insert(HySStockDataList csStockDataList, out int[] iRowIndexes)
        {
            const string csMyMethodName = "Insert(HySStockDataList)";
            AssertLoaded();

            iRowIndexes = null;

            // 登録データを作成する
            long iRecordCount = csStockDataList.GetCount();
            string[][] csNewRows = new string[iRecordCount][];
            csStockDataList.SetCursorFirst();
            for (int i = 0; i < iRecordCount; ++i)
            {
                HySDataRoot csDataRoot = csStockDataList.GetCursorData() as HySStockData;
                HySStockData csStockData = csDataRoot as HySStockData;
                if (csStockData == null)
                {
                    HySDBALog.WriteOnline(GetCurrentClassName(), csMyMethodName, "HySStockDataList.GetCursorData is not HySStockData or null",
                        "HySStockData.GetCursorData", csDataRoot,
                        "index", i);
                    return false;   // データ型が違う
                }

                // 登録日時を設定する
                csStockData.SetRegisertTime(HySCalendar.CreateNowTime());

                // 登録行データを作成する
                if (!CreateDBRow(csStockData, out csNewRows[i]))
                {
                    return false;   // データ内容に不備がある
                }
                csStockDataList.MoveCursorNext();
            }

            // INSERT可能か調べる
            foreach (string[] csNewRow in csNewRows)
            {
                if (!CanInsertRow(csNewRow))
                {
                    return false;   // 重複あり
                }
            }

            // 内部リストに行を追加する
            iRowIndexes = new int[csNewRows.Length];
           
            for (int i = 0; i < csNewRows.Length; ++i)
            {
                m_csDataFileContents.Add(csNewRows[i]);
                iRowIndexes[i] = m_csDataFileContents.Count - 1;
            }

            return true;
        }

        /// <summary><para>method outline:</para>
        /// <para>データを更新する</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para> bool bRtn = Update(csStockData, out iRowIndex) </para>
        /// </example>
        /// <param name="csStockData">DB登録データ</param>
        /// <param name="iRowIndex">更新した行のインデックス</param>
        /// <returns>true:成功、false:失敗</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        public virtual bool Update(HySStockData csStockData, out int iRowIndex)
        {
            AssertLoaded();

            iRowIndex = -1;

            // 登録データを作成する
            string[] csNewRow;
            if (!CreateDBRow(csStockData, out csNewRow))
            {
                return false;   // データ内容に不備がある
            }

            // UPDATEする行を検索する
            int iUpdateRowIndex;
            if (!FindUpdateRow(csNewRow, out iUpdateRowIndex))
            {
                return false;   // 該当データなし
            }

            // 内部リストの行を更新
            csNewRow.CopyTo(m_csDataFileContents[iUpdateRowIndex], 0);
            iRowIndex = iUpdateRowIndex;

            return true;
        }

        /// <summary><para>method outline:</para>
        /// <para>データを削除する(ID指定)</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para> bool bRtn = Delete(csID, out csDeletedRow) </para>
        /// </example>
        /// <param name="csID">削除するデータのID</param>
        /// <param name="csDeletedRow">削除された行のデータ</param>
        /// <returns>true:成功、false:失敗(該当データなし)</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        public virtual bool Delete(HySID csID, out string[] csDeletedRow)
        {
            AssertLoaded();

            csDeletedRow = null;

            int iRowIndex;
            if (!FindRowByID(csID, out iRowIndex))
            {
                return false;   // 該当データなし
            }

            // 要素を削除する
            csDeletedRow = m_csDataFileContents[iRowIndex];
            m_csDataFileContents.RemoveAt(iRowIndex);

            return true;
        }

        /// <summary><para>method outline:</para>
        /// <para>データを削除する(行インデックス指定)</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para> bool bRtn = Delete(iRowIndex, out csDeletedRow) </para>
        /// </example>
        /// <param name="iRowIndex">削除対象データの行インデックス</param>
        /// <param name="csDeletedRow">削除された行のデータ</param>
        /// <returns>true:成功、false:失敗(該当データなし)</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>複数行を削除する場合、行インデックスの大→小の順に行うこと。</para>
        /// </remarks>
        public virtual bool Delete(int iRowIndex, out string[] csDeletedRow)
        {
            const string csMyMethodName = "Delete(int, out string[])";

            AssertLoaded();
            csDeletedRow = null;

            // 行インデックス範囲チェック
            if (iRowIndex < 0 || iRowIndex >= m_csDataFileContents.Count)
            {
                HySDBALog.WriteOnline(m_csMyClassName, csMyMethodName, "RowIndex out of range",
                    "DataCount", m_csDataFileContents.Count,
                    "RowIndex", iRowIndex);
                return false;
            }

            // 要素を削除する
            csDeletedRow = m_csDataFileContents[iRowIndex];
            m_csDataFileContents.RemoveAt(iRowIndex);

            return true;
        }


        /// <summary><para>method outline:</para>
        /// <para>行検索</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para> bool bRtn = Select(csQueryCtlData, out iRowIndexes) </para>
        /// </example>
        /// <param name="csQueryCtlData">検索条件</param>
        /// <param name="iRowIndexes">検索結果の行インデックス配列</param>
        /// <returns>true:成功、false:失敗(該当データなし)</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        public virtual bool Select(HySQueryCtlData csQueryCtlData, out int[] iRowIndexes)
        {
            const string csMyMethodName = "Select";
            AssertLoaded();

            iRowIndexes = null;

            // フィールドごとの検索条件を並べる
            HySQueryEntryData[] csQueryEntryList = ExpandQueryEntryData(csQueryCtlData);

            // フィールドごとの比較関数を設定する
            IsMatchField[] csDelegateList = new IsMatchField[csQueryEntryList.Length];
            for (int i = 0; i < csDelegateList.Length; ++i)
            {
                HySQueryEntryData csQueryEntry = csQueryEntryList[i];
                if (csQueryEntry == null)
                {
                    continue;
                }
                IsMatchField csDelegate = null;
                Type csFieldDataType = csQueryEntry.GetDataType();
                HySQueryCompareMode iCompareMode = csQueryEntry.GetCompareMode();
                bool bUnsupportedCombination = false;

                if (csFieldDataType == typeof(HySID))
                {
                    // HySID -- EQUALS
                    if (iCompareMode == HySQueryCompareMode.EQUALS)
                    {
                        csDelegate = delegate(string csDBVal, object csValueToCompare)
                        {
                            return csDBVal.Equals(((HySID)csValueToCompare).ToString());
                        };
                    }
                    // HySID -- IN
                    else if (iCompareMode == HySQueryCompareMode.IN)
                    {
                        csDelegate = delegate(string csDBVal, object csValueToCompare)
                        {
                            foreach (HySID csID in (HySID[])csValueToCompare)
                            {
                                if (csDBVal.Equals(csID.ToString()))
                                {
                                    return true;
                                }
                            }
                            return false;
                        };
                    }
                    // HySID -- NOT
                    else if (iCompareMode == HySQueryCompareMode.NOT)
                    {
                        csDelegate = delegate(string csDBVal, object csValueToCompare)
                        {
                            return !(csDBVal.Equals(((HySID)csValueToCompare).ToString()));
                        };
                    }
                    else
                    {
                        bUnsupportedCombination = true;
                    }
                }
                else if (csFieldDataType == typeof(HySString))
                {
                    // HySString -- EQUALS
                    if (iCompareMode == HySQueryCompareMode.EQUALS)
                    {
                        csDelegate = delegate(string csDBVal, object csValueToCompare)
                        {
                            return csDBVal.Equals(((HySString)csValueToCompare).ToString());
                        };
                    }
                    // HySString -- LIKE_BEGIN
                    else if (iCompareMode == HySQueryCompareMode.LIKE_BEGIN)
                    {
                        csDelegate = delegate(string csDBVal, object csValueToCompare)
                        {
                            return csDBVal.StartsWith(((HySString)csValueToCompare).ToString());
                        };
                    }
                    // HySString -- LIKE_END
                    else if (iCompareMode == HySQueryCompareMode.LIKE_END)
                    {
                        csDelegate = delegate(string csDBVal, object csValueToCompare)
                        {
                            return csDBVal.EndsWith(((HySString)csValueToCompare).ToString());
                        };
                    }
                    // HySString -- IN
                    else if (iCompareMode == HySQueryCompareMode.IN)
                    {
                        csDelegate = delegate(string csDBVal, object csValueToCompare)
                        {
                            foreach (HySString csHStr in (HySString[])csValueToCompare)
                            {
                                if (csDBVal.Equals(csHStr.ToString()))
                                {
                                    return true;
                                }
                            }
                            return false;
                        };
                    }
                    // HySString -- NOT
                    else if (iCompareMode == HySQueryCompareMode.NOT)
                    {
                        csDelegate = delegate(string csDBVal, object csValueToCompare)
                        {
                            return !(csDBVal.Equals(((HySString)csValueToCompare).ToString()));
                        };
                    }
                    else
                    {
                        bUnsupportedCombination = true;
                    }
                }
                else if (csFieldDataType == typeof(HySTime))
                {
                    // HySTime -- EQUALS
                    if (iCompareMode == HySQueryCompareMode.EQUALS)
                    {
                        csDelegate = delegate(string csDBVal, object csValueToCompare)
                        {
                            HySTime csDBHySTimeValue = HySCalendar.CreateTime(csDBVal);
                            return csDBHySTimeValue.Equals((HySTime)csValueToCompare);
                        };
                    }
                    // HySTime -- BETWEEN
                    else if (iCompareMode == HySQueryCompareMode.BETWEEN)
                    {
                        csDelegate = delegate(string csDBVal, object csValueToCompare)
                        {
                            HySTime csDBHySTimeValue = HySCalendar.CreateTime(csDBVal);
                            HySTime csFromTime = ((HySTime[])csValueToCompare)[0];
                            HySTime csToTime = ((HySTime[])csValueToCompare)[1];
                            bool bFrom = true;
                            bool bTo = true;
                            // 開始時刻が指定されている場合、それより後もしくは一致していなければならない
                            if (csFromTime != null)
                            {
                                bFrom = (csDBHySTimeValue.After(csFromTime) || csDBHySTimeValue.Equals(csFromTime));
                            }
                            // 終了時刻が指定されている場合、それより前もしくは一致していなければならない
                            if (csToTime != null)
                            {
                                bTo = (csDBHySTimeValue.Before(csToTime) || csDBHySTimeValue.Equals(csToTime));
                            }

                            return bFrom && bTo;
                            //if (csDBHySTimeValue.After(csFromTime) && csDBHySTimeValue.Before(csToTime))
                            //{
                            //    return true;    // FromとToの間にある→OK
                            //}
                            //else if (csFromTime.Equals(csDBHySTimeValue))
                            //{
                            //    return true;    // Fromと等しい→OK
                            //}
                            //else if (csToTime.Equals(csDBHySTimeValue))
                            //{
                            //    return true;    // Toと等しい→OK
                            //}
                            //else
                            //{
                            //    return false;   // 不一致
                            //}
                        };
                    }
                    // HySTime -- IN
                    else if (iCompareMode == HySQueryCompareMode.IN)
                    {
                        csDelegate = delegate(string csDBVal, object csValueToCompare)
                        {
                            foreach (HySTime csHTime in (HySTime[])csValueToCompare)
                            {
                                HySTime csDBHySTimeValue = HySCalendar.CreateTime(csDBVal);
                                if (csDBHySTimeValue.Equals(csHTime))
                                {
                                    return true;
                                }
                            }
                            return false;
                        };
                    }
                    // HySTime -- NOT
                    else if (iCompareMode == HySQueryCompareMode.NOT)
                    {
                        csDelegate = delegate(string csDBVal, object csValueToCompare)
                        {
                            HySTime csDBHySTimeValue = HySCalendar.CreateTime(csDBVal);
                            return !(csDBHySTimeValue.Equals((HySTime)csValueToCompare));
                        };
                    }
                    else
                    {
                        bUnsupportedCombination = true;
                    }
                }
                else if (csFieldDataType == typeof(int))
                {
                    // int -- EQUALS
                    if (iCompareMode == HySQueryCompareMode.EQUALS)
                    {
                        csDelegate = delegate(string csDBVal, object csValueToCompare)
                        {
                            return csDBVal.Equals(((int)csValueToCompare).ToString());
                        };
                    }
                    // int -- BETWEEN
                    else if (iCompareMode == HySQueryCompareMode.BETWEEN)
                    {
                        csDelegate = delegate(string csDBVal, object csValueToCompare)
                        {
                            // DBの値を数値化して比較
                            int iDBValue;
                            if (!int.TryParse(csDBVal, out iDBValue))
                            {
                                return false;
                            }
                            int iFromValue = ((int[])csValueToCompare)[0];
                            int iToValue = ((int[])csValueToCompare)[1];
                            if (iFromValue <= iDBValue && iDBValue <= iToValue)
                            {
                                return true;
                            }
                            else
                            {
                                return false;   // 不一致
                            }
                        };
                    }
                    // HySTime -- IN
                    else if (iCompareMode == HySQueryCompareMode.IN)
                    {
                        csDelegate = delegate(string csDBVal, object csValueToCompare)
                        {
                            foreach (int iValue in (int[])csValueToCompare)
                            {
                                if (csDBVal.Equals(iValue.ToString()))
                                {
                                    return true;
                                }
                            }
                            return false;
                        };
                    }
                    // HySTime -- NOT
                    else if (iCompareMode == HySQueryCompareMode.NOT)
                    {
                        csDelegate = delegate(string csDBVal, object csValueToCompare)
                        {
                            return !(csDBVal.Equals(((int)csValueToCompare).ToString()));
                        };
                    }
                    else
                    {
                        bUnsupportedCombination = true;
                    }
                }
                else if (csFieldDataType == typeof(HySObjectKind))
                {
                    // HySObjectKind -- EQUALS
                    if (iCompareMode == HySQueryCompareMode.EQUALS)
                    {
                        csDelegate = delegate(string csDBVal, object csValueToCompare)
                        {
                            return csDBVal.Equals(((HySObjectKind)csValueToCompare).ToString());
                        };
                    }
                    // HySObjectKind -- IN
                    else if (iCompareMode == HySQueryCompareMode.IN)
                    {
                        csDelegate = delegate(string csDBVal, object csValueToCompare)
                        {
                            foreach (HySObjectKind csID in (HySObjectKind[])csValueToCompare)
                            {
                                if (csDBVal.Equals(csID.ToString()))
                                {
                                    return true;
                                }
                            }
                            return false;
                        };
                    }
                    // HySObjectKind -- NOT
                    else if (iCompareMode == HySQueryCompareMode.NOT)
                    {
                        csDelegate = delegate(string csDBVal, object csValueToCompare)
                        {
                            return !(csDBVal.Equals(((HySObjectKind)csValueToCompare).ToString()));
                        };
                    }
                    else
                    {
                        bUnsupportedCombination = true;
                    }
                }
                else if (csFieldDataType == typeof(HySStockData.PeservedPeriod))
                {
                    // HySStockData.PeservedPeriod -- EQUALS
                    if (iCompareMode == HySQueryCompareMode.EQUALS)
                    {
                        csDelegate = delegate(string csDBVal, object csValueToCompare)
                        {
                            if ((HySStockData.PeservedPeriod)csValueToCompare == HySStockData.PeservedPeriod.Eternity)
                            {
                                return csDBVal.Equals(PRESERVE_PERIOD_ETERNITY);
                            }
                            else
                            {
                                return csDBVal.Equals(PRESERVE_PERIOD_TEMPORARY);
                            }
                        };
                    }
                    // HySStockData.PeservedPeriod -- NOT
                    else if (iCompareMode == HySQueryCompareMode.NOT)
                    {
                        csDelegate = delegate(string csDBVal, object csValueToCompare)
                        {
                            if ((HySStockData.PeservedPeriod)csValueToCompare == HySStockData.PeservedPeriod.Eternity)
                            {
                                return !(csDBVal.Equals(PRESERVE_PERIOD_ETERNITY));
                            }
                            else
                            {
                                return !(csDBVal.Equals(PRESERVE_PERIOD_TEMPORARY));
                            }
                        };
                    }
                    else
                    {
                        bUnsupportedCombination = true;
                    }
                }

                // 非サポートの組み合わせが設定された場合、常に失敗するようにする
                if (bUnsupportedCombination)
                {
                    csDelegate = delegate(string csDBVal, object csValueToCompare)
                    {
                        return false;
                    };
                    HySDBALog.WriteOnline(m_csMyClassName, csMyMethodName, "unsupported query",
                        "FieldDataType", csFieldDataType.Name,
                        "CompareMode", iCompareMode);
                }
                csDelegateList[i] = csDelegate;
            }

            List<int> csRowIndexList = new List<int>();

            // 検索処理：行のループ
            for (int iRowIndex = 0; iRowIndex < m_csDataFileContents.Count; ++iRowIndex)
            {
                string[] csRow = m_csDataFileContents[iRowIndex];
                bool bUnmatch = false;  // 「不一致あり」フラグ
                // 検索処理：列のループ
                for (int iFieldIndex = 0; iFieldIndex < csQueryEntryList.Length; ++iFieldIndex)
                {
                    // 検索条件が設定されていれば比較実行
                    HySQueryEntryData csQueryEntry = csQueryEntryList[iFieldIndex];
                    IsMatchField csDelegate = csDelegateList[iFieldIndex];
                    if (csDelegate != null)
                    {
                        if (!csDelegate(csRow[iFieldIndex], csQueryEntry.GetValueToCompare()))
                        {
                            // 全てANDで連結されているものとして、1つでも不一致があればループを抜ける
                            bUnmatch = true;
                            break;
                        }
                    }
                }
                // 「不一致あり」の場合、この行はおしまい
                if (bUnmatch)
                {
                    continue;
                }

                // 行インデックスをリストに追加する
                csRowIndexList.Add(iRowIndex);
            }

            // 行インデックスリストを配列にコピーして終了
            iRowIndexes = new int[csRowIndexList.Count];
            csRowIndexList.CopyTo(iRowIndexes);

            return true;
        }

        /// <summary><para>method outline:</para>
        /// <para>行検索（ID配列指定）</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para> bool bRtn = FindRowsByIDs(csIDs, out iRowIndexes) </para>
        /// </example>
        /// <param name="csIDs">データのID配列</param>
        /// <param name="iRowIndexes">行インデックス配列</param>
        /// <returns>true:成功、false:失敗(1つ以上該当データなし)</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        public virtual bool FindRowsByIDs(HySIdentifier[] csIDs, out int[] iRowIndexes)
        {
            AssertLoaded();

            iRowIndexes = null;

            // HySID→string変換
            string[] csIDStrings = new string[csIDs.Length];
            for (int i = 0; i < csIDStrings.Length; ++i)
            {
                csIDStrings[i] = csIDs[i].ToString();
            }
            // 行インデックス配列作成(IDと同じ個数)
            int[] iOutRowIndexes = new int[csIDStrings.Length];

            // 内部テーブルからIDを検索する。
            // 1つでも見つからない場合、処理失敗とする。
            int iIDNo = 0;
            int iIDFieldIndex = GetIDFieldIndex();
            foreach (string csIDStr in csIDStrings)
            {
                bool bFound = false;
                for (int iRowIndex = 0; iRowIndex < m_csDataFileContents.Count; ++iRowIndex)
                {
                    if (m_csDataFileContents[iRowIndex][iIDFieldIndex].Equals(csIDStr))
                    {
                        iOutRowIndexes[iIDNo++] = iRowIndex;
                        bFound = true;
                        break;
                    }
                }
                if (!bFound)
                {
                    return false;   // 該当データなし
                }
            }
            iRowIndexes = iOutRowIndexes;
            return true;
        }

        /// <summary><para>method outline:</para>
        /// <para>行検索(ID指定)</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para> bool bRtn = FindRowByID(csID, out iRowIndex) </para>
        /// </example>
        /// <param name="csID">データID</param>
        /// <param name="iRowIndex">行インデックス</param>
        /// <returns>true:成功(該当データあり)、false:失敗(該当データなし)</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        public virtual bool FindRowByID(HySID csID, out int iRowIndex)
        {
            AssertLoaded();

            iRowIndex = -1;

            // IDでチェック
            string csIDString = csID.ToString();
            int iIDFieldIndex = GetIDFieldIndex();
            for (int iIdx = 0; iIdx < m_csDataFileContents.Count; ++iIdx)
            {
                if (m_csDataFileContents[iIdx][iIDFieldIndex].Equals(csIDString))
                {
                    // 発見、インデックスを返却
                    iRowIndex = iIdx;
                    return true;
                }
            }
            return false;   // 該当データなし
        }

        /// <summary><para>method outline:</para>
        /// <para>行列指定で値を取得する</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para> string csVal = GetFieldData(iRowIndex, iFieldIndex) </para>
        /// </example>
        /// <param name="iRowIndex">行インデックス</param>
        /// <param name="iFieldIndex">フィールドインデックス</param>
        /// <returns>DBの値</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>DB実装固有の管理情報などのために使用することを想定している。</para>
        /// </remarks>
        public virtual string GetFieldData(int iRowIndex, int iFieldIndex)
        {
            AssertLoaded();

            return m_csDataFileContents[iRowIndex][iFieldIndex];
        }

        /// <summary><para>method outline:</para>
        /// <para>行列指定で値を設定する</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para> SetFieldData(iRowIndex, iFieldIndex, csValue) </para>
        /// </example>
        /// <param name="iRowIndex">行インデックス</param>
        /// <param name="iFieldIndex">フィールドインデックス</param>
        /// <param name="csValue">DBに設定する値</param>
        /// <returns>無し</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>DB実装固有の管理情報などのために使用することを想定している。</para>
        /// </remarks>
        public virtual void SetFieldData(int iRowIndex, int iFieldIndex, string csValue)
        {
            AssertLoaded();

            m_csDataFileContents[iRowIndex][iFieldIndex] = csValue;
        }

        /// <summary><para>method outline:</para>
        /// <para>変更をCSVファイルに反映する</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para> bool bRtn = Commit() </para>
        /// </example>
        /// <param name="">無し</param>
        /// <returns>true:成功、false:失敗</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>コミットを実行するまで全ての変更はファイルに反映されない。</para>
        /// </remarks>
        public virtual bool Commit()
        {
            const string csMyMethodName = "Commit";

            AssertLoaded();

            string csOriginalFilePath = m_csDataFilePath;
            string csTempFilePath = csOriginalFilePath + TMP_FILE_EXTENSION;
            string csOldFilePath = csOriginalFilePath + OLD_FILE_EXTENSION;

            // 削除処理によりデータ内容がなくなった場合
            if (m_csDataFileContents.Count == 0)
            {
                try
                {
                    // 元ファイルの削除を行う
                    if (File.Exists(csOriginalFilePath))
                    {
                        HySDBAFileIOWrapper.FileDelete(csOriginalFilePath);
                    }
                }
                catch (Exception ex)
                {
                    HySDBALog.WriteOnline(GetCurrentClassName(), csMyMethodName, "exception at delete file",
                        "oldfile", csOriginalFilePath,
                        "exception", ex);
                    return false;
                }
            }
            else
            {
                // 内容を一時ファイルに保存する
                HySFile csTempFile = new HySFile(csTempFilePath);
                if (csTempFile.Open(HySFile.OPEN_MODE.OPEN_OR_CREAT, HySFile.READ_WRITE_MODE.WRITE, HySFile.DIRECTORY_MODE.MK_DIR) != 0)
                {
                    HySDBALog.WriteOnline(GetCurrentClassName(), csMyMethodName, "cannot open tmpfile",
                        "tmpfilepath", csTempFilePath);
                    return false;
                }
                bool bSaveOK = true;
                try
                {
                    foreach (string[] csRow in m_csDataFileContents)
                    {
                        string csJoinedString = string.Join(",", csRow);
                        if (csTempFile.WriteText(csJoinedString) != 0)
                        {
                            bSaveOK = false;
                            break;
                        }
                    }
                }
                catch (Exception ex)
                {
                    HySDBALog.WriteOnline(GetCurrentClassName(), csMyMethodName, "cannot save tmpfile",
                        "tmpfilepath", csTempFilePath,
                        "exception", ex);
                    bSaveOK = false;
                }
                finally
                {
                    csTempFile.Close();
                }

                if (!bSaveOK)
                {
                    HySDBAFileIOWrapper.FileDelete(csTempFilePath);
                    return false;   // 一時ファイル保存に失敗
                }

                // 新旧ファイルの置き換え
                try
                {
                    // ファイル名を変更する(1) オリジナル→.old
                    if (File.Exists(csOriginalFilePath))
                    {
                        HySDBAFileIOWrapper.FileMove(csOriginalFilePath, csOldFilePath);
                    }
                    // ファイル名を変更する(2) .tmp→オリジナル
                    HySDBAFileIOWrapper.FileMove(csTempFilePath, csOriginalFilePath);
                    // 最後に.oldを消す
                    HySDBAFileIOWrapper.FileDelete(csOldFilePath);
                }
                catch (Exception ex)
                {
                    HySDBALog.WriteOnline(GetCurrentClassName(), csMyMethodName, "exception at move file",
                        "orgfilepath", csOriginalFilePath,
                        "tmpfilepath", csTempFilePath,
                        "oldfile", csOldFilePath,
                        "exception", ex);
                    // この状態の復旧作業は困難なため、ログ出力のみとする
                    return false;
                }
            }
            return true;
        }

        /// <summary><para>method outline:</para>
        /// <para>データファイルのロックを解放する</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>Unlock()</para>
        /// </example>
        /// <param name="">無し</param>
        /// <returns>無し</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        public virtual void Unlock()
        {
            if (m_csLockFile != null)
            {
                m_csLockFile.UnLock();
                m_csLockFile = null;
            }
            m_csDataFileContents = null;
        }

        /// <summary><para>method outline:</para>
        /// <para>（抽象メソッド）データファイルパスを取得する</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>string csFilePath = GetDataFilePath()</para>
        /// </example>
        /// <param name="">無し</param>
        /// <returns>データファイルパス</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>アクセス対象のCSVファイルのパスを取得する。サブクラスがオーバーライドすること。</para>
        /// </remarks>
        protected abstract string GetDataFilePath();

        /// <summary><para>method outline:</para>
        /// <para>（抽象メソッド）DBに登録する行を作成する</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>bool bRtn = CreateDBRow(csStockData、out csNewRow)</para>
        /// </example>
        /// <param name="csStockData">DBに登録するデータ</param>
        /// <param name="csNewRow">(出力)DBに登録する文字列配列</param>
        /// <returns>true:データ作成成功、false:データ作成失敗(INSERT/UPDATEできない)</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        protected abstract bool CreateDBRow(HySStockData csStockData, out string[] csNewRow);

        /// <summary><para>method outline:</para>
        /// <para>（抽象メソッド）フィールド数を取得する</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>int iCnt = GetFieldNum()</para>
        /// </example>
        /// <param name="">無し</param>
        /// <returns>CSVファイルのフィールド数</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        protected abstract int GetFieldNum();

        /// <summary><para>method outline:</para>
        /// <para>（抽象メソッド）IDフィールドのインデックスを取得する</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>int iIndex = GetIDFieldIndex()</para>
        /// </example>
        /// <param name="">無し</param>
        /// <returns>CSVファイルのIDフィールドのインデックス</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        protected abstract int GetIDFieldIndex();

        /// <summary><para>method outline:</para>
        /// <para>（抽象メソッド）挿入可能かチェックする</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>bool bRtn = CanInsertRow( csNewRow )</para>
        /// </example>
        /// <param name="csNewRow">行データ</param>
        /// <returns>true:挿入可能　false:挿入不可</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        protected abstract bool CanInsertRow(string[] csNewRow);

        /// <summary><para>method outline:</para>
        /// <para>（抽象メソッド）更新行を検索する</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>bool bRtn = FindUpdateRow(csUpdateRow, out iRowIndex)</para>
        /// </example>
        /// <param name="csUpdateRow">更新対象行データ</param>
        /// <param name="iRowIndex">行番号</param>
        /// <returns>true:対応行有り　false:対応行無し</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        protected abstract bool FindUpdateRow(string[] csUpdateRow, out int iRowIndex);

        /// <summary><para>method outline:</para>
        /// <para>（抽象メソッド）検索条件を展開する</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>HySQueryEntryData[] csQuery = ExpandQueryEntryData(csQueryCtlData)</para>
        /// </example>
        /// <param name="csQueryCtlData">検索条件</param>
        /// <returns>検索条件の個別エントリをフィールド順に並べた配列。該当ないフィールドはnull。</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        protected abstract HySQueryEntryData[] ExpandQueryEntryData(HySQueryCtlData csQueryCtlData);


        #region ユーティリティメソッド

        /// <summary><para>method outline:</para>
        /// <para>DBの値をHySTimeに変換する</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>HySTime csTime = HySCSVDBABase.DBValueToHySTime(csDBValue)</para>
        /// </example>
        /// <param name="csDBValue">DBの値(日付時刻文字列)</param>
        /// <returns>変換結果の日付時刻</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        public static HySTime DBValueToHySTime(string csDBValue)
        {
            return HySCalendar.CreateTime(csDBValue);
        }

        /// <summary><para>method outline:</para>
        /// <para>DBの値をintに変換する</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>int iVal = HySCSVDBABase.DBValueToInt(csDBValue)</para>
        /// </example>
        /// <param name="csDBValue">DBの値(数値文字列)</param>
        /// <returns>変換結果の数値</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        public static int DBValueToInt(string csDBValue)
        {
            int iValue = 0;
            int.TryParse(csDBValue, out iValue);
            return iValue;
        }

        /// <summary><para>method outline:</para>
        /// <para>DBの値を保存期限区分に変換する</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>HySStockData.PeservedPeriod csVal = HySCSVDBABase.DBValueToPeservedPeriod(csDBValue)</para>
        /// </example>
        /// <param name="csDBValue">DBの値</param>
        /// <returns>変換結果の保存期限区分</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        public static HySStockData.PeservedPeriod DBValueToPeservedPeriod(string csDBValue)
        {
            HySStockData.PeservedPeriod iPeservedPeriod;
            if (csDBValue == PRESERVE_PERIOD_TEMPORARY)
            {
                iPeservedPeriod = HySStockData.PeservedPeriod.Temporary;
            }
            else
            {
                iPeservedPeriod = HySStockData.PeservedPeriod.Eternity;
            }
            return iPeservedPeriod;
        }

        /// <summary><para>method outline:</para>
        /// <para>保存期限区分をDBの値に変換する</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>string csVal = HySCSVDBABase.PreservePeriodToDBValue(iPreservePeriod)</para>
        /// </example>
        /// <param name="iPreservePeriod">保存期限区分</param>
        /// <returns>DBの値</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        public static string PreservePeriodToDBValue(HySStockData.PeservedPeriod iPreservePeriod)
        {
            string csPreservePeriod;
            if (iPreservePeriod == HySStockData.PeservedPeriod.Temporary)
            {
                csPreservePeriod = "0";
            }
            else
            {
                csPreservePeriod = "1";
            }
            return csPreservePeriod;
        }

        /// <summary><para>method outline:</para>
        /// <para>時刻データをDBの値に変換する</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>string csVal = HySCSVDBABase.HySTimeToDBValue(csHySTime)</para>
        /// </example>
        /// <param name="csHySTime">時刻データ</param>
        /// <returns>DBの値</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        public static string HySTimeToDBValue(HySTime csHySTime)
        {
            string csHySTimeString;
            csHySTimeString = HySCalendar.ToString(csHySTime, HySCalendar.FORMAT.lSW_YEAR);
            return csHySTimeString;
        }


        #endregion

        #region privateメソッド

        /// <summary><para>method outline:</para>
        /// <para>アンロック中の表明</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>AssertUnlocked()</para>
        /// </example>
        /// <param name="">無し</param>
        /// <returns>無し</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>ロック中の場合は例外(InvalidOperationException)が送出される。</para>
        /// </remarks>
        private void AssertUnlocked()
        {
            if (m_csLockFile != null)
            {
                string csMsg = string.Format("[{0}] must be unlocked", m_csDataFilePath);
                throw new InvalidOperationException(csMsg);
            }
        }

        /// <summary><para>method outline:</para>
        /// <para>ロック中の表明</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>AssertLocked()</para>
        /// </example>
        /// <param name="">無し</param>
        /// <returns>無し</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>アンロック中の場合は例外(InvalidOperationException)が送出される。</para>
        /// </remarks>
        protected virtual void AssertLocked()
        {
            if (m_csLockFile == null)
            {
                string csMsg = string.Format("[{0}] must be locked", m_csDataFilePath);
                throw new InvalidOperationException(csMsg);
            }
        }

        /// <summary><para>method outline:</para>
        /// <para>ロード済みの表明(ロック中の表明を含む)</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>AssertLoaded()</para>
        /// </example>
        /// <param name="">無し</param>
        /// <returns>無し</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>未ロードの場合は例外(InvalidOperationException)が送出される。</para>
        /// </remarks>
        private void AssertLoaded()
        {
            AssertLocked();
            if (m_csDataFileContents == null)
            {
                string csMsg = string.Format("[{0}] must be loaded", m_csDataFilePath);
                throw new InvalidOperationException(csMsg);
            }
        }

        /// <summary><para>method outline:</para>
        /// <para>現在のクラス名取得</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>string csName = GetCurrentClassName()</para>
        /// </example>
        /// <param name="">無し</param>
        /// <returns>thisのクラス名し</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        private string GetCurrentClassName()
        {
            return string.Format("{0}({1})", m_csMyClassName, this.GetType().Name);
        }
        #endregion
    }
}
