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;
using CommonMP.HYSSOP.CoreImpl.HSDBA;
using CommonMP.HYSSOP.CoreImpl.HSDBA.FileBase;

namespace CommonMP.HYSSOP.CoreImpl.HSDBA.FileBase
{
    /// <summary><para>class outline:</para>
    /// <para>演算結果データDBAクラス</para>
    /// </summary>
    /// <remarks><para>remarks:</para>
    /// <para>演算結果データDBへのアクセスを提供する。</para>
    /// <para>本クラスの外部I/Fで扱うIDは、分割登録された個々のデータファイルを識別するユニークIDではなく、
    /// それらをマージした「演算結果ID」であることに注意。</para>
    /// <para>性能上の問題から、本DBは複数のサブフォルダで分割管理する</para>
    /// <para>本クラスではサブフォルダ分割に関わる部分の処理を担当し、サブフォルダ内の処理は
    /// <see cref="HySCalResultDataDBASub"/>クラスに委譲する。</para>
    /// <para>検索条件によっては複数のサブフォルダをまたがるケースが存在する。このような場合に
    /// I/Oエラー等が発生すると、データベースに不整合が発生する可能性がある。</para>
    /// </remarks>
    /// <remarks><para>history:</para>
    /// <para>[CommonMP][ver 1.0.0][2009/06/22][新規作成]</para>
    /// </remarks>
    public class HySCalResultDataDBA: HySCommonDBA
    {
        /// <summary>
        /// 自クラス名(ログ出力用)
        /// </summary>
        private const string m_csMyClassName = "HySCalResultDataDBA";

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

        /// <summary>
        /// DBフォルダパス名を環境設定から取得するためのキー
        /// </summary>
        public const string ENVKEY_DBPATH = "HSDBA_CALRESULTDATA_PATH";

        /// <summary>
        /// デフォルトＤＢパス
        /// </summary>
        public const string DEFAULT_DBPATH = @"\SystemData\db\calresultdata";

        #region publicメソッドのオーバーライド

        /// <summary><para>method outline:</para>
        /// <para>1件新規登録</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>bool bRtn = RegisterNew(csStockData)</para>
        /// </example>
        /// <param name="csStockData">DB登録データ</param>
        /// <returns>true:成功, false:失敗</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        public override bool RegisterNew(HySStockData csStockData)
        {
            const string csMyMethodName = "RegisterNew(HySStockData)";

            // 一括登録バージョンで実行する
            HySStockDataList csStockDataList = new HySStockDataList();
            csStockDataList.AddLast(csStockData);
            if (!RegisterNew(csStockDataList))
            {
                HySDBALog.WriteOnline(m_csMyClassName, csMyMethodName, "RegisterNew(HySStockDataList) error");
                return false;
            }
            return true;
        }

        /// <summary><para>method outline:</para>
        /// <para>複数件一括新規登録</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para> bool bRtn = RegisterNew(csStockDataList) </para>
        /// </example>
        /// <param name="csStockDataList">DB登録データ</param>
        /// <returns>true:成功, false:失敗</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>前処理として、HySStockDataの追加検索キー(KeyInfo)に演算ロット情報の一部を設定する</para>
        /// </remarks>
        public override bool RegisterNew(HySStockDataList csStockDataList)
        {
            const string csMyMethodName = "RegisterNew(HySStockDataList)";

            // 登録データに追加検索キーを付与する
            long lDataCount = csStockDataList.GetCount();
            csStockDataList.SetCursorFirst();
            for (long lIdx = 0; lIdx < lDataCount; ++lIdx)
            {
                HySStockData csStockData = (HySStockData)csStockDataList.GetCursorData();
                csStockDataList.MoveCursorNext();
                if (!SetKeyInfoOfStockData(csStockData))
                {
                    HySDBALog.WriteOnline(m_csMyClassName, csMyMethodName, "SetKeyInfoOfStockData() error");
                    return false;
                }
            }

            // 登録データをサブフォルダごとに振り分ける
            Dictionary<string, HySStockDataList> csDispatchedStockDataList;
            if (!DispatchHySStockDataList(csStockDataList, out csDispatchedStockDataList))
            {
                HySDBALog.WriteOnline(m_csMyClassName, csMyMethodName, "csDispatchedStockDataList() error");
                return false;
            }

            // サブフォルダDBに処理を委譲する
            string csDBPath = GetDBBasePath();
            foreach (string csSubFolderPath in csDispatchedStockDataList.Keys)
            {
                HySCalResultDataDBASub csDBASub = new HySCalResultDataDBASub(Path.Combine(csDBPath, csSubFolderPath));
                if (!csDBASub.RegisterNew(csDispatchedStockDataList[csSubFolderPath]))
                {
                    HySDBALog.WriteOnline(m_csMyClassName, csMyMethodName, "HySCalResultDataDBASub.RegisterNew() error",
                        "SubFolderPath", csSubFolderPath);
                    return false;
                }
            }
            return true;
        }

        /// <summary><para>method outline:</para>
        /// <para>1件更新</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para> bool bRtn = Update(csStockData) </para>
        /// </example>
        /// <param name="csStockData">DB登録データ</param>
        /// <returns>true:成功, false:失敗</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>このメソッドで更新を実行すると、分割登録された元の複数レコードは全て削除される。</para>
        /// <para>本DBでは分割登録された個々のレコードに対する更新機能を提供しない。</para>
        /// </remarks>
        public override bool Update(HySStockData csStockData)
        {
            const string csMyMethodName = "Update";

            HySIdentifier csCalcResultID = csStockData.GetID();    // 演算結果ID

            // 演算結果IDをサブフォルダパスに変換する
            string csSubFolderPath;
            if (!ConvertCalcResultIDToSubFolderPath(csCalcResultID, out csSubFolderPath))
            {
                HySDBALog.WriteOnline(m_csMyClassName, csMyMethodName, "ConvertCalcResultIDToSubFolderPath() error");
                return false;
            }

            // サブフォルダDBAを作成する
            HySCalResultDataDBASub csDBASub = new HySCalResultDataDBASub(Path.Combine(GetDBBasePath(), csSubFolderPath));

            // 元のデータを検索する
            HySStockDataList csOldDataList;
            HySQueryCtlData csQueryCtlData = ConvertCalcResultIDToQuery(csCalcResultID);
            csQueryCtlData.SetIsMetaDataOnly(true);
            if (!csDBASub.SearchList(csQueryCtlData, out csOldDataList))
            {
                HySDBALog.WriteOnline(m_csMyClassName, csMyMethodName, "HySCalResultDataDBASub.SearchList() error");
                return false;
            }
            if (csOldDataList.GetCount() == 0)
            {   // 登録データなし→終了
                HySDBALog.WriteOnline(m_csMyClassName, csMyMethodName, "data not exists",
                    "ID", csCalcResultID);
                return false;
            }

            // 更新用追加キー設定
            if (!SetKeyInfoOfStockDataForUpdate(csStockData))
            {
                HySDBALog.WriteOnline(m_csMyClassName, csMyMethodName, "SetKeyInfoOfStockDataForUpdate() error",
                    "ID", csCalcResultID);
                return false;
            }

            // 新規データとして登録する
            if (!csDBASub.RegisterNew(csStockData))
            {
                HySDBALog.WriteOnline(m_csMyClassName, csMyMethodName, "HySCalResultDataDBASub.Update() error",
                    "ID", csCalcResultID,
                    "SubFolderPath", csSubFolderPath);
                return false;
            }

            // 元のデータを削除する
            long lDataCount = csOldDataList.GetCount();
            for (long lIdx = 0; lIdx < lDataCount; ++lIdx)
            {
                // ユニークID指定で削除する
                HySStockData csOldStockData = (HySStockData)csOldDataList.GetData(lIdx);
                if (!csDBASub.Delete(csOldStockData.GetID()))
                {
                    HySDBALog.WriteOnline(m_csMyClassName, csMyMethodName, "warning: old data remove error");
                }
            }

            return true;
        }

