// == LICENSE INFORMATION ==
/*
 * First author tiritomato 2013.
 * This program is distributed under the GNU Lesser General Public License(LGPL ver3).
 * support blog (Japanese only) http://d.hatena.ne.jp/tiri_tomato/
 */
// == LICENSE INFORMATION ==

namespace DotNetEx.IO
{
    //! @addtogroup DotNetEx-IO名前空間
    //! @{

    /// @cond <summary>パス文字列クラス(開発中)。</summary> @endcond
    //! @brief パス文字列クラス(開発中)。
    //! @details ここで言うパスとは、ルート要素に相対指定が続く文字列全般を指します。
    //! @par
    //!     \>\> 通常、ディレクトリやファイルパスのほとんどの部分は、単にスラッシュやバックスラッシュで区切られるだけの文字列です。
    //!     ただWindowsのドライブ指定やURLのプロトコル指定などパスに「コマンド的な前置詞」がつく事も一般的で、この前置詞部分をRoot要素として、Relationと別々に管理します。</small>
    //!
    //! またポリシーと呼ばれる解析設定(ルートと相対指定を分離するための正規表現パターンや、大文字小文字の無視、ディレクトリ区切りや無効文字郡の設定)が、
    //! DotNetEx.IO.PathPolicyクラスと、読み取り用のDotNetEx.IO.IPathPolicyインターフェースとして提供されます。標準的なポリシーは既にプリセットされていて継承によるカスタマイズも可能です。
    //! @par 特徴
    //! 全体的に見て、.Netの標準Path処理ライブラリに比べると、DotNetEx.IO.Pathクラスは、かなり性格が異なります。
    //! @par
    //! - 具体的にファイルの有無などを問う事はない。
    //!
    //!     「存在しないパスだから正確に処理ができず例外発生する」といった仕様はありません。一定のルールで記述された文字列を文字列として処理するだけのクラスです。
    //!     解析設定が矛盾すると例外が起こりますが、扱う文字列の文法不整合などは例外とみなさず暗黙に整形されます（おおむね「ただの誤入力」のような扱いで要素が削除される傾向が強い）。
    //! .
    //! - つまり、意図しないパスに変形させられるかも
    //!
    //!     代入するだけで相当数の文法解析工程や補正処理に通されます。とくにWindowsパスとして一般的に使えそうもない部位はガリガリ削られていくので、
    //!     もしかすると代入するだけで意図しないパスに変形させられるかもしれません。
    //!     その意味では、例えばUNIXなど本当に制限の少ないファイルシステムも含めてしっかりサポートしたい時のパス処理には適しません。
    //!     一方で「パスの誤入力のようです。ちょっと修正してみましたが、やっぱり開けませんでした」といった、ヒューマンエラーの解決を含めた対話系の処理を簡単に提供できます。<br/>
    //!     <small>なお積極的に整形されるのはRelationで、対照的にRootは、最初に文字列解析して分離された後、前後の空白トリムが行われる程度です。その代わり、パス同士の結合処理などRootだけ丸ごと無視されて消える事があります。</small>
    //! .
    //! また単なる文字列処理なので、例えばハードリンクした同じ内容の別名のファイルがあっても、パス文字列としては一致しないため、Pathクラスで比較しても同じものとは判定できません。
    //! @par IPathPolicyの設定方法について
    //! @details PathクラスはPath.PolicyプロパティでPathクラス個別に文字列の解析ポリシーを保持する事が出来ます(コンストラクトでも文字列とPolicyを指定できる)。
    //! 個別のPath.Policyは、コピーコンストラクトや部分抽出するプロパティなどで生成した新しい(部分)クローンPathに伝播していく特性があります。この時次のような問題があります。
    //! - Path同士の処理をする時、異なるポリシーが衝突するシナリオがありうる。
    //! - 全てのPath.Policyを明示的かつ適切に管理するには、無視できない設計コストがかかる。
    //!
    //! そのためPath.Policyはnullで省略出来ます。この時はシステム共通の静的なPathPolicy.Currentが参照されます。一方のPath.Policyがnullで、もう一方がnullでなければ、nullでない方のPath.Policyが使用されます。
    //! どちらのPath.Policyもnullではなく、異なるポリシーインスタンスを参照する時だけ衝突してPolicyConflictExceptionがスローされます。よって次のようなスタイルが推奨されます。
    //! - Pathクラスは基本的にPath.Policyをnullで省略し、PathPolicy.Currentから一括管理する。
    //! - 局所的な特例処理を行うところだけ一時的に明示的なPath.Policyを設定し、伝播を止めて衝突を回避するなど、プログラマが細かく個別に管理する。
    //! .
    //!
    //! @par デフォルトの解析ポリシー
    //! @details Windowsのドライブ指定から始まるパスとUNCパス、あるいはスキーム指定の付いたURIなどをだいたい画一的に（少なくとも例外をスローせずに）
    //! 管理できるポリシーが設定されています。
    //! - ディレクトリセパレータはスラッシュまたはバックスラッシュです
    //! - 「最後にコロンが出現して、そこに続いて1文字以上連続するディレクトリセパレータ」または「最初に１文字以上連続するディレクトリセパレータ」までがRoot。
    //! - ルート以外は全てRelationとして認識されます
    //!     - /document/item.txt ルート＝"/" 相対パス="document/item.txt"
    //!     - //document/item.txt ルート＝"//" 相対パス="document/item.txt"
    //!     - user:/document/item.txt ルート＝"user:/" 相対パス="document/item.txt"
    //!     - c:/document/item.txt ルート＝"c:/" 相対パス="document/item.txt"
    //!     - //document/item.txt ルート＝"//" 相対パス="document/item.txt"
    //!     - file:///c:/document/item.txt ルート＝"file:///c:/" 相対パス="document/item.txt"
    //!     
    //!     標準の解析ポリシーでは、最初のディレクトリセパレータは常にルート側に吸収されています。なおこの標準の解析ポリシーが一般的パス書式の中で問題になるケースとしては、UNIXの環境変数のパス設定のように、
    //!     コロンを使って複数のパスを連結した文字列があります。これをそのまま代入すると、最後のコロンが出現するまでの部分が長大なRootとして解析されます。
    //!     このケースでは新しく専用のポリシーを作って調整するか、あらかじめSplitしてください。<br>
    //!     また簡易式ではないURL形式で、ユーザー名やパスワードあるいはポート番号などを含めるフォーマットも対応していません。
    //!     .
    //! .
    //!
    //! @par 暗黙の整形処理
    //!     DotNetEx.IO.Pathでは以下の整形処理が強制的に実行されます（ポリシーを変更すればセパレータ文字など変えられますが、以下の基本的な動作そのものを停止する事は対応していません）。
    //! @details
    //! @par
    //!     - 末尾のディレクトリセパレータは常に削除
    //!     
    //!         パス文字列だけではファイルとフォルダの区別はありません。ルート要素を除き、末尾のディレクトリセパレータは常に削除されます。
    //!         
    //!         例）"C:\folder\" >> "C:\folder"
    //!
    //!     - ディレクトリ/ファイル要素名のトリム
    //!   
    //!         ルート要素を除き、相対パス中のディレクトリ/ファイル要素名の前後にある空白は常にトリムされます。
    //!   
    //!         例）"C:\ folder \" >> "C:\folder"
    //!
    //!     - 連続するディレクトリセパレータの削除
    //!
    //!         ルート要素を除き、相対パス部分で出現する連続セパレータは全て最初の一つだけが残り、他は削除されます(空白のディレクトリ名をはさんでも連続するセパレータとみなされます)。
    //!   
    //!         例）"\\folder\ /hoge" >> "\\folder\hoge"
    //!
    //!     - 末尾の無為な拡張子セパレータ（またはその連続）の削除と、例外的に保護されるケース。
    //!   
    //!         Windows環境では、「ファイルパスの末尾に拡張子区切りのドットと空白が連続すると無視される（その名前で作る事も出来ない）」というルールがあります。
    //!         さらに言えば、"C:\hoge.txt.."というパスで"c:\hoge.txt"を開く事が出来ます。
    //!         このため比較処理を行う場合には"C:\hoge.txt"と"C:\hoge.txt.."は一致する方が望ましくなり、Pathクラスでは、文字列を代入した時点で末尾の..（ポリシーで指定された拡張子区切り、またはその連続）が削除されるようになっています。
    //!         ただし、相対パスを意味する.や..も存在し、末尾の無為な拡張子区切りと判別できません。
    //!         なのでポリシーによって指定された相対パスのパターンとディレクトリ/ファイル名が前方一致する時、それらの最長文字数だけは無為な拡張子とはみなされないようになります。
    //!         つまり"C:\... ."は"C:\.."になり、"C:\..hoge. . .\.parent\test.txt..."は"C:\..hoge\.parent\test.txt"になります。<br/>
    //!         <small>なおこの前置相対パス保護機構は、字句解析などを挟まず単純な一致部分保護しか行いません（大文字小文字無視のフラグだけは反映されます）。
    //!         よって"C:\ . .hoge. . ."は"C:\. .hoge"になりますが、"C:\ . . . . ."は"C:\."になります（スペースを挟むため..は一致せず最初の１ドットだけ一致保護されて残りの部分は末尾無為ドットとして削除される）。
    //!         またこの前置相対パス保護機構は、Pathインスタンスをコンストラクトする時のRelation部分の分離解析時だけで作用します。Root部分では初期化も含めてこのような整形処理は通されません。</small>
    //!     .
    //!
    //! @par 文字コード
    //! 文字コードは.Netの文字列としてUTF16を想定しますがUCS4（サロゲートペア）は対応していません。
    public class Path : System.IComparable, System.IComparable<Path>, System.IComparable<System.String>
    {
        /// @cond <summary>処理範囲オプション</summary> @endcond
        //! @brief 処理範囲オプション
        [System.Flags]
        public enum TargetOption
        {
            /// @cond <summary>どこも処理しません。</summary> @endcond
            None = 0,
            /// @cond <summary>Relation部分だけ処理します。</summary> @endcond
            Relation = 0x1,
            /// @cond <summary>Root部分だけ処理します。</summary> @endcond
            Root = 0x2,
            /// @cond <summary>RootとRelationのどちらも処理します。</summary> @endcond
            All = TargetOption.Relation | TargetOption.Root,
        }