        /// <summary><para>method outline:</para>
        /// <para>複数件削除(検索条件指定)</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para> bool bRtn = Delete(csQueryCtlData) </para>
        /// </example>
        /// <param name="csQueryCtlData">削除対象データの検索条件</param>
        /// <returns>true:成功, false:失敗</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        public override bool Delete(HySQueryCtlData csQueryCtlData)
        {
            const string csMyMethodName = "Delete(HySQueryCtlData)";

            // 検索条件にIDが含まれる場合、演算結果IDに置き換える
            ReplaceQueryEntryOfID(csQueryCtlData);

            // 検索条件からサブフォルダパスを取得する
            string[] csSubFolderNames;
            if (!GetSubFolderListFromQuery(csQueryCtlData, out csSubFolderNames))
            {
                HySDBALog.WriteOnline(m_csMyClassName, csMyMethodName, "GetSubFolderListFromQuery() error");
                return false;
            }

            // サブフォルダDBに処理を委譲する
            string csDBPath = GetDBBasePath();
            //int iErrorCount = 0;
            foreach (string csSubFolderPath in csSubFolderNames)
            {
                HySCalResultDataDBASub csDBASub = new HySCalResultDataDBASub(Path.Combine(csDBPath, csSubFolderPath));
                if (!csDBASub.Delete(csQueryCtlData))
                {
                    HySDBALog.WriteOnline(m_csMyClassName, csMyMethodName, "HySCalResultDataDBASub.Delete() error",
                        "SubFolderPath", csSubFolderPath);
                    return false;
                }
            }

            return true;
        }

        /// <summary><para>method outline:</para>
        /// <para>複数件削除(ID配列指定)</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para> bool bRtn = Delete(csIDs) </para>
        /// </example>
        /// <param name="csIDs">削除対象データのID配列</param>
        /// <returns>true:成功, false:失敗</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>このメソッドは非常に低速なので、使用する場合注意すること。</para>
        /// </remarks>
        public override bool Delete(HySIdentifier[] csIDs)
        {
            const string csMyMethodName = "Delete(HySID[])";

            // 演算結果IDごとに削除を実行する
            foreach (HySIdentifier csID in csIDs)
            {
                // 演算結果IDをサブフォルダパスに変換する
                string csSubFolderPath;
                if (!ConvertCalcResultIDToSubFolderPath(csID, out csSubFolderPath))
                {
                    HySDBALog.WriteOnline(m_csMyClassName, csMyMethodName, "ConvertCalcResultIDToSubFolderPath() error");
                    return false;
                }

                //// 演算結果IDを検索条件に変換する
                HySQueryCtlData csQuery = ConvertCalcResultIDToQuery(csID);

                // サブフォルダDBに処理を委譲する
                string csDBPath = GetDBBasePath();
                HySCalResultDataDBASub csDBASub = new HySCalResultDataDBASub(Path.Combine(csDBPath, csSubFolderPath));
                if (!csDBASub.Delete(csQuery))
                {
                    HySDBALog.WriteOnline(m_csMyClassName, csMyMethodName, "HySCalResultDataDBASub.Delete(HySQueryCtlData) error",
                        "ID", csID,
                        "SubFolderPath", csSubFolderPath);
                    return false;
                }
            }

            return true;
        }

        /// <summary><para>method outline:</para>
        /// <para>1件削除</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para> bool bRtn = Delete(csID) </para>
        /// </example>
        /// <param name="csID">削除対象データのID</param>
        /// <returns>true:成功, false:失敗</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>このメソッドは非常に低速なので、使用する場合注意すること。</para>
        /// </remarks>
        public override bool Delete(HySIdentifier csID)
        {
            const string csMyMethodName = "Delete(HySID)";

            // 配列バージョンで実行する
            HySIdentifier[] csIDs = new HySIdentifier[] { csID };
            if (!Delete(csIDs))
            {
                HySDBALog.WriteOnline(m_csMyClassName, csMyMethodName, "Delete(HySIdentifier[]) error",
                    "ID", csID);
                return false;
            }
            return true;
        }

        /// <summary><para>method outline:</para>
        /// <para>1件データ取得(ID指定)</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para> bool bRtn = GetData(csID, csStockData) </para>
        /// </example>
        /// <param name="csID">取得対象データID</param>
        /// <param name="csStockData">DB登録データ</param>
        /// <returns>true:成功, false:失敗</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>このメソッドは非常に低速なので、使用する場合は注意すること。</para>
        /// </remarks>
        public override bool GetData(HySIdentifier csID, out HySStockData csStockData)
        {
            const string csMyMethodName = "GetData";
            csStockData = null;

            // 配列バージョンで実行
            HySIdentifier[] csIDs = new HySIdentifier[] { csID };
            HySStockDataList csStockDataList;
            if (!GetDataList(csIDs, out csStockDataList))
            {
                HySDBALog.WriteOnline(m_csMyClassName, csMyMethodName, "GetDataList() error");
                return false;
            }

            // 出力パラメータ設定
            csStockData = (HySStockData)csStockDataList.GetFirstData();

            return true;
        }