        /// @cond <summary>ポリシーに設定された正規表現でルートと相対パスを解析するのに失敗する時スローされます。</summary> @endcond
        //! @brief ポリシーに設定された正規表現でルートと相対パスを解析するのに失敗する時スローされます。
        public class RootParseException : Exception
        {
            public readonly System.String Path;
            public readonly System.String RootParsePattern;
            public readonly System.String RootParsePatternRootGroupName;
            public readonly System.String RootParsePatternPathGroupName;
            public RootParseException(IPathPolicy policy, System.String path, System.Exception innerEx)
                : base("Path.RootParseException", innerEx)
            {
                this.Path = path;
                RootParsePattern = policy.RootParsePattern;
                RootParsePatternRootGroupName = policy.RootParsePatternRootGroupName;
                RootParsePatternPathGroupName = policy.RootParsePatternPathGroupName;
            }
        }

        /// @cond <summary>パス同士の比較や連結などの処理でポリシーが競合する時スローされます。</summary> @endcond
        //! @brief パス同士の比較や連結などの処理でポリシーが競合する時スローされます。
        public class PolicyConflictException : Exception
        {
            public readonly Path PathA;
            public readonly Path PathB;
            public readonly IPathPolicy PolicyA;
            public readonly IPathPolicy PolicyB;
            public IPathPolicy CurrentPolicy { get { return PathPolicy.Current; } }
            public PolicyConflictException(IPathPolicy policyA, IPathPolicy policyB)
                : base("Path.PolicyConflictException")
            {
                this.PolicyA = policyA;
                this.PolicyB = policyB;
            }
            public PolicyConflictException(Path pathA, Path pathB)
                : base("Path.PolicyConflictException")
            {
                this.PathA = pathA;
                if (pathA != null) this.PolicyA = pathA.Policy;
                this.PathB = pathB;
                if (pathB != null) this.PolicyB = pathB.Policy;
            }
        }

        // operator overrides
        public override bool Equals(object obj) { return CompareTo(obj) == 0; }
        public bool Equals(Path other) { return CompareTo(other) == 0; }
        public bool Equals(System.String other) { return CompareTo(other) == 0; }
        public static bool Equals(Path pathA, Path pathB) { return Compare(pathA, pathB) == 0; }
        public static bool Equals(System.String pathA, System.String pathB) { return Compare(pathA, pathB, PathPolicy.Current) == 0; }
        public static bool Equals(System.String pathA, System.String pathB, IPathPolicy policy) { return Compare(pathA, pathB, policy) == 0; }
        public static bool Equals(Path pathA, System.String pathB) { return Compare(pathA, pathB) == 0; }
        public override int GetHashCode()
        {
            System.String Value = ToString();
            if (System.String.IsNullOrWhiteSpace(Value)) System.String.Empty.GetHashCode();
            if ((Policy ?? PathPolicy.Current).IsIgnoreCase ?? PathPolicy.Default.IsIgnoreCase.Value) return Value.ToUpper().GetHashCode();
            return Value.GetHashCode();
        }
        /// @cond <summary>内部のRootとRelationを足したパス全体を文字列として取得します。</summary> @endcond
        //! @brief 内部のRootとRelationを足したパス全体を文字列として取得します。
        public override string ToString() { return (m_root ?? System.String.Empty) + (m_relation ?? System.String.Empty); }
        public static int Compare(Path pathA, Path pathB)
        {
            if (ReferenceEquals(pathA, pathB)) return 0;
            if (ReferenceEquals(pathA, null)) return -1;
            if (ReferenceEquals(pathB, null)) return 1;
            return Compare(pathA.ToString(), pathB.ToString(), AnyPolicy(pathA, pathB));
        }
        public static int Compare(Path pathA, System.String pathB)
        {
            if (ReferenceEquals(pathA, pathB)) return 0;
            if (ReferenceEquals(pathA, null)) return -1;
            if (ReferenceEquals(pathB, null)) return 1;
            return Compare(pathA.ToString(), pathB.ToString(), pathA.Policy);
        }
        public static int Compare(System.String pathA, System.String pathB) { return Compare(pathA, pathB, PathPolicy.Current); }
        public static int Compare(System.String pathA, System.String pathB, IPathPolicy policy)
        {
            return System.String.Compare(pathA, pathB,
                ((policy ?? PathPolicy.Current).IsIgnoreCase ?? PathPolicy.Default.IsIgnoreCase.Value) ?
                System.StringComparison.OrdinalIgnoreCase : System.StringComparison.Ordinal);
        }
        public int CompareTo(System.Object obj)
        {
            Path other = obj as Path;
            if (ReferenceEquals(other, null) == false) return CompareTo(other);
            System.String target = null;
            if (obj != null) target = obj.ToString();
            return Compare(this.ToString(), obj.ToString(), Policy);
        }
        public int CompareTo(Path other)
        {
            System.String otherValue = null; if (ReferenceEquals(other, null) == false) otherValue = other.ToString();
            return Compare(this.ToString(), otherValue, AnyPolicy(other));
        }
        public int CompareTo(System.String other) { return Compare(this.ToString(), other, Policy); }
        public static implicit operator Path(System.String src) { return new Path(src); }
        public static bool operator ==(Path pathA, Path pathB) { return Equals(pathA, pathB); }
        public static bool operator !=(Path pathA, Path pathB) { return !Equals(pathA, pathB); }
        public static Path operator +(Path pathA, Path pathB)
        {
            if ((ReferenceEquals(pathB, null)) || (pathB.HasRelation == false)) return pathA;
            Path ret = new Path(pathA);
            ret.Combine(pathB);
            return ret;
        }
        public static Path operator +(Path pathA, System.String pathB)
        {
            if (pathA == null) pathA = Empty;
            return pathA + new Path(pathB, pathA.Policy);
        }
        public static Path operator +(System.String pathA, Path pathB)
        {
            if (pathB == null) pathB = Empty;
            return new Path(pathA, pathB.Policy) + pathB;
        }

        /// @cond <summary>空のパスを取得します。</summary> @endcond
        //! @brief 空のパスを返します。
        public static readonly Path Empty;

        /// @cond <summary>拡張子を取得,設定します。</summary> @endcond
        //! @brief 拡張子を取得,設定します。
        //! @details Policy.IsMultiExtentionの設定によって、Multi/SingleExtentionが切り替わります。
        public Path Extention
        {
            get { return UnsafeInitialized(System.String.Empty, GetExtentionName(m_relation, Policy), Policy); }
            set
            {
                MergePolicy(value);
                if (System.String.IsNullOrWhiteSpace(m_relation)) m_relation = System.String.Empty;
                System.String removeExtention = GetExtentionName(m_relation, Policy);
                m_relation = m_relation.Substring(0, m_relation.Length - removeExtention.Length);
                if (value != null) m_relation += GetExtentionName(value.m_relation, Policy);
            }
        }

        /// @cond <summary>拡張子を持っているか確認します。</summary> @endcond
        //! @brief 拡張子を持っているか確認します。
        public bool HasExtention { get { return System.String.IsNullOrWhiteSpace(GetExtentionName(m_relation, Policy, false)) == false; } }
        /// @cond <summary>親要素を持っているか確認します。ディレクトリ区切りが存在し、なおかつディレクトリ区切りより上の階層のパスが空ではない時にTrueを返します。</summary> @endcond
        //! @brief 親要素を持っているか確認します。ディレクトリ区切りが存在し、なおかつディレクトリ区切りより上の階層のパスが空ではない時にTrueを返します。
        public bool HasParent
        {
            get
            {
                System.String parent = null, sep = null, name = null;
                GetDirectoryParsedStrings(ref parent, ref sep, ref name, m_relation, Policy);
                return ((System.String.IsNullOrWhiteSpace(parent) == false) &&
                    (System.String.IsNullOrWhiteSpace(sep) == false));
            }
        }
        /// @cond <summary>ルート要素を持っているか取得します。</summary> @endcond
        //! @brief ルート要素を持っているか取得します。
        public bool HasRoot { get { return System.String.IsNullOrWhiteSpace(m_root) == false; } }
        /// @cond <summary>相対パス要素(ルート以外の部分)を持っているか取得します。</summary> @endcond
        //! @brief 相対パス要素(ルート以外の部分)を持っているか取得します。
        public bool HasRelation { get { return System.String.IsNullOrWhiteSpace(m_relation) == false; } }
        /// @cond <summary>空のパスか確認します。(HasRoot == false) &amp;&amp; (HasRelation == false) に等しい値を返します。</summary> @endcond
        //! @brief 空のパスか確認します。(HasRoot == false) && (HasRelation == false) に等しい値を返します。
        public bool IsEmpty { get { return (HasRoot == false) && (HasRelation == false); } }
        /// @cond <summary>相対パス(ルート以外の要素)だけで構築されているか取得します。(HasRoot == false) &amp;&amp; HasRelationに等しい値を取得します。</summary> @endcond
        //! @brief 相対パス(ルート以外の要素)だけで構築されているか取得します。(HasRoot == false) && HasRelationに等しい値を取得します。
        public bool IsRelativeOnly { get { return (HasRoot == false) && HasRelation; } }
        /// @cond <summary>ルート要素だけで構築されているか取得します。HasRoot &amp;&amp; (HasRelation == false)に等しい値を取得します。</summary> @endcond
        //! @brief ルート要素だけで構築されているか取得します。HasRoot && (HasRelation == false)に等しい値を取得します。
        public bool IsRootOnly { get { return HasRoot && (HasRelation == false); } }

        /// @cond <summary>複数拡張子を取得,設定します。</summary> @endcond
        //! @brief 複数拡張子を取得,設定します。
        //! @par @details .bmp.png のような複数の拡張子を扱う時に使用します。.pngのように最後の一つだけを扱う時はSingleExtentionを使用してください。
        //! @par @details MultiExtentionでは、拡張子の前にあるドットは可能な限り長く取得します。
        //! @par @details 例）test..bmp..png >> MultiExtention="..bmp..png" SingleExtention=".png"
        //! @note ディレクトリ/ファイル要素名が相対パス要素(Policy.RelativeCurrentDirectoryName/RelativeParentDirectoryName)に完全一致する場合、拡張子は常に空です。
        public Path MultiExtention
        {
            get { return UnsafeInitialized(System.String.Empty, GetExtentionName(m_relation, Policy, true), Policy); }
            set
            {
                MergePolicy(value);
                if (System.String.IsNullOrWhiteSpace(m_relation)) m_relation = System.String.Empty;
                System.String removeExtention = GetExtentionName(m_relation, Policy, true);
                m_relation = m_relation.Substring(0, m_relation.Length - removeExtention.Length);
                if (value != null) m_relation += GetExtentionName(value.m_relation, Policy, true);
            }
        }

        /// @cond <summary>子要素を取得,設定します。</summary> @endcond
        //! @brief 子要素を取得,設定します。
        public Path Name
        {
            get { return UnsafeInitialized(System.String.Empty, GetLastElementName(m_relation, Policy), Policy); }
            set
            {
                MergePolicy(value);
                if (System.String.IsNullOrWhiteSpace(m_relation)) m_relation = System.String.Empty;
                System.String removeLastElement = GetLastElementName(m_relation, Policy);
                m_relation = m_relation.Substring(0, m_relation.Length - removeLastElement.Length);
                if (value != null) m_relation += GetLastElementName(value.m_relation, Policy);
            }
        }

        /// @cond <summary>親要素を取得,設定します。</summary> @endcond
        //! @brief 親要素を取得,設定します。
        //! @details 取得する時、ディレクトリ区切りがない時には空の文字列が返ります。
        //! - 取得する時、ルート要素は常にコピーされます。
        //! - 取得する時、Relationにディレクトリ区切り文字がない時は、Relationが空のPathが返されます。
        //! - 代入する時、デスティネーション側のルート要素は常にコピーされます。
        //! - 代入する時、デスティネーション側のRelationにディレクトリ区切り文字がない時は、デスティネーションのRelation全てが子要素として残ります。
        //! - 代入する時、ソース側のRelationにディレクトリ区切りがない時は、ソースのRelation全てが親要素として挿入されます。
        //! 親にnullや空のデータを設定すると、相対パス部分からはディレクトリ区切りや現在の親のデータが全て消えて末端の要素名だけが残ります。
        public Path Parent
        {
            get
            {
                System.String parent = null, sep = null, name = null;
                GetDirectoryParsedStrings(ref parent, ref sep, ref name, m_relation, Policy);
                if (System.String.IsNullOrWhiteSpace(sep)) parent = System.String.Empty;
                return UnsafeInitialized(m_root, parent, Policy);
            }
            set
            {
                MergePolicy(value);

                System.String destParent = null, destSep = null, destName = null;
                GetDirectoryParsedStrings(ref destParent, ref destSep, ref destName, m_relation, Policy);

                System.String srcParent = null, srcSep = null, srcName = null, srcRelation = null;
                if (value != null) srcRelation = value.m_relation;
                GetDirectoryParsedStrings(ref srcParent, ref srcSep, ref srcName, srcRelation, Policy);

                if (System.String.IsNullOrWhiteSpace(srcParent)) m_relation = destName;
                else
                {
                    if (System.String.IsNullOrWhiteSpace(destSep))
                    {
                        IPathPolicy policy = Policy ?? PathPolicy.Current;
                        if ((policy.DirectorySeparators != null) && (0 < policy.DirectorySeparators.Count))
                        {
                            destSep = new System.String(policy.DirectorySeparators[0], 1);
                        }
                    }
                    m_relation = srcParent + destSep + destName;
                }
            }
        }

        /// @cond <summary>解析設定を保持するポリシーインターフェースを取得、設定します。</summary> @endcond
        //! @brief 解析設定を保持するポリシーインターフェースを取得、設定します。
        public IPathPolicy Policy;

        /// @cond <summary>相対パス（ドライブレター以外の部分）を取得、設定します。</summary> @endcond
        //! @brief 相対パス（ドライブレター以外の部分）を取得、設定します。
        public Path Relation
        {
            get { return UnsafeInitialized(System.String.Empty, m_relation, Policy); }
            set
            {
                MergePolicy(value);
                m_relation = null;
                if (value != null) m_relation = value.m_relation;
                if (System.String.IsNullOrWhiteSpace(m_relation)) m_relation = System.String.Empty;
            }
        }

        /// @cond <summary>ディレクトリとして存在するか取得します。現在のToRelativeDirectoryPath()が返す文字列をパスとして検査します。</summary> @endcond
        //! @brief ディレクトリとして存在するか取得します。現在のToRelativeDirectoryPath()が返す文字列をパスとして検査します。
        public bool RelativeDirectoryPathExists { get { return System.IO.Directory.Exists(ToRelativeDirectoryPath()); } }

        /// @cond <summary>ファイルとして存在するか取得します。現在のToRelativeFilePath()が返す文字列をパスとして検査します。</summary> @endcond
        //! @brief ファイルとして存在するか取得します。現在のToRelativeFilePath()が返す文字列をパスとして検査します。
        public bool RelativeFilePathExists { get { return System.IO.File.Exists(ToRelativeFilePath()); } }

        /// @cond <summary>パスが存在するか取得します。RelativeDirectoryPathExists||RelativeFilePathExistsに等しい値を返します。</summary> @endcond
        //! @brief パスが存在するか取得します。RelativeDirectoryPathExists||RelativeFilePathExistsに等しい値を返します。
        public bool RelativePathExists { get { return RelativeDirectoryPathExists || RelativeFilePathExists; } }

        /// @cond <summary>ルート（ドライブレター）を取得、設定します。</summary> @endcond
        //! @brief ルート（ドライブレター）を取得、設定します。
        public Path Root
        {
            get { return UnsafeInitialized(m_root, System.String.Empty, Policy); }
            set
            {
                MergePolicy(value);
                m_root = null;
                if (value != null) m_root = value.m_root;
                if (System.String.IsNullOrWhiteSpace(m_root)) m_root = System.String.Empty;
            }
        }

        /// @cond <summary>拡張子を取得,設定します。</summary> @endcond
        //! @brief 拡張子を取得,設定します。
        //! @details .bmp.png のような複数の拡張子を扱う時はMultiExtentionを使用してください。
        //! @par @details SingleExtentionでは、拡張子の前にあるドットは１文字だけ取得します。
        //! @par @details 例）test..bmp..png >> MultiExtention="..bmp..png" SingleExtention=".png"
        //! @note ディレクトリ/ファイル要素名が相対パス要素(Policy.RelativeCurrentDirectoryName/RelativeParentDirectoryName)に完全一致する場合、拡張子は常に空です。
        public Path SingleExtention
        {
            get { return UnsafeInitialized(System.String.Empty, GetExtentionName(m_relation, Policy, false), Policy); }
            set
            {
                MergePolicy(value);
                if (System.String.IsNullOrWhiteSpace(m_relation)) m_relation = System.String.Empty;
                System.String removeExtention = GetExtentionName(m_relation, Policy, false);
                m_relation = m_relation.Substring(0, m_relation.Length - removeExtention.Length) + GetExtentionName(value.m_relation, Policy, false);
            }
        }

        /// @cond <summary>ディレクトリとして存在するか取得します。現在のToSystemDirectoryPath()が返す文字列をパスとして検査します。</summary> @endcond
        //! @brief ディレクトリとして存在するか取得します。現在のToSystemDirectoryPath()が返す文字列をパスとして検査します。
        public bool SystemDirectoryPathExists { get { return System.IO.Directory.Exists(ToSystemDirectoryPath()); } }

        /// @cond <summary>ファイルとして存在するか取得します。現在のToSystemFilePath()が返す文字列をパスとして検査します。</summary> @endcond
        //! @brief ファイルとして存在するか取得します。現在のToSystemFilePath()が返す文字列をパスとして検査します。
        public bool SystemFilePathExists { get { return System.IO.File.Exists(ToSystemFilePath()); } }

        /// @cond <summary>パスが存在するか取得します。SystemDirectoryPathExists||SystemFilePathExistsに等しい値を返します。</summary> @endcond
        //! @brief パスが存在するか取得します。SystemDirectoryPathExists||SystemFilePathExistsに等しい値を返します。
        public bool SystemPathExists { get { return SystemDirectoryPathExists || SystemFilePathExists; } }

        //! @brief 静的コンストラクタ
        static Path() { Empty = new Path(); }

        /// @cond <summary>空のPathクラスをコンストラクトします。</summary> @endcond
        //! @brief デフォルトコンストラクタ。空のPathクラスをコンストラクトします。
        public Path()
        {
            Policy = null;
            m_root = System.String.Empty;
            m_relation = System.String.Empty;
        }

        /// @cond <summary>コンストラクタ。任意のポリシーを設定して空のPathクラスを初期化します。</summary> @endcond
        //! @brief コンストラクタ。任意のポリシーを設定して空のPathクラスを初期化します。
        //! @deteils ポリシーはクラスごとに保持され、コピー時に設定がコピーされていきます。
        //! ポリシーにnullが設定されている場合は、それぞれの処理の都度、PathPolicy.Currentがロードされます。
        //! 一括してポリシーを置換するときは、PathPolicyインスタンスを生成してパラメータを調整し、PathPolicy.Currentにセットします。
        //! クラス個別にポリシーを設定する時は、クラスインスタンスのPolicyメンバに任意のPathPolicyインスタンスをセットします。
        public Path(IPathPolicy policy)
        {
            this.Policy = policy;
            m_root = System.String.Empty;
            m_relation = System.String.Empty;
        }

        /// @cond <summary>コンストラクタ。文字列から新しいパスを作成します。</summary> @endcond
        //! @brief コンストラクタ。文字列から新しいパスを作成します。
        public Path(System.String src)
            : this(src, null)
        { }

        /// @cond <summary>コンストラクタ。文字列から新しいパスを作成します。</summary> @endcond
        //! @brief コンストラクタ。文字列から新しいパスを作成します。
        //! @deteils ポリシーはクラスごとに保持され、コピー時に設定がコピーされていきます。
        //! ポリシーにnullが設定されている場合は、それぞれの処理の都度、PathPolicy.Currentがロードされます。
        //! 一括してポリシーを置換するときは、PathPolicyインスタンスを生成してパラメータを調整し、PathPolicy.Currentにセットします。
        //! クラス個別にポリシーを設定する時は、クラスインスタンスのPolicyメンバに任意のPathPolicyインスタンスをセットします。
        public Path(System.String src, IPathPolicy policy)
            : this()
        {
            this.Policy = policy;
            m_root = System.String.Empty;
            m_relation = System.String.Empty;
            GetRootParsedStrings(ref m_root, ref m_relation, src, policy);
        }