        /// <summary><para>method outline:</para>
        /// <para>複数件データ取得(ID配列指定)</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para> bool bRtn = GetDataList(csIDs, csStockDataList) </para>
        /// </example>
        /// <param name="csIDs">取得対象データID配列</param>
        /// <param name="csStockDataList">DB登録データ一覧</param>
        /// <returns>true:成功, false:失敗</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>このメソッドは非常に低速なので、使用する場合は注意すること。</para>
        /// </remarks>
        public override bool GetDataList(HySIdentifier[] csIDs, out HySStockDataList csStockDataList)
        {
            const string csMyMethodName = "GetDataList";
            csStockDataList = null;

            HySStockDataList csStockDataListMerged = new HySStockDataList();

            // 演算結果IDごとに実行する
            foreach (HySIdentifier csID in csIDs)
            {
                // 演算結果IDをサブフォルダパスに変換する
                string csSubFolderPath;
                if (!ConvertCalcResultIDToSubFolderPath(csID, out csSubFolderPath))
                {
                    HySDBALog.WriteOnline(m_csMyClassName, csMyMethodName, "ConvertCalcResultIDToSubFolderPath() error");
                    return false;
                }

                // 演算結果IDを検索条件に変換する
                HySQueryCtlData csQuery = ConvertCalcResultIDToQuery(csID);

                // サブフォルダDBに処理を委譲する
                string csDBPath = GetDBBasePath();
                HySStockDataList csStockDataListBySubFolder;
                HySCalResultDataDBASub csDBASub = new HySCalResultDataDBASub(Path.Combine(csDBPath, csSubFolderPath));
                if (!csDBASub.SearchList(csQuery, out csStockDataListBySubFolder))
                {
                    HySDBALog.WriteOnline(m_csMyClassName, csMyMethodName, "HySCalResultDataDBASub.SearchList() error",
                        "SubFolderPath", csSubFolderPath);
                    return false;
                }
                // 見つからない→エラーで終了
                if (csStockDataListBySubFolder.GetCount() == 0)
                {
                    HySDBALog.WriteOnline(m_csMyClassName, csMyMethodName, "data not found",
                        "ID", csID);
                    return false;
                }

                // 出力結果をマージする
                MergeStockDataList(csStockDataListBySubFolder, csStockDataListMerged);

            }
            // データIDを演算結果IDにすりかえる
            ReplaceDataID(csStockDataListMerged);

            // 出力設定
            csStockDataList = csStockDataListMerged;

            return true;
        }

        /// <summary><para>method outline:</para>
        /// <para>複数件データ取得(検索条件指定)</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>bool bRtn  = SearchList(csQueryCtlData, out csStockDataList)</para>
        /// </example>
        /// <param name="csQueryCtlData">検索条件</param>
        /// <param name="csStockDataList">検索結果一覧</param>
        /// <returns>true:=正常, false:=異常</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        public override bool SearchList(HySQueryCtlData csQueryCtlData, out HySStockDataList csStockDataList)
        {
            const string csMyMethodName = "SearchList";
            csStockDataList = null;

            // 検索条件にIDが含まれる場合、演算結果IDに置き換える
            ReplaceQueryEntryOfID(csQueryCtlData);

            // 検索条件からサブフォルダパスを取得する
            string[] csSubFolderNames;
            if (!GetSubFolderListFromQuery(csQueryCtlData, out csSubFolderNames))
            {
                HySDBALog.WriteOnline(m_csMyClassName, csMyMethodName, "GetSubFolderListFromQuery() error");
                return false;
            }
            //// 検索条件で指定されていない場合、全てのサブフォルダを対象とする
            //if (csSubFolderNames.Length == 0)
            //{
            //    csSubFolderNames = GetAllSubFolderPath();
            //}

            // サブフォルダDBに処理を委譲する
            string csDBPath = GetDBBasePath();
            HySStockDataList csOutStockDataList = new HySStockDataList();
            SortedDictionary<string, SortedList<long, HySStockData>> csClassified =
				new SortedDictionary<string, SortedList<long, HySStockData>>();
            foreach (string csSubFolderPath in csSubFolderNames)
            {
                HySStockDataList csStockDataListBySubFolder;
                HySCalResultDataDBASub csDBASub = new HySCalResultDataDBASub(Path.Combine(csDBPath, csSubFolderPath));
                if (!csDBASub.SearchList(csQueryCtlData, out csStockDataListBySubFolder))
                {
                    HySDBALog.WriteOnline(m_csMyClassName, csMyMethodName, "HySCalResultDataDBASub.SearchList() error",
                        "SubFolderPath", csSubFolderPath);
                    return false;
                }

                SortedDictionary<string, SortedList<long, HySStockData>> csClassifiedSub =
					new SortedDictionary<string, SortedList<long, HySStockData>>();

				// 演算結果ID毎に分類
				ClassifyByCalResultID(csStockDataListBySubFolder, csClassifiedSub);

				// 検索件数上限設定のある場合は、検索件数制限する
                if (csQueryCtlData.GetLimitOfSearchResults() >= 0)
				{
					Limit(csClassifiedSub, csQueryCtlData.GetLimitOfSearchResults() - csClassified.Count);
				}

				// サブフォルダの結果をコピー
				foreach(string csKey in csClassifiedSub.Keys)
				{
					csClassified[csKey]=csClassifiedSub[csKey];
				}

				// 検索件数上限に達した場合は検索打ち切り
                if (csQueryCtlData.GetLimitOfSearchResults() >= 0 &&
				    (csClassified.Count == csQueryCtlData.GetLimitOfSearchResults()))
                {
                    break;
                }
            }

            // 演算結果をマージする
            HySStockDataList csStockDataListMerged = new HySStockDataList();
            MergeByCalResultID(csClassified, csStockDataListMerged);

            // データIDを演算結果IDに置き換える
            ReplaceDataID(csStockDataListMerged);

            // 出力設定
            csStockDataList = csStockDataListMerged;
            return true;
        }

        #endregion

        /// <summary>
        /// DB登録データに追加検索キーを設定する
        /// </summary>
        /// <remarks>
        /// <para>追加する情報は次の通り</para>
        /// <list type="number">
        /// <item>演算結果ID (非公開データ)</item>
        /// </list>
        /// </remarks>
        /// <param name="csStockData">DB登録データ</param>
        /// <returns>true:成功、false:失敗(必須キーが未設定)</returns>
        private static bool SetKeyInfoOfStockData(HySStockData csStockData)
        {
            const string csMyMethodName = "SetKeyInfoOfStockData";
            HySID csCalcResultID;
            if (!CreateCalcResultID(csStockData, out csCalcResultID))
            {
                HySDBALog.WriteOnline(m_csMyClassName, csMyMethodName, "CreateCalcResultID() error");
                return false;
            }

            long lKeyNumber = 0;
            HySDataHashTable csKeyInfo = csStockData.GetKeyInfo(ref lKeyNumber);

            // 追加検索キーを一旦クリアする
            HySString[] csKeys = new HySString[] {
                HySCalResultMetaKeyDefines.CALC_RESULT_ID
            };
            foreach (HySString csKey in csKeys)
            {
                if (csKeyInfo.GetObject(csKey) != null)
                {
                    csKeyInfo.DeleteData(csKey);
                }
            }

            // 追加検索キーに最新データをセットする
            csKeyInfo.AddObject(HySCalResultMetaKeyDefines.CALC_RESULT_ID, csCalcResultID);

            return true;
        }

        /// <summary><para>method outline:</para>
        /// <para>更新用追加検索キー設定</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>bool bRtn = SetKeyInfoOfStockDataForUpdate(csStockData)</para>
        /// </example>
        /// <param name="csStockData">更新用DB登録データ</param>
        /// <returns>true:成功、false:失敗</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>更新の場合には演算結果連番が設定されていないため(利用者はマージされたデータを受け取って
        ///       いるので、連番の存在を知らない)、演算結果連番を設定する。通常の追加検索キーも設定する。</para>
        /// </remarks>
        private static bool SetKeyInfoOfStockDataForUpdate(HySStockData csStockData)
        {
            if (!SetKeyInfoOfStockData(csStockData))
            {
                return false;
            }

            // 更新ではデータの分割数1となるので、演算結果連番は0でよい
            long lKeyNumber = 0;
            HySDataHashTable csKeyInfo = csStockData.GetKeyInfo(ref lKeyNumber);
            csKeyInfo.AddObject(HySCalResultMetaKeyDefines.CALC_RESULT_SEQNO, new HySLong(0));
            return true;            
        }

        /// <summary><para>method outline:</para>
        /// <para>演算結果ID生成</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>bool bRtn = CreateCalcResultID(csStockData, csCalcResultID)</para>
        /// </example>
        /// <param name="csStockData">DB保存データ</param>
        /// <param name="csCalcResultID">演算結果ID</param>
        /// <returns>true:成功、false:失敗(キーデータ不足)</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        private static bool CreateCalcResultID(HySStockData csStockData, out HySID csCalcResultID)
        {
            const string csMyMethodName = "CreateCalcResultID";
            csCalcResultID = null;

            // キーに必要なパラメータを取得する
            long lKeyNumber = 0;
            HySDataHashTable csKeyInfo = csStockData.GetKeyInfo(ref lKeyNumber);
            if (lKeyNumber == 0)
            {
                HySDBALog.WriteOnline(m_csMyClassName, csMyMethodName, "KeyInfo.KeyNumber is zero");
                return false;
            }
            HySObjectKind csSimKind = csStockData.GetSimKind();
            HySID csProjID = csStockData.GetSimID();
            HySObjectKind csDataKind = csStockData.GetDataKind();
            HySID csCalcLotID = (HySID)csKeyInfo.GetObject(HySCalResultDataQueryFieldNames.LOT_ID);
            HySID csElementID = (HySID)csKeyInfo.GetObject(HySCalResultDataQueryFieldNames.ELEMENT_ID);
            if ((object)csSimKind == null ||
                (object)csProjID == null ||
                (object)csCalcLotID == null ||
                (object)csElementID == null ||
                (object)csDataKind == null)
            {
                HySDBALog.WriteOnline(m_csMyClassName, csMyMethodName, "required field(s) not specified",
                    "SimKind", csSimKind,
                    "ProjID", csProjID,
                    "LotID", csCalcLotID,
                    "ElementID", csElementID,
                    "DataKind", csDataKind);
                return false;
            }

            // 演算結果IDを生成する
            string csSimKindString = csSimKind.ToString();
            string csProjIDString = csProjID.ToString();
            string csCalcLotIDString = csCalcLotID.ToString();
            string csDataKindString = csDataKind.ToString();
            string csElementIDString = csElementID.ToString();

            string csCalcResultIDString = HySFileRecordIDUtility.CreateIDString(
                csSimKindString,    // シミュレータ種別
                csProjIDString,     // プロジェクトID
                csCalcLotIDString,  // 演算ロットID
                csDataKindString,   // データ種別
                csElementIDString   // 要素ID
                );
            csCalcResultID = new HySID(csCalcResultIDString);
            return true;
        }

        /// <summary><para>method outline:</para>
        /// <para>検索条件のIDエントリ置き換え</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>ReplaceQueryEntryOfID(csQueryCtlData)</para>
        /// </example>
        /// <param name="csQueryCtlData">検索条件データ</param>
        /// <returns>無し</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>検索条件にIDエントリが含まれる場合、フィールド名を演算結果IDに置き換える。</para>
        /// <para><see cref="SearchList"/>および<see cref="Delete(HySQueryCtlData)"/>の前処理。</para>
        /// </remarks>
        private static void ReplaceQueryEntryOfID(HySQueryCtlData csQueryCtlData)
        {
            HySQueryEntryData csQueryEntryData = csQueryCtlData.GetQueryEntryData(HySCalResultDataQueryFieldNames.ID);
            if (csQueryEntryData != null)
            {
                // IDのエントリを削除する
                csQueryCtlData.RemoveQueryEntryData(HySCalResultDataQueryFieldNames.ID);
                // フィールド名を演算結果IDに変更して再登録する
                csQueryEntryData.SetFieldName(HySCalResultMetaKeyDefines.CALC_RESULT_ID);
                csQueryCtlData.SetQueryEntryData(csQueryEntryData);
            }
        }