        /// @cond <summary>コピーコンストラクタ</summary> @endcond
        //! @brief コピーコンストラクタ
        public Path(Path src)
            : this()
        {
            if (src != null)
            {
                m_root = src.m_root;
                m_relation = src.m_relation;
                Policy = src.Policy;
            }
        }

        /// @cond <summary>コピーコンストラクタ（ポリシーの伝播を止めて別の値を設定します）。</summary> @endcond
        //! @brief コピーコンストラクタ（ポリシーの伝播を止めて別の値を設定します）。
        public Path(Path src, IPathPolicy policy)
            : this(policy)
        {
            if (src != null)
            {
                m_root = src.m_root;
                m_relation = src.m_relation;
            }
        }

        /// @cond <summary>パスを後ろに連結します。</summary> @endcond
        //! @brief パスを後ろに連結します。
        //! @details 現在の自分のRelationの後ろにcombinePath.Relationが連結します。combinePath側のパスのルート要素は無視されます。
        //! 接続時にはセパレータ挿入チェックが行われます。相対文法は解決されずそのままデータに残ります。
        //! 自身のPolicyがnullである時は連結パス側のPolicyが自分のポリシーにマージされます。
        //! @exception PolicyConflictException 両方ともPolicyがnullでなく、それぞれ違うPolicyインスタンスを参照している
        public void Combine(Path combinePath)
        {
            if (ReferenceEquals(combinePath, null) || (combinePath.HasRelation == false)) return;
            MergePolicy(combinePath);
            IPathPolicy policy = Policy ?? PathPolicy.Current;
            System.String combineRelation = combinePath.m_relation.TrimStart();
            if ((policy.DirectorySeparators != null) && (0 < policy.DirectorySeparators.Count))
            {
                System.Char[] dirSeps = new System.Char[policy.DirectorySeparators.Count];
                policy.DirectorySeparators.CopyTo(dirSeps, 0);
                if (combineRelation.IndexOfAny(dirSeps) != 0) combineRelation = new System.String(dirSeps[0], 1) + combineRelation;
            }
            m_relation += combineRelation;
        }

        /// @cond <summary>パスを後ろに連結します。</summary> @endcond
        //! @brief パスを後ろに連結します。
        //! @details 現在の自分のRelationの後ろにcombinePath.Relationが連結します。combinePath側のパスのルート要素は無視されます。
        //! 接続時にはセパレータ挿入チェックが行われます。相対文法は解決されずそのままデータに残ります。
        //! 自身のPolicyがnullである時は連結パス側のPolicyが自分のポリシーにマージされます。
        //! @exception PolicyConflictException 両方ともPolicyがnullでなく、それぞれ違うPolicyインスタンスを参照している
        public void Combine(System.String combinePath) { Combine(new Path(combinePath, Policy)); }

        //! @brief ２つのパスの共通部分（積集合）を取得します。
        //! @return 前方一致で検査した２つのパスの共通部分（積集合）。
        //! @par
        //! まずルート要素の完全一致を検査して、ルートが完全一致するなら相対パス要素が比較されます。ルートが完全一致しないなら、ルート要素も相対要素も空のパスが返ります。
        //! @par
        //! - ポリシーにディレクトリセパレータが設定されていない場合
        //!
        //!     一文字単位で比較し、一致した部分までのパスが返されます。
        //! .
        //! - ディレクトリセパレータが設定されている場合
        //!
        //!     ディレクトリ単位で比較し、一致した部分までのパスが返されます。
        //! .
        //! @note 比較時にはポリシーの設定によって大文字小文字が無視されたり、複数設定されたディレクトリセパレータ同士も同じ文字として判定されます。この時は、最終的な積集合にいずれも自分側のデータがコピーされます。
        public Path Intersect(Path path)
        {
            Path ret = new Path(this);
            ret.MergePolicy(path);
            IPathPolicy policy = ret.Policy ?? PathPolicy.Current;

            System.StringComparison cmpOpt = System.StringComparison.Ordinal;
            System.Text.RegularExpressions.RegexOptions opt = System.Text.RegularExpressions.RegexOptions.None;
            if (policy.IsIgnoreCase ?? PathPolicy.Default.IsIgnoreCase.Value)
            {
                cmpOpt = System.StringComparison.OrdinalIgnoreCase;
                opt = System.Text.RegularExpressions.RegexOptions.IgnoreCase;
            }

            System.String root = System.String.Empty;
            System.String relation = System.String.Empty;
            System.String pathARoot = m_root;
            System.String pathARelation = m_relation;
            System.String pathBRoot = System.String.Empty;
            System.String pathBRelation = System.String.Empty;
            if (path != null)
            {
                pathBRoot = path.m_root;
                pathBRelation = path.m_relation;
            }

            if (System.String.Equals(pathARoot, pathBRoot, cmpOpt))
            {
                root = pathARoot;
                if ((policy.DirectorySeparators != null) && (0 < policy.DirectorySeparators.Count))
                {
                    // output source
                    System.Collections.Generic.List<System.String> dirs = new System.Collections.Generic.List<string>();
                    // regex-parse : StringBySeparators+StringByNotSeparators collection
                    System.String EscapedSeparators = System.String.Empty;
                    foreach (System.Char ch in policy.DirectorySeparators) EscapedSeparators += "\\" + ch;
                    System.String dirParsePattern = System.String.Format("(?<sep>[{0}\\s]*)(?<element>[^{0}]+)", EscapedSeparators);
                    System.Text.RegularExpressions.Regex dirParser = new System.Text.RegularExpressions.Regex(dirParsePattern, opt);
                    System.Text.RegularExpressions.MatchCollection dirMatchesA = dirParser.Matches(pathARelation);
                    System.Text.RegularExpressions.MatchCollection dirMatchesB = dirParser.Matches(pathBRelation);
                    int matchCount = System.Math.Min(dirMatchesA.Count, dirMatchesB.Count);
                    for (int index = 0; index < matchCount; index++)
                    {
                        System.String sepA = dirMatchesA[index].Groups["sep"].Value.Trim();
                        System.String sepB = dirMatchesB[index].Groups["sep"].Value.Trim();
                        if (1 < sepA.Length) sepA = sepA.Substring(0, 1); // first separator only
                        if (1 < sepB.Length) sepB = sepB.Substring(0, 1); // first separator only
                        if (sepA.Length != sepB.Length) break;
                        System.String elementA = dirMatchesA[index].Groups["element"].Value.Trim();
                        System.String elementB = dirMatchesB[index].Groups["element"].Value.Trim();
                        if ((System.String.IsNullOrWhiteSpace(elementA)) || (System.String.Equals(elementA, elementB, cmpOpt) == false)) break;
                        dirs.Add(sepA + elementA);
                    }
                    relation = System.String.Join(System.String.Empty, dirs);
                }
                else relation = DotNetEx.String.Extention.Intersect(pathARelation, pathBRelation, cmpOpt);
            }

            ret.m_root = root;
            ret.m_relation = relation;
            return ret;
        }

        //! @brief ２つのパスの共通部分（積集合）を取得します。
        //! @return 前方一致で検査した２つのパスの共通部分（積集合）。
        //! @par
        //! まずルート要素の完全一致を検査して、ルートが完全一致するなら相対パス要素が比較されます。ルートが完全一致しないなら、ルート要素も相対要素も空のパスが返ります。
        //! @par
        //! - ポリシーにディレクトリセパレータが設定されていない場合
        //!
        //!     一文字単位で比較し、一致した部分までのパスが返されます。
        //! .
        //! - ディレクトリセパレータが設定されている場合
        //!
        //!     ディレクトリ単位で比較し、一致した部分までのパスが返されます。
        //! .
        //! @note 比較時にはポリシーの設定によって大文字小文字が無視されたり、複数設定されたディレクトリセパレータ同士も同じ文字として判定されます。この時は、最終的な積集合にいずれも自分側のデータがコピーされます。
        public Path Intersect(System.String path) { return Intersect(new Path(path)); }

        /// @cond <summary>パスがnullまたはEmptyに一致するか検査します。</summary> @endcond
        //! @brief パスがnullまたはEmptyに一致するか検査します。
        public static bool IsNullOrEmpty(Path src)
        {
            if (ReferenceEquals(src, null)) return true;
            return System.String.IsNullOrWhiteSpace(src.ToString());
        }

        //! @brief 自分のパスから、目標とするパスへ向かう相対パスを生成します。
        //! @return 目標とするパスへ向かう相対パス。
        //! - targetがnullの時は空のパスへ向かう相対パス（＝自分のディレクトリ要素数だけ親参照のパス要素が並ぶ相対パス）。
        //! - ポリシーにディレクトリセパレータが設定されていない時は空のパス。
        //! @par "me.Relation + me.RelationTo(target) = target"
        //! 掲題の式が成立するよう自分のパスから目標とするパスへ向かう相対パスを生成します。（自分自身はファイル名ではなくディレクトリとして認識します）
        //! @par ルート要素は不問
        //! この処理ではルート要素が考慮されず、返されるPathのルート要素は常に空になります。よって最初に挙げた等式は、meとtargetのルートが等しい場合のみ成立します。
        //! @note 自分自身とtargetに与えられたパスに相対指定要素が含まれる場合、内部でそれぞれ相対要素を解決したデータを作り、そこから相対パスを抽出します。
        public Path RelationTo(Path target) { return RelationTo(target, -1); }