        /// <summary><para>method outline:</para>
        /// <para>演算結果IDをサブフォルダパスに変換</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>bool bRtn = ConvertCalcResultIDToSubFolderPath(csID, csSubFolderPath)</para>
        /// </example>
        /// <param name="csID">演算結果ID</param>
        /// <param name="csSubFolderPath">サブフォルダパス(相対パス)</param>
        /// <returns>true:成功、false:失敗(不正なID形式)</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        private static bool ConvertCalcResultIDToSubFolderPath(HySIdentifier csID, out string csSubFolderPath)
        {
            const string csMyMethodName = "ConvertCalcResultIDToSubFolderPath";

            csSubFolderPath = null;
            // 演算結果IDからサブフォルダを特定する
            string[] csIDParts = HySFileRecordIDUtility.SplitIDString(csID.ToString());
            if (csIDParts.Length != 5)
            {
                HySDBALog.WriteOnline(m_csMyClassName, csMyMethodName, "invalid ID specified",
                    "ID", csID.ToString());
                return false;
            }
            csSubFolderPath = HySFileRecordIDUtility.CreateIDStringWithoutEscaping(csIDParts[0], csIDParts[1], csIDParts[2]);
            return true;
        }

        /// <summary><para>method outline:</para>
        /// <para>演算結果ID配列をサブフォルダパス配列に変換</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>bool bRtn = ConvertCalcResultIDToSubFolderPaths(csIDs, csSubFolderPaths)</para>
        /// </example>
        /// <param name="csIDs">演算結果ID配列</param>
        /// <param name="csSubFolderPaths">サブフォルダパス配列(相対パス)</param>
        /// <returns>true:成功、false:失敗(不正なID形式)</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        private static bool ConvertCalcResultIDToSubFolderPaths(HySIdentifier[] csIDs, out string[] csSubFolderPaths)
        {
            const string csMyMethodName = "ConvertCalcResultIDToSubFolderPaths";

            csSubFolderPaths = new string[csIDs.Length];
            for (int idx = 0; idx < csSubFolderPaths.Length; ++idx)
            {
                if (!ConvertCalcResultIDToSubFolderPath(csIDs[idx], out csSubFolderPaths[idx]))
                {
                    HySDBALog.WriteOnline(m_csMyClassName, csMyMethodName, "ConvertCalcResultIDToSubFolderPath() error");
                    return false;
                }
            }
            return true;
        }

        /// <summary><para>method outline:</para>
        /// <para>演算結果IDを検索条件に変換</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>HySQueryCtlData csQueryCtlData = ConvertCalcResultIDToQuery(csID)</para>
        /// </example>
        /// <param name="csID">演算結果ID</param>
        /// <returns>検索条件</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        private static HySQueryCtlData ConvertCalcResultIDToQuery(HySIdentifier csID)
        {
            HySQueryCtlData csQuery = new HySQueryCtlData();
            csQuery.SetQueryEntryData(new HySQueryEntryData(HySCalResultMetaKeyDefines.CALC_RESULT_ID, typeof(HySID),
                HySQueryCompareMode.EQUALS, csID));
            return csQuery;
        }

        /// <summary><para>method outline:</para>
        /// <para>演算結果ID配列を検索条件に変換</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>HySQueryCtlData csQueryCtlData = ConvertCalcResultIDsToQuery(csIDs)</para>
        /// </example>
        /// <param name="csIDs">演算結果ID</param>
        /// <returns>検索条件</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        private static HySQueryCtlData ConvertCalcResultIDsToQuery(HySIdentifier[] csIDs)
        {
            HySQueryCtlData csQuery = new HySQueryCtlData();
            csQuery.SetQueryEntryData(new HySQueryEntryData(HySCalResultMetaKeyDefines.CALC_RESULT_ID, typeof(HySID),
                HySQueryCompareMode.IN, csIDs));
            return csQuery;
        }

        /// <summary><para>method outline:</para>
        /// <para>DB基準パス取得(フルパス/相対パス指定)</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>string csDBPath = GetDBBasePath(bIsFullPath)</para>
        /// </example>
        /// <param name="bIsFullPath">true:フルパス、false:相対パス</param>
        /// <returns>パス文字列</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        private string GetDBBasePath(bool bIsFullPath)
        {
            // 環境設定からDBフォルダを取得する
            string csDBPath = null;
            HySString csHySStringDBPath = HySEnvInf.GetEnvInf(ENVKEY_DBPATH);
            if (((object)csHySStringDBPath) != null)
            {
                csDBPath = csHySStringDBPath.ToString();
            }
            else
            {
                csDBPath = DEFAULT_DBPATH;  // fallback value
            }
            // 末尾にディレクトリ区切り文字を追加する
            if (csDBPath[csDBPath.Length - 1] != Path.DirectorySeparatorChar)
            {
                csDBPath += Path.DirectorySeparatorChar;
            }

            // フルパス指定の時、DBディレクトリと連結する
            if (bIsFullPath)
            {
                string csHomePath = HySEnvInf.GetDBDirectory().ToString();
                csDBPath = csHomePath + csDBPath;
            }

            return csDBPath;
        }

        /// <summary><para>method outline:</para>
        /// <para>DB基準パス取得(相対パス指定)</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>string csDBPath = GetDBBasePath()</para>
        /// </example>
        /// <param name="">無し</param>
        /// <returns>パス文字列</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        private string GetDBBasePath()
        {
            return GetDBBasePath(false);
        }

        /// <summary><para>method outline:</para>
        /// <para>DB保存データからサブフォルダ名の取得</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>bool bRtn = GetSubFolderPathFromStockData(csStockData, csSubFolderPath)</para>
        /// </example>
        /// <param name="csStockData">DB保存データ</param>
        /// <param name="csSubFolderPath">csStockDataが格納されるべきサブフォルダの名称(相対パス)</param>
        /// <returns>true:成功、false:失敗(キーとなるデータが設定されていない)</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>実行時刻から年月を取得し、サブフォルダ文字列を構築する</para>
        /// </remarks>
        private bool GetSubFolderPathFromStockData(HySStockData csStockData, out string csSubFolderPath)
        {
            const string csMyMethodName = "GetSubFolderPathFromStockData";

            csSubFolderPath = null;

            // 必要なキー情報を取得
            long lKeyNumber = 0;
            HySDataHashTable csKeyInfo = csStockData.GetKeyInfo(ref lKeyNumber);
            HySObjectKind csSimKind = csStockData.GetSimKind();
            HySID csProjID = csStockData.GetSimID();
            HySID csCalcLotID = null;
            if (lKeyNumber > 0)
            {
                csCalcLotID = (HySID)csKeyInfo.GetObject(HySCalResultDataQueryFieldNames.LOT_ID);
            }
            // キー情報不足チェック
            if (null == csSimKind || null == csProjID || null == csCalcLotID)
            {
                HySDBALog.WriteOnline(m_csMyClassName, csMyMethodName, "key field(s) not specified",
                    "simulator-kind", csSimKind,
                    "project-id", csProjID,
                    "calc-lot-id", csCalcLotID);
                return false;
            }

            // 共通ID文字列生成ロジックでフォルダ名に変換する
            csSubFolderPath = HySFileRecordIDUtility.CreateIDString(csSimKind.ToString(), csProjID.ToString(), csCalcLotID.ToString());
            return true;
        }

        /// <summary><para>method outline:</para>
        /// <para>HySStockDataListに格納されたDB格納データをサブフォルダごとに振り分け</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>bool bRtn = DispatchHySStockDataList(csStockDataList, csStockDataListDict)</para>
        /// </example>
        /// <param name="csStockDataList">DB格納データリスト</param>
        /// <param name="csStockDataListDict">DB格納データリスト辞書。サブフォルダ名がキー</param>
        /// <returns>true:成功、false:失敗(不正なデータが混在している)</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        private bool DispatchHySStockDataList(HySStockDataList csStockDataList, out Dictionary<string, HySStockDataList> csStockDataListDict)
        {
            const string csMyMethodName = "DispatchHySStockDataList";

            csStockDataListDict = new Dictionary<string, HySStockDataList>();
            long lDataCount = csStockDataList.GetCount();
            csStockDataList.SetCursorFirst();

            for (long lIdx = 0; lIdx < lDataCount; ++lIdx)
            {
                // DB保存データを取得し、カーソルを進める
                HySStockData csStockData = (HySStockData)csStockDataList.GetCursorData();
                csStockDataList.MoveCursorNext();

                // DB保存データからサブフォルダ名を取得する
                string csSubFolderName;
                if (!GetSubFolderPathFromStockData(csStockData, out csSubFolderName))
                {
                    HySDBALog.WriteOnline(m_csMyClassName, csMyMethodName, "GetSubFolderPathFromStockData() error");
                    return false;
                }

                // 振り分けStockDataListを辞書から取得する
                HySStockDataList csDispatchedStockDataList;
                if (csStockDataListDict.ContainsKey(csSubFolderName))
                {
                    csDispatchedStockDataList = csStockDataListDict[csSubFolderName];
                }
                else
                {
                    // 未登録なら作って登録する
                    csDispatchedStockDataList = new HySStockDataList();
                    csStockDataListDict.Add(csSubFolderName, csDispatchedStockDataList);
                }

                // DB保存データを振り分ける
                csDispatchedStockDataList.AddLast(csStockData);
            }

            return true;
        }

        /// <summary><para>method outline:</para>
        /// <para>検索条件からサブフォルダパスを取得</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>bool bRtn = GetSubFolderListFromQuery(csQuery, csSubFolderPaths)</para>
        /// </example>
        /// <param name="csQuery">検索条件</param>
        /// <param name="csSubFolderPaths">サブフォルダパス(相対パス)</param>
        /// <returns>true:成功、false:失敗(不正なデータ)</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        private bool GetSubFolderListFromQuery(HySQueryCtlData csQuery, out string[] csSubFolderPaths)
        {
            const string csMyMethodName = "GetSubFolderListFromQuery";
            csSubFolderPaths = null;

            List<string> csSimKindList = new List<string>();
            List<string> csProjIDList = new List<string>();
            List<string> csCalcLotIDList = new List<string>();

            HySQueryEntryData csQEntry;

            // 検索条件からシミュレータ種別を取得する
            csQEntry = csQuery.GetQueryEntryData(HySCalResultDataQueryFieldNames.SIM_KIND);
            if (csQEntry != null)
            {
                HySQueryCompareMode eCompareMode = csQEntry.GetCompareMode();
                if (eCompareMode == HySQueryCompareMode.EQUALS)
                {
                    csSimKindList.Add(((HySObjectKind)csQEntry.GetValueToCompare()).ToString());
                }
                else if (eCompareMode == HySQueryCompareMode.IN)
                {
                    HySObjectKind[] csIDs = (HySObjectKind[])csQEntry.GetValueToCompare();
                    foreach (HySObjectKind csID in csIDs)
                    {
                        csSimKindList.Add(csID.ToString());
                    }
                }
                else if (eCompareMode == HySQueryCompareMode.NOT)
                {
                    // 全フォルダを探索する必要があるので何もしない
                }
                else
                {
                    // 非サポートの組み合わせ
                    HySDBALog.WriteOnline(m_csMyClassName, csMyMethodName, "unsupported query",
                        "FieldName", csQEntry.GetFieldName(),
                        "CompareMode", csQEntry.GetCompareMode());
                    return false;
                }
            }
            // 検索条件からプロジェクトIDを取得する
            csQEntry = csQuery.GetQueryEntryData(HySCalResultDataQueryFieldNames.SIM_ID);
            if (csQEntry != null)
            {
                HySQueryCompareMode eCompareMode = csQEntry.GetCompareMode();
                if (eCompareMode == HySQueryCompareMode.EQUALS)
                {
                    csProjIDList.Add(((HySID)csQEntry.GetValueToCompare()).ToString());
                }
                else if (eCompareMode == HySQueryCompareMode.IN)
                {
                    HySID[] csIDs = (HySID[])csQEntry.GetValueToCompare();
                    foreach (HySID csID in csIDs)
                    {
                        csProjIDList.Add(csID.ToString());
                    }
                }
                else if (eCompareMode == HySQueryCompareMode.NOT)
                {
                    // 全フォルダを探索する必要があるので何もしない
                }
                else
                {
                    // 非サポートの組み合わせ
                    HySDBALog.WriteOnline(m_csMyClassName, csMyMethodName, "unsupported query",
                        "FieldName", csQEntry.GetFieldName(),
                        "CompareMode", csQEntry.GetCompareMode());
                    return false;
                }
            }
            // 検索条件から演算ロットIDを取得する
            csQEntry = csQuery.GetQueryEntryData(HySCalResultDataQueryFieldNames.LOT_ID);
            if (csQEntry != null)
            {
                HySQueryCompareMode eCompareMode = csQEntry.GetCompareMode();
                if (eCompareMode == HySQueryCompareMode.EQUALS)
                {
                    csCalcLotIDList.Add(((HySID)csQEntry.GetValueToCompare()).ToString());
                }
                else if (eCompareMode == HySQueryCompareMode.IN)
                {
                    HySID[] csIDs = (HySID[])csQEntry.GetValueToCompare();
                    foreach (HySID csID in csIDs)
                    {
                        csCalcLotIDList.Add(csID.ToString());
                    }
                }
                else if (eCompareMode == HySQueryCompareMode.NOT)
                {
                    // 全フォルダを探索する必要があるので何もしない
                }
                else
                {
                    // 非サポートの組み合わせ
                    HySDBALog.WriteOnline(m_csMyClassName, csMyMethodName, "unsupported query",
                        "FieldName", csQEntry.GetFieldName(),
                        "CompareMode", csQEntry.GetCompareMode());
                    return false;
                }
            }

            // シミュレータ種別、プロジェクトIDと演算ロットIDからフォルダパスを組み立てる
            if (csSimKindList.Count == 0)
            {
                csSimKindList.Add(string.Empty);
            }
            if (csProjIDList.Count == 0)
            {
                csProjIDList.Add(string.Empty);
            }
            if (csCalcLotIDList.Count == 0)
            {
                csCalcLotIDList.Add(string.Empty);
            }
            DirectoryInfo csDBBaseDirInfo = new DirectoryInfo(GetDBBasePath(true)); // DB基準フォルダのDirectoryInfo
            if (!csDBBaseDirInfo.Exists)
            {
                csSubFolderPaths = new string[0];
                return true;
            }
            Dictionary<string, string> csSubFolderPathDict = new Dictionary<string, string>();  // サブフォルダ名辞書(重複除去用)
            foreach (string csSimKind in csSimKindList)
            {
                foreach (string csProjID in csProjIDList)
                {
                    foreach (string csCalcLotID in csCalcLotIDList)
                    {
                        // 検索パターンを生成する(ワイルドカードを含む場合がある)
                        string csPattern = HySFileRecordIDUtility.CreateIDWildCardPattern(csSimKind, csProjID, csCalcLotID);
                        // サブディレクトリを検索する
                        DirectoryInfo[] csSubDirInfos = csDBBaseDirInfo.GetDirectories(csPattern, SearchOption.TopDirectoryOnly);
                        // 見つかったものを辞書に入れる
                        foreach (DirectoryInfo csSubDirInfo in csSubDirInfos)
                        {
                            string csFolderName = csSubDirInfo.Name;
                            if (!csSubFolderPathDict.ContainsKey(csFolderName))
                            {
                                csSubFolderPathDict.Add(csFolderName, csFolderName);
                            }
                        }
                    }
                }
            }

            // 出力パラメータ設定
            csSubFolderPaths = new string[csSubFolderPathDict.Count];
            int i = 0;
            foreach (string csSubFolderPath in csSubFolderPathDict.Keys)
            {
                csSubFolderPaths[i++] = csSubFolderPath;
            }

            return true;
        }