        //! @brief 自分のパスから、目標とするパスへ向かう相対パスを生成します。
        //! @param [in] directorySeparatorIndex 相対パスを生成するのに使用するセパレータのインデックス。範囲外のときは最初のディレクトリセパレータ。
        //! @return 目標とするパスへ向かう相対パス。
        //! - targetがnullの時は空のパスへ向かう相対パス（＝自分のディレクトリ要素数だけ親参照のパス要素が並ぶ相対パス）。
        //! - ポリシーにディレクトリセパレータが設定されていない時は空のパス。
        //! @par "me.Relation + me.RelationTo(target) = target"
        //! 掲題の式が成立するよう自分のパスから目標とするパスへ向かう相対パスを生成します。（自分自身はファイル名ではなくディレクトリとして認識します）
        //! @par ルート要素は不問
        //! この処理ではルート要素が考慮されず、返されるPathのルート要素は常に空になります。よって最初に挙げた等式は、meとtargetのルートが等しい場合のみ成立します。
        //! @note 自分自身とtargetに与えられたパスに相対指定要素が含まれる場合、内部でそれぞれ相対要素を解決したデータを作り、そこから相対パスを抽出します。
        public Path RelationTo(Path target, int directorySeparatorIndex)
        {
            Path ret = new Path(Policy);
            ret.MergePolicy(target);
            IPathPolicy policy = ret.Policy ?? PathPolicy.Current;
            if (System.String.IsNullOrEmpty(policy.RelativeParentDirectoryName) ||
                (policy.DirectorySeparators == null) || (policy.DirectorySeparators.Count <= 0)) return Empty;
            if ((directorySeparatorIndex < 0) || (policy.DirectorySeparators.Count <= directorySeparatorIndex)) directorySeparatorIndex = 0;
            System.String directorySeparator = new System.String(policy.DirectorySeparators[directorySeparatorIndex], 1);

            System.String targetRelation = System.String.Empty;
            if (target != null) targetRelation = GetNormalizedRelation(target.m_relation, target.Policy, TargetOption.None, -1);
            System.String originalRelation = GetNormalizedRelation(m_relation, Policy, TargetOption.None, -1);

            System.StringComparison cmpOpt = System.StringComparison.Ordinal;
            System.Text.RegularExpressions.RegexOptions opt = System.Text.RegularExpressions.RegexOptions.None;
            if (policy.IsIgnoreCase ?? PathPolicy.Default.IsIgnoreCase.Value)
            {
                cmpOpt = System.StringComparison.OrdinalIgnoreCase;
                opt = System.Text.RegularExpressions.RegexOptions.IgnoreCase;
            }

            System.Collections.Generic.List<System.String> dirIntegrate = new System.Collections.Generic.List<System.String>();

            System.String EscapedSeparators = System.String.Empty;
            foreach (System.Char ch in (Policy ?? PathPolicy.Current).DirectorySeparators) EscapedSeparators += "\\" + ch;
            System.String dirParsePatternOrg = System.String.Format("(?<sep>[{0}\\s]*)(?<element>[^{0}]+)", EscapedSeparators);
            System.Text.RegularExpressions.MatchCollection dirMatchesOrg =
                new System.Text.RegularExpressions.Regex(dirParsePatternOrg, opt).Matches(originalRelation);

            EscapedSeparators = System.String.Empty;
            foreach (System.Char ch in (target.Policy ?? PathPolicy.Current).DirectorySeparators) EscapedSeparators += "\\" + ch;
            System.String dirParsePatternTarget = System.String.Format("(?<sep>[{0}\\s]*)(?<element>[^{0}]+)", EscapedSeparators);
            System.Text.RegularExpressions.MatchCollection dirMatchesTarget =
                new System.Text.RegularExpressions.Regex(dirParsePatternTarget, opt).Matches(targetRelation);

            // count intersect
            int intersectCount = 0, intersectCountMax = System.Math.Min(dirMatchesOrg.Count, dirMatchesTarget.Count);
            for (int index = 0; index < intersectCountMax; index++)
            {
                System.String sepOrg = dirMatchesOrg[index].Groups["sep"].Value.Trim();
                System.String sepTarget = dirMatchesTarget[index].Groups["sep"].Value.Trim();
                if (0 < sepOrg.Length) sepOrg = directorySeparator;
                if (0 < sepTarget.Length) sepTarget = directorySeparator;
                System.String elementOrg = dirMatchesOrg[index].Groups["element"].Value.Trim();
                System.String elementTarget = dirMatchesTarget[index].Groups["element"].Value.Trim();
                if ((System.String.IsNullOrWhiteSpace(elementOrg)) || (System.String.Equals(elementOrg, elementTarget, cmpOpt) == false)) break;
                intersectCount++;
            }

            // pop remaining directories from original path
            int popCount = dirMatchesOrg.Count - intersectCount;
            if (0 < popCount)
            {
                System.String originalRelationPop = DotNetEx.String.Extention.Repeat(directorySeparator + policy.RelativeParentDirectoryName, popCount - 1);
                dirIntegrate.Add(policy.RelativeParentDirectoryName + originalRelationPop);
            }

            // push remaining directories from target path
            for (int index = intersectCount; index < dirMatchesTarget.Count; index++)
            {
                System.String elementTarget = dirMatchesTarget[index].Groups["element"].Value.Trim();
                if (System.String.IsNullOrWhiteSpace(elementTarget) == false)
                {
                    dirIntegrate.Add(((0 < dirIntegrate.Count) ? directorySeparator : System.String.Empty) + elementTarget);
                }
            }

            ret.m_relation = System.String.Join(System.String.Empty, dirIntegrate);
            if (System.String.IsNullOrWhiteSpace(ret.m_relation)) ret.m_relation = System.String.Empty;
            return ret;
        }

        //! @brief 自分のパスから、目標とするパスへ向かう相対パスを生成します。
        //! @return 目標とするパスへ向かう相対パス。
        //! - targetがnullの時は空のパスへ向かう相対パス（＝自分のディレクトリ要素数だけ親参照のパス要素が並ぶ相対パス）。
        //! - ポリシーにディレクトリセパレータが設定されていない時は空のパス。
        //! @par "me.Relation + me.RelationTo(target) = target"
        //! 掲題の式が成立するよう自分のパスから目標とするパスへ向かう相対パスを生成します。（自分自身はファイル名ではなくディレクトリとして認識します）
        //! @par ルート要素は不問
        //! この処理ではルート要素が考慮されず、返されるPathのルート要素は常に空になります。よって最初に挙げた等式は、meとtargetのルートが等しい場合のみ成立します。
        //! @note 自分自身とtargetに与えられたパスに相対指定要素が含まれる場合、内部でそれぞれ相対要素を解決したデータを作り、そこから相対パスを抽出しています。
        public Path RelationTo(System.String target) { return RelationTo(new Path(target), -1); }

        //! @brief 自分のパスから、目標とするパスへ向かう相対パスを生成します。
        //! @param [in] directorySeparatorIndex 相対パスを生成するのに使用するセパレータのインデックス。範囲外のときは最初のディレクトリセパレータ。
        //! @return 目標とするパスへ向かう相対パス。
        //! - targetがnullの時は空のパスへ向かう相対パス（＝自分のディレクトリ要素数だけ親参照のパス要素が並ぶ相対パス）。
        //! - ポリシーにディレクトリセパレータが設定されていない時は空のパス。
        //! @par "me.Relation + me.RelationTo(target) = target"
        //! 掲題の式が成立するよう自分のパスから目標とするパスへ向かう相対パスを生成します。（自分自身はファイル名ではなくディレクトリとして認識します）
        //! @par ルート要素は不問
        //! この処理ではルート要素が考慮されず、返されるPathのルート要素は常に空になります。よって最初に挙げた等式は、meとtargetのルートが等しい場合のみ成立します。
        //! @note 自分自身とtargetに与えられたパスに相対指定要素が含まれる場合、内部でそれぞれ相対要素を解決したデータを作り、そこから相対パスを抽出しています。
        public Path RelationTo(System.String target, int directorySeparatorIndex) { return RelationTo(new Path(target), directorySeparatorIndex); }

        /// @cond <summary>相対パスを解決します。</summary> @endcond
        //! @brief 相対パスを解決します。
        //! @details ディレクトリ/ファイル名要素がPolicy.RelativeCurrentDirectoryNameに完全に一致する階層は削除され、
        //! Policy.RelativeParentDirectoryNameに完全に一致する階層があれば、その階層と一つ上位の階層が削除されます。
        //! ルートまで戻ってなおPolicy.RelativeParentDirectoryNameが記述されていても例外は生じず、ルートパスを返します。
        //! ポリシーのパラメータで、ディレクトリセパレータ設定がnullやEmptyだった時はなにもせずに処理に返します。セパレータの統一処理は行われません。
        public void RelativeNormalize() { RelativeNormalize(TargetOption.None, -1); }

        /// @cond <summary>相対パスを解決します。</summary> @endcond
        //! @brief 相対パスを解決します。
        //! @details ディレクトリ/ファイル名要素がPolicy.RelativeCurrentDirectoryNameに完全に一致する階層は削除され、
        //! Policy.RelativeParentDirectoryNameに完全に一致する階層があれば、その階層と一つ上位の階層が削除されます。
        //! ルートまで戻ってなおPolicy.RelativeParentDirectoryNameが記述されていても例外は生じず、ルートパスを返します。
        //! ポリシーのパラメータで、ディレクトリセパレータ設定がnullやEmptyだった時はなにもせずに処理に返します。セパレータの統一処理は行われません。
        //! また引数を渡してセパレータの統一処理も同時に行います。ポリシーに設定されたDirectorySeparatorsの最初のセパレータ文字に統一されます。
        //! @param [in] separatorNormalizeTarget セパレータの統一処理を行う部分を指定します。SeparatorNormalizeのoptオプションと同じです。
        public void RelativeNormalize(TargetOption separatorNormalizeTarget) { RelativeNormalize(separatorNormalizeTarget, 0); }

        /// @cond <summary>相対パスを解決します。</summary> @endcond
        //! @brief 相対パスを解決します。
        //! @details ディレクトリ/ファイル名要素がPolicy.RelativeCurrentDirectoryNameに完全に一致する階層は削除され、
        //! Policy.RelativeParentDirectoryNameに完全に一致する階層があれば、その階層と一つ上位の階層が削除されます。
        //! ルートまで戻ってなおPolicy.RelativeParentDirectoryNameが記述されていても例外は生じず、ルートパスを返します。
        //! ポリシーのパラメータで、ディレクトリセパレータ設定がnullやEmptyだった時はなにもせずに処理に返します。
        //! また引数を渡してセパレータの統一処理も同時に行うことが出来ます。
        //! @param [in] separatorNormalizeTarget SeparatorNormalizeのoptオプションと同じです。
        //! @param [in] separatorNormalizeIndex SeparatorNormalizeのseparatorIndexオプションと同じです。
        public void RelativeNormalize(TargetOption separatorNormalizeTarget, int separatorNormalizeIndex)
        {
            m_relation = GetNormalizedRelation(m_relation, Policy, separatorNormalizeTarget, separatorNormalizeIndex);
            if ((separatorNormalizeTarget & TargetOption.Root) == TargetOption.Root) SeparatorNormalize(TargetOption.Root, separatorNormalizeIndex);
        }

        /// @cond <summary>セパレータを統一します。相対パス部分を、標準のセパレータ文字(ポリシーに設定されたセパレータ文字配列の先頭)に統一します。</summary> @endcond
        //! @brief セパレータを統一します。
        //! @details ポリシーには複数のセパレータを許容できるよう設定できますが、SeparatorNormalize()は、代入されたパス上にあらわれるセパレータを置換してひとつのセパレータ文字に統一します。
        //! @par @details 引数のないSeparatorNormalize()は、Relation部分だけを標準のセパレータ文字(ポリシーに設定されたセパレータ文字配列の先頭)に統一します。
        //! @note ポリシーにDirectorySeparatorsが設定されていないときは何もしません。
        public void SeparatorNormalize()
        {
            SeparatorNormalize(TargetOption.Relation, 0);
        }

        /// @cond <summary>セパレータを統一します。標準のセパレータ文字(ポリシーに設定されたセパレータ文字配列の先頭)に統一します。</summary> @endcond
        //! @brief セパレータを統一します。
        //! @param [in] opt セパレータを統一する部分指定
        //! @details ポリシーには複数のセパレータを許容できるよう設定できますが、SeparatorNormalize()は、代入されたパス上にあらわれるセパレータを置換してひとつのセパレータ文字に統一します。
        //! @par @details インデックス指定のないSeparatorNormalize()は、標準のセパレータ文字(ポリシーに設定されたセパレータ文字配列の先頭)に統一します。
        //! @note ポリシーにDirectorySeparatorsが設定されていないときは何もしません。
        public void SeparatorNormalize(TargetOption opt)
        {
            SeparatorNormalize(opt, 0);
        }

        /// @cond <summary>インデックスを指定してセパレータを統一します。</summary> @endcond
        //! @brief セパレータを統一します。
        //! @param [in] opt セパレータを統一する部分指定
        //! @param [in] separatorCode 統一して残すセパレータ文字。Policy.DirectorySeparatorsにseparatorCodeが見つからなければ何もしません。
        //! @details ポリシーには複数のセパレータを許容できるよう設定できますが、SeparatorNormalize()は、代入されたパス上にあらわれるセパレータを置換してひとつのセパレータ文字に統一します。
        //! @note ポリシーにDirectorySeparatorsが設定されていないときは何もしません。
        public void SeparatorNormalize(TargetOption opt, System.Char separatorCode)
        {
            IPathPolicy policy = Policy ?? PathPolicy.Current;
            if ((opt == TargetOption.None) || (policy.DirectorySeparators == null)) return;
            int separatorIndex = policy.DirectorySeparators.IndexOf(separatorCode);
            if (0 <= separatorIndex) SeparatorNormalize(opt, separatorIndex);
        }

        /// @cond <summary>インデックスを指定してセパレータを統一します。</summary> @endcond
        //! @brief セパレータを統一します。
        //! @param [in] opt セパレータを統一する部分指定
        //! @param [in] separatorIndex 統一して残すセパレータ文字のインデックス。このインデックスでPolicy.DirectorySeparatorsを参照します。範囲外のときは0番目の要素を使用します。
        //! @details ポリシーには複数のセパレータを許容できるよう設定できますが、SeparatorNormalize()は、代入されたパス上にあらわれるセパレータを置換してひとつのセパレータ文字に統一します。
        //! @note ポリシーにDirectorySeparatorsが設定されていないときは何もしません。
        public void SeparatorNormalize(TargetOption opt, int separatorIndex)
        {
            IPathPolicy policy = Policy ?? PathPolicy.Current;
            if ((opt == TargetOption.None) || (policy.DirectorySeparators == null) || (policy.DirectorySeparators.Count <= 0)) return;
            if ((separatorIndex < 0) || (policy.DirectorySeparators.Count <= separatorIndex)) separatorIndex = 0;
            System.Char replaceTo = policy.DirectorySeparators[separatorIndex];
            for (int index = 0; index < policy.DirectorySeparators.Count; index++)
            {
                if (policy.DirectorySeparators[index] == replaceTo) continue;
                if ((opt & TargetOption.Root) == TargetOption.Root)
                    m_root = m_root.Replace(policy.DirectorySeparators[index], replaceTo);
                if ((opt & TargetOption.Relation) == TargetOption.Relation)
                    m_relation = m_relation.Replace(policy.DirectorySeparators[index], replaceTo);
            }
        }

        /// @cond <summary>RelativeNormalizeを行ってから末尾にシステムのディレクトリセパレータ(System.IO.Path.DirectorySeparatorChar)を追加し、ディレクトリ形式の文字列としてパスの内容を文字列形式に変換します。</summary> @endcond
        //! @brief RelativeNormalizeを行ってから末尾にシステムのディレクトリセパレータ(System.IO.Path.DirectorySeparatorChar)を追加し、ディレクトリ形式の文字列としてパスの内容を文字列形式に変換します。
        //! @details ポリシーにDirectorySeparatorsが登録されていれば、それらは全てシステムのディレクトリセパレータに統一されます
        //! （SeparatorNormalize()とは異なり、DirectorySeparatorsがシステムのディレクトリセパレータを含まなくても統一します）。
        public System.String ToAbsoluteSystemDirectoryPath()
        {
            Path tmp = new Path(this);
            tmp.RelativeNormalize(TargetOption.Relation);
            IPathPolicy policy = tmp.Policy ?? PathPolicy.Current;
            if (policy.DirectorySeparators != null)
            {
                for (int index = 0; index < policy.DirectorySeparators.Count; index++)
                {
                    if (policy.DirectorySeparators[index] == System.IO.Path.DirectorySeparatorChar) break;
                    tmp.m_relation = m_relation.Replace(policy.DirectorySeparators[index], System.IO.Path.DirectorySeparatorChar);
                }
            }
            return tmp.ToString() + (System.String.IsNullOrWhiteSpace(tmp.m_relation) ? System.String.Empty : new System.String(System.IO.Path.DirectorySeparatorChar, 1));
        }

        /// @cond <summary>RelativeNormalizeを行って、ファイル形式の文字列としてパスの内容を文字列形式に変換します。</summary> @endcond
        //! @brief RelativeNormalizeを行って、ファイル形式の文字列としてパスの内容を文字列形式に変換します。
        //! @details ポリシーにDirectorySeparatorsが登録されていれば、それらは全てシステムのディレクトリセパレータに統一されます
        //! （SeparatorNormalize()とは異なり、DirectorySeparatorsがシステムのディレクトリセパレータを含まなくても統一します）。
        public System.String ToAbsoluteSystemFilePath()
        {
            Path tmp = new Path(this);
            tmp.RelativeNormalize(TargetOption.Relation);
            IPathPolicy policy = tmp.Policy ?? PathPolicy.Current;
            if (policy.DirectorySeparators != null)
            {
                for (int index = 0; index < policy.DirectorySeparators.Count; index++)
                {
                    if (policy.DirectorySeparators[index] == System.IO.Path.DirectorySeparatorChar) break;
                    tmp.m_relation = m_relation.Replace(policy.DirectorySeparators[index], System.IO.Path.DirectorySeparatorChar);
                }
            }
            return tmp.ToString();
        }

        /// @cond <summary>末尾にシステムの相対ディレクトリセパレータ(System.IO.Path.AltDirectorySeparatorChar)を追加し、相対ディレクトリ形式の文字列としてパスの内容を文字列形式に変換します。</summary> @endcond
        //! @brief 末尾にシステムの相対ディレクトリセパレータ(System.IO.Path.AltDirectorySeparatorChar)を追加し、相対ディレクトリ形式の文字列としてパスの内容を文字列形式に変換します。
        //! @details ポリシーにDirectorySeparatorsが登録されていれば、それらは全てシステムの相対ディレクトリセパレータに統一されます
        //! （SeparatorNormalize()とは異なり、DirectorySeparatorsがシステムの相対ディレクトリセパレータを含まなくても統一します）。
        //! @note なお、ルート要素は無加工のまま残ります。厳密に相対パスだけを生成する時はsrc.Relation.ToRelativeDirectoryPath()を利用してください。
        public System.String ToRelativeDirectoryPath()
        {
            Path tmp = new Path(this);
            IPathPolicy policy = tmp.Policy ?? PathPolicy.Current;
            if (policy.DirectorySeparators != null)
            {
                for (int index = 0; index < policy.DirectorySeparators.Count; index++)
                {
                    if (policy.DirectorySeparators[index] == System.IO.Path.AltDirectorySeparatorChar) break;
                    tmp.m_relation = m_relation.Replace(policy.DirectorySeparators[index], System.IO.Path.AltDirectorySeparatorChar);
                }
            }
            return tmp.ToString() + (System.String.IsNullOrWhiteSpace(tmp.m_relation) ? System.String.Empty : new System.String(System.IO.Path.AltDirectorySeparatorChar, 1));
        }

        /// @cond <summary>相対ファイル形式の文字列としてパスの内容を文字列形式に変換します。</summary> @endcond
        //! @brief 相対ファイル形式の文字列としてパスの内容を文字列形式に変換します。
        //! @details ポリシーにDirectorySeparatorsが登録されていれば、それらは全てシステムの相対ディレクトリセパレータに統一されます
        //! （SeparatorNormalize()とは異なり、DirectorySeparatorsがシステムの相対ディレクトリセパレータを含まなくても統一します）。
        //! @note なお、ルート要素は無加工のまま残ります。厳密に相対パスだけを生成する時はsrc.Relation.ToRelativeFilePath()を利用してください。
        public System.String ToRelativeFilePath()
        {
            Path tmp = new Path(this);
            IPathPolicy policy = tmp.Policy ?? PathPolicy.Current;
            if (policy.DirectorySeparators != null)
            {
                for (int index = 0; index < policy.DirectorySeparators.Count; index++)
                {
                    if (policy.DirectorySeparators[index] == System.IO.Path.AltDirectorySeparatorChar) break;
                    tmp.m_relation = m_relation.Replace(policy.DirectorySeparators[index], System.IO.Path.AltDirectorySeparatorChar);
                }
            }
            return tmp.ToString();
        }