        /// <summary><para>method outline:</para>
        /// <para>全てのサブフォルダパス取得</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>string[] path = GetAllSubFolderPath()</para>
        /// </example>
        /// <param name="">無し</param>
        /// <returns>サブフォルダパス</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        private string[] GetAllSubFolderPath()
        {
            // DB基準パス(フルパス)を取得する
            string csDBPath = GetDBBasePath(true);

            // DB基準パス配下のディレクトリ一覧を収集する
            List<string> csSubFolderPathList = new List<string>();
            foreach (string csSubFolderPath in Directory.GetDirectories(csDBPath))
            {
                csSubFolderPathList.Add(csSubFolderPath.Replace(csDBPath, string.Empty));
            }
            return csSubFolderPathList.ToArray();
        }

        /// <summary><para>method outline:</para>
        /// <para>データのIDを演算結果IDに差し替え</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>ReplaceDataID(csStockDataList)</para>
        /// </example>
        /// <param name="csStockDataList">DB保存データリスト</param>
        /// <returns>無し</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        private static void ReplaceDataID(HySStockDataList csStockDataList)
        {
            long lDataCount2 = csStockDataList.GetCount();
            csStockDataList.SetCursorFirst();
            for (long lIdx = 0; lIdx < lDataCount2; ++lIdx)
            {
                HySStockData csStockData = (HySStockData)csStockDataList.GetCursorData();
                csStockDataList.MoveCursorNext();

                ReplaceDataID(csStockData);
            }
        }

        /// <summary><para>method outline:</para>
        /// <para>データのIDを演算結果IDに差し替え</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>ReplaceDataID(csStockData)</para>
        /// </example>
        /// <param name="csStockData">DB保存データ</param>
        /// <returns>無し</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>DB保存データのIDを、追加キー情報の演算結果IDに入れ替える。
        /// また、追加キー情報にある演算結果連番を削除する。</para>
        /// <para><see cref="SearchList"/>、<see cref="GetData"/>、<see cref="GetDataList"/>の共通処理。</para>
        /// </remarks>
        private static void ReplaceDataID(HySStockData csStockData)
        {
            long lKeyNumber = 0;
            HySDataHashTable csKeyInfo = csStockData.GetKeyInfo(ref lKeyNumber);
            HySID csCalcResultID = (HySID)csKeyInfo.GetObject(HySCalResultMetaKeyDefines.CALC_RESULT_ID);
            // データIDを置き換える
            csStockData.SetID(csCalcResultID);
            // 非公開キーを削除する
            csKeyInfo.DeleteData(HySCalResultMetaKeyDefines.CALC_RESULT_ID);
            csKeyInfo.DeleteData(HySCalResultDataQueryFieldNames.CALC_RESULT_SEQNO);
        }

        /// <summary><para>method outline:</para>
        /// <para>演算結果ID取得</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>HySID csID = GetCalcResultIDFromStockData(csStockData)</para>
        /// </example>
        /// <param name="csStockData">DB保存データ</param>
        /// <returns>演算結果ID</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        private static HySID GetCalcResultIDFromStockData(HySStockData csStockData)
        {
            long lKeyNumber = 0;
            HySDataHashTable csKeyInfo = csStockData.GetKeyInfo(ref lKeyNumber);
            HySID csCalcResultID = (HySID)csKeyInfo.GetObject(HySCalResultMetaKeyDefines.CALC_RESULT_ID);
            return csCalcResultID;
        }

        /// <summary><para>method outline:</para>
        /// <para>演算結果連番取得</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>HySLong csSeq = GetCalcResultSeqnoFromStockData(csStockData)</para>
        /// </example>
        /// <param name="csStockData">DB保存データ</param>
        /// <returns>演算結果連番</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        private static HySLong GetCalcResultSeqnoFromStockData(HySStockData csStockData)
        {
            long lKeyNumber = 0;
            HySDataHashTable csKeyInfo = csStockData.GetKeyInfo(ref lKeyNumber);
            HySLong csCalcResultSeqno = (HySLong)csKeyInfo.GetObject(HySCalResultMetaKeyDefines.CALC_RESULT_SEQNO);
            return csCalcResultSeqno;
        }