        /// @cond <summary>末尾にシステムのディレクトリセパレータ(System.IO.Path.DirectorySeparatorChar)を追加し、ディレクトリ形式の文字列としてパスの内容を文字列形式に変換します。</summary> @endcond
        //! @brief 末尾にシステムのディレクトリセパレータ(System.IO.Path.DirectorySeparatorChar)を追加し、ディレクトリ形式の文字列としてパスの内容を文字列形式に変換します。
        //! @details ポリシーにDirectorySeparatorsが登録されていれば、それらは全てシステムのディレクトリセパレータに統一されます
        //! （SeparatorNormalize()とは異なり、DirectorySeparatorsがシステムのディレクトリセパレータを含まなくても統一します）。
        public System.String ToSystemDirectoryPath()
        {
            Path tmp = new Path(this);
            IPathPolicy policy = tmp.Policy ?? PathPolicy.Current;
            if (policy.DirectorySeparators != null)
            {
                for (int index = 0; index < policy.DirectorySeparators.Count; index++)
                {
                    if (policy.DirectorySeparators[index] == System.IO.Path.DirectorySeparatorChar) break;
                    tmp.m_relation = m_relation.Replace(policy.DirectorySeparators[index], System.IO.Path.DirectorySeparatorChar);
                }
            }
            return tmp.ToString() + (System.String.IsNullOrWhiteSpace(tmp.m_relation) ? System.String.Empty : new System.String(System.IO.Path.DirectorySeparatorChar, 1));
        }

        /// @cond <summary>ファイル形式の文字列としてパスの内容を文字列形式に変換します。</summary> @endcond
        //! @brief ファイル形式の文字列としてパスの内容を文字列形式に変換します。
        //! @details ポリシーにDirectorySeparatorsが登録されていれば、それらは全てシステムのディレクトリセパレータに統一されます
        //! （SeparatorNormalize()とは異なり、DirectorySeparatorsがシステムのディレクトリセパレータを含まなくても統一します）。
        public System.String ToSystemFilePath()
        {
            Path tmp = new Path(this);
            IPathPolicy policy = tmp.Policy ?? PathPolicy.Current;
            if (policy.DirectorySeparators != null)
            {
                for (int index = 0; index < policy.DirectorySeparators.Count; index++)
                {
                    if (policy.DirectorySeparators[index] == System.IO.Path.DirectorySeparatorChar) break;
                    tmp.m_relation = m_relation.Replace(policy.DirectorySeparators[index], System.IO.Path.DirectorySeparatorChar);
                }
            }
            return tmp.ToString();
        }

        // private members
        private System.String m_relation;
        private System.String m_root;

        /// @cond <summary>コンフリクト例外チェックしつついずれかのPolicyを取得します。メンバへのマージはしません。nullになる時はnullではなくカレントポリシーを取得します。競合すると例外がスローされます。</summary> @endcond
        //! @brief コンフリクト例外チェックしつついずれかのPolicyを取得します。メンバへのマージはしません。nullになる時はnullではなくカレントポリシーを取得します。
        protected IPathPolicy AnyPolicy(Path merge) { return AnyPolicy(this, merge); }
        /// @cond <summary>コンフリクト例外チェックしつついずれかのPolicyを取得します。メンバへのマージはしません。nullになる時はnullではなくカレントポリシーを取得します。競合すると例外がスローされます。</summary> @endcond
        //! @brief コンフリクト例外チェックしつついずれかのPolicyを取得します。メンバへのマージはしません。nullになる時はnullではなくカレントポリシーを取得します。
        protected static IPathPolicy AnyPolicy(Path pathA, Path pathB)
        {
            IPathPolicy ret = null;
            if (pathA != null) ret = pathA.Policy;
            if (pathB != null)
            {
                if (ret == null) ret = pathB.Policy;
                else if ((pathB.Policy != null) && (ReferenceEquals(ret, pathB.Policy) == false))
                {
                    throw new Path.PolicyConflictException(pathA, pathB);
                }
            }
            if (ret == null) ret = PathPolicy.Current;
            return ret;
        }

        /// @cond <summary>末尾のファイル/ディレクトリ要素名を取得します。区切りが取得できない時は全体が返ります。</summary> @endcond
        //! @brief 末尾のファイル/ディレクトリ要素名を取得します。区切りが取得できない時は全体が返ります。
        private static System.String GetLastElementName(System.String path, IPathPolicy policy)
        {
            if (policy == null) policy = PathPolicy.Current;
            if (System.String.IsNullOrWhiteSpace(path)) return System.String.Empty;
            if ((policy.DirectorySeparators == null) || (policy.DirectorySeparators.Count <= 0)) return path;
            System.Char[] dirsep = new System.Char[policy.DirectorySeparators.Count];
            policy.DirectorySeparators.CopyTo(dirsep, 0);
            int index = path.LastIndexOfAny(dirsep);
            if (index < 0) return path;
            return path.Substring(index + 1);
        }

        /// @cond <summary>末尾の拡張子名を取得します。拡張子区切りから始まる文字列を取得します。</summary> @endcond
        //! @brief 末尾の拡張子名を取得します。拡張子区切りから始まる文字列を取得します。
        //! @details 拡張子区切り文字が連続する時は、最初の拡張子区切り文字から始まる文字列を取得します。拡張子が取得できない時は空の文字列が返ります。
        //! policy.IsRemoveTrailExtentionSeparatorがtrueであれば、末尾に空白と拡張子区切りだけが連続する部分が見つかる時に切り詰められます。
        //! @param [in] multiMatch 多重拡張子全体を取得する時はtrueにします。
        private static System.String GetExtentionName(System.String path, IPathPolicy policy, bool multiMatch)
        {
            if (policy == null) policy = PathPolicy.Current;
            if (System.String.IsNullOrWhiteSpace(path) || (policy.ExtentionSeparators == null) || (policy.ExtentionSeparators.Count <= 0))
                return System.String.Empty;

            System.StringComparison cmpOpt = System.StringComparison.Ordinal;
            System.Text.RegularExpressions.RegexOptions opt = System.Text.RegularExpressions.RegexOptions.None;
            if (policy.IsIgnoreCase ?? PathPolicy.Default.IsIgnoreCase.Value)
            {
                cmpOpt = System.StringComparison.OrdinalIgnoreCase;
                opt = System.Text.RegularExpressions.RegexOptions.IgnoreCase;
            }

            System.String extCodes = System.String.Empty;
            foreach (System.Char ch in policy.ExtentionSeparators) extCodes += "\\" + ch;

            System.String sepCodes = System.String.Empty;
            if (policy.DirectorySeparators != null) foreach (System.Char ch in policy.DirectorySeparators) sepCodes += "\\" + ch;

            System.String ext = GetLastElementName(path, policy).Trim();

            if ((policy.RelativeParentDirectoryName != null) && (System.String.IsNullOrEmpty(policy.RelativeParentDirectoryName) == false))
            {
                if (ext.Equals(policy.RelativeParentDirectoryName, cmpOpt)) return System.String.Empty;
            }
            if ((policy.RelativeCurrentDirectoryName != null) && (System.String.IsNullOrEmpty(policy.RelativeCurrentDirectoryName) == false))
            {
                if (ext.Equals(policy.RelativeCurrentDirectoryName, cmpOpt)) return System.String.Empty;
            }

            System.String extPattern = System.String.Format("([{0}]{2}[^{0}{1}]*){2}$", extCodes, sepCodes, multiMatch ? "+" : System.String.Empty);
            return new System.Text.RegularExpressions.Regex(extPattern, opt).Match(ext).Value;
        }

        /// @cond <summary>末尾の拡張子名を取得します。拡張子区切りから始まる文字列を取得します。</summary> @endcond
        //! @brief 末尾の拡張子名を取得します。拡張子区切りから始まる文字列を取得します。
        //! @details 拡張子区切り文字が連続する時は、最初の拡張子区切り文字から始まる文字列を取得します。
        //! @param [in] multiMatch 多重拡張子全体を取得する時はtrueにします。
        private static System.String GetExtentionName(System.String path, IPathPolicy policy)
        {
            if (policy == null) policy = PathPolicy.Current;
            return GetExtentionName(path, policy, policy.IsMultiExtention ?? PathPolicy.Default.IsMultiExtention.Value);
        }

        /// @cond <summary>パスを示す文字列から、親と末尾の部分に分離します。</summary> @endcond
        //! @brief パスを示す文字列から、親と末尾の部分に分離します。
        //! @details 区切りを見つけることが出来ない時は、parentとnameには等しくpathがコピーされ、sepにはSystem.String.Emptyが代入されます。
        private static void GetDirectoryParsedStrings(ref System.String parent, ref System.String sep, ref System.String name, System.String path, IPathPolicy policy)
        {
            if (System.String.IsNullOrWhiteSpace(path))
            {
                parent = sep = name = System.String.Empty;
                return;
            }
            if (policy == null) policy = PathPolicy.Current;
            if ((policy.DirectorySeparators == null) || (policy.DirectorySeparators.Count <= 0)) sep = System.String.Empty;
            else
            {
                System.Text.RegularExpressions.RegexOptions opt = System.Text.RegularExpressions.RegexOptions.None;
                if (policy.IsIgnoreCase ?? PathPolicy.Default.IsIgnoreCase.Value) opt = System.Text.RegularExpressions.RegexOptions.IgnoreCase;

                System.String sepCodes = System.String.Empty;
                foreach (System.Char ch in policy.DirectorySeparators) sepCodes += "\\" + ch;
                System.Text.RegularExpressions.Match match =
                    new System.Text.RegularExpressions.Regex(System.String.Format("(?<parent>.*?)(?<sep>[{0}]*)(?<name>[^{0}]*)$", sepCodes), opt).Match(path);
                parent = match.Groups["parent"].Value;
                sep = match.Groups["sep"].Value;
                name = match.Groups["name"].Value;
            }

            if (System.String.IsNullOrWhiteSpace(sep)) parent = name = path;
        }

        //! @brief 相対パス要素が解決された文字列を返します。
        private static System.String GetNormalizedRelation(System.String src, IPathPolicy policy, TargetOption separatorNormalizeTarget, int separatorNormalizeIndex)
        {
            if (policy == null) policy = PathPolicy.Current;
            if ((policy.DirectorySeparators == null) || (policy.DirectorySeparators.Count <= 0)) return src;

            if ((separatorNormalizeIndex < 0) || (policy.DirectorySeparators.Count <= separatorNormalizeIndex))
                separatorNormalizeTarget = TargetOption.None;

            System.String unifySeparator = null;
            if ((separatorNormalizeTarget & TargetOption.Relation) == TargetOption.Relation) unifySeparator = new System.String(policy.DirectorySeparators[separatorNormalizeIndex], 1);

            System.String altCurrent = System.String.IsNullOrWhiteSpace(policy.RelativeCurrentDirectoryName) ? null : policy.RelativeCurrentDirectoryName;
            System.String altParent = System.String.IsNullOrWhiteSpace(policy.RelativeParentDirectoryName) ? null : policy.RelativeParentDirectoryName;

            if ((altCurrent == null) && (altParent == null)) return src;

            System.StringComparison cmpOpt = System.StringComparison.Ordinal;
            System.Text.RegularExpressions.RegexOptions opt = System.Text.RegularExpressions.RegexOptions.None;
            if (policy.IsIgnoreCase ?? PathPolicy.Default.IsIgnoreCase.Value)
            {
                cmpOpt = System.StringComparison.OrdinalIgnoreCase;
                opt = System.Text.RegularExpressions.RegexOptions.IgnoreCase;
            }

            System.String EscapedSeparators = System.String.Empty;
            foreach (System.Char ch in policy.DirectorySeparators) EscapedSeparators += "\\" + ch;
            System.String dirParsePattern = System.String.Format("(?<sep>[{0}\\s]*)(?<element>[^{0}]+)", EscapedSeparators);

            // output source
            System.Collections.Generic.List<System.String> trimmedDirs = new System.Collections.Generic.List<string>();
            System.Text.RegularExpressions.Regex dirParser = new System.Text.RegularExpressions.Regex(dirParsePattern, opt);
            foreach (System.Text.RegularExpressions.Match dirMatch in dirParser.Matches(src))
            {
                System.String sep = dirMatch.Groups["sep"].Value.Trim();
                System.String element = dirMatch.Groups["element"].Value.Trim();
                if (System.String.IsNullOrWhiteSpace(element) || System.String.Equals(altCurrent, element, cmpOpt)) continue;
                else if (System.String.Equals(altParent, element, cmpOpt))
                {
                    if (0 < trimmedDirs.Count) trimmedDirs.RemoveAt(trimmedDirs.Count - 1);
                }
                else
                {
                    if (1 < sep.Length)
                    {
                        if (unifySeparator != null) sep = unifySeparator;
                        else sep = sep.Substring(0, 1); // first separator only
                    }
                    trimmedDirs.Add(((trimmedDirs.Count <= 0) ? System.String.Empty : sep) + element);
                }
            }

            System.String ret = System.String.Join(System.String.Empty, trimmedDirs);
            if (System.String.IsNullOrWhiteSpace(ret)) ret = System.String.Empty;
            return ret;
        }

        /// @cond <summary>パスを示す文字列から不正な文字を削除し、ルートとルート以外の部分に分離します。</summary> @endcond
        //! @brief パスを示す文字列から不正な文字を削除し、ルートとルート以外の部分に分離します。
        //! @details ディレクトリは一度区切りごとに分解し、ひとつひとつトリムしてから再構築されます。ルート以外の部分で連続するディレクトリ記号は先頭の記号一つだけが残ります。
        private static void GetRootParsedStrings(ref System.String root, ref System.String path, System.String src, IPathPolicy policy)
        {
            if (policy == null) policy = PathPolicy.Current;

            System.StringComparison cmpOpt = System.StringComparison.Ordinal;
            System.Text.RegularExpressions.RegexOptions opt = System.Text.RegularExpressions.RegexOptions.None;
            if (policy.IsIgnoreCase ?? PathPolicy.Default.IsIgnoreCase.Value)
            {
                cmpOpt = System.StringComparison.OrdinalIgnoreCase;
                opt = System.Text.RegularExpressions.RegexOptions.IgnoreCase;
            }

            // remove invalid char codes
            if (System.String.IsNullOrWhiteSpace(src)) src = System.String.Empty;
            else if (policy.InvalidChars != null) foreach (System.Char ch in policy.InvalidChars) src = src.Replace(new System.String(ch, 1), System.String.Empty);

            // parse root and path
            System.String _path = null, _root = null;
            if ((System.String.IsNullOrWhiteSpace(policy.RootParsePattern) == false) &&
                (System.String.IsNullOrWhiteSpace(policy.RootParsePatternRootGroupName) == false) &&
                (System.String.IsNullOrWhiteSpace(policy.RootParsePatternPathGroupName) == false))
            {
                try
                {
                    System.Text.RegularExpressions.Regex rootParser = new System.Text.RegularExpressions.Regex(policy.RootParsePattern, opt);
                    System.Text.RegularExpressions.Match match = rootParser.Match(src);
                    _root = match.Groups[policy.RootParsePatternRootGroupName].Value.Trim();
                    _path = match.Groups[policy.RootParsePatternPathGroupName].Value;
                }
                catch (System.Exception ex) { throw new Path.RootParseException(policy, path, ex); }
            }
            else
            {
                path = src.Trim();
                return;
            }

            path = _path;
            root = System.String.IsNullOrWhiteSpace(_root) ? System.String.Empty : _root;

            if (policy.IsRemoveWhitespaceFromRoot ?? PathPolicy.Default.IsRemoveWhitespaceFromRoot.Value) root = System.String.Join(System.String.Empty, root.Split(null));

            System.Char[] trimExtentionSepCodes = null;
            System.Collections.Generic.List<System.String> protectElementHeader = null;
            bool IsRemoveTrailExtentionSeparator = (policy.IsRemoveTrailExtentionSeparator ?? PathPolicy.Default.IsRemoveTrailExtentionSeparator.Value) &&
                (policy.ExtentionSeparators != null) && (0 < policy.ExtentionSeparators.Count);
            if (IsRemoveTrailExtentionSeparator)
            {
                trimExtentionSepCodes = new System.Char[policy.ExtentionSeparators.Count];
                policy.ExtentionSeparators.CopyTo(trimExtentionSepCodes, 0);
                protectElementHeader = new System.Collections.Generic.List<System.String>(2);
                if ((policy.RelativeParentDirectoryName != null) && (System.String.IsNullOrEmpty(policy.RelativeParentDirectoryName) == false))
                {
                    protectElementHeader.Add(policy.RelativeParentDirectoryName);
                }
                if ((policy.RelativeCurrentDirectoryName != null) && (System.String.IsNullOrEmpty(policy.RelativeCurrentDirectoryName) == false))
                {
                    protectElementHeader.Add(policy.RelativeCurrentDirectoryName);
                }
                if ((1 < protectElementHeader.Count) && (protectElementHeader[0].Length < protectElementHeader[1].Length)) protectElementHeader.Reverse();
            }

            if (System.String.IsNullOrEmpty(path)) path = System.String.Empty;
            else
            {
                // path = split >> trim >> removeEmpty >> join
                if ((policy.DirectorySeparators == null) && (policy.DirectorySeparators.Count <= 0)) path = path.Trim();
                else
                {
                    // output source
                    System.Collections.Generic.List<System.String> trimmedDirs = new System.Collections.Generic.List<string>();
                    // regex-parse : StringBySeparators+StringByNotSeparators collection
                    System.String EscapedSeparators = System.String.Empty;
                    foreach (System.Char ch in policy.DirectorySeparators) EscapedSeparators += "\\" + ch;
                    System.String dirParsePattern = System.String.Format("(?<sep>[{0}\\s]*)(?<element>[^{0}]+)", EscapedSeparators);
                    System.Text.RegularExpressions.Regex dirParser = new System.Text.RegularExpressions.Regex(dirParsePattern, opt);
                    foreach (System.Text.RegularExpressions.Match dirMatch in dirParser.Matches(path))
                    {
                        System.String sep = dirMatch.Groups["sep"].Value.Trim();
                        System.String element = dirMatch.Groups["element"].Value.Trim();
                        if (IsRemoveTrailExtentionSeparator)
                        {
                            System.String protectHead = null;
                            foreach (System.String checkHead in protectElementHeader)
                            {
                                if (element.StartsWith(checkHead, cmpOpt))
                                {
                                    protectHead = element.Substring(0, checkHead.Length);
                                    element = element.Substring(checkHead.Length);
                                    break;
                                }
                            }
                            while ((0 < element.Length) && ((element.Length - 1) <= element.LastIndexOfAny(trimExtentionSepCodes)))
                            {
                                element = element.TrimEnd(trimExtentionSepCodes).TrimEnd(null);
                            }
                            if (protectHead != null) element = protectHead + element;
                        }
                        if (System.String.IsNullOrWhiteSpace(element)) continue;
                        if (1 < sep.Length) sep = sep.Substring(0, 1); // first separator only
                        trimmedDirs.Add(sep + element);
                    }
                    // join output
                    path = System.String.Join(System.String.Empty, trimmedDirs.ToArray());
                    if (System.String.IsNullOrWhiteSpace(path)) path = System.String.Empty;
                }
            }
        }

        /// @cond <summary>コンフリクト例外チェックしつつPolicyメンバを外部Policyとマージします。Policyメンバはnullのままになるケースがあります。</summary> @endcond
        //! @brief コンフリクト例外チェックしつつPolicyメンバを外部Policyとマージします。Policyメンバはnullのままになるケースがあります。
        protected void MergePolicy(Path merge)
        {
            if (ReferenceEquals(merge, null)) return;
            if (Policy == null) Policy = merge.Policy;
            else if ((merge.Policy != null) && (ReferenceEquals(Policy, merge.Policy) == false))
            {
                throw new Path.PolicyConflictException(this, merge);
            }
        }

        /// @cond <summary>安全ではない初期化がされた新しいインスタンスを返します。</summary> @endcond
        //! @brief 安全ではない初期化がされた新しいインスタンスを返します。
        //! @details 引数のrelationとrootには、内部に格納するメンバとしてすでに正しい状態にあるデータを渡してください。
        //! 具体的には、GetParsedString()で取得するか、あるいは既にPathクラスのプライベートメンバとして格納されている値を部分的にコピーしたインスタンスを作成するための
        //! メソッドとして利用します。
        private static Path UnsafeInitialized(System.String root, System.String relation, IPathPolicy policy)
        {
            if (System.String.IsNullOrWhiteSpace(root)) root = System.String.Empty;
            if (System.String.IsNullOrWhiteSpace(relation)) relation = System.String.Empty;
            if ((policy == null) && (root == System.String.Empty) && (relation == System.String.Empty)) return Empty;
            Path ret = new Path();
            ret.m_relation = relation;
            ret.m_root = root;
            ret.Policy = policy;
            return ret;
        }
    }

    //! @}
}