        /// <summary><para>method outline:</para>
        /// <para>DB保存データをマージ</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>MergeStockDataList(csStockDataListIn, csStockDataListMerged)</para>
        /// </example>
        /// <param name="csStockDataListIn">入力検索結果(マージ前)</param>
        /// <param name="csStockDataListMerged">出力検索結果(マージ後)</param>
        /// <returns>無し</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>演算結果データDB特有の処理。</para>
        /// <para>複数の検索結果レコードを演算結果ID、演算結果連番でソートする。</para>
        /// <para>同一演算結果IDのレコードは、演算結果連番順にマージして1レコードにする。</para>
        /// </remarks>
        private static void MergeStockDataList(HySStockDataList csStockDataListIn, HySStockDataList csStockDataListMerged)
        {
            SortedDictionary<string, SortedList<long, HySStockData>> csClassified =
				new SortedDictionary<string,SortedList<long,HySStockData>>();
            ClassifyByCalResultID(csStockDataListIn, csClassified);
            MergeByCalResultID(csClassified, csStockDataListMerged);
        }

        /// <summary><para>method outline:</para>
        /// <para>演算結果分類(演算結果ID)</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>ClassifyByCalResultID(csStockDataList, csClassified)</para>
        /// </example>
        /// <param name="csStockDataList">演算結果データリスト</param>
        /// <param name="csClassified">演算結果ID毎に分類された演算結果データ</param>
        /// <returns>無し</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>演算結果ID毎に分類された演算結果データは、演算結果連番で順にソートされている。</para>
        /// </remarks>
        private static void ClassifyByCalResultID(HySStockDataList csStockDataList, SortedDictionary<string, SortedList<long, HySStockData>> csClassified)
        {
            // 検索結果を演算結果ID、演算結果連番で振り分ける
            csStockDataList.SetCursorFirst();
            for (long i = 0; i < csStockDataList.GetCount(); ++i)
            {
                HySStockData csStockData = (HySStockData)csStockDataList.GetCursorData();

                string csCalResultID = ((HySIdentifier)GetCalcResultIDFromStockData(csStockData)).ToString();
                long lSeqNo = GetCalcResultSeqnoFromStockData(csStockData).m_lValue;

                if (!csClassified.ContainsKey(csCalResultID))
                {
                    csClassified.Add(csCalResultID, new SortedList<long, HySStockData>());
                }
 
                csClassified[csCalResultID].Add(lSeqNo, csStockData);
                csStockDataList.MoveCursorNext();
            }
        }

        /// <summary><para>method outline:</para>
        /// <para>要素数制限処理</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>Limit(csData, iNumLimit)</para>
        /// </example>
        /// <param name="csData">コレクションデータ</param>
        /// <param name="iNumLimit">制限値</param>
        /// <returns>無し</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>csDataの要素数をiNumLimitで指定された値以下に制限する。</para>
        /// </remarks>
		private static void Limit(SortedDictionary<string, SortedList<long, HySStockData>> csData, int iNumLimit)
		{
			int iRemainder = iNumLimit - csData.Count;
			// 残りが負の場合は制限値を超えているので制限値にまでに切り詰める
			if (iRemainder < 0)
			{
                KeyValuePair<string, SortedList<long, HySStockData>>[] csKeys =
					new KeyValuePair<string, SortedList<long, HySStockData>>[csData.Count];
				csData.CopyTo(csKeys, 0);
				Array.Reverse(csKeys);	// 演算結果IDでソートした場合の後ろの部分を取り除くため
				Array.Resize(ref csKeys, -iRemainder);
				foreach(KeyValuePair<string, SortedList<long, HySStockData>> csKey in csKeys)
				{
					csData.Remove(csKey.Key);
				}
			}
		}

        /// <summary><para>method outline:</para>
        /// <para>演算結果データマージ処理</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>MergeByCalResultID(csData, csStockDataListMerged)</para>
        /// </example>
        /// <param name="csData">演算結果ID毎に分類された演算結果データ</param>
        /// <param name="csStockDataListMerged">マージされた演算結果データ</param>
        /// <returns>無し</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>csDataの連番の先頭のHySStockDataは変更(演算結果連番のマージに使用)されて、csStockDataListMergedに設定される。</para>
        /// </remarks>
        private static void MergeByCalResultID(SortedDictionary<string, SortedList<long, HySStockData>> csData, HySStockDataList csStockDataListMerged)
        {
            const string csMyMethodName = "MergeByCalResultID";
            // 演算結果IDごとにマージする
            foreach (string csCalResultID in csData.Keys)
            {
                // 演算結果IDをキーに保存データのソート済みリストを取り出す
                SortedList<long, HySStockData> csList = csData[csCalResultID];
                // 先頭の時系列データを取り出す
                HySTimeSeriesBase csFirst = GetTimeSeriesBase(csList, 0);
                // 実データがある場合、2番目以降の時系列データを先頭の時系列データに追加する
                if (csFirst != null)
                {
                    for (int i = 1; i < csList.Keys.Count; ++i)
                    {
                        HySTimeSeriesBase csNext = GetTimeSeriesBase(csList, i);
                        if (csNext == null)
                        {
                            HySDBALog.WriteOnline(m_csMyClassName, csMyMethodName, "warning: next data is null",
                                "ID", csCalResultID);
                            continue;
                        }
                        csFirst.SetMaxHoldDataNum(csFirst.GetCount() + csNext.GetCount());
                        csFirst.AddTimeSeriesData(csNext);
                    }
                }
                // 先頭データを出力リストに移しかえる
                csStockDataListMerged.AddLast(csList[csList.Keys[0]]);
            }
        }

        /// <summary><para>method outline:</para>
        /// <para>時系列データ取得</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>HySTimeSeriesBase csTimeSeries = GetTimeSeriesBase(csList, iPos)</para>
        /// </example>
        /// <param name="csList">演算結果連番でソートされた演算結果データ</param>
        /// <param name="iPos">取得位置</param>
        /// <returns>時系列データ(演算結果データ)</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>演算結果連番でソートされたHySStockDataから時系列データを取得するためのヘルパ関数。</para>
        /// </remarks>
        private static HySTimeSeriesBase GetTimeSeriesBase(SortedList<long, HySStockData> csList, int iPos)
        {
            HySStockData csStockData = csList[csList.Keys[iPos]];
            return (HySTimeSeriesBase)csStockData.GetData();
        }
    }
}
