OSDN Git Service

#27621 言語間リンク取得時に<includeonly>, <noinclude>を考慮するよう修正,
authorhoneplus <honeplus@users.osdn.me>
Sun, 26 Feb 2012 20:40:51 +0000 (20:40 +0000)
committerhoneplus <honeplus@users.osdn.me>
Sun, 26 Feb 2012 20:40:51 +0000 (20:40 +0000)
#27313 ピリオド暫定対応で通信エラーが誤検知されることがあったのを修正,
ステータス管理関連・画面のスレッド周りの処理などをリファクタリング・修正,
パーサー周りのテストケースを不足分を中心に整備

git-svn-id: http://svn.osdn.net/svnroot/wptscs/trunk@25 7cc79d57-4d93-40a1-83d5-ec7b38613dec

58 files changed:
HmLib/HmLib.csproj
HmLib/Models/IgnoreCaseDictionary.cs
HmLib/Models/IgnoreCaseSet.cs
HmLib/Parsers/AbstractParser.cs
HmLib/Parsers/AbstractTextParser.cs
HmLib/Parsers/CacheParser.cs
HmLib/Parsers/IParser.cs
HmLib/Parsers/XmlElementParser.cs
HmLib/Parsers/XmlParser.cs
HmLib/Utilities/CollectionUtils.cs
HmLib/Utilities/LockObject.cs
HmLib/Utilities/ObjectUtils.cs
HmLib/Utilities/StatusManager.cs [new file with mode: 0644]
HmLib/Utilities/StringUtils.cs
HmLib/Utilities/Validate.cs
HmLib/Utilities/XmlUtils.cs
HmLibTest/HmLibTest.csproj
HmLibTest/Parsers/AbstractElementTest.cs [new file with mode: 0644]
HmLibTest/Parsers/AbstractParserTest.cs [new file with mode: 0644]
HmLibTest/Parsers/AbstractTextParserTest.cs [new file with mode: 0644]
HmLibTest/Parsers/CacheParserTest.cs
HmLibTest/Parsers/XmlCommentElementParserTest.cs
HmLibTest/Parsers/XmlElementParserTest.cs
HmLibTest/Parsers/XmlParserTest.cs
HmLibTest/Utilities/StatusManagerTest.cs [new file with mode: 0644]
HmLibTest/Utilities/ValidateTest.cs
Wikipedia 翻訳支援ツール.asta
Wptscs/Logics/MediaWikiTranslator.cs
Wptscs/Logics/Translator.cs
Wptscs/MainForm.cs
Wptscs/Parsers/MediaWikiHeadingParser.cs
Wptscs/Parsers/MediaWikiLinkParser.cs
Wptscs/Parsers/MediaWikiNowikiParser.cs
Wptscs/Parsers/MediaWikiParser.cs
Wptscs/Parsers/MediaWikiPreparser.cs [new file with mode: 0644]
Wptscs/Parsers/MediaWikiRedirectParser.cs
Wptscs/Parsers/MediaWikiTemplateParser.cs
Wptscs/Parsers/MediaWikiVariableParser.cs
Wptscs/Readme.txt
Wptscs/Utilities/EndPeriodException.cs [new file with mode: 0644]
Wptscs/Websites/MediaWiki.cs
Wptscs/Websites/MediaWikiPage.cs
Wptscs/Wikipedia.xml
Wptscs/Wptscs.csproj
WptscsTest/Data/MediaWiki/en/Template_Partial.xml [new file with mode: 0644]
WptscsTest/Data/MediaWiki/en/Template_Table cell templates_doc.xml [new file with mode: 0644]
WptscsTest/Logics/MediaWikiTranslatorTest.cs
WptscsTest/Logics/TranslatorTest.cs
WptscsTest/Parsers/MediaWikiHeadingParserTest.cs
WptscsTest/Parsers/MediaWikiLinkParserTest.cs
WptscsTest/Parsers/MediaWikiNowikiParserTest.cs
WptscsTest/Parsers/MediaWikiParserTest.cs
WptscsTest/Parsers/MediaWikiPreparserTest.cs [new file with mode: 0644]
WptscsTest/Parsers/MediaWikiRedirectParserTest.cs
WptscsTest/Parsers/MediaWikiTemplateParserTest.cs
WptscsTest/Parsers/MediaWikiVariableParserTest.cs
WptscsTest/Websites/MediaWikiPageTest.cs
WptscsTest/WptscsTest.csproj

index c594eeb..ee04280 100644 (file)
@@ -43,6 +43,7 @@
     <Compile Include="Models\IgnoreCaseDictionary.cs" />
     <Compile Include="Models\IgnoreCaseSet.cs" />
     <Compile Include="Models\MemoryCache.cs" />
+    <Compile Include="Utilities\StatusManager.cs" />
     <Compile Include="Parsers\CacheParser.cs" />
     <Compile Include="Parsers\IElement.cs" />
     <Compile Include="Parsers\IParser.cs" />
index 3f5c5cc..c967473 100644 (file)
@@ -171,13 +171,13 @@ namespace Honememo.Models
             get
             {
                 // 小文字に変換し、マップを経てラップインスタンスにアクセス
-                return this.Dictionary[this.KeyMap[Validate.NotNull(key).ToLower()]];
+                return this.Dictionary[this.KeyMap[Validate.NotNull(key, "key").ToLower()]];
             }
 
             set
             {
                 // 小文字に変換し、マップを経てラップインスタンスにアクセス
-                string k = Validate.NotNull(key).ToLower();
+                string k = Validate.NotNull(key, "key").ToLower();
                 string orgKey;
                 if (this.KeyMap.TryGetValue(k, out orgKey))
                 {
@@ -206,7 +206,7 @@ namespace Honememo.Models
         public void Add(string key, TValue value)
         {
             // 小文字に変換し、マップを経てラップインスタンスにアクセス
-            string k = Validate.NotNull(key).ToLower();
+            string k = Validate.NotNull(key, "key").ToLower();
             this.KeyMap.Add(k, key);
             this.Dictionary.Add(key, value);
         }
@@ -221,7 +221,7 @@ namespace Honememo.Models
         {
             // 同期が取れていることを前提に、キーマップのみ確認する
             // (ラップインスタンスまで見ると、他の例外が起こりえて面倒なため)
-            return this.KeyMap.ContainsKey(Validate.NotNull(key).ToLower());
+            return this.KeyMap.ContainsKey(Validate.NotNull(key, "key").ToLower());
         }
 
         /// <summary>
@@ -237,7 +237,7 @@ namespace Honememo.Models
         public bool Remove(string key)
         {
             // 小文字に変換し、マップを経てラップインスタンスにアクセス
-            string k = Validate.NotNull(key).ToLower();
+            string k = Validate.NotNull(key, "key").ToLower();
             string orgKey;
             bool removed = false;
             if (this.KeyMap.TryGetValue(k, out orgKey))
@@ -268,7 +268,7 @@ namespace Honememo.Models
             value = default(TValue);
 
             // 小文字に変換し、マップを経てラップインスタンスにアクセス
-            string k = Validate.NotNull(key).ToLower();
+            string k = Validate.NotNull(key, "key").ToLower();
             string orgKey;
             if (this.KeyMap.TryGetValue(k, out orgKey))
             {
@@ -287,7 +287,7 @@ namespace Honememo.Models
         public void Add(KeyValuePair<string, TValue> item)
         {
             // 小文字に変換し、マップを経てラップインスタンスにアクセス
-            string k = Validate.NotNull(item.Key).ToLower();
+            string k = Validate.NotNull(item.Key, "item.key").ToLower();
             this.KeyMap.Add(k, item.Key);
             this.Dictionary.Add(item);
         }
@@ -340,7 +340,7 @@ namespace Honememo.Models
         public void CopyTo(KeyValuePair<string, TValue>[] array, int arrayIndex)
         {
             // 入力値チェック
-            Validate.NotNull(array);
+            Validate.NotNull(array, "array");
             if (arrayIndex < 0)
             {
                 throw new ArgumentOutOfRangeException("arrayIndex");
index 548d5f6..ff8f98d 100644 (file)
@@ -68,7 +68,7 @@ namespace Honememo.Models
         /// <exception cref="ArgumentNullException"><paramref name="collection"/>が<c>null</c>の場合。</exception>
         public IgnoreCaseSet(IEnumerable<string> collection) : this()
         {
-            foreach (string s in Validate.NotNull(collection))
+            foreach (string s in Validate.NotNull(collection, "collection"))
             {
                 this.Add(s);
             }
@@ -163,7 +163,7 @@ namespace Honememo.Models
         public bool Add(string item)
         {
             // 小文字に変換し、マップを経てラップインスタンスにアクセス
-            string k = Validate.NotNull(item).ToLower();
+            string k = Validate.NotNull(item, "item").ToLower();
             string orgItem;
             bool contains = this.KeyMap.TryGetValue(k, out orgItem);
             if (contains)
@@ -197,7 +197,7 @@ namespace Honememo.Models
         public bool Contains(string item)
         {
             // 同期が取れていることを前提に、キーマップのみ確認する
-            return this.KeyMap.ContainsKey(Validate.NotNull(item).ToLower());
+            return this.KeyMap.ContainsKey(Validate.NotNull(item, "item").ToLower());
         }
 
         /// <summary>
@@ -220,7 +220,7 @@ namespace Honememo.Models
         public void CopyTo(string[] array, int arrayIndex)
         {
             // 入力値チェック
-            Validate.NotNull(array);
+            Validate.NotNull(array, "array");
             if (arrayIndex < 0)
             {
                 throw new ArgumentOutOfRangeException("arrayIndex");
@@ -246,7 +246,7 @@ namespace Honememo.Models
         /// <exception cref="ArgumentNullException"><paramref name="other"/>が<c>null</c>の場合。</exception>
         public void ExceptWith(IEnumerable<string> other)
         {
-            foreach (string s in Validate.NotNull(other))
+            foreach (string s in Validate.NotNull(other, "other"))
             {
                 this.Remove(s);
             }
@@ -295,7 +295,7 @@ namespace Honememo.Models
         public bool IsProperSupersetOf(IEnumerable<string> other)
         {
             int count = 0;
-            foreach (string s in Validate.NotNull(other))
+            foreach (string s in Validate.NotNull(other, "other"))
             {
                 ++count;
                 if (!this.Contains(s))
@@ -327,7 +327,7 @@ namespace Honememo.Models
         public bool IsSupersetOf(IEnumerable<string> other)
         {
             int count = 0;
-            foreach (string s in Validate.NotNull(other))
+            foreach (string s in Validate.NotNull(other, "other"))
             {
                 ++count;
                 if (!this.Contains(s))
@@ -347,7 +347,7 @@ namespace Honememo.Models
         /// <exception cref="ArgumentNullException"><paramref name="other"/>が<c>null</c>の場合。</exception>
         public bool Overlaps(IEnumerable<string> other)
         {
-            foreach (string s in Validate.NotNull(other))
+            foreach (string s in Validate.NotNull(other, "other"))
             {
                 if (this.Contains(s))
                 {
@@ -370,7 +370,7 @@ namespace Honememo.Models
         public bool Remove(string item)
         {
             // 小文字に変換し、マップを経てラップインスタンスにアクセス
-            string k = Validate.NotNull(item).ToLower();
+            string k = Validate.NotNull(item, "item").ToLower();
             string orgItem;
             bool removed = false;
             if (this.KeyMap.TryGetValue(k, out orgItem))
@@ -433,7 +433,7 @@ namespace Honememo.Models
         public void UnionWith(IEnumerable<string> other)
         {
             // 重複は上書きされるだけなので単純に全部追加
-            foreach (string s in Validate.NotNull(other))
+            foreach (string s in Validate.NotNull(other, "other"))
             {
                 this.Add(s);
             }
index 7469d41..7802d1d 100644 (file)
@@ -11,6 +11,7 @@
 namespace Honememo.Parsers
 {
     using System;
+    using Honememo.Utilities;
 
     /// <summary>
     /// <see cref="IParser"/>を実装するための実装支援用抽象クラスです。
@@ -24,14 +25,15 @@ namespace Honememo.Parsers
         /// </summary>
         /// <param name="s">解析対象の文字列。</param>
         /// <returns>解析結果。</returns>
+        /// <exception cref="ArgumentNullException"><c>null</c>が指定された場合。</exception>
         /// <exception cref="FormatException">文字列が解析できないフォーマットの場合。</exception>
         /// <remarks>
-        /// <see cref="TryParse"/>を呼び出す。<c>TryParse</c>の結果が<c>false</c>の場合、例外として返す。
+        /// <see cref="TryParse"/>を呼び出す。<see cref="TryParse"/>の結果が<c>false</c>の場合、例外として返す。
         /// </remarks>
         public virtual IElement Parse(string s)
         {
             IElement result;
-            if (this.TryParse(s, out result))
+            if (this.TryParse(Validate.NotNull(s, "s"), out result))
             {
                 return result;
             }
@@ -70,12 +72,14 @@ namespace Honememo.Parsers
         /// <param name="result">解析した結果要素。</param>
         /// <param name="parsers">解析に用いるパーサー。指定された順に使用。</param>
         /// <returns>いずれかのパーサーで解析できた場合<c>true</c>。</returns>
+        /// <exception cref="ArgumentNullException"><paramref name="s"/>または<paramref name="parsers"/>が<c>null</c>の場合。</exception>
         /// <exception cref="ArgumentOutOfRangeException">インデックスが文字列の範囲外の場合。</exception>
         protected virtual bool TryParseAt(string s, int index, out IElement result, params IParser[] parsers)
         {
+            Validate.InRange(s, index, "s", "index");
             char c = s[index];
             string substr = null;
-            foreach (IParser parser in parsers)
+            foreach (IParser parser in Validate.NotNull(parsers, "parsers"))
             {
                 if (parser.IsPossibleParse(c))
                 {
index 0f43edd..77ef533 100644 (file)
@@ -44,10 +44,12 @@ namespace Honememo.Parsers
         /// <param name="result">解析結果。</param>
         /// <param name="delimiters">解析を終了する文字列(複数指定可)。</param>
         /// <returns>解析に成功した場合<c>true</c>。</returns>
+        /// <exception cref="ArgumentNullException"><paramref name="delimiters"/>が<c>null</c>の場合。</exception>
         /// <remarks>指定された文字列が出現しない場合、最終位置まで解析を行う。</remarks>
         public virtual bool TryParseToDelimiter(string s, out IElement result, params string[] delimiters)
         {
             // 終了条件のデリゲートに置き換え、そちらの処理にまとめる
+            Validate.NotNull(delimiters, "delimiters");
             return this.TryParseToEndCondition(
                 s,
                 (string str, int index)
@@ -76,6 +78,13 @@ namespace Honememo.Parsers
         /// <remarks>指定された終了条件を満たさない場合、最終位置まで解析を行う。</remarks>
         public virtual bool TryParseToEndCondition(string s, IsEndCondition condition, out IElement result)
         {
+            if (s == null)
+            {
+                // nullの場合だけは解析失敗とする
+                result = null;
+                return false;
+            }
+
             // 文字列を1文字ずつチェックし、その内容に応じた要素のリストを作成する
             ListElement list = new ListElement();
             StringBuilder b = new StringBuilder();
@@ -148,9 +157,11 @@ namespace Honememo.Parsers
         /// </summary>
         /// <param name="list">追加されるリスト。</param>
         /// <param name="b">追加する文字列。</param>
+        /// <exception cref="ArgumentNullException"><paramref name="list"/>または<paramref name="b"/>が<c>null</c>の場合。</exception>
         protected virtual void FlashText(ref ListElement list, ref StringBuilder b)
         {
-            if (b.Length > 0)
+            Validate.NotNull(list, "list");
+            if (Validate.NotNull(b, "b").Length > 0)
             {
                 list.Add(new TextElement(b.ToString()));
                 b.Clear();
index daaeda7..f6a2bba 100644 (file)
@@ -50,7 +50,7 @@ namespace Honememo.Parsers
         /// <exception cref="ArgumentException"><paramref name="capacity"/>が0以下の値。</exception>
         public CacheParser(IParser parser, int capacity)
         {
-            this.parser = Validate.NotNull(parser);
+            this.parser = Validate.NotNull(parser, "parser");
             this.caches = new MemoryCache<int, IElement>(capacity);
         }
 
@@ -61,7 +61,7 @@ namespace Honememo.Parsers
         /// <exception cref="ArgumentNullException"><paramref name="parser"/>が<c>null</c>。</exception>
         public CacheParser(IParser parser)
         {
-            this.parser = Validate.NotNull(parser);
+            this.parser = Validate.NotNull(parser, "parser");
             this.caches = new MemoryCache<int, IElement>();
         }
 
@@ -74,6 +74,7 @@ namespace Honememo.Parsers
         /// </summary>
         /// <param name="s">解析対象の文字列。</param>
         /// <returns>解析結果。</returns>
+        /// <exception cref="ArgumentNullException"><c>null</c>が指定された場合。</exception>
         /// <exception cref="FormatException">
         /// 文字列が解析できないフォーマットの場合。
         /// <see cref="TryParse"/>にて解析失敗がキャッシュされている場合もこの例外を返す。
@@ -108,6 +109,13 @@ namespace Honememo.Parsers
         /// </remarks>
         public bool TryParse(string s, out IElement result)
         {
+            if (s == null)
+            {
+                // nullだけ先にチェック
+                result = null;
+                return false;
+            }
+
             // TryParseが呼ばれた場合その処理結果を返す。
             // キャッシュの場合キャッシュに値があれば成功と返す。
             bool called = false;
@@ -153,7 +161,7 @@ namespace Honememo.Parsers
         private IElement GetAndAddIfEmpty(string s, MemoryCache<string, IElement>.ReturnCacheValue function)
         {
             // まずキャッシュを確認
-            int hashCode = Validate.NotNull(s).GetHashCode();
+            int hashCode = Validate.NotNull(s, "s").GetHashCode();
             IElement element;
             if (this.TryGetValue(hashCode, s, out element))
             {
index 21a4f76..3c22c88 100644 (file)
@@ -24,6 +24,7 @@ namespace Honememo.Parsers
         /// </summary>
         /// <param name="s">解析対象の文字列。</param>
         /// <returns>解析結果。</returns>
+        /// <exception cref="ArgumentNullException"><c>null</c>が指定された場合。</exception>
         /// <exception cref="FormatException">文字列が解析できないフォーマットの場合。</exception>
         /// <remarks>解析に失敗した場合は、各種例外を投げる。</remarks>
         IElement Parse(string s);
index bf239b6..60457b9 100644 (file)
@@ -30,6 +30,11 @@ namespace Honememo.Parsers
         /// </summary>
         private XmlParser parser;
 
+        /// <summary>
+        /// 特定のXML/HTMLタグのみを解析対象とする場合そのタグ名。
+        /// </summary>
+        private IList<string> targets = new List<string>();
+
         #endregion
         
         #region コンストラクタ
@@ -54,7 +59,37 @@ namespace Honememo.Parsers
 
         #endregion
 
-        #region プロパティ
+        #region 公開プロパティ
+
+        /// <summary>
+        /// 特定のXML/HTMLタグのみを解析対象とする場合そのタグ名。
+        /// </summary>
+        /// <exception cref="ArgumentNullException"><c>null</c>が指定された場合。</exception>
+        /// <remarks>
+        /// <para>
+        /// 空の場合、全てのXML/HTMLタグを解析対象とする。
+        /// </para>
+        /// <para>
+        /// &lt;br&gt;タグのように閉じタグが存在しないケースで時間がかかってしまうケースが存在するため、
+        /// 必要なタグのみを処理するための機能として実装。
+        /// </para>
+        /// </remarks>
+        public virtual IList<string> Targets
+        {
+            get
+            {
+                return this.targets;
+            }
+
+            set
+            {
+                this.targets = Validate.NotNull(value);
+            }
+        }
+
+        #endregion
+
+        #region protectedプロパティ
 
         /// <summary>
         /// このパーサーが参照する<see cref="XmlParser"/>。
@@ -103,9 +138,13 @@ namespace Honememo.Parsers
                 return false;
             }
 
-            // タグ名確認、コメント(<!--)やちゃんと始まっていないもの(< tag>とか)もここで除外
+            // タグ名確認、コメント(<!--)やちゃんと始まっていないもの(< tag>とか)もここで除外。
+            // 処理対象のタグが限定されている場合で、それ以外のものもここで除外。
             string name = s.Substring(1, index - 1);
-            if (!this.ValidateName(name))
+            if (!this.ValidateName(name)
+                || (this.targets.Count > 0
+                && !(!this.parser.IgnoreCase && this.targets.Contains(name))
+                && !(this.parser.IgnoreCase && CollectionUtils.ContainsIgnoreCase(this.targets, name))))
             {
                 return false;
             }
index b3e58f3..5f0e6a9 100644 (file)
@@ -158,6 +158,8 @@ namespace Honememo.Parsers
         /// <param name="index">処理インデックス。</param>
         /// <param name="result">解析した結果要素。</param>
         /// <returns>解析できた場合<c>true</c>。</returns>
+        /// <exception cref="ArgumentNullException"><paramref name="s"/>が<c>null</c>の場合。</exception>
+        /// <exception cref="ArgumentOutOfRangeException">インデックスがテキストの範囲外の場合。</exception>
         /// <exception cref="ObjectDisposedException"><see cref="Dispose"/>が実行済みの場合。</exception>
         protected override bool TryParseElementAt(string s, int index, out IElement result)
         {
@@ -174,9 +176,11 @@ namespace Honememo.Parsers
         /// </summary>
         /// <param name="list">追加されるリスト。</param>
         /// <param name="b">追加する文字列。</param>
+        /// <exception cref="ArgumentNullException"><paramref name="list"/>または<paramref name="b"/>が<c>null</c>の場合。</exception>
         protected override void FlashText(ref ListElement list, ref StringBuilder b)
         {
-            if (b.Length > 0)
+            Validate.NotNull(list, "list");
+            if (Validate.NotNull(b, "b").Length > 0)
             {
                 string s = b.ToString();
                 XmlTextElement e = new XmlTextElement(this.Decode(s));
index 519fdef..275ffac 100644 (file)
@@ -29,7 +29,7 @@ namespace Honememo.Utilities
         /// <exception cref="ArgumentNullException"><paramref name="collection"/>が<c>null</c>の場合。</exception>
         public static bool ContainsIgnoreCase(IEnumerable<string> collection, string item)
         {
-            foreach (string s in Validate.NotNull(collection))
+            foreach (string s in Validate.NotNull(collection, "collection"))
             {
                 if (s == item || (s != null && item != null && s.ToLower() == item.ToLower()))
                 {
@@ -53,7 +53,7 @@ namespace Honememo.Utilities
         /// <remarks><paramref name="array"/>中に<c>null</c>要素が存在するのは可。</remarks>
         public static string[] Trim(string[] array)
         {
-            string[] result = new string[Validate.NotNull(array).Length];
+            string[] result = new string[Validate.NotNull(array, "array").Length];
             for (int i = 0; i < array.Length; i++)
             {
                 string s = array[i];
index 9f6487f..c98e53a 100644 (file)
@@ -44,7 +44,7 @@ namespace Honememo.Utilities
         {
             // ロックオブジェクトを取得、この時点では特にロック不要
             // ※ ConcurrentDictionaryを使用しており、一回の処理で更新しているため
-            int hashcode = Validate.NotNull(param).GetHashCode();
+            int hashcode = Validate.NotNull(param, "param").GetHashCode();
             object lockObject;
             if (this.locks.TryGetValue(hashcode, out lockObject))
             {
index e6e195f..f2223ca 100644 (file)
@@ -17,7 +17,7 @@ namespace Honememo.Utilities
     /// </summary>
     /// <remarks>
     /// Apache Commons Lang - ObjectUtils
-    /// http://commons.apache.org/lang/api/org/apache/commons/lang/ObjectUtils.html
+    /// http://commons.apache.org/lang/api-2.6/org/apache/commons/lang/ObjectUtils.html
     /// </remarks>
     public static class ObjectUtils
     {
diff --git a/HmLib/Utilities/StatusManager.cs b/HmLib/Utilities/StatusManager.cs
new file mode 100644 (file)
index 0000000..542621d
--- /dev/null
@@ -0,0 +1,175 @@
+// ================================================================================================
+// <summary>
+//      状態変化を管理するためのクラスソース</summary>
+//
+// <copyright file="StatusManager.cs" company="honeplusのメモ帳">
+//      Copyright (C) 2012 Honeplus. All rights reserved.</copyright>
+// <author>
+//      Honeplus</author>
+// ================================================================================================
+
+namespace Honememo.Utilities
+{
+    using System;
+    using System.Collections.Generic;
+
+    /// <summary>
+    /// 状態変化を管理するためのクラスです。
+    /// </summary>
+    /// <typeparam name="T">管理する状態の型。</typeparam>
+    /// <remarks>
+    /// このクラスは<see cref="IDisposable"/>を実装しますが、
+    /// これはusingブロックでのステータス変更を目的としたもので、
+    /// 特に解放が必要なリソースを持っているわけではありません。
+    /// インスタンス解放時の<see cref="Dispose"/>は不要です。
+    /// </remarks>
+    public class StatusManager<T> : IDisposable
+    {
+        #region private変数
+
+        /// <summary>
+        /// 現在のステータス。
+        /// </summary>
+        private T status;
+
+        /// <summary>
+        /// <see cref="Switch"/>で使用する変更前のステータス。
+        /// </summary>
+        private Stack<T> oldStatus = new Stack<T>();
+
+        #endregion
+
+        #region コンストラクタ
+
+        /// <summary>
+        /// 指定されたステータスで初期化されたインスタンスを作成。
+        /// </summary>
+        /// <param name="status">ステータス。</param>
+        public StatusManager(T status)
+        {
+            this.status = status;
+        }
+
+        /// <summary>
+        /// ステータスが<typeparamref name="T"/>型のデフォルト値で初期化されたインスタンスを作成。
+        /// </summary>
+        public StatusManager()
+        {
+        }
+
+        #endregion
+
+        #region イベント
+
+        /// <summary>
+        /// ステータス変化後に呼ばれるイベント。
+        /// </summary>
+        public event EventHandler Changed;
+
+        #endregion
+
+        #region プロパティ
+
+        /// <summary>
+        /// 現在のステータス。
+        /// </summary>
+        /// <remarks>
+        /// このプロパティからステータスを更新した時点で、
+        /// <see cref="Switch"/>
+        /// で処理中の変更前のステータスは全て消去されます。
+        /// </remarks>
+        public T Status
+        {
+            get
+            {
+                return this.status;
+            }
+
+            set
+            {
+                lock (this.oldStatus)
+                {
+                    this.oldStatus.Clear();
+                    this.status = value;
+                }
+
+                this.CallChangedEvent();
+            }
+        }
+
+        #endregion
+
+        #region 公開メソッド
+
+        /// <summary>
+        /// ステータスを一時的に切り替える。
+        /// </summary>
+        /// <param name="status">新しいステータス。</param>
+        /// <returns>このオブジェクト。</returns>
+        /// <remarks>
+        /// このメソッドで変更したステータスは、<see cref="Dispose"/>のタイミングで元の値に戻ります。
+        /// 入れ子で再帰的に呼び出すことも可能です。
+        /// </remarks>
+        /// <example>
+        /// ステータスを一時的に変更する場合、このメソッドを下記のように使用する。
+        /// <code>
+        /// using (var sm = this.statusManager.Switch("実行中"))
+        /// {
+        ///     // ステータス変更中に行う処理
+        /// }
+        /// </code>
+        /// </example>
+        public virtual StatusManager<T> Switch(T status)
+        {
+            lock (this.oldStatus)
+            {
+                this.oldStatus.Push(this.status);
+                this.status = status;
+            }
+
+            this.CallChangedEvent();
+            return this;
+        }
+
+        /// <summary>
+        /// <see cref="Switch"/>で変更されたステータスを元に戻す。
+        /// </summary>
+        public virtual void Dispose()
+        {
+            lock (this.oldStatus)
+            {
+                if (this.oldStatus.Count > 0)
+                {
+                    this.status = this.oldStatus.Pop();
+                }
+
+                this.CallChangedEvent();
+            }
+        }
+
+        /// <summary>
+        /// ステータスを初期状態に戻す。
+        /// </summary>
+        public virtual void Clear()
+        {
+            this.Status = default(T);
+        }
+
+        #endregion
+
+        #region 内部メソッド
+
+        /// <summary>
+        /// <see cref="Changed"/>イベントに指定された処理を呼び出す。
+        /// </summary>
+        private void CallChangedEvent()
+        {
+            if (this.Changed != null)
+            {
+                this.Changed(this, EventArgs.Empty);
+            }
+        }
+
+        #endregion
+    }
+}
index ed570bd..81b5597 100644 (file)
@@ -16,7 +16,7 @@ namespace Honememo.Utilities
     /// <summary>
     /// 文字列処理に関するユーティリティクラスです。
     /// </summary>
-    /// <remarks>一部メソッドは、Apache Commons Lang の StringUtils やJava標準の String を参考にしています。</remarks>
+    /// <remarks>一部メソッドは、Apache Commons LangのStringUtilsやJava標準のStringを参考にしています。</remarks>
     public static class StringUtils
     {
         #region 定数
@@ -163,8 +163,8 @@ namespace Honememo.Utilities
         public static string FormatDollarVariable(string format, params object[] args)
         {
             // nullチェック
-            Validate.NotNull(format);
-            Validate.NotNull(args);
+            Validate.NotNull(format, "format");
+            Validate.NotNull(args, "args");
 
             // 正規表現で$1~$数値のパラメータ部分を抜き出し、対応するパラメータに置き換える
             // 対応するパラメータが存在しない場合、空文字列となる
index 97f64d3..d354d42 100644 (file)
@@ -1,6 +1,6 @@
 // ================================================================================================
 // <summary>
-//      Apache Commons LangのValidateを参考にしたユーティリティクラスソース。</summary>
+//      バリデート処理に関するユーティリティクラスソース。</summary>
 //
 // <copyright file="Validate.cs" company="honeplusのメモ帳">
 //      Copyright (C) 2012 Honeplus. All rights reserved.</copyright>
 namespace Honememo.Utilities
 {
     using System;
+    using System.Collections.Generic;
 
     /// <summary>
-    /// Apache Commons LangのValidateを参考にしたユーティリティクラスです。
+    /// バリデート処理に関するユーティリティクラスです。
     /// </summary>
-    /// <remarks>
-    /// Apache Commons Lang - Validate
-    /// http://commons.apache.org/lang/api/org/apache/commons/lang/Validate.html
-    /// </remarks>
+    /// <remarks>一部メソッドは、Apache Commons LangのValidateを参考にしています。</remarks>
     public static class Validate
     {
         #region NotNullメソッド
@@ -26,25 +24,12 @@ namespace Honememo.Utilities
         /// <summary>
         /// 渡されたオブジェクトをチェックし、<c>null</c>の場合に例外をスローする。
         /// </summary>
-        /// <param name="obj"><c>null</c>かどうかをチェックするオブジェクト。</param>
-        /// <returns>渡されたオブジェクト。</returns>
-        /// <remarks>オブジェクトが<c>null</c>の場合に例外に渡されるパラメータ名は "value"。</remarks>
-        /// <exception cref="ArgumentNullException">オブジェクトが<c>null</c>。</exception>
         /// <typeparam name="T">オブジェクトの型。</typeparam>
-        public static T NotNull<T>(T obj)
-        {
-            return Validate.NotNull(obj, "value");
-        }
-
-        /// <summary>
-        /// 渡されたオブジェクトをチェックし、<c>null</c>の場合に例外をスローする。
-        /// </summary>
         /// <param name="obj"><c>null</c>かどうかをチェックするオブジェクト。</param>
-        /// <param name="paramName">オブジェクトが<c>null</c>の場合に例外に渡されるパラメータ名。</param>
+        /// <param name="paramName">オブジェクトが<c>null</c>の場合に例外に渡されるパラメータ名。デフォルトは<c>value</c>。</param>
         /// <returns>渡されたオブジェクト。</returns>
         /// <exception cref="ArgumentNullException">オブジェクトが<c>null</c>。</exception>
-        /// <typeparam name="T">オブジェクトの型。</typeparam>
-        public static T NotNull<T>(T obj, string paramName)
+        public static T NotNull<T>(T obj, string paramName = "value")
         {
             if (obj == null)
             {
@@ -62,30 +47,13 @@ namespace Honememo.Utilities
         /// 渡された文字列をチェックし、空(<c>null</c>または長さ0)の場合に例外をスローする。
         /// </summary>
         /// <param name="str">空かどうかをチェックする文字列。</param>
+        /// <param name="paramName">文字列が空の場合に例外に渡されるパラメータ名。デフォルトは<c>value</c>。</param>
         /// <returns>渡された文字列。</returns>
-        /// <remarks>文字列が空の場合に例外に渡されるパラメータ名は "value"。</remarks>
         /// <exception cref="ArgumentNullException">文字列が<c>null</c>。</exception>
         /// <exception cref="ArgumentException">文字列が長さ0。</exception>
-        public static string NotEmpty(string str)
+        public static string NotEmpty(string str, string paramName = "value")
         {
-            return Validate.NotEmpty(str, "value");
-        }
-
-        /// <summary>
-        /// 渡された文字列をチェックし、空(<c>null</c>または長さ0)の場合に例外をスローする。
-        /// </summary>
-        /// <param name="str">空かどうかをチェックする文字列。</param>
-        /// <param name="paramName">文字列が空の場合に例外に渡されるパラメータ名。</param>
-        /// <returns>渡された文字列。</returns>
-        /// <exception cref="ArgumentNullException">文字列が<c>null</c>。</exception>
-        /// <exception cref="ArgumentException">文字列が長さ0。</exception>
-        public static string NotEmpty(string str, string paramName)
-        {
-            if (str == null)
-            {
-                throw new ArgumentNullException(paramName);
-            }
-            else if (str == String.Empty)
+            if (NotNull(str, paramName) == String.Empty)
             {
                 throw new ArgumentException("The validated string is empty", paramName);
             }
@@ -101,35 +69,57 @@ namespace Honememo.Utilities
         /// 渡された文字列をチェックし、空(<c>null</c>または空か空白のみ)の場合に例外をスローする。
         /// </summary>
         /// <param name="str">空かどうかをチェックする文字列。</param>
+        /// <param name="paramName">文字列が空の場合に例外に渡されるパラメータ名。デフォルトは<c>value</c>。</param>
         /// <returns>渡された文字列。</returns>
-        /// <remarks>文字列が空の場合に例外に渡されるパラメータ名は "value"。</remarks>
         /// <exception cref="ArgumentNullException">文字列が<c>null</c>。</exception>
         /// <exception cref="ArgumentException">文字列が空か空白のみ。</exception>
-        public static string NotBlank(string str)
+        public static string NotBlank(string str, string paramName = "value")
         {
-            return Validate.NotBlank(str, "value");
+            if (String.IsNullOrWhiteSpace(NotNull(str, paramName)))
+            {
+                throw new ArgumentException("The validated string is blank", paramName);
+            }
+
+            return str;
         }
 
+        #endregion
+
+        #region InRangeメソッド
+
         /// <summary>
-        /// 渡された文字列をチェックし、空(<c>null</c>または空か空白のみ)の場合に例外をスローする。
+        /// 渡された文字列をチェックし、文字列が<c>null</c>またはインデックスが範囲外の場合に例外をスローする。
         /// </summary>
-        /// <param name="str">空かどうかをチェックする文字列。</param>
-        /// <param name="paramName">文字列が空の場合に例外に渡されるパラメータ名。</param>
-        /// <returns>渡された文字列。</returns>
-        /// <exception cref="ArgumentNullException">文字列が<c>null</c>。</exception>
-        /// <exception cref="ArgumentException">文字列が空か空白のみ。</exception>
-        public static string NotBlank(string str, string paramName)
+        /// <param name="str">文字列長をチェックする文字列。</param>
+        /// <param name="index">文字列内に含まれることが期待されるインデックス。</param>
+        /// <param name="paramNameStr">文字列が<c>null</c>の場合に例外に渡されるパラメータ名。デフォルトは<c>value</c>。</param>
+        /// <param name="paramNameIndex">インデックスが範囲外の場合に例外に渡されるパラメータ名。</param>
+        /// <exception cref="ArgumentNullException"><paramref name="str"/>が<c>null</c>の場合。</exception>
+        /// <exception cref="ArgumentOutOfRangeException">インデックスが範囲外の場合。</exception>
+        public static void InRange(string str, int index, string paramNameStr = "value", string paramNameIndex = "index")
         {
-            if (str == null)
+            if (NotNull(str, paramNameStr).Length <= index || index < 0)
             {
-                throw new ArgumentNullException(paramName);
+                throw new ArgumentOutOfRangeException(paramNameIndex);
             }
-            else if (String.IsNullOrWhiteSpace(str))
+        }
+
+        /// <summary>
+        /// 渡されたリストをチェックし、リストが<c>null</c>またはインデックスが範囲外の場合に例外をスローする。
+        /// </summary>
+        /// <typeparam name="T">リスト内のオブジェクトの型。</typeparam>
+        /// <param name="list">長さをチェックするリスト。</param>
+        /// <param name="index">リスト内に含まれることが期待されるインデックス。</param>
+        /// <param name="paramNameList">リストが<c>null</c>の場合に例外に渡されるパラメータ名。デフォルトは<c>value</c>。</param>
+        /// <param name="paramNameIndex">インデックスが範囲外の場合に例外に渡されるパラメータ名。デフォルトは<c>index</c>。</param>
+        /// <exception cref="ArgumentNullException"><paramref name="list"/>が<c>null</c>の場合。</exception>
+        /// <exception cref="ArgumentOutOfRangeException">インデックスが範囲外の場合。</exception>
+        public static void InRange<T>(IList<T> list, int index, string paramNameList = "value", string paramNameIndex = "index")
+        {
+            if (NotNull(list, paramNameList).Count <= index || index < 0)
             {
-                throw new ArgumentException("The validated string is blank", paramName);
+                throw new ArgumentOutOfRangeException(paramNameIndex);
             }
-
-            return str;
         }
 
         #endregion
index 482d6ed..75f4b10 100644 (file)
@@ -3,7 +3,7 @@
 //      XMLの処理に関するユーティリティクラスソース。</summary>
 //
 // <copyright file="XmlUtils.cs" company="honeplusのメモ帳">
-//      Copyright (C) 2011 Honeplus. All rights reserved.</copyright>
+//      Copyright (C) 2012 Honeplus. All rights reserved.</copyright>
 // <author>
 //      Honeplus</author>
 // ================================================================================================
@@ -115,8 +115,7 @@ namespace Honememo.Utilities
         /// </remarks>
         public static string XmlEncode(string s)
         {
-            Validate.NotNull(s);
-            return s.Replace("&", "&amp;").Replace("<", "&lt;")
+            return Validate.NotNull(s, "s").Replace("&", "&amp;").Replace("<", "&lt;")
                 .Replace(">", "&gt;").Replace("\"", "&quot;").Replace("\'", "&apos;");
         }
 
@@ -131,8 +130,7 @@ namespace Honememo.Utilities
         /// </remarks>
         public static string XmlDecode(string s)
         {
-            Validate.NotNull(s);
-            return s.Replace("&lt;", "<").Replace("&gt;", ">")
+            return Validate.NotNull(s, "s").Replace("&lt;", "<").Replace("&gt;", ">")
                 .Replace("&quot;", "\"").Replace("&apos;", "\'").Replace("&amp;", "&");
         }
 
index f67b801..d6a1da4 100644 (file)
@@ -52,6 +52,9 @@
     <Compile Include="Parsers\XmlElementParserTest.cs" />
     <Compile Include="Parsers\XmlCommentElementParserTest.cs" />
     <Compile Include="Parsers\CacheParserTest.cs" />
+    <Compile Include="Parsers\AbstractParserTest.cs" />
+    <Compile Include="Parsers\AbstractElementTest.cs" />
+    <Compile Include="Parsers\AbstractTextParserTest.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
     <Compile Include="Parsers\XmlParserTest.cs" />
     <Compile Include="Utilities\ObjectUtilsTest.cs" />
@@ -60,6 +63,7 @@
     <Compile Include="Utilities\XmlUtilsTest.cs" />
     <Compile Include="Utilities\CollectionUtilsTest.cs" />
     <Compile Include="Utilities\LockObjectTest.cs" />
+    <Compile Include="Utilities\StatusManagerTest.cs" />
   </ItemGroup>
   <ItemGroup>
     <ProjectReference Include="..\HmLib\HmLib.csproj">
diff --git a/HmLibTest/Parsers/AbstractElementTest.cs b/HmLibTest/Parsers/AbstractElementTest.cs
new file mode 100644 (file)
index 0000000..401207a
--- /dev/null
@@ -0,0 +1,59 @@
+// ================================================================================================
+// <summary>
+//      AbstractElementのテストクラスソース。</summary>
+//
+// <copyright file="AbstractElementTest.cs" company="honeplusのメモ帳">
+//      Copyright (C) 2012 Honeplus. All rights reserved.</copyright>
+// <author>
+//      Honeplus</author>
+// ================================================================================================
+
+namespace Honememo.Parsers
+{
+    using System;
+    using NUnit.Framework;
+
+    /// <summary>
+    /// <see cref="AbstractElement"/>のテストクラスです。
+    /// </summary>
+    /// <remarks>テストには最小実装の<see cref="TextElement"/>を使用。</remarks>
+    [TestFixture]
+    class AbstractElementTest
+    {
+        #region インタフェース実装プロパティテストケース
+
+        /// <summary>
+        /// <see cref="AbstractElement.ParsedString"/>プロパティテストケース。
+        /// </summary>
+        [Test]
+        public void TestParsedString()
+        {
+            // 値が普通に設定できること
+            AbstractElement element = new TextElement();
+            Assert.IsNull(element.ParsedString);
+            element.ParsedString = "test";
+            Assert.AreEqual("test", element.ParsedString);
+            element.ParsedString = null;
+            Assert.IsNull(element.ParsedString);
+        }
+
+        #endregion
+
+        #region インタフェース実装メソッドテストケース
+
+        /// <summary>
+        /// <see cref="AbstractElement.ToString"/>メソッドテストケース。
+        /// </summary>
+        [Test]
+        public void TestToString()
+        {
+            // ParsedStringが設定されている場合その値が返ること
+            AbstractElement element = new TextElement("Text element string");
+            Assert.AreEqual("Text element string", element.ToString());
+            element.ParsedString = "ParsedString string";
+            Assert.AreEqual("ParsedString string", element.ToString());
+        }
+
+        #endregion
+    }
+}
diff --git a/HmLibTest/Parsers/AbstractParserTest.cs b/HmLibTest/Parsers/AbstractParserTest.cs
new file mode 100644 (file)
index 0000000..71e8b13
--- /dev/null
@@ -0,0 +1,197 @@
+// ================================================================================================
+// <summary>
+//      AbstractParserのテストクラスソース。</summary>
+//
+// <copyright file="AbstractParserTest.cs" company="honeplusのメモ帳">
+//      Copyright (C) 2012 Honeplus. All rights reserved.</copyright>
+// <author>
+//      Honeplus</author>
+// ================================================================================================
+
+namespace Honememo.Parsers
+{
+    using System;
+    using NUnit.Framework;
+
+    /// <summary>
+    /// <see cref="AbstractParser"/>のテストクラスです。
+    /// </summary>
+    [TestFixture]
+    class AbstractParserTest
+    {
+        #region インタフェース実装メソッドテストケース
+
+        /// <summary>
+        /// <see cref="AbstractParser.Parse"/>メソッドテストケース。
+        /// </summary>
+        [Test]
+        public void TestParse()
+        {
+            // TryParseを呼んだ結果が返ること
+            TestParser parser = new TestParser();
+            parser.Success = true;
+            Assert.AreEqual(String.Empty, parser.Parse(String.Empty).ToString());
+            Assert.AreEqual("test", parser.Parse("test").ToString());
+        }
+
+        /// <summary>
+        /// <see cref="AbstractParser.Parse"/>メソッドテストケース(null)。
+        /// </summary>
+        [Test]
+        [ExpectedException(typeof(ArgumentNullException))]
+        public void TestParseNull()
+        {
+            TestParser parser = new TestParser();
+            parser.Success = true;
+            parser.Parse(null);
+        }
+
+        /// <summary>
+        /// <see cref="AbstractParser.Parse"/>メソッドテストケース(解析失敗)。
+        /// </summary>
+        [Test]
+        [ExpectedException(typeof(FormatException))]
+        public void TestParseFail()
+        {
+            // TryParseがfalseを返した場合、例外を投げる
+            TestParser parser = new TestParser();
+            parser.Success = false;
+            parser.Parse(String.Empty);
+        }
+
+        /// <summary>
+        /// <see cref="AbstractParser.IsPossibleParse"/>メソッドテストケース。
+        /// </summary>
+        [Test]
+        public void TestIsPossibleParse()
+        {
+            // このクラスでは何を渡してもtrueが返る
+            TestParser parser = new TestParser();
+            Assert.IsTrue(parser.IsPossibleParse('a'));
+            Assert.IsTrue(parser.IsPossibleParse('<'));
+            Assert.IsTrue(parser.IsPossibleParse('>'));
+            Assert.IsTrue(parser.IsPossibleParse('='));
+            Assert.IsTrue(parser.IsPossibleParse('-'));
+            Assert.IsTrue(parser.IsPossibleParse('['));
+            Assert.IsTrue(parser.IsPossibleParse('{'));
+        }
+
+        #endregion
+
+        #region 実装支援用メソッドテストケース
+
+        /// <summary>
+        /// <see cref="AbstractParser.TryParseAt"/>メソッドテストケース。
+        /// </summary>
+        [Test]
+        public void TestTryParseAt()
+        {
+            // 指定したパーサーで該当のインデックスの文字を解析する
+            IElement element;
+            TestParser parser = new TestParser();
+
+            parser.Success = true;
+            Assert.IsFalse(parser.TryParseAt("a", 0, out element));
+            Assert.IsNull(element);
+            Assert.IsTrue(parser.TryParseAt("a", 0, out element, parser));
+            Assert.AreEqual("a", element.ToString());
+
+            Assert.IsFalse(parser.TryParseAt("test[[test]]", 4, out element));
+            Assert.IsNull(element);
+            Assert.IsTrue(parser.TryParseAt("test[[test]]", 4, out element, parser));
+            Assert.AreEqual("[[test]]", element.ToString());
+
+            parser.Success = false;
+            Assert.IsFalse(parser.TryParseAt("a", 0, out element, parser));
+            Assert.IsNull(element);
+            Assert.IsFalse(parser.TryParseAt("test[[test]]", 4, out element, parser));
+            Assert.IsNull(element);
+        }
+
+        /// <summary>
+        /// <see cref="AbstractParser.TryParseAt"/>メソッドテストケース(null)。
+        /// </summary>
+        [Test]
+        [ExpectedException(typeof(ArgumentNullException))]
+        public void TestTryParseAtNull()
+        {
+            IElement element;
+            TestParser parser = new TestParser();
+            parser.Success = true;
+            parser.TryParseAt(null, 0, out element);
+        }
+
+        /// <summary>
+        /// <see cref="AbstractParser.TryParseAt"/>メソッドテストケース(範囲外)。
+        /// </summary>
+        [Test]
+        [ExpectedException(typeof(ArgumentOutOfRangeException))]
+        public void TestTryParseAtOutOfRange()
+        {
+            IElement element;
+            TestParser parser = new TestParser();
+            parser.Success = true;
+            parser.TryParseAt(String.Empty, 1, out element);
+        }
+
+        #endregion
+
+        #region テスト用AbstractParser実装
+
+        /// <summary>
+        /// テスト用<see cref="AbstractParser"/>実装クラスです。
+        /// </summary>
+        private class TestParser : AbstractParser
+        {
+            #region テスト用プロパティ
+
+            /// <summary>
+            /// <see cref="TryParse"/>の戻り値。
+            /// </summary>
+            public bool Success
+            {
+                get;
+                set;
+            }
+
+            #endregion
+
+            #region テスト用メソッド実装
+
+            /// <summary>
+            /// 渡された文字列の解析を行う。
+            /// </summary>
+            /// <param name="s">解析対象の文字列。</param>
+            /// <param name="result">解析結果。渡された文字列をそのまま要素にして返す。</param>
+            /// <returns><see cref="Success"/>の設定値。</returns>
+            public override bool TryParse(string s, out IElement result)
+            {
+                result = new TextElement(s);
+                return this.Success;
+            }
+
+            #endregion
+
+            #region 非公開メソッドテスト用のオーラーライドメソッド
+
+            /// <summary>
+            /// 渡されたテキストの指定されたインデックス位置を各種解析処理で解析する。
+            /// </summary>
+            /// <param name="s">解析するテキスト。</param>
+            /// <param name="index">処理インデックス。</param>
+            /// <param name="result">解析した結果要素。</param>
+            /// <param name="parsers">解析に用いるパーサー。指定された順に使用。</param>
+            /// <returns>いずれかのパーサーで解析できた場合<c>true</c>。</returns>
+            /// <exception cref="ArgumentNullException"><paramref name="s"/>または<paramref name="parsers"/>が<c>null</c>の場合。</exception>
+            /// <exception cref="ArgumentOutOfRangeException">インデックスが文字列の範囲外の場合。</exception>
+            public new bool TryParseAt(string s, int index, out IElement result, params IParser[] parsers)
+            {
+                return base.TryParseAt(s, index, out result, parsers);
+            }
+
+            #endregion
+        }
+
+        #endregion
+    }
+}
diff --git a/HmLibTest/Parsers/AbstractTextParserTest.cs b/HmLibTest/Parsers/AbstractTextParserTest.cs
new file mode 100644 (file)
index 0000000..d879a85
--- /dev/null
@@ -0,0 +1,255 @@
+// ================================================================================================
+// <summary>
+//      AbstractTextParserのテストクラスソース。</summary>
+//
+// <copyright file="AbstractTextParserTest.cs" company="honeplusのメモ帳">
+//      Copyright (C) 2012 Honeplus. All rights reserved.</copyright>
+// <author>
+//      Honeplus</author>
+// ================================================================================================
+
+namespace Honememo.Parsers
+{
+    using System;
+    using System.Collections.Generic;
+    using System.Text;
+    using Honememo.Utilities;
+    using NUnit.Framework;
+
+    /// <summary>
+    /// <see cref="AbstractTextParser"/>のテストクラスです。
+    /// </summary>
+    [TestFixture]
+    class AbstractTextParserTest
+    {
+        #region インタフェース実装メソッドテストケース
+
+        /// <summary>
+        /// <see cref="AbstractTextParser.TryParseToEndCondition"/>メソッドテストケース。
+        /// </summary>
+        [Test]
+        public void TestTryParseToEndCondition()
+        {
+            IElement element;
+            TestTextParser parser = new TestTextParser();
+
+            // conditionの指定がないときは、文字列を最後までTryParseElementAtで解析
+            parser.Success = true;
+            Assert.IsFalse(parser.TryParseToEndCondition(null, null, out element));
+            Assert.IsNull(element);
+
+            Assert.IsTrue(parser.TryParseToEndCondition(String.Empty, null, out element));
+            Assert.AreEqual(String.Empty, element.ToString());
+            Assert.IsInstanceOf(typeof(TextElement), element);
+
+            Assert.IsTrue(parser.TryParseToEndCondition("0123456789", null, out element));
+            Assert.AreEqual("0123456789", element.ToString());
+            Assert.IsInstanceOf(typeof(ListElement), element);
+            ListElement list = (ListElement)element;
+            Assert.AreEqual(10, list.Count);
+            foreach (IElement e in list)
+            {
+                Assert.IsInstanceOf(typeof(TextElement), e);
+            }
+
+            // conditionが指定されている場合は、その条件を満たすまで
+            Assert.IsTrue(parser.TryParseToEndCondition(
+                "0123456789",
+                (string s, int index) => s[index] == '5',
+                out element));
+            Assert.AreEqual("01234", element.ToString());
+            Assert.IsInstanceOf(typeof(ListElement), element);
+        }
+
+        /// <summary>
+        /// <see cref="AbstractTextParser.TryParseToDelimiter"/>メソッドテストケース。
+        /// </summary>
+        [Test]
+        public void TestTryParseToDelimiter()
+        {
+            IElement element;
+            TestTextParser parser = new TestTextParser();
+
+            // delimitersの指定がないときは、condition無しのTryParseToEndConditionと同じ
+            parser.Success = true;
+            Assert.IsFalse(parser.TryParseToDelimiter(null, out element));
+            Assert.IsNull(element);
+
+            Assert.IsTrue(parser.TryParseToDelimiter(String.Empty, out element));
+            Assert.AreEqual(String.Empty, element.ToString());
+            Assert.IsInstanceOf(typeof(TextElement), element);
+
+            Assert.IsTrue(parser.TryParseToDelimiter("[[test]] is good", out element));
+            Assert.AreEqual("[[test]] is good", element.ToString());
+            Assert.IsInstanceOf(typeof(ListElement), element);
+
+            // delimitersが指定されている場合は、その文字列まで
+            // ※ 本当は "test]] is good" にした状態で用いる
+            Assert.IsTrue(parser.TryParseToDelimiter("[[test]] is good", out element, "]]"));
+            Assert.AreEqual("[[test", element.ToString());
+            Assert.IsInstanceOf(typeof(ListElement), element);
+
+            // delimitersは複数指定可能、先に見つけたもの優先
+            Assert.IsTrue(parser.TryParseToDelimiter("[[test]] is good", out element, "]]", "s"));
+            Assert.AreEqual("[[te", element.ToString());
+            Assert.IsInstanceOf(typeof(ListElement), element);
+
+            // delimitersの指定があっても見つからないときは最後まで処理する
+            Assert.IsTrue(parser.TryParseToDelimiter("[[test]] is good", out element, "}}"));
+            Assert.AreEqual("[[test]] is good", element.ToString());
+            Assert.IsInstanceOf(typeof(ListElement), element);
+        }
+
+        /// <summary>
+        /// <see cref="AbstractTextParser.TryParseToDelimiter"/>メソッドテストケース(null)。
+        /// </summary>
+        [Test]
+        [ExpectedException(typeof(ArgumentNullException))]
+        public void TestTryParseToDelimiterNull()
+        {
+            IElement element;
+            string[] delimiters = null;
+            new TestTextParser().TryParseToDelimiter(null, out element, delimiters);
+        }
+
+        /// <summary>
+        /// <see cref="AbstractTextParser.TryParse"/>メソッドテストケース。
+        /// </summary>
+        [Test]
+        public void TestTryParse()
+        {
+            IElement element;
+            TestTextParser parser = new TestTextParser();
+
+            // condition無しのTryParseToEndConditionと同じ
+            parser.Success = true;
+            Assert.IsFalse(parser.TryParse(null, out element));
+            Assert.IsNull(element);
+
+            Assert.IsTrue(parser.TryParse(String.Empty, out element));
+            Assert.AreEqual(String.Empty, element.ToString());
+            Assert.IsInstanceOf(typeof(TextElement), element);
+
+            Assert.IsTrue(parser.TryParse("0123456789", out element));
+            Assert.AreEqual("0123456789", element.ToString());
+            Assert.IsInstanceOf(typeof(ListElement), element);
+            ListElement list = (ListElement)element;
+        }
+
+        #endregion
+
+        #region 実装支援用メソッドテストケース
+
+        /// <summary>
+        /// <see cref="AbstractTextParser.FlashText"/>メソッドテストケース(正常系)。
+        /// </summary>
+        [Test]
+        public void TestFlashText()
+        {
+            // ビルダーに値が詰まっている場合、その内容をリストに追加してクリアする
+            ListElement list = new ListElement();
+            StringBuilder b = new StringBuilder();
+            TestTextParser parser = new TestTextParser();
+
+            parser.FlashText(ref list, ref b);
+            Assert.AreEqual(0, list.Count);
+            Assert.AreEqual(String.Empty, b.ToString());
+
+            b.Append("1st string");
+            parser.FlashText(ref list, ref b);
+            Assert.AreEqual(1, list.Count);
+            Assert.AreEqual("1st string", list[0].ToString());
+            Assert.IsInstanceOf(typeof(TextElement), list[0]);
+            Assert.AreEqual(String.Empty, b.ToString());
+
+            b.Append("2nd string");
+            parser.FlashText(ref list, ref b);
+            Assert.AreEqual(2, list.Count);
+            Assert.AreEqual("1st string", list[0].ToString());
+            Assert.AreEqual("2nd string", list[1].ToString());
+            Assert.IsInstanceOf(typeof(TextElement), list[1]);
+            Assert.AreEqual(String.Empty, b.ToString());
+        }
+
+        /// <summary>
+        /// <see cref="AbstractTextParser.FlashText"/>メソッドテストケース(リストがnull)。
+        /// </summary>
+        [Test]
+        [ExpectedException(typeof(ArgumentNullException))]
+        public void TestFlashTextListNull()
+        {
+            ListElement list = null;
+            StringBuilder b = new StringBuilder();
+            new TestTextParser().FlashText(ref list, ref b);
+        }
+
+        /// <summary>
+        /// <see cref="AbstractTextParser.FlashText"/>メソッドテストケース(ビルダーがnull)。
+        /// </summary>
+        [Test]
+        [ExpectedException(typeof(ArgumentNullException))]
+        public void TestFlashTextBNull()
+        {
+            ListElement list = new ListElement();
+            StringBuilder b = null;
+            new TestTextParser().FlashText(ref list, ref b);
+        }
+
+        #endregion
+
+        #region テスト用AbstractTextParser実装
+
+        /// <summary>
+        /// テスト用<see cref="AbstractTextParser"/>実装クラスです。
+        /// </summary>
+        private class TestTextParser : AbstractTextParser
+        {
+            #region テスト用プロパティ
+
+            /// <summary>
+            /// <see cref="TryParseElementAt"/>の戻り値。
+            /// </summary>
+            public bool Success
+            {
+                get;
+                set;
+            }
+
+            #endregion
+
+            #region テスト用メソッド実装
+
+            /// <summary>
+            /// 渡されたテキストの指定されたインデックス位置を各種解析処理で解析する。
+            /// </summary>
+            /// <param name="s">解析するテキスト。</param>
+            /// <param name="index">処理インデックス。</param>
+            /// <param name="result">解析結果。渡された文字列のインデックス位置の値を要素にして返す。</param>
+            /// <returns><see cref="Success"/>の設定値。</returns>
+            protected override bool TryParseElementAt(string s, int index, out IElement result)
+            {
+                result = new TextElement(s[index].ToString());
+                return this.Success;
+            }
+
+            #endregion
+
+            #region 非公開メソッドテスト用のオーラーライドメソッド
+
+            /// <summary>
+            /// 文字列が空でない場合、リストにText要素を追加して、文字列をリセットする。
+            /// </summary>
+            /// <param name="list">追加されるリスト。</param>
+            /// <param name="b">追加する文字列。</param>
+            /// <exception cref="ArgumentNullException"><paramref name="list"/>または<paramref name="b"/>が<c>null</c>の場合。</exception>
+            public new void FlashText(ref ListElement list, ref StringBuilder b)
+            {
+                base.FlashText(ref list, ref b);
+            }
+
+            #endregion
+        }
+
+        #endregion
+    }
+}
index 1ce59f5..5f48fab 100644 (file)
@@ -14,10 +14,10 @@ namespace Honememo.Parsers
     using NUnit.Framework;
 
     /// <summary>
-    /// CacheParserのテストクラスです。
+    /// <see cref="CacheParser"/>のテストクラスです。
     /// </summary>
     [TestFixture]
-    public class CacheParserTest
+    class CacheParserTest
     {
         #region コンストラクタテストケース
 
@@ -37,7 +37,7 @@ namespace Honememo.Parsers
         #region IParserインタフェース実装メソッドテストケース
 
         /// <summary>
-        /// Parseメソッドテストケース(正常系)。
+        /// <see cref="CacheParser.Parse"/>メソッドテストケース(正常系)。
         /// </summary>
         [Test]
         public void TestParse()
@@ -68,7 +68,18 @@ namespace Honememo.Parsers
         }
 
         /// <summary>
-        /// Parseメソッドテストケース(異常系)。
+        /// <see cref="CacheParser.Parse"/>メソッドテストケース(null)。
+        /// </summary>
+        [Test]
+        [ExpectedException(typeof(ArgumentNullException))]
+        public void TestParseNull()
+        {
+            // nullはどのParserをラップしている場合も自前で例外
+            new CacheParser(new XmlCommentElementParser()).Parse(null);
+        }
+
+        /// <summary>
+        /// <see cref="CacheParser.Parse"/>メソッドテストケース(解析失敗)。
         /// </summary>
         [Test]
         [ExpectedException(typeof(FormatException))]
@@ -79,7 +90,7 @@ namespace Honememo.Parsers
         }
 
         /// <summary>
-        /// TryParseメソッドテストケース。
+        /// <see cref="CacheParser.TryParse"/>メソッドテストケース。
         /// </summary>
         [Test]
         public void TestTryParse()
@@ -117,10 +128,16 @@ namespace Honememo.Parsers
             Assert.IsNull(element);
             Assert.IsFalse(parser.TryParse(text, out element));
             Assert.IsNull(element);
+
+            text = null;
+            Assert.IsFalse(parser.TryParse(text, out element));
+            Assert.IsNull(element);
+            Assert.IsFalse(parser.TryParse(text, out element));
+            Assert.IsNull(element);
         }
 
         /// <summary>
-        /// IsPossibleParseメソッドテストケース。
+        /// <see cref="CacheParser.IsPossibleParse"/>メソッドテストケース。
         /// </summary>
         [Test]
         public void TestIsPossibleParse()
index 3d4eb80..1507ca4 100644 (file)
@@ -3,7 +3,7 @@
 //      XmlCommentElementParserのテストクラスソース。</summary>
 //
 // <copyright file="XmlCommentElementParserTest.cs" company="honeplusのメモ帳">
-//      Copyright (C) 2011 Honeplus. All rights reserved.</copyright>
+//      Copyright (C) 2012 Honeplus. All rights reserved.</copyright>
 // <author>
 //      Honeplus</author>
 // ================================================================================================
@@ -14,15 +14,15 @@ namespace Honememo.Parsers
     using NUnit.Framework;
 
     /// <summary>
-    /// XmlCommentElementParserのテストクラスです。
+    /// <see cref="XmlCommentElementParser"/>のテストクラスです。
     /// </summary>
     [TestFixture]
-    public class XmlCommentElementParserTest
+    class XmlCommentElementParserTest
     {
         #region インタフェース実装メソッドテストケース
 
         /// <summary>
-        /// TryParseメソッドテストケース。
+        /// <see cref="XmlCommentElementParser.TryParse"/>メソッドテストケース。
         /// </summary>
         [Test]
         public void TestTryParse()
@@ -49,10 +49,14 @@ namespace Honememo.Parsers
             Assert.IsNull(comment);
             Assert.IsFalse(parser.TryParse("<! --test-->", out comment));
             Assert.IsNull(comment);
+            Assert.IsFalse(parser.TryParse(String.Empty, out comment));
+            Assert.IsNull(comment);
+            Assert.IsFalse(parser.TryParse(null, out comment));
+            Assert.IsNull(comment);
         }
 
         /// <summary>
-        /// IsPossibleParseメソッドテストケース。
+        /// <see cref="XmlCommentElementParser.IsPossibleParse"/>メソッドテストケース。
         /// </summary>
         [Test]
         public void TestIsPossibleParse()
index 4710f04..ad64e5f 100644 (file)
 namespace Honememo.Parsers
 {
     using System;
-    using System.Collections.Generic;
-    using System.Linq;
     using NUnit.Framework;
 
     /// <summary>
-    /// XmlElementParserのテストクラスです。
+    /// <see cref="XmlElementParser"/>のテストクラスです。
     /// </summary>
     [TestFixture]
-    public class XmlElementParserTest
+    class XmlElementParserTest
     {
         #region private変数
 
         /// <summary>
-        /// 前処理・後処理で毎回生成/解放されるXmlParser
+        /// 前処理・後処理で毎回生成/解放される<see cref="XmlParser"/>
         /// </summary>
         private XmlParser xmlParser;
 
@@ -35,24 +33,55 @@ namespace Honememo.Parsers
         /// <summary>
         /// テストの前処理。
         /// </summary>
+        /// <remarks><see cref="XmlParser.Dispose"/>が必要な<see cref="XmlParser"/>の生成。</remarks>
         [SetUp]
         public void SetUp()
         {
-            // Disposeが必要なXmlParserの生成/解放
             this.xmlParser = new XmlParser();
         }
 
         /// <summary>
         /// テストの後処理。
         /// </summary>
+        /// <remarks><see cref="XmlParser.Dispose"/>が必要な<see cref="XmlParser"/>の解放。</remarks>
         [TearDown]
         public void TearDown()
         {
-            // Disposeが必要なXmlParserの生成/解放
-            if (this.xmlParser != null)
-            {
-                this.xmlParser.Dispose();
-            }
+            this.xmlParser.Dispose();
+        }
+
+        #endregion
+
+        #region 公開プロパティテストケース
+
+        /// <summary>
+        /// <see cref="XmlElementParser.Targets"/>プロパティテストケース。
+        /// </summary>
+        [Test]
+        public void TestTargets()
+        {
+            XmlElementParser parser = new XmlElementParser(this.xmlParser);
+
+            // 初期状態でオブジェクトが存在すること
+            Assert.IsNotNull(parser.Targets);
+            Assert.AreEqual(0, parser.Targets.Count);
+            parser.Targets.Add("span");
+            Assert.AreEqual("span", parser.Targets[0]);
+
+            // 設定すればそのオブジェクトが入ること
+            string[] targets = new string[] { "div", "p" };
+            parser.Targets = targets;
+            Assert.AreSame(targets, parser.Targets);
+        }
+
+        /// <summary>
+        /// <see cref="XmlElementParser.Targets"/>プロパティテストケース(null)。
+        /// </summary>
+        [Test]
+        [ExpectedException(typeof(ArgumentNullException))]
+        public void TestTargetsNull()
+        {
+            new XmlElementParser(this.xmlParser).Targets = null;
         }
 
         #endregion
@@ -60,7 +89,7 @@ namespace Honememo.Parsers
         #region インタフェース実装メソッドテストケース
 
         /// <summary>
-        /// TryParseメソッドテストケース(実例)。
+        /// <see cref="XmlElementParser.TryParse"/>メソッドテストケース(実例)。
         /// </summary>
         [Test]
         public void TestTryParse()
@@ -115,7 +144,7 @@ namespace Honememo.Parsers
         }
 
         /// <summary>
-        /// TryParseメソッドテストケース(基本形)。
+        /// <see cref="XmlElementParser.TryParse"/>メソッドテストケース(基本形)。
         /// </summary>
         [Test]
         public void TestTryParseNormal()
@@ -159,7 +188,7 @@ namespace Honememo.Parsers
         }
 
         /// <summary>
-        /// TryParseメソッドテストケース(普通でNGパターン)。
+        /// <see cref="XmlElementParser.TryParse"/>メソッドテストケース(普通でNGパターン)。
         /// </summary>
         [Test]
         public void TestTryParseNormalNg()
@@ -171,10 +200,14 @@ namespace Honememo.Parsers
             Assert.IsNull(element);
             Assert.IsFalse(parser.TryParse("<!-- comment -->", out element));
             Assert.IsNull(element);
+            Assert.IsFalse(parser.TryParse(String.Empty, out element));
+            Assert.IsNull(element);
+            Assert.IsFalse(parser.TryParse(null, out element));
+            Assert.IsNull(element);
         }
 
         /// <summary>
-        /// TryParseメソッドテストケース(単一のパターン)。
+        /// <see cref="XmlElementParser.TryParse"/>メソッドテストケース(単一のパターン)。
         /// </summary>
         [Test]
         public void TestTryParseSingle()
@@ -218,7 +251,7 @@ namespace Honememo.Parsers
         }
 
         /// <summary>
-        /// TryParseメソッドテストケース(不正な構文)。
+        /// <see cref="XmlElementParser.TryParse"/>メソッドテストケース(不正な構文)。
         /// </summary>
         [Test]
         public void TestTryParseLazy()
@@ -255,7 +288,7 @@ namespace Honememo.Parsers
         }
 
         /// <summary>
-        /// TryParseメソッドテストケース(不正でNG)。
+        /// <see cref="XmlElementParser.TryParse"/>メソッドテストケース(不正でNG)。
         /// </summary>
         [Test]
         public void TestTryParseLazyNg()
@@ -280,7 +313,7 @@ namespace Honememo.Parsers
         }
 
         /// <summary>
-        /// TryParseメソッドテストケース(HTML)。
+        /// <see cref="XmlElementParser.TryParse"/>メソッドテストケース(HTML)。
         /// </summary>
         [Test]
         public void TestTryParseHtml()
@@ -331,7 +364,7 @@ namespace Honememo.Parsers
         }
 
         /// <summary>
-        /// TryParseメソッドテストケース(大文字小文字)。
+        /// <see cref="XmlElementParser.TryParse"/>メソッドテストケース(大文字小文字)。
         /// </summary>
         [Test]
         public void TestTryParseIgnoreCase()
@@ -361,12 +394,35 @@ namespace Honememo.Parsers
             Assert.AreEqual(0, xmlElement.Attributes.Count);
         }
 
-        #endregion
+        /// <summary>
+        /// <see cref="XmlElementParser.TryParse"/>メソッドテストケース(タグ限定)。
+        /// </summary>
+        [Test]
+        public void TestTryParseTargets()
+        {
+            IElement element;
+            XmlElementParser parser = new XmlElementParser(this.xmlParser);
+
+            // 特定のタグのみを処理対象とするよう指定する
+            parser.Targets = new string[] { "div", "span" };
+            Assert.IsFalse(parser.TryParse("<h1>test</h1>", out element));
+            Assert.IsFalse(parser.TryParse("<br />", out element));
+            Assert.IsTrue(parser.TryParse("<div>test</div>", out element));
+            Assert.AreEqual("<div>test</div>", element.ToString());
 
-        #region 公開メソッドテストケース
+            // XmlParserに大文字小文字無視が指定されている場合、ここも無視する
+            Assert.IsTrue(parser.TryParse("<sPan>test</span>", out element));
+            Assert.AreEqual("<sPan>test</span>", element.ToString());
+
+            // 指定されていない場合、区別する
+            this.xmlParser.IgnoreCase = false;
+            Assert.IsFalse(parser.TryParse("<sPan>test</span>", out element));
+            Assert.IsTrue(parser.TryParse("<span>test</span>", out element));
+            Assert.AreEqual("<span>test</span>", element.ToString());
+        }
 
         /// <summary>
-        /// IsElementPossibleメソッドテストケース。
+        /// <see cref="XmlElementParser.IsPossibleParse"/>メソッドテストケース。
         /// </summary>
         [Test]
         public void TestIsElementPossible()
index b6e7805..3e6cf74 100644 (file)
@@ -3,7 +3,7 @@
 //      XmlParserのテストクラスソース。</summary>
 //
 // <copyright file="XmlParserTest.cs" company="honeplusのメモ帳">
-//      Copyright (C) 2011 Honeplus. All rights reserved.</copyright>
+//      Copyright (C) 2012 Honeplus. All rights reserved.</copyright>
 // <author>
 //      Honeplus</author>
 // ================================================================================================
@@ -16,20 +16,92 @@ namespace Honememo.Parsers
     using NUnit.Framework;
 
     /// <summary>
-    /// XmlParserのテストクラスです。
+    /// <see cref="XmlParser"/>のテストクラスです。
     /// </summary>
     [TestFixture]
-    public class XmlParserTest
+    class XmlParserTest
     {
-        #region ã\82¤ã\83³ã\82¿ã\83\95ã\82§ã\83¼ã\82¹å®\9fè£\85ã\83¡ã\82½ã\83\83ã\83\89テストケース
+        #region ã\83\97ã\83­ã\83\91ã\83\86ã\82£テストケース
 
         /// <summary>
-        /// Parseメソッドテストケース。
+        /// <see cref="XmlParser.Parsers"/>プロパティテストケース。
+        /// </summary>
+        [Test]
+        public void TestParsers()
+        {
+            using (XmlParser parser = new XmlParser())
+            {
+                // 初期状態で値が格納されていること
+                Assert.IsNotNull(parser.Parsers);
+                Assert.AreEqual(2, parser.Parsers.Length);
+
+                // 設定すればそのオブジェクトが入ること
+                IParser[] parsers = new IParser[0];
+                parser.Parsers = parsers;
+                Assert.AreSame(parsers, parser.Parsers);
+            }
+        }
+
+        /// <summary>
+        /// <see cref="XmlParser.Parsers"/>プロパティテストケース(null)。
+        /// </summary>
+        [Test]
+        [ExpectedException(typeof(ArgumentNullException))]
+        public void TestParsersNull()
+        {
+            using (XmlParser parser = new XmlParser())
+            {
+                parser.Parsers = null;
+            }
+        }
+
+        /// <summary>
+        /// <see cref="XmlParser.IgnoreCase"/>プロパティテストケース。
+        /// </summary>
+        [Test]
+        public void TestIgnoreCase()
+        {
+            using (XmlParser parser = new XmlParser())
+            {
+                // 初期値はtrue、値を設定すればその値に変わる
+                Assert.IsTrue(parser.IgnoreCase);
+                parser.IgnoreCase = false;
+                Assert.IsFalse(parser.IgnoreCase);
+                parser.IgnoreCase = true;
+                Assert.IsTrue(parser.IgnoreCase);
+            }
+        }
+
+        /// <summary>
+        /// <see cref="XmlParser.IsHtml"/>プロパティテストケース。
+        /// </summary>
+        [Test]
+        public void TestIsHtml()
+        {
+            using (XmlParser parser = new XmlParser())
+            {
+                // 初期値はfalse、値を設定すればその値に変わる
+                Assert.IsFalse(parser.IsHtml);
+                parser.IsHtml = true;
+                Assert.IsTrue(parser.IsHtml);
+                parser.IsHtml = false;
+                Assert.IsFalse(parser.IsHtml);
+            }
+        }
+
+        #endregion
+
+        #region IParserインタフェースメソッドテストケース
+
+        // ※ 2012年2月現在、IParser, ITextParserの各メソッド実装は抽象クラス側で共通になっており、
+        //    改造部分はどこかでやればテストされるのでそれで割愛
+
+        /// <summary>
+        /// <see cref="IParser.Parse"/>メソッドテストケース。
         /// </summary>
         [Test]
         public void TestParse()
         {
-            // ※ 現状解析が失敗するパターンは無い
             using (XmlParser parser = new XmlParser())
             {
                 Assert.AreEqual("test", parser.Parse("test").ToString());
@@ -48,32 +120,48 @@ namespace Honememo.Parsers
         }
 
         /// <summary>
-        /// TryParseメソッドテストケース
+        /// <see cref="IParser.Parse"/>メソッドテストケース(null)
         /// </summary>
         [Test]
-        public void TestTryParse()
+        [ExpectedException(typeof(ArgumentNullException))]
+        public void TestParseNull()
         {
-            // ※ 現状解析が失敗するパターンは無い
-            IElement element;
             using (XmlParser parser = new XmlParser())
             {
-                Assert.IsTrue(parser.TryParse("test", out element));
-                Assert.IsInstanceOf(typeof(TextElement), element);
-                Assert.AreEqual("test", element.ToString());
-
-                Assert.IsTrue(parser.TryParse("testbefore<p>testinner</p><!--comment-->testafter", out element));
-                Assert.IsInstanceOf(typeof(ICollection<IElement>), element);
-                ICollection<IElement> collection = (ICollection<IElement>)element;
-                Assert.AreEqual(4, collection.Count);
-                Assert.AreEqual("testbefore", collection.ElementAt(0).ToString());
-                Assert.IsInstanceOf(typeof(XmlElement), collection.ElementAt(1));
-                Assert.AreEqual("<p>testinner</p>", collection.ElementAt(1).ToString());
-                Assert.IsInstanceOf(typeof(XmlCommentElement), collection.ElementAt(2));
-                Assert.AreEqual("<!--comment-->", collection.ElementAt(2).ToString());
-                Assert.AreEqual("testafter", collection.ElementAt(3).ToString());
+                parser.Parse(null);
             }
         }
 
+        /// <summary>
+        /// <see cref="IParser.Parse"/>メソッドテストケース(Dispose)。
+        /// </summary>
+        [Test]
+        [ExpectedException(typeof(ObjectDisposedException))]
+        public void TestParseDispose()
+        {
+            XmlParser parser = new XmlParser();
+            parser.Dispose();
+            IElement result;
+            parser.TryParse("test", out result);
+        }
+
+        #endregion
+
+        #region IDisposableインタフェース実装メソッドテストケース
+
+        /// <summary>
+        /// <see cref="XmlParser.Dispose"/>メソッドテストケース。
+        /// </summary>
+        [Test]
+        public void TestDispose()
+        {
+            // 循環参照のあるParsersを解放する
+            XmlParser parser = new XmlParser();
+            Assert.IsNotNull(parser.Parsers);
+            parser.Dispose();
+            Assert.IsNull(parser.Parsers);
+        }
+
         #endregion
     }
 }
diff --git a/HmLibTest/Utilities/StatusManagerTest.cs b/HmLibTest/Utilities/StatusManagerTest.cs
new file mode 100644 (file)
index 0000000..f21f4ef
--- /dev/null
@@ -0,0 +1,136 @@
+// ================================================================================================
+// <summary>
+//      StatusManagerのテストクラスソース。</summary>
+//
+// <copyright file="StatusManagerTest.cs" company="honeplusのメモ帳">
+//      Copyright (C) 2012 Honeplus. All rights reserved.</copyright>
+// <author>
+//      Honeplus</author>
+// ================================================================================================
+
+namespace Honememo.Utilities
+{
+    using System;
+    using NUnit.Framework;
+
+    /// <summary>
+    /// <see cref="StatusManager&lt;T&gt;"/>のテストクラスです。
+    /// </summary>
+    [TestFixture]
+    class StatusManagerTest
+    {
+        #region プロパティテストケース
+
+        /// <summary>
+        /// <see cref="StatusManager&lt;T&gt;.Status"/>プロパティテストケース。
+        /// </summary>
+        [Test]
+        public void TestStatus()
+        {
+            var sm = new StatusManager<string>();
+
+            // 初期状態ではnull
+            Assert.IsNull(sm.Status);
+
+            // 値を設定すればその値が入る
+            sm.Status = "test";
+            Assert.AreEqual("test", sm.Status);
+
+            // 更新時はChangedイベントが呼ばれる
+            bool called = false;
+            sm.Changed += new EventHandler(delegate { called = true; });
+            sm.Status = "test2";
+            Assert.IsTrue(called);
+            Assert.AreEqual("test2", sm.Status);
+
+            // Switchで値が設定されていた場合、Statusを更新すると戻らなくなる
+            sm.Switch("switchstatus");
+            Assert.AreEqual("switchstatus", sm.Status);
+            sm.Status = "newstatus";
+            Assert.AreEqual("newstatus", sm.Status);
+            sm.Dispose();
+            Assert.AreEqual("newstatus", sm.Status);
+        }
+
+        #endregion
+
+        #region 公開メソッドテストケース
+
+        /// <summary>
+        /// <see cref="StatusManager&lt;T&gt;.Switch"/>,
+        /// <see cref="StatusManager&lt;T&gt;.Dispose"/>メソッドテストケース。
+        /// </summary>
+        [Test]
+        public void TestSwitch()
+        {
+            var sm = new StatusManager<string>();
+
+            // 初期状態ではnull
+            Assert.IsNull(sm.Status);
+
+            // Switchでステータスが更新、Disposeで元の値に戻る
+            using (var sm1 = sm.Switch("switch1"))
+            {
+                Assert.AreEqual("switch1", sm.Status);
+
+                // 入れ子も可能
+                using (var sm2 = sm.Switch("switch2"))
+                {
+                    Assert.AreEqual("switch2", sm.Status);
+
+                    using (var sm3 = sm.Switch("switch3"))
+                    {
+                        Assert.AreEqual("switch3", sm.Status);
+                    }
+
+                    Assert.AreEqual("switch2", sm.Status);
+                }
+
+                Assert.AreEqual("switch1", sm.Status);
+
+                // 設定時と戻り時はChangedイベントが呼ばれる
+                int count = 0;
+                sm.Changed += new EventHandler(delegate { ++count; });
+                Assert.AreEqual(0, count);
+                using (var sm2 = sm.Switch("switch4"))
+                {
+                    Assert.AreEqual(1, count);
+                    Assert.AreEqual("switch4", sm.Status);
+                }
+
+                Assert.AreEqual(2, count);
+                Assert.AreEqual("switch1", sm.Status);
+            }
+
+            Assert.IsNull(sm.Status);
+        }
+
+        /// <summary>
+        /// <see cref="StatusManager&lt;T&gt;.Clear"/>メソッドテストケース。
+        /// </summary>
+        [Test]
+        public void TestClear()
+        {
+            var sm = new StatusManager<int>();
+
+            // ステータスを初期状態に戻す
+            Assert.AreEqual(0, sm.Status);
+            sm.Status = 5;
+            Assert.AreEqual(5, sm.Status);
+            sm.Clear();
+            Assert.AreEqual(0, sm.Status);
+
+            // Switchで値が設定されていた場合、Clearすると戻らなくなる
+            sm.Switch(10);
+            Assert.AreEqual(10, sm.Status);
+            sm.Switch(20);
+            Assert.AreEqual(20, sm.Status);
+            sm.Clear();
+            Assert.AreEqual(0, sm.Status);
+            sm.Dispose();
+            Assert.AreEqual(0, sm.Status);
+        }
+
+        #endregion
+    }
+}
index 63eb50b..64b600a 100644 (file)
@@ -3,7 +3,7 @@
 //      Validateのテストクラスソース。</summary>
 //
 // <copyright file="ValidateTest.cs" company="honeplusのメモ帳">
-//      Copyright (C) 2011 Honeplus. All rights reserved.</copyright>
+//      Copyright (C) 2012 Honeplus. All rights reserved.</copyright>
 // <author>
 //      Honeplus</author>
 // ================================================================================================
 namespace Honememo.Utilities
 {
     using System;
+    using System.Collections.Generic;
     using NUnit.Framework;
 
     /// <summary>
-    /// Validateのテストクラスです。
+    /// <see cref="Validate"/>のテストクラスです。
     /// </summary>
     [TestFixture]
-    public class ValidateTest
+    class ValidateTest
     {
         #region NotNullメソッドテストケース
 
         /// <summary>
-        /// NotNullメソッドテストケース(正常系)。
+        /// <see cref="Validate.NotNull&lt;T&gt;(T, string)"/>メソッドテストケース(正常系)。
         /// </summary>
         [Test]
         public void TestNotNull()
         {
-            // 引数一つ
+            // パラメータ名指定無し
             Assert.AreEqual(String.Empty, Validate.NotNull(String.Empty));
             Assert.AreEqual("not null", Validate.NotNull("not null"));
 
-            // 引数二つ
+            // パラメータ名指定有り
             Assert.AreEqual(String.Empty, Validate.NotNull(String.Empty, null));
             Assert.AreEqual(String.Empty, Validate.NotNull(String.Empty, "test"));
             Assert.AreEqual("not null", Validate.NotNull("not null", "test"));
         }
 
         /// <summary>
-        /// NotNullメソッドテストケース(異常系)。
+        /// <see cref="Validate.NotNull&lt;T&gt;(T, string)"/>メソッドテストケース(異常系)。
         /// </summary>
         [Test]
         public void TestNotNullNg()
         {
+            // obj = nullのチェック
             try
             {
-                // 引数一つ
+                // パラメータ名指定無し
                 Validate.NotNull<object>(null);
                 Assert.Fail("expected ArgumentNullException");
             }
@@ -54,9 +56,10 @@ namespace Honememo.Utilities
                 Assert.AreEqual("value", ex.ParamName);
             }
 
+            // 例外パラメータ名の確認
             try
             {
-                // 引数二つ
+                // パラメータ名指定有り
                 Validate.NotNull<object>(null, "test");
                 Assert.Fail("expected ArgumentNullException");
             }
@@ -67,7 +70,7 @@ namespace Honememo.Utilities
 
             try
             {
-                // 引数二つnull
+                // パラメータ名指定有りnull
                 Validate.NotNull<object>(null, null);
                 Assert.Fail("expected ArgumentNullException");
             }
@@ -82,27 +85,28 @@ namespace Honememo.Utilities
         #region NotEmptyメソッドテストケース
 
         /// <summary>
-        /// NotEmptyメソッドテストケース(正常系)。
+        /// <see cref="Validate.NotEmpty(string, string)"/>メソッドテストケース(正常系)。
         /// </summary>
         [Test]
         public void TestNotEmpty()
         {
-            // 引数一つ
+            // パラメータ名指定無し
             Assert.AreEqual("not empty", Validate.NotEmpty("not empty"));
 
-            // 引数二つ
+            // パラメータ名指定有り
             Assert.AreEqual("not empty", Validate.NotEmpty("not empty", "test"));
         }
 
         /// <summary>
-        /// NotEmptyメソッドテストケース(異常系)。
+        /// <see cref="Validate.NotEmpty(string, string)"/>メソッドテストケース(異常系)。
         /// </summary>
         [Test]
         public void TestNotEmptyNg()
         {
+            // str = nullのチェック
             try
             {
-                // 引数一つ
+                // パラメータ名指定無し
                 Validate.NotEmpty(null);
                 Assert.Fail("expected ArgumentNullException");
             }
@@ -111,9 +115,10 @@ namespace Honememo.Utilities
                 Assert.AreEqual("value", ex.ParamName);
             }
 
+            // 例外パラメータ名の確認
             try
             {
-                // 引数二つ
+                // パラメータ名指定有り
                 Validate.NotEmpty(null, "test");
                 Assert.Fail("expected ArgumentNullException");
             }
@@ -124,7 +129,7 @@ namespace Honememo.Utilities
 
             try
             {
-                // 引数二つnull
+                // パラメータ名指定有りnull
                 Validate.NotEmpty(null, null);
                 Assert.Fail("expected ArgumentNullException");
             }
@@ -133,9 +138,10 @@ namespace Honememo.Utilities
                 Assert.IsNull(ex.ParamName);
             }
 
+            // 空文字列のチェック
             try
             {
-                // 引数一つ
+                // パラメータ名指定無し
                 Validate.NotEmpty(String.Empty);
                 Assert.Fail("expected ArgumentException");
             }
@@ -144,9 +150,10 @@ namespace Honememo.Utilities
                 Assert.AreEqual("value", ex.ParamName);
             }
 
+            // 例外パラメータ名の確認
             try
             {
-                // 引数二つ
+                // パラメータ名指定有り
                 Validate.NotEmpty(String.Empty, "test");
                 Assert.Fail("expected ArgumentException");
             }
@@ -161,27 +168,28 @@ namespace Honememo.Utilities
         #region NotBlankメソッドテストケース
 
         /// <summary>
-        /// NotBlankメソッドテストケース(正常系)。
+        /// <see cref="Validate.NotBlank(string, string)"/>メソッドテストケース(正常系)。
         /// </summary>
         [Test]
         public void TestNotBlank()
         {
-            // 引数一つ
+            // パラメータ名指定無し
             Assert.AreEqual("not blank", Validate.NotBlank("not blank"));
 
-            // 引数二つ
+            // パラメータ名指定有り
             Assert.AreEqual("not blank", Validate.NotBlank("not blank", "test"));
         }
 
         /// <summary>
-        /// NotBlankメソッドテストケース(異常系)。
+        /// <see cref="Validate.NotBlank(string, string)"/>メソッドテストケース(異常系)。
         /// </summary>
         [Test]
         public void TestNotBlankNg()
         {
+            // str = nullのチェック
             try
             {
-                // 引数一つ
+                // パラメータ名指定無し
                 Validate.NotBlank(null);
                 Assert.Fail("expected ArgumentNullException");
             }
@@ -190,9 +198,10 @@ namespace Honememo.Utilities
                 Assert.AreEqual("value", ex.ParamName);
             }
 
+            // 例外パラメータ名の確認
             try
             {
-                // 引数二つ
+                // パラメータ名指定有り
                 Validate.NotBlank(null, "test");
                 Assert.Fail("expected ArgumentNullException");
             }
@@ -203,7 +212,7 @@ namespace Honememo.Utilities
 
             try
             {
-                // 引数二つnull
+                // パラメータ名指定有りnull
                 Validate.NotBlank(null, null);
                 Assert.Fail("expected ArgumentNullException");
             }
@@ -212,9 +221,10 @@ namespace Honememo.Utilities
                 Assert.IsNull(ex.ParamName);
             }
 
+            // 空白のチェック
             try
             {
-                // 引数一つ
+                // パラメータ名指定無し
                 Validate.NotBlank("  ");
                 Assert.Fail("expected ArgumentException");
             }
@@ -223,9 +233,10 @@ namespace Honememo.Utilities
                 Assert.AreEqual("value", ex.ParamName);
             }
 
+            // 例外パラメータ名の確認
             try
             {
-                // 引数二つ
+                // パラメータ名指定有り
                 Validate.NotBlank("   ", "test");
                 Assert.Fail("expected ArgumentException");
             }
@@ -236,5 +247,245 @@ namespace Honememo.Utilities
         }
 
         #endregion
+
+        #region InRangeメソッドテストケース
+
+        /// <summary>
+        /// <see cref="Validate.InRange(string, int, string, string)"/>
+        /// メソッドテストケース(正常系)。
+        /// </summary>
+        [Test]
+        public void TestInRangeStr()
+        {
+            // ※ 例外が起きなければOK
+            // パラメータ名指定無し
+            Validate.InRange("1", 0);
+            Validate.InRange("range text", 9);
+
+            // パラメータ名指定有り
+            Validate.InRange("1", 0, "test", "testindex");
+            Validate.InRange("range text", 9, "test", "testindex");
+        }
+
+        /// <summary>
+        /// <see cref="Validate.InRange(string, int, string, string)"/>
+        /// メソッドテストケース(異常系)。
+        /// </summary>
+        [Test]
+        public void TestInRangeStrNg()
+        {
+            // str = nullのチェック
+            string str = null;
+            try
+            {
+                // パラメータ名指定無し
+                Validate.InRange(str, 0);
+                Assert.Fail("expected ArgumentNullException");
+            }
+            catch (ArgumentNullException ex)
+            {
+                Assert.AreEqual("value", ex.ParamName);
+            }
+
+            // 例外パラメータ名の確認
+            try
+            {
+                // パラメータ名指定有り
+                Validate.InRange(str, 0, "test", "testindex");
+                Assert.Fail("expected ArgumentNullException");
+            }
+            catch (ArgumentNullException ex)
+            {
+                Assert.AreEqual("test", ex.ParamName);
+            }
+
+            try
+            {
+                // パラメータ名指定有りnull
+                Validate.InRange(str, 0, null, null);
+                Assert.Fail("expected ArgumentNullException");
+            }
+            catch (ArgumentNullException ex)
+            {
+                Assert.IsNull(ex.ParamName);
+            }
+
+            // indexが範囲外のチェック
+            try
+            {
+                // パラメータ名指定無し
+                Validate.InRange(String.Empty, 0);
+                Assert.Fail("expected ArgumentOutOfRangeException");
+            }
+            catch (ArgumentOutOfRangeException ex)
+            {
+                Assert.AreEqual("index", ex.ParamName);
+            }
+
+            try
+            {
+                // パラメータ名指定無し
+                Validate.InRange("range text", 10);
+                Assert.Fail("expected ArgumentOutOfRangeException");
+            }
+            catch (ArgumentOutOfRangeException ex)
+            {
+                Assert.AreEqual("index", ex.ParamName);
+            }
+
+            try
+            {
+                // パラメータ名指定無し
+                Validate.InRange("range text", -1);
+                Assert.Fail("expected ArgumentOutOfRangeException");
+            }
+            catch (ArgumentOutOfRangeException ex)
+            {
+                Assert.AreEqual("index", ex.ParamName);
+            }
+
+            // 例外パラメータ名の確認
+            try
+            {
+                // パラメータ名指定有り
+                Validate.InRange(String.Empty, 0, "test", "testindex");
+                Assert.Fail("expected ArgumentOutOfRangeException");
+            }
+            catch (ArgumentOutOfRangeException ex)
+            {
+                Assert.AreEqual("testindex", ex.ParamName);
+            }
+
+            try
+            {
+                // パラメータ名指定有りnull
+                Validate.InRange(String.Empty, 0, null, null);
+                Assert.Fail("expected ArgumentOutOfRangeException");
+            }
+            catch (ArgumentOutOfRangeException ex)
+            {
+                Assert.IsNull(ex.ParamName);
+            }
+        }
+
+        /// <summary>
+        /// <see cref="Validate.InRange&lt;T&gt;(IList&lt;T&gt;, int, string, string)"/>
+        /// メソッドテストケース(正常系)。
+        /// </summary>
+        [Test]
+        public void TestInRangeIList()
+        {
+            // ※ 例外が起きなければOK
+            // パラメータ名指定無し
+            Validate.InRange(new object[1], 0);
+            Validate.InRange(new object[10], 9);
+
+            // パラメータ名指定有り
+            Validate.InRange(new object[1], 0, "test", "testindex");
+            Validate.InRange(new object[10], 9, "test", "testindex");
+        }
+
+        /// <summary>
+        /// <see cref="Validate.InRange&lt;T&gt;(IList&lt;T&gt;, int, string, string)"/>
+        /// メソッドテストケース(異常系)。
+        /// </summary>
+        [Test]
+        public void TestInRangeIListNg()
+        {
+            // list = nullのチェック
+            object[] list = null;
+            try
+            {
+                // パラメータ名指定無し
+                Validate.InRange(list, 0);
+                Assert.Fail("expected ArgumentNullException");
+            }
+            catch (ArgumentNullException ex)
+            {
+                Assert.AreEqual("value", ex.ParamName);
+            }
+
+            // 例外パラメータ名の確認
+            try
+            {
+                // パラメータ名指定有り
+                Validate.InRange(list, 0, "test", "testindex");
+                Assert.Fail("expected ArgumentNullException");
+            }
+            catch (ArgumentNullException ex)
+            {
+                Assert.AreEqual("test", ex.ParamName);
+            }
+
+            try
+            {
+                // パラメータ名指定有りnull
+                Validate.InRange(list, 0, null, null);
+                Assert.Fail("expected ArgumentNullException");
+            }
+            catch (ArgumentNullException ex)
+            {
+                Assert.IsNull(ex.ParamName);
+            }
+
+            // index範囲外のチェック
+            try
+            {
+                // パラメータ名指定無し
+                Validate.InRange(new object[0], 0);
+                Assert.Fail("expected ArgumentOutOfRangeException");
+            }
+            catch (ArgumentOutOfRangeException ex)
+            {
+                Assert.AreEqual("index", ex.ParamName);
+            }
+
+            try
+            {
+                // パラメータ名指定無し
+                Validate.InRange(new object[10], 10);
+                Assert.Fail("expected ArgumentOutOfRangeException");
+            }
+            catch (ArgumentOutOfRangeException ex)
+            {
+                Assert.AreEqual("index", ex.ParamName);
+            }
+
+            try
+            {
+                // パラメータ名指定無し
+                Validate.InRange(new object[10], -1);
+                Assert.Fail("expected ArgumentOutOfRangeException");
+            }
+            catch (ArgumentOutOfRangeException ex)
+            {
+                Assert.AreEqual("index", ex.ParamName);
+            }
+
+            // 例外パラメータ名の確認
+            try
+            {
+                // パラメータ名指定有り
+                Validate.InRange(new object[0], 0, "test", "testindex");
+                Assert.Fail("expected ArgumentOutOfRangeException");
+            }
+            catch (ArgumentOutOfRangeException ex)
+            {
+                Assert.AreEqual("testindex", ex.ParamName);
+            }
+
+            try
+            {
+                // パラメータ名指定有りnull
+                Validate.InRange(new object[0], 0, null, null);
+                Assert.Fail("expected ArgumentOutOfRangeException");
+            }
+            catch (ArgumentOutOfRangeException ex)
+            {
+                Assert.IsNull(ex.ParamName);
+            }
+        }
+
+        #endregion
     }
 }
index ef20b53..2196921 100644 (file)
Binary files a/Wikipedia 翻訳支援ツール.asta and b/Wikipedia 翻訳支援ツール.asta differ
index d744718..4e50bcf 100644 (file)
@@ -27,7 +27,7 @@ namespace Honememo.Wptscs.Logics
     using Honememo.Wptscs.Websites;
 
     /// <summary>
-    /// Wikipedia用の翻訳支援処理実装クラスです。
+    /// MediaWiki用の翻訳支援処理実装クラスです。
     /// </summary>
     public class MediaWikiTranslator : Translator
     {
@@ -43,8 +43,12 @@ namespace Honememo.Wptscs.Logics
         #region コンストラクタ
 
         /// <summary>
-        /// インスタンスを生成する
+        /// MediaWikiでの翻訳支援処理を行うトランスレータを作成
         /// </summary>
+        /// <remarks>
+        /// 別途プロパティに必要なパラメータを設定する必要あり。
+        /// 通常は<see cref="Translator.Create"/>にて設定ファイルから作成する。
+        /// </remarks>
         public MediaWikiTranslator()
         {
             // このクラス用のロガーと、デフォルトの確認処理としてメッセージダイアログ版を設定
@@ -130,10 +134,12 @@ namespace Honememo.Wptscs.Logics
 
             // 対象記事に言語間リンクが存在する場合、処理を継続するか確認
             // ※ 言語間リンク取得中は、処理状態を解析中に変更
-            MediaWikiLink interlanguage = null;
-            this.ChangeStatusInExecuting(
-                () => interlanguage = article.GetInterlanguage(this.To.Language.Code),
-                Resources.StatusParsing);
+            MediaWikiLink interlanguage;
+            using (var sm = this.StatusManager.Switch(Resources.StatusParsing))
+            {
+                interlanguage = article.GetInterlanguage(this.To.Language.Code);
+            }
+
             if (interlanguage != null)
             {
                 // 確認処理の最中は処理時間をカウントしない(ダイアログ等を想定するため)
@@ -153,9 +159,16 @@ namespace Honememo.Wptscs.Logics
             // 言語間リンク・定型句の変換、実行中は処理状態を解析中に設定
             this.Logger.AddSeparator();
             this.Logger.AddResponse(Resources.LogMessageStartParseAndReplace);
-            this.ChangeStatusInExecuting(
-                () => this.Text += this.ReplaceElement(article.Element, article).ToString(),
-                Resources.StatusParsing);
+            using (var sm = this.StatusManager.Switch(Resources.StatusParsing))
+            {
+                IElement element;
+                using (MediaWikiParser parser = new MediaWikiParser(this.From))
+                {
+                    element = parser.Parse(article.Text);
+                }
+
+                this.Text += this.ReplaceElement(element, article).ToString();
+            }
 
             // 記事の末尾に新しい言語間リンクと、コメントを追記
             this.Text += this.CreateEnding(article);
index 93f71ef..d58d37c 100644 (file)
@@ -1,6 +1,6 @@
 // ================================================================================================
 // <summary>
-//      翻訳支援処理を実装するための共通クラスソース</summary>
+//      翻訳支援処理を実装するための抽象クラスソース</summary>
 //
 // <copyright file="Translator.cs" company="honeplusのメモ帳">
 //      Copyright (C) 2012 Honeplus. All rights reserved.</copyright>
@@ -16,24 +16,21 @@ namespace Honememo.Wptscs.Logics
     using System.Net;
     using System.Net.NetworkInformation;
     using System.Reflection;
+    using Honememo.Models;
     using Honememo.Utilities;
     using Honememo.Wptscs.Models;
     using Honememo.Wptscs.Properties;
+    using Honememo.Wptscs.Utilities;
     using Honememo.Wptscs.Websites;
 
     /// <summary>
-    /// 翻訳支援処理を実装するための共通クラスです。
+    /// 翻訳支援処理を実装するための抽象クラスです。
     /// </summary>
     public abstract class Translator
     {
         #region private変数
 
         /// <summary>
-        /// 処理状態メッセージ。
-        /// </summary>
-        private string status = String.Empty;
-
-        /// <summary>
         /// 変換後テキスト。
         /// </summary>
         private string text = String.Empty;
@@ -48,40 +45,59 @@ namespace Honememo.Wptscs.Logics
         #region コンストラクタ
 
         /// <summary>
-        /// ã\82¤ã\83³ã\82¹ã\82¿ã\83³ã\82¹ã\82\92ç\94\9fæ\88\90ã\81\99ã\82\8b
+        /// ã\83\88ã\83©ã\83³ã\82¹ã\83¬ã\83¼ã\82¿ã\82\92ä½\9cæ\88\90
         /// </summary>
         public Translator()
         {
+            // ステータス管理については更新イベントを連鎖させる
             this.Stopwatch = new Stopwatch();
             this.Logger = new Logger();
+            this.StatusManager = new StatusManager<string>();
+            this.StatusManager.Changed += new EventHandler(
+                delegate
+                {
+                    if (this.StatusUpdated != null)
+                    {
+                        this.StatusUpdated(this, EventArgs.Empty);
+                    }
+                });
         }
 
         #endregion
 
-        #region デリゲート
-
-        /// <summary>
-        /// <see cref="ChangeStatusInExecuting"/> で実行する処理のためのデリゲート。
-        /// </summary>
-        protected delegate void MethodWithChangeStatus();
-
-        #endregion
-
         #region イベント
 
         /// <summary>
         /// ログ更新伝達イベント。
         /// </summary>
-        public event EventHandler LogUpdate;
+        public event EventHandler LogUpdated;
 
         /// <summary>
         /// 処理状態更新伝達イベント。
         /// </summary>
-        public event EventHandler StatusUpdate;
+        public event EventHandler StatusUpdated;
 
         #endregion
 
-        #region プロパティ
+        #region 公開プロパティ
+
+        /// <summary>
+        /// 翻訳元言語のサイト。
+        /// </summary>
+        public Website From
+        {
+            get;
+            set;
+        }
+
+        /// <summary>
+        /// 翻訳先言語のサイト。
+        /// </summary>
+        public Website To
+        {
+            get;
+            set;
+        }
 
         /// <summary>
         /// 言語間の項目の対訳表。
@@ -119,16 +135,8 @@ namespace Honememo.Wptscs.Logics
         {
             get
             {
-                return this.status;
-            }
-
-            protected set
-            {
-                this.status = StringUtils.DefaultString(value);
-                if (this.StatusUpdate != null)
-                {
-                    this.StatusUpdate(this, EventArgs.Empty);
-                }
+                // 内部的に実際に管理しているのはStatusManager
+                return StringUtils.DefaultString(this.StatusManager.Status);
             }
         }
 
@@ -166,27 +174,14 @@ namespace Honememo.Wptscs.Logics
             set;
         }
 
-        /// <summary>
-        /// 翻訳元言語のサイト。
-        /// </summary>
-        public Website From
-        {
-            get;
-            set;
-        }
+        #endregion
 
-        /// <summary>
-        /// 翻訳先言語のサイト。
-        /// </summary>
-        public Website To
-        {
-            get;
-            set;
-        }
+        #region 実装支援用プロパティ
 
         /// <summary>
         /// ログテキスト生成用ロガー。
         /// </summary>
+        /// <exception cref="ArgumentNullException"><c>null</c>が指定された場合。</exception>
         protected Logger Logger
         {
             get
@@ -198,10 +193,27 @@ namespace Honememo.Wptscs.Logics
             {
                 // nullは不可。また、ロガー変更後はイベントを設定
                 this.logger = Validate.NotNull(value);
-                this.logger.LogUpdate += this.GetLogUpdate;
+                this.logger.LogUpdate += new EventHandler(
+                    delegate
+                    {
+                        if (this.LogUpdated != null)
+                        {
+                            this.LogUpdated(this, EventArgs.Empty);
+                        }
+                    });
             }
         }
 
+        /// <summary>
+        /// ステータス管理用オブジェクト。
+        /// </summary>
+        /// <exception cref="ArgumentNullException"><c>null</c>が指定された場合。</exception>
+        protected StatusManager<string> StatusManager
+        {
+            get;
+            private set;
+        }
+
         #endregion
 
         #region 静的メソッド
@@ -246,7 +258,7 @@ namespace Honememo.Wptscs.Logics
 
         #endregion
 
-        #region publicメソッド
+        #region 公開メソッド
 
         /// <summary>
         /// 翻訳支援処理実行。
@@ -288,14 +300,14 @@ namespace Honememo.Wptscs.Logics
             finally
             {
                 // 終了後は処理状態をクリア、処理時間を測定終了
-                this.Status = String.Empty;
+                this.StatusManager.Clear();
                 this.Stopwatch.Stop();
             }
         }
         
         #endregion
 
-        #region protectedメソッド
+        #region 実装が必要なテンプレートメソッド
 
         /// <summary>
         /// 翻訳支援処理実行部の本体。
@@ -305,6 +317,10 @@ namespace Honememo.Wptscs.Logics
         /// <remarks>テンプレートメソッド的な構造になっています。</remarks>
         protected abstract void RunBody(string name);
 
+        #endregion
+
+        #region 実装支援用メソッド
+
         /// <summary>
         /// ログ出力によるエラー処理を含んだページ取得処理。
         /// </summary>
@@ -329,12 +345,11 @@ namespace Honememo.Wptscs.Logics
             this.ThrowExceptionIfCanceled();
 
             // ページ取得処理、実行中は処理状態を変更
-            bool success = false;
-            Page result = null;
-            this.ChangeStatusInExecuting(
-                () => success = this.TryGetPageBody(title, out result),
-                Resources.StatusDownloading);
-            page = result;
+            bool success;
+            using (var sm = this.StatusManager.Switch(Resources.StatusDownloading))
+            {
+                success = this.TryGetPageBody(title, out page);
+            }
 
             // 通信終了後にも再度終了要求を確認
             this.ThrowExceptionIfCanceled();
@@ -353,31 +368,9 @@ namespace Honememo.Wptscs.Logics
             }
         }
 
-        /// <summary>
-        /// 指定された処理を実行する間、処理状態を渡された値に更新する。
-        /// 処理終了後は以前の処理状態に戻す。
-        /// </summary>
-        /// <param name="method">実行する処理。</param>
-        /// <param name="status">処理状態。</param>
-        protected void ChangeStatusInExecuting(MethodWithChangeStatus method, string status)
-        {
-            // 現在の処理状態を保存、新しい処理状態をセットし、処理を実行する
-            string oldStatus = this.Status;
-            this.Status = status;
-            try
-            {
-                method();
-            }
-            finally
-            {
-                // 処理状態を以前の状態に戻す
-                this.Status = oldStatus;
-            }
-        }
-
         #endregion
 
-        #region privateメソッド
+        #region 内部処理用メソッド
 
         /// <summary>
         /// 翻訳支援処理実行時の初期化処理。
@@ -386,7 +379,7 @@ namespace Honememo.Wptscs.Logics
         {
             // 変数を初期化
             this.Logger.Clear();
-            this.Status = String.Empty;
+            this.StatusManager.Clear();
             this.Stopwatch.Reset();
             this.Text = String.Empty;
             this.CancellationPending = false;
@@ -401,38 +394,27 @@ namespace Honememo.Wptscs.Logics
         private bool Ping(string server)
         {
             // サーバー接続チェック、実行中は処理状態を変更
-            bool result = false;
-            this.ChangeStatusInExecuting(
-                () => result = this.PingBody(server),
-                Resources.StatusPinging);
-            return result;
-        }
-
-        /// <summary>
-        /// サーバー接続チェック本体。
-        /// </summary>
-        /// <param name="server">サーバー名。</param>
-        /// <returns><c>true</c> 接続成功。</returns>
-        private bool PingBody(string server)
-        {
-            // サーバー接続チェック
-            Ping ping = new Ping();
-            try
+            using (var sm = this.StatusManager.Switch(Resources.StatusPinging))
             {
-                PingReply reply = ping.Send(server);
-                if (reply.Status != IPStatus.Success)
+                // サーバー接続チェック
+                Ping ping = new Ping();
+                try
+                {
+                    PingReply reply = ping.Send(server);
+                    if (reply.Status != IPStatus.Success)
+                    {
+                        this.Logger.AddMessage(Resources.ErrorMessageConnectionFailed, reply.Status.ToString());
+                        return false;
+                    }
+                }
+                catch (Exception e)
                 {
-                    this.Logger.AddMessage(Resources.ErrorMessageConnectionFailed, reply.Status.ToString());
+                    this.Logger.AddMessage(Resources.ErrorMessageConnectionFailed, e.InnerException.Message);
                     return false;
                 }
-            }
-            catch (Exception e)
-            {
-                this.Logger.AddMessage(Resources.ErrorMessageConnectionFailed, e.InnerException.Message);
-                return false;
-            }
 
-            return true;
+                return true;
+            }
         }
 
         /// <summary>
@@ -464,7 +446,7 @@ namespace Honememo.Wptscs.Logics
                 // ページ無しによる例外も正常終了
                 return true;
             }
-            catch (NotSupportedException)
+            catch (EndPeriodException)
             {
                 // 末尾がピリオドで終わるページが処理できない既知の不具合への対応、警告メッセージを出す
                 this.Logger.AddResponse(Resources.LogMessageErrorPageName, title);
@@ -485,20 +467,6 @@ namespace Honememo.Wptscs.Logics
             }
         }
 
-        /// <summary>
-        /// ロガーのログ状態更新イベント用。
-        /// </summary>
-        /// <param name="sender">イベント発生オブジェクト。</param>
-        /// <param name="e">発生したイベント。</param>
-        private void GetLogUpdate(object sender, EventArgs e)
-        {
-            // もともとこのクラスにあったログ通知イベントをロガーに移動したため、入れ子で呼び出す
-            if (this.LogUpdate != null)
-            {
-                this.LogUpdate(this, EventArgs.Empty);
-            }
-        }
-
         #endregion
     }
 }
index a579bbd..8b2dd74 100644 (file)
@@ -46,6 +46,11 @@ namespace Honememo.Wptscs
         /// </summary>
         private int logLength;
 
+        /// <summary>
+        /// ステータス管理用オブジェクト。
+        /// </summary>
+        private StatusManager<string> statusManager;
+
         #endregion
 
         #region コンストラクタ
@@ -71,7 +76,9 @@ namespace Honememo.Wptscs
         private void MainForm_Load(object sender, EventArgs e)
         {
             // フォームの初期設定
-            Control.CheckForIllegalCrossThreadCalls = false;
+            this.statusManager = new StatusManager<string>();
+            this.statusManager.Changed += new EventHandler(
+                delegate { this.toolStripStatusLabelStatus.Text = StringUtils.DefaultString(this.statusManager.Status); });
 
             // 表示言語選択メニュー、設定選択メニューの初期設定
             this.InitializeDropDownButtonLanguage();
@@ -307,6 +314,13 @@ namespace Honememo.Wptscs
             // 画面をロック
             this.LockOperation();
 
+            // 表示領域を初期化、処理時間更新用にタイマーを起動
+            this.textBoxLog.Clear();
+            this.logLength = 0;
+            this.textBoxLog.AppendText(String.Format(Resources.LogMessageStart, FormUtils.ApplicationName(), DateTime.Now));
+            this.toolStripStatusLabelStopwatch.Text = String.Format(Resources.ElapsedTime, TimeSpan.Zero);
+            this.timerStatusStopwatch.Start();
+
             // バックグラウンド処理を実行
             this.backgroundWorkerRun.RunWorkerAsync();
         }
@@ -320,8 +334,10 @@ namespace Honememo.Wptscs
         {
             // 処理を中断
             this.buttonStop.Enabled = false;
-            if (this.backgroundWorkerRun.IsBusy == true)
+            if (this.backgroundWorkerRun.IsBusy)
             {
+                // ※ CancelAsyncだけにしたいが、それをTranslatorに伝播させる方法
+                //    がないため直接そちらにも設定
                 System.Diagnostics.Debug.WriteLine("MainForm.-Stop_Click > 処理中断");
                 this.backgroundWorkerRun.CancelAsync();
                 if (this.translator != null)
@@ -338,64 +354,46 @@ namespace Honememo.Wptscs
         /// <param name="e">発生したイベント。</param>
         private void BackgroundWorkerRun_DoWork(object sender, DoWorkEventArgs e)
         {
-            try
-            {
-                // 初期化と開始メッセージ、別スレッドになるので表示言語も再度設定
-                Program.LoadSelectedCulture();
-                this.textBoxLog.Clear();
-                this.logLength = 0;
-                this.textBoxLog.AppendText(String.Format(Resources.LogMessageStart, FormUtils.ApplicationName(), DateTime.Now));
+            // 戻り値を失敗で初期化
+            e.Result = false;
 
-                // 翻訳支援処理ロジックのオブジェクトを生成
-                this.translator = Translator.Create(this.config, this.comboBoxSource.Text, this.comboBoxTarget.Text);
+            // 別スレッドになるので表示言語を再度設定
+            Program.LoadSelectedCulture();
 
-                // ログ・処理状態更新通知を受け取るためのイベント登録
-                // 処理時間更新用にタイマーを起動
-                this.translator.LogUpdate += new EventHandler(this.GetLogUpdate);
-                this.translator.StatusUpdate += new EventHandler(this.GetStatusUpdate);
-                this.Invoke((MethodInvoker)delegate { this.timerStatusStopwatch.Start(); });
+            // フォーム要素から必要なパラメータ一式を取得
+            string source = null;
+            string target = null;
+            string title = null;
+            this.Invoke((MethodInvoker)delegate
+            {
+                source = this.comboBoxSource.Text;
+                target = this.comboBoxTarget.Text;
+                title = this.textBoxArticle.Text.Trim();
+            });
 
-                // 翻訳支援処理を実行
-                bool success = true;
-                try
-                {
-                    this.translator.Run(this.textBoxArticle.Text.Trim());
-                }
-                catch (ApplicationException)
-                {
-                    // 中止要求で停止した場合、その旨イベントに格納する
-                    e.Cancel = this.backgroundWorkerRun.CancellationPending;
-                    success = false;
-                }
-                finally
-                {
-                    // 処理時間更新用のタイマーを終了
-                    this.Invoke((MethodInvoker)delegate { this.timerStatusStopwatch.Stop(); });
-                }
+            // 翻訳支援処理ロジックのオブジェクトを生成
+            this.translator = Translator.Create(this.config, source, target);
 
-                // 実行結果から、ログと変換後テキストをファイル出力
-                this.WriteResult(success);
-            }
-            catch (WebException ex)
-            {
-                // 想定外の通信エラー(↓とまとめてもよいが、こちらはサーバーの状況などで発生しやすいので)
-                this.textBoxLog.AppendText(Environment.NewLine + String.Format(Resources.ErrorMessageConnectionFailed, ex.Message) + Environment.NewLine);
-                if (ex.Response != null)
-                {
-                    // 出せるならエラーとなったURLも出力
-                    this.textBoxLog.AppendText(Resources.RightArrow + " " + String.Format(Resources.LogMessageErrorURL, ex.Response.ResponseUri) + Environment.NewLine);
-                }
-            }
-            catch (Exception ex)
+            // ログ・処理状態更新通知を受け取るためのイベント登録
+            this.translator.LogUpdated += new EventHandler(
+                delegate { this.Invoke((MethodInvoker)delegate { this.UpdateLog(); }); });
+            this.translator.StatusUpdated += new EventHandler(
+                delegate { this.Invoke((MethodInvoker)delegate { this.statusManager.Status = this.translator.Status; }); });
+
+            // 翻訳支援処理を実行
+            try
             {
-                // 想定外のエラー
-                this.textBoxLog.AppendText(Environment.NewLine + String.Format(Resources.ErrorMessageDevelopmentError, ex.Message, ex.StackTrace) + Environment.NewLine);
+                this.translator.Run(title);
             }
-            finally
+            catch (ApplicationException)
             {
-                // トランスレータを解放
-                this.translator = null;
+                // 中止要求で停止した場合、その旨イベントに格納する
+                e.Cancel = this.backgroundWorkerRun.CancellationPending;
+                return;
             }
+
+            // ここまで成功した場合のみ処理結果を成功とする
+            e.Result = true;
         }
 
         /// <summary>
@@ -405,29 +403,53 @@ namespace Honememo.Wptscs
         /// <param name="e">発生したイベント。</param>
         private void BackgroundWorkerRun_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
         {
-            // 設定ファイルのキャッシュ情報を更新
-            // ※ 微妙に時間がかかるので、ステータスバーに通知
-            try
+            // 中止ボタンをロック、処理時間更新用のタイマーを終了
+            this.buttonStop.Enabled = false;
+            this.timerStatusStopwatch.Stop();
+
+            if (e.Error != null)
             {
-                this.toolStripStatusLabelStatus.Text = Resources.StatusCacheUpdating;
-                try
+                // 処理中で想定外のエラーが発生していた場合ここで通知
+                if (e.Error is WebException)
                 {
-                    this.config.Save();
+                    // 想定外の通信エラー(↓とまとめてもよいが、こちらはサーバーの状況などで発生しやすいので)
+                    WebException ex = (WebException)e.Error;
+                    this.textBoxLog.AppendText(Environment.NewLine + String.Format(Resources.ErrorMessageConnectionFailed, ex.Message) + Environment.NewLine);
+                    if (ex.Response != null)
+                    {
+                        // 出せるならエラーとなったURLも出力
+                        this.textBoxLog.AppendText(Resources.RightArrow + " " + String.Format(Resources.LogMessageErrorURL, ex.Response.ResponseUri) + Environment.NewLine);
+                    }
                 }
-                finally
+                else
                 {
-                    this.toolStripStatusLabelStatus.Text = String.Empty;
+                    // 想定外のエラー
+                    this.textBoxLog.AppendText(Environment.NewLine + String.Format(Resources.ErrorMessageDevelopmentError, e.Error.Message, e.Error.StackTrace) + Environment.NewLine);
                 }
             }
-            catch (Exception ex)
+            else
             {
-                FormUtils.WarningDialog(
-                    Resources.WarningMessageCacheSaveFailed,
-                    ex.Message);
+                // 実行結果から、ログと変換後テキストをファイル出力
+                this.WriteResult(!e.Cancelled && (bool)e.Result);
+
+                // 設定ファイルのキャッシュ情報を更新
+                try
+                {
+                    // ※ 微妙に時間がかかるので、ステータスバーに通知
+                    using (var sm = this.statusManager.Switch(Resources.StatusCacheUpdating))
+                    {
+                        this.config.Save();
+                    }
+                }
+                catch (Exception ex)
+                {
+                    FormUtils.WarningDialog(Resources.WarningMessageCacheSaveFailed, ex.Message);
+                }
             }
 
-            // 画面をロック中から解放
+            // 画面をロック中から戻す、トランスレータを解放
             this.Release();
+            this.translator = null;
         }
 
         #region イベント実装支援用メソッド
@@ -473,8 +495,7 @@ namespace Honememo.Wptscs
         private void WriteResult(bool success)
         {
             // 若干時間がかかるのでステータスバーに通知
-            this.toolStripStatusLabelStatus.Text = Resources.StatusFileWriting;
-            try
+            using (var sm = this.statusManager.Switch(Resources.StatusFileWriting))
             {
                 // 使用可能な出力ファイル名を生成
                 string fileName;
@@ -510,11 +531,6 @@ namespace Honememo.Wptscs
                     this.textBoxLog.AppendText(String.Format(Resources.LogMessageFileSaveFailed, Path.Combine(this.textBoxSaveDirectory.Text, logName), ex.Message));
                 }
             }
-            finally
-            {
-                // ステータスバーをクリア
-                this.toolStripStatusLabelStatus.Text = String.Empty;
-            }
         }
 
         /// <summary>
@@ -553,11 +569,9 @@ namespace Honememo.Wptscs
         }
 
         /// <summary>
-        /// 翻訳支援処理クラスのログ更新イベント用
+        /// 翻訳支援処理クラスのログ更新反映
         /// </summary>
-        /// <param name="sender">イベント発生オブジェクト。</param>
-        /// <param name="e">発生したイベント。</param>
-        private void GetLogUpdate(object sender, EventArgs e)
+        private void UpdateLog()
         {
             // 前回以降に追加されたログをテキストボックスに出力
             int length = this.translator.Log.Length;
@@ -569,17 +583,6 @@ namespace Honememo.Wptscs
             this.logLength = length;
         }
 
-        /// <summary>
-        /// 翻訳支援処理クラスの処理状態更新イベント用。
-        /// </summary>
-        /// <param name="sender">イベント発生オブジェクト。</param>
-        /// <param name="e">発生したイベント。</param>
-        private void GetStatusUpdate(object sender, EventArgs e)
-        {
-            // 処理状態をステータスバーに通知
-            this.toolStripStatusLabelStatus.Text = this.translator.Status;
-        }
-
         #endregion
 
         #endregion
@@ -593,8 +596,11 @@ namespace Honememo.Wptscs
         /// <param name="e">発生したイベント。</param>
         private void TimerStatusStopwatch_Tick(object sender, EventArgs e)
         {
-            // 処理時間をステータスバーに反映
-            this.toolStripStatusLabelStopwatch.Text = String.Format(Resources.ElapsedTime, this.translator.Stopwatch.Elapsed);
+            if (this.translator != null)
+            {
+                // 処理時間をステータスバーに反映
+                this.toolStripStatusLabelStopwatch.Text = String.Format(Resources.ElapsedTime, this.translator.Stopwatch.Elapsed);
+            }
         }
 
         /// <summary>
@@ -844,19 +850,14 @@ namespace Honememo.Wptscs
         private void LoadConfig()
         {
             // 設定ファイルの読み込み
-            // ※ 微妙に時間がかかるので、ステータスバーに通知
             string file = Settings.Default.LastSelectedConfiguration + Settings.Default.ConfigurationExtension;
             try
             {
-                this.toolStripStatusLabelStatus.Text = Resources.StatusConfigReading;
-                try
+                // ※ 微妙に時間がかかるので、ステータスバーに通知
+                using (var sm = this.statusManager.Switch(Resources.StatusConfigReading))
                 {
                     this.config = Config.GetInstance(file);
                 }
-                finally
-                {
-                    this.toolStripStatusLabelStatus.Text = String.Empty;
-                }
             }
             catch (FileNotFoundException ex)
             {
index 91c8363..85bc013 100644 (file)
@@ -57,8 +57,12 @@ namespace Honememo.Wptscs.Parsers
         /// </remarks>
         public override bool TryParse(string s, out IElement result)
         {
-            // å\87ºå\8a\9bå\80¤å\88\9dæ\9c\9få\8c\96
+            // å\85¥å\8a\9bå\80¤ç¢ºèª\8dã\80\81空ã\81®å ´å\90\88ã\81¯å\8d³çµ\82äº\86
             result = null;
+            if (String.IsNullOrEmpty(s))
+            {
+                return false;
+            }
 
             // 始まりの = の数を数える
             // ※ 構文はWikipediaのプレビューで色々試して確認、足りなかったり間違ってたりするかも・・・
index 7e8aedb..993c8f5 100644 (file)
@@ -58,7 +58,7 @@ namespace Honememo.Wptscs.Parsers
             result = null;
 
             // 開始条件 [[ のチェック
-            if (!s.StartsWith(MediaWikiLink.DelimiterStart))
+            if (s == null || !s.StartsWith(MediaWikiLink.DelimiterStart))
             {
                 return false;
             }
@@ -118,7 +118,8 @@ namespace Honememo.Wptscs.Parsers
                     }
 
                     // 変数・コメント以外で { } または < > [ ] \n が含まれている場合、リンクは無効
-                    // TODO: <noinclude>も含まれていてOKだが、2012年1月現在未対応
+                    // ※ <noinclude>等も含まれていてOKだが、そちらはこの処理では対応しない
+                    //    (というかこのクラスとしては対応できない。必要なら前処理で展開/除去する)
                     if ((c == '<') || (c == '>') || (c == '[') || (c == ']') || (c == '{') || (c == '}') || (c == '\n'))
                     {
                         break;
index 3470cb5..850b14d 100644 (file)
@@ -20,6 +20,15 @@ namespace Honememo.Wptscs.Parsers
     /// <summary>
     /// MediaWikiのnowikiブロックを解析するパーサークラスです。
     /// </summary>
+    /// <remarks>
+    /// <para>
+    /// nowikiブロックでは、MediaWikiの各種構文やコメントも含めたhtmlタグが全て無効化されます。
+    /// </para>
+    /// <para>
+    /// ブロック内の再帰解析を行わないパーサーというだけなので、
+    /// 使おうと思えばMediaWiki以外のページに対しても使用可能です。
+    /// </para>
+    /// </remarks>
     public class MediaWikiNowikiParser : XmlElementParser
     {
         #region 定数宣言
@@ -27,7 +36,7 @@ namespace Honememo.Wptscs.Parsers
         /// <summary>
         /// nowikiタグ。
         /// </summary>
-        private static readonly string nowikiTag = "nowiki";
+        private static readonly string NowikiTag = "nowiki";
 
         #endregion
 
@@ -36,8 +45,8 @@ namespace Honememo.Wptscs.Parsers
         /// <summary>
         /// MediaWikiのnowikiブロックを解析するためのパーサーを作成する。
         /// </summary>
-        /// <param name="parser">このパーサーが参照する<see cref="MediaWikiParser"/>。</param>
-        public MediaWikiNowikiParser(MediaWikiParser parser)
+        /// <param name="parser">このパーサーが参照する<see cref="XmlParser"/>。</param>
+        public MediaWikiNowikiParser(XmlParser parser)
         {
             // nowikiブロックではMediaWikiの各種構文やコメントも含むhtmlタグも全て無効なため、
             // 親クラスにそうした処理を含まない空のXMLParserを指定する。
@@ -48,10 +57,39 @@ namespace Honememo.Wptscs.Parsers
             this.Parser.Parsers = new IParser[0];
             this.Parser.IgnoreCase = parser.IgnoreCase;
             this.Parser.IsHtml = parser.IsHtml;
+
+            // nowikiタグ専用に設定。変更されると困るので読み取り専用リストに
+            List<string> list = new List<string>();
+            list.Add(NowikiTag);
+            base.Targets = list.AsReadOnly();
         }
 
         #endregion
-        
+
+        #region 公開プロパティ
+
+        /// <summary>
+        /// このパーサーの解析対象のタグ。
+        /// </summary>
+        /// <exception cref="NotSupportedException">値を更新しようとした場合。</exception>
+        /// <remarks>
+        /// このパーサーはnowikiタグ専用です。値の変更・追加はできません。
+        /// </remarks>
+        public override IList<string> Targets
+        {
+            get
+            {
+                return base.Targets;
+            }
+
+            set
+            {
+                throw new NotSupportedException(NowikiTag + " only");
+            }
+        }
+
+        #endregion
+
         #region インタフェース実装メソッド
         
         /// <summary>
@@ -70,24 +108,21 @@ namespace Honememo.Wptscs.Parsers
             IElement element;
             if (base.TryParse(s, out element))
             {
+                // nowiki区間は内部要素を全てテキストとして扱う
                 XmlElement xmlElement = (XmlElement)element;
-                if (xmlElement.Name.ToLower() == MediaWikiNowikiParser.nowikiTag)
+                XmlTextElement innerElement = new XmlTextElement();
+                StringBuilder b = new StringBuilder();
+                foreach (IElement e in xmlElement)
                 {
-                    // nowiki区間は内部要素を全てテキストとして扱う
-                    XmlTextElement innerElement = new XmlTextElement();
-                    StringBuilder b = new StringBuilder();
-                    foreach (IElement e in xmlElement)
-                    {
-                        b.Append(e.ToString());
-                    }
-
-                    innerElement.Raw = b.ToString();
-                    innerElement.ParsedString = b.ToString();
-                    xmlElement.Clear();
-                    xmlElement.Add(innerElement);
-                    result = xmlElement;
-                    return true;
+                    b.Append(e.ToString());
                 }
+
+                innerElement.Raw = b.ToString();
+                innerElement.ParsedString = b.ToString();
+                xmlElement.Clear();
+                xmlElement.Add(innerElement);
+                result = xmlElement;
+                return true;
             }
 
             return false;
index 68e130c..f227acc 100644 (file)
@@ -44,7 +44,6 @@ namespace Honememo.Wptscs.Parsers
             // ※ 通常は意味が無いが、複雑なテンプレート等で解析失敗が多発し、
             //    何度も同じ文字列を解析してしまうときに非常に時間がかかるため
             this.Website = site;
-            this.IncludeType = IncludeTypeEnum.None;
             this.CommentParser = new XmlCommentElementParser();
             this.NowikiParser = new MediaWikiNowikiParser(this);
             this.LinkParser = new CacheParser(new MediaWikiLinkParser(this));
@@ -68,42 +67,6 @@ namespace Honememo.Wptscs.Parsers
 
         #endregion
 
-        #region 列挙型
-
-        /// <summary>
-        /// インクルードとして解析するか非インクルードとして解析するかを指定する列挙型です。
-        /// </summary>
-        public enum IncludeTypeEnum
-        {
-            /// <summary>
-            /// インクルードされたページとして解析します。
-            /// </summary>
-            /// <remarks>
-            /// &lt;includeonly&gt;タグのブロックが存在する場合、その中身が展開されます。
-            /// &lt;noinclude&gt;タグのブロックは存在しないものとして扱います。
-            /// </remarks>
-            Include,
-
-            /// <summary>
-            /// インクルードではないページとして解析します。
-            /// </summary>
-            /// <remarks>
-            /// &lt;noinclude&gt;タグが存在する場合、その中身が展開されます。
-            /// &lt;includeonly&gt;タグのブロックは存在しないものとして扱います。
-            /// </remarks>
-            Noinclude,
-
-            /// <summary>
-            /// インクルードの判断を行いません。
-            /// </summary>
-            /// <remarks>
-            /// &lt;includeonly&gt;, &lt;noinclude&gt;いずれのタグもただの文字列として扱います。
-            /// </remarks>
-            None
-        }
-
-        #endregion
-
         #region 公開プロパティ
 
         /// <summary>
@@ -123,16 +86,6 @@ namespace Honememo.Wptscs.Parsers
             }
         }
 
-        /// <summary>
-        /// インクルードとして解析するか非インクルードとして解析するかの指定。
-        /// </summary>
-        /// <remarks>初期値は <see cref="IncludeTypeEnum.None"/>。</remarks>
-        public IncludeTypeEnum IncludeType
-        {
-            get;
-            set;
-        }
-
         #endregion
 
         #region 関連クラス公開プロパティ
@@ -209,9 +162,15 @@ namespace Honememo.Wptscs.Parsers
         /// <exception cref="ObjectDisposedException"><see cref="Dispose"/>が実行済みの場合。</exception>
         public override bool TryParseToEndCondition(string s, IsEndCondition condition, out IElement result)
         {
-            // 子パーサーが解放済みかのチェック(同時にnullになるので代表でNowikiParser)
-            if (this.NowikiParser == null)
+            if (s == null)
+            {
+                // nullの場合だけは解析失敗とする
+                result = null;
+                return false;
+            }
+            else if (this.NowikiParser == null)
             {
+                // 子パーサーが解放済みの場合Dispose済みで処理不可(同時にnullになるので代表でNowikiParser)
                 throw new ObjectDisposedException(this.GetType().Name);
             }
 
diff --git a/Wptscs/Parsers/MediaWikiPreparser.cs b/Wptscs/Parsers/MediaWikiPreparser.cs
new file mode 100644 (file)
index 0000000..d5f839f
--- /dev/null
@@ -0,0 +1,224 @@
+// ================================================================================================
+// <summary>
+//      MediaWikiページ解析の前処理用の解析を行うパーサークラスソース</summary>
+//
+// <copyright file="MediaWikiPreparser.cs" company="honeplusのメモ帳">
+//      Copyright (C) 2012 Honeplus. All rights reserved.</copyright>
+// <author>
+//      Honeplus</author>
+// ================================================================================================
+
+namespace Honememo.Wptscs.Parsers
+{
+    using System;
+    using System.Collections.Generic;
+    using Honememo.Parsers;
+    using Honememo.Utilities;
+
+    /// <summary>
+    /// MediaWikiページ解析の前処理用の解析を行うパーサークラスです。
+    /// </summary>
+    /// <remarks>
+    /// <para>
+    /// MediaWikiのページに対して、コメント, includeonly, noincludeの解析を行います。
+    /// </para>
+    /// <para>
+    /// <see cref="IParser.Parse"/>等では上記要素を解析した結果を返します。
+    /// <see cref="MediaWikiParser"/>の前処理として使用する場合は、
+    /// 解析結果に対して別途
+    /// <see cref="FilterByNoinclude"/>, <see cref="FilterByInclude"/>
+    /// の処理を行うか、
+    /// <see cref="PreprocessByNoinclude"/>, <see cref="PreprocessByInclude"/>
+    /// を用いてください。
+    /// </para>
+    /// </remarks>
+    public class MediaWikiPreparser : XmlParser
+    {
+        #region 定数宣言
+
+        /// <summary>
+        /// includeonlyタグ。
+        /// </summary>
+        private static readonly string IncludeonlyTag = "includeonly";
+
+        /// <summary>
+        /// noincludeタグ。
+        /// </summary>
+        private static readonly string NoincludeTag = "noinclude";
+
+        #endregion
+
+        #region コンストラクタ
+
+        /// <summary>
+        /// MediaWikiのページの前処理用の解析を行うパーサーを作成する。
+        /// </summary>
+        public MediaWikiPreparser()
+        {
+            // コメント, nowiki, includeonly, noincludeのみを処理対象とする
+            this.Parsers = new IParser[]
+            {
+                new XmlCommentElementParser(),
+                new MediaWikiNowikiParser(this),
+                new XmlElementParser(this) { Targets = new string[] { IncludeonlyTag, NoincludeTag } }
+            };
+        }
+
+        #endregion
+
+        #region 静的メソッド
+
+        /// <summary>
+        /// 通常ページ解析用の前処理を行う。
+        /// </summary>
+        /// <param name="text">ページテキスト。</param>
+        /// <returns>前処理を行ったページテキスト。</returns>
+        /// <remarks>通常のページ解析を行う際はこの処理を用いる。</remarks>
+        /// <exception cref="ArgumentNullException"><c>null</c>が指定された場合。</exception>
+        public static string PreprocessByNoinclude(string text)
+        {
+            return PreprocessTag(text, false);
+        }
+
+        /// <summary>
+        /// インクルードページ解析用の前処理を行う。
+        /// </summary>
+        /// <param name="text">ページテキスト。</param>
+        /// <returns>前処理を行ったページテキスト。</returns>
+        /// <remarks>テンプレート呼び出しによるページ解析を行う際はこの処理を用いる。</remarks>
+        /// <exception cref="ArgumentNullException"><c>null</c>が指定された場合。</exception>
+        public static string PreprocessByInclude(string text)
+        {
+            return PreprocessTag(text, true);
+        }
+
+        #endregion
+
+        #region 公開メソッド
+
+        /// <summary>
+        /// 渡された解析結果内のnoinclude要素の展開と、includeonly, コメント要素の除去を行う。
+        /// </summary>
+        /// <param name="element">
+        /// 要素を展開/除去する解析結果。
+        /// <see cref="ListElement"/>の場合その内部の上記要素が更新/除去される。
+        /// noincludeの<see cref="XmlElement"/>の場合、内部要素の展開を、
+        /// includeonlyまたは<see cref="XmlCommentElement"/>要素の場合、<c>null</c>への置き換えを行う。
+        /// </param>
+        /// <exception cref="ArgumentNullException"><c>null</c>が指定された場合。</exception>
+        /// <remarks>通常のページ解析を行う際はこの処理を用いる。</remarks>
+        public void FilterByNoinclude(ref IElement element)
+        {
+            this.FilterTag(ref element, false);
+        }
+
+        /// <summary>
+        /// 渡された解析結果内のincludeonly要素の展開と、noinclude, コメント要素の除去を行う。
+        /// </summary>
+        /// <param name="element">
+        /// 要素を展開/除去する解析結果。
+        /// <see cref="ListElement"/>の場合その内部の上記要素が更新/除去される。
+        /// includeonlyの<see cref="XmlElement"/>の場合、内部要素の展開を、
+        /// noincludeまたは<see cref="XmlCommentElement"/>要素の場合、<c>null</c>への置き換えを行う。
+        /// </param>
+        /// <exception cref="ArgumentNullException"><c>null</c>が指定された場合。</exception>
+        /// <remarks>テンプレート呼び出しによるページ解析を行う際はこの処理を用いる。</remarks>
+        public void FilterByInclude(ref IElement element)
+        {
+            this.FilterTag(ref element, true);
+        }
+
+        #endregion
+
+        #region 内部処理用メソッド
+
+        /// <summary>
+        /// 指定されたパラメータに応じた、ページ解析用の前処理を行う。
+        /// </summary>
+        /// <param name="text">ページテキスト。</param>
+        /// <param name="include">インクルードとして処理する場合<c>true</c>。</param>
+        /// <returns>前処理を行ったページテキスト。</returns>
+        /// <exception cref="ArgumentNullException"><paramref name="text"/>が<c>null</c>の場合。</exception>
+        private static string PreprocessTag(string text, bool include)
+        {
+            IElement element;
+            using (MediaWikiPreparser parser = new MediaWikiPreparser())
+            {
+                element = parser.Parse(text);
+                parser.FilterTag(ref element, include);
+            }
+
+            return ObjectUtils.ToString(element);
+        }
+
+        /// <summary>
+        /// 渡された解析結果から、指定に応じてincludeonly, noinclude、またコメント要素を展開/除去する。
+        /// </summary>
+        /// <param name="element">
+        /// 要素を展開/除去する解析結果。
+        /// <see cref="ListElement"/>の場合その内部の上記要素が更新/除去される。
+        /// includeonly, noincludeの<see cref="XmlElement"/>の場合、更新または<c>null</c>への置き換えを、
+        /// <see cref="XmlCommentElement"/>要素の場合、<c>null</c>への置き換えを行う。
+        /// </param>
+        /// <param name="include">インクルードとして処理する場合<c>true</c>。</param>
+        /// <exception cref="ArgumentNullException"><paramref name="element"/>が<c>null</c>の場合。</exception>
+        private void FilterTag(ref IElement element, bool include)
+        {
+            if (Validate.NotNull(element, "element") is XmlCommentElement)
+            {
+                // XMLコメントは除去
+                element = null;
+                return;
+            }
+            else if (element is XmlElement)
+            {
+                // XML/HTML要素の場合、タグの種類を見て対応
+                XmlElement xml = (XmlElement)element;
+                string name = xml.Name.ToLower();
+                if ((include && name == IncludeonlyTag)
+                    || (!include && name == NoincludeTag))
+                {
+                    // インクルードでincludeonly、非インクルードでnoincludeは中身を展開
+                    // ※ このリストは、この後↓でもう一度処理される
+                    ListElement list = new ListElement();
+                    list.AddRange(xml);
+                    element = list;
+                }
+                else if ((include && name == NoincludeTag)
+                    || (!include && name == IncludeonlyTag))
+                {
+                    // インクルードでnoinclude、非インクルードでincludeonlyは中身を展開
+                    element = null;
+                    return;
+                }
+                else
+                {
+                    // それ以外のXML/HTML要素はそのまま
+                    return;
+                }
+            }
+
+            if (element is ListElement)
+            {
+                // リスト要素の場合、中身を再帰的に処理
+                // ※ XML/HTMLの処理で中身を展開したものもここで処理
+                ListElement list = (ListElement)element;
+                for (int i = list.Count - 1; i >= 0; i--)
+                {
+                    IElement e = list[i];
+                    this.FilterTag(ref e, include);
+                    if (e == null)
+                    {
+                        list.RemoveAt(i);
+                    }
+                    else
+                    {
+                        list[i] = e;
+                    }
+                }
+            }
+        }
+
+        #endregion
+    }
+}
index 75218d5..4c3f912 100644 (file)
@@ -45,9 +45,16 @@ namespace Honememo.Wptscs.Parsers
         /// <remarks>MediaWikiのページ全体を渡す必要がある。</remarks>
         public override bool TryParse(string s, out IElement result)
         {
+            // 入力値確認、空の場合は即終了
+            result = null;
+            if (String.IsNullOrEmpty(s))
+            {
+                return false;
+            }
+
             // 日本語版みたいに、#REDIRECTと言語固有の#転送みたいなのがあると思われるので、
             // 翻訳元言語とデフォルトの設定でチェック
-            result = null;
+            string lower = s.ToLower();
             for (int i = 0; i < 2; i++)
             {
                 string format = this.Website.Redirect;
@@ -57,7 +64,7 @@ namespace Honememo.Wptscs.Parsers
                 }
 
                 if (!String.IsNullOrEmpty(format)
-                    && s.ToLower().StartsWith(format.ToLower()))
+                    && lower.StartsWith(format.ToLower()))
                 {
                     if (this.LinkParser.TryParse(s.Substring(format.Length).TrimStart(), out result))
                     {
index 94582be..f714f5a 100644 (file)
@@ -62,7 +62,7 @@ namespace Honememo.Wptscs.Parsers
             result = null;
 
             // 開始条件 {{ のチェック
-            if (!s.StartsWith(MediaWikiTemplate.DelimiterStart))
+            if (s == null || !s.StartsWith(MediaWikiTemplate.DelimiterStart))
             {
                 return false;
             }
@@ -112,7 +112,8 @@ namespace Honememo.Wptscs.Parsers
                     }
 
                     // 変数・コメント以外で < > [ ] { } が含まれている場合、リンクは無効
-                    // TODO: <noinclude>も含まれていてOKだが、2012年1月現在未対応
+                    // ※ <noinclude>等も含まれていてOKだが、そちらはこの処理では対応しない
+                    //    (というかこのクラスとしては対応できない。必要なら前処理で展開/除去する)
                     if ((c == '<') || (c == '>') || (c == '[') || (c == ']') || (c == '{') || (c == '}'))
                     {
                         break;
index bb2dbd3..1cb45ce 100644 (file)
@@ -57,7 +57,7 @@ namespace Honememo.Wptscs.Parsers
             result = null;
 
             // 開始条件 {{{ のチェック
-            if (!s.StartsWith(MediaWikiVariable.DelimiterStart))
+            if (s == null || !s.StartsWith(MediaWikiVariable.DelimiterStart))
             {
                 return false;
             }
index 3923d35..d37497c 100644 (file)
@@ -1,7 +1,7 @@
 =====================================================================
 【タイトル】 Wikipedia 翻訳支援ツール
 【ファイル】 wptscs120.zip
-【作成月日】 2012/x/xx
+【作成月日】 2012/3/xx
 【制 作 者】 Honeplus
 【動作環境】 Windows Vista/7 での動作を確認。要.NET Framework 4.0 Client Profile
 【配布形態】 修正BSDライセンス
@@ -164,11 +164,12 @@ Ver1.11  2012/02/19 英語リソースの追加(ツールチップは除く)
                     トランスレータ周りのソース中心にリファクタリングを実施。
                     ※ 1.10とは設定ファイルの互換性がありません。
 
-Ver1.20  2012/0x/xx Wiktionary/Wikitravelでの動作に対応。
+Ver1.20  2012/03/xx Wiktionary/Wikitravelでの動作に対応。
                     設定の切り替え/追加機能を追加。
+                    言語間リンク取得時に<includeonly>, <noinclude>を考慮するよう修正。
                     見出しの置き換えに改行区切りで複数の語句を登録できるよう改良。
                     非常に複雑なテンプレートで極端に時間がかかっていたのを改善。
-                    その他画面表示周りの処理の改善/小不具合を修正。
+                    その他ソースの改善/小不具合を修正。
                     ※ 1.11とは設定ファイル名が変化しており、古い設定は自動で読み込まれません。
 
 
diff --git a/Wptscs/Utilities/EndPeriodException.cs b/Wptscs/Utilities/EndPeriodException.cs
new file mode 100644 (file)
index 0000000..8fd218d
--- /dev/null
@@ -0,0 +1,46 @@
+// ================================================================================================
+// <summary>
+//      末尾がピリオドのページが取得できない既知の不具合に該当することを表す例外クラスソース</summary>
+//
+// <copyright file="EndPeriodException.cs" company="honeplusのメモ帳">
+//      Copyright (C) 2012 Honeplus. All rights reserved.</copyright>
+// <author>
+//      Honeplus</author>
+// ================================================================================================
+
+namespace Honememo.Wptscs.Utilities
+{
+    using System;
+
+    /// <summary>
+    /// 末尾がピリオドのページが取得できない既知の不具合に該当することを表す例外クラスです。
+    /// </summary>
+    /// <remarks>
+    /// <para>
+    /// 末尾がピリオドのページが取得できない既知の不具合への暫定対応。
+    /// もともとただの<see cref="NotSupportedException"/>を投げていたが、
+    /// スキーム名が不正な場合等にもこの例外が飛ぶことが判明したため、
+    /// 区別できるように作成。
+    /// </para>
+    /// <para>
+    /// この問題は<see cref="IWebProxy"/>実装クラスでおきているため、
+    /// このパッケージに定義する。
+    /// </para>
+    /// </remarks>
+    public class EndPeriodException : NotSupportedException
+    {
+        #region コンストラクタ
+
+        /// <summary>
+        /// 指定したエラーメッセージを使用して、
+        /// 新しい例外インスタンスを作成します。
+        /// </summary>
+        /// <param name="message">エラーメッセージ。</param>
+        public EndPeriodException(string message)
+            : base(message)
+        {
+        }
+
+        #endregion
+    }
+}
index cd88e02..9cbb427 100644 (file)
@@ -419,7 +419,7 @@ namespace Honememo.Wptscs.Websites
         /// <param name="title">ページタイトル。</param>
         /// <returns>取得したページ。</returns>
         /// <exception cref="FileNotFoundException">ページが存在しない場合。</exception>
-        /// <exception cref="NotSupportedException">末尾がピリオドのページの場合(既知の不具合への対応)。</exception>
+        /// <exception cref="EndPeriodException">末尾がピリオドのページの場合(既知の不具合への対応)。</exception>
         /// <remarks>ページの取得に失敗した場合(通信エラーなど)は、その状況に応じた例外を投げる。</remarks>
         public override Page GetPage(string title)
         {
@@ -437,7 +437,7 @@ namespace Honememo.Wptscs.Websites
             {
                 // 末尾がピリオドのページが取得できない既知の不具合への暫定対応
                 // 対処方法が不明なため、せめて例外を投げて検知する
-                throw new NotSupportedException(title + " is not suppoted");
+                throw new EndPeriodException(title + " is not suppoted");
             }
 
             // ページのXMLデータをMediaWikiサーバーから取得
index f108362..a076d55 100644 (file)
@@ -31,11 +31,6 @@ namespace Honememo.Wptscs.Websites
         /// </summary>
         private MediaWikiLink redirect;
 
-        /// <summary>
-        /// ページの本文をパーサーで要素単位に解析した結果。
-        /// </summary>
-        private IElement element;
-
         #endregion
 
         #region コンストラクタ
@@ -110,7 +105,6 @@ namespace Honememo.Wptscs.Websites
                 // 本文は普通に格納
                 base.Text = value;
                 this.redirect = null;
-                this.element = null;
 
                 // 本文格納のタイミングでリダイレクトページ(#REDIRECT等)かを判定
                 if (!String.IsNullOrEmpty(base.Text))
@@ -146,35 +140,6 @@ namespace Honememo.Wptscs.Websites
             }
         }
 
-        /// <summary>
-        /// ページの本文をパーサーで要素単位に解析した結果。
-        /// </summary>
-        /// <exception cref="InvalidOperationException"><see cref="Text"/>が<c>null</c>の場合。</exception>
-        /// <remarks>get時にページの解析を行う。</remarks>
-        public IElement Element
-        {
-            get
-            {
-                // Textが設定されている場合のみ有効
-                this.ValidateIncomplete();
-                if (this.element == null)
-                {
-                    // ページサイズによっては時間がかかるので、必要な場合だけ実施
-                    using (MediaWikiParser parser = new MediaWikiParser(this.Website))
-                    {
-                        this.element = parser.Parse(this.Text);
-                    }
-                }
-
-                return this.element;
-            }
-
-            protected set
-            {
-                this.element = value;
-            }
-        }
-
         #endregion
         
         #region 公開メソッド
@@ -186,13 +151,14 @@ namespace Honememo.Wptscs.Websites
         /// <returns>言語間リンク。見つからない場合は<c>null</c>。</returns>
         /// <exception cref="InvalidOperationException"><see cref="Text"/>が<c>null</c>の場合。</exception>
         /// <remarks>言語間リンクが複数存在する場合は、先に発見したものを返す。</remarks>
-        public MediaWikiLink GetInterlanguage(string code)
+        public virtual MediaWikiLink GetInterlanguage(string code)
         {
             // Textが設定されている場合のみ有効
             this.ValidateIncomplete();
 
-            // 記事を解析し、その結果から言語間リンクを探索
-            return this.GetInterlanguage(code, this.Element);
+            // ページ本文から言語間リンクを探索
+            // ※ 自ページの解析なのでnoincludeとして前処理を行う
+            return this.GetInterlanguage(code, MediaWikiPreparser.PreprocessByNoinclude(this.Text));
         }
 
         /// <summary>
@@ -251,7 +217,7 @@ namespace Honememo.Wptscs.Websites
         /// </summary>
         /// <param name="link">このページ内のリンク。</param>
         /// <returns>変換した記事名。</returns>
-        public string Normalize(MediaWikiLink link)
+        public virtual string Normalize(MediaWikiLink link)
         {
             string title = StringUtils.DefaultString(link.Title);
             if (link.IsSubpage())
@@ -297,7 +263,7 @@ namespace Honememo.Wptscs.Websites
         /// 不完全な場合、例外をスローする。
         /// </summary>
         /// <exception cref="InvalidOperationException">オブジェクトは不完全。</exception>
-        protected void ValidateIncomplete()
+        protected virtual void ValidateIncomplete()
         {
             if (String.IsNullOrEmpty(this.Text))
             {
@@ -307,6 +273,25 @@ namespace Honememo.Wptscs.Websites
         }
 
         /// <summary>
+        /// 指定されたページテキストから言語間リンクを取得。
+        /// </summary>
+        /// <param name="code">言語コード。</param>
+        /// <param name="text">ページテキスト。</param>
+        /// <returns>言語間リンク。見つからない場合は<c>null</c>。</returns>
+        /// <remarks>言語間リンクが複数存在する場合は、先に発見したものを返す。</remarks>
+        private MediaWikiLink GetInterlanguage(string code, string text)
+        {
+            // 渡されたテキストを要素単位に解析し、その結果から言語間リンクを探索する
+            IElement element;
+            using (MediaWikiParser parser = new MediaWikiParser(this.Website))
+            {
+                element = parser.Parse(text);
+            }
+
+            return this.GetInterlanguage(code, element);
+        }
+
+        /// <summary>
         /// 指定されたページ解析結果要素から言語間リンクを取得。
         /// </summary>
         /// <param name="code">言語コード。</param>
@@ -411,7 +396,10 @@ namespace Honememo.Wptscs.Websites
             if (subpage != null)
             {
                 // サブページの言語間リンクを返す
-                return subpage.GetInterlanguage(code);
+                // ※ テンプレート呼び出しなのでincludeとして前処理を行う
+                return subpage.GetInterlanguage(
+                    code,
+                    MediaWikiPreparser.PreprocessByInclude(subpage.Text));
             }
 
             // 未発見の場合null
index bd2d3c7..d587a3d 100644 (file)
       <Item From="Template:?" To="Template:?" />
       <Item From="Template:Doc" To="Template:Documentation" Redirect="Template:Documentation" />
       <Item From="Template:Documentation" To="Template:Documentation" />
-      <Item From="Template:Free" To="Template:Free" />
       <Item From="Template:Multicol-break" To="Template:Multicol-break" />
       <Item From="Template:Multicol-end" To="Template:Multicol-end" />
-      <Item From="Template:Nom" To="Template:Nom" />
-      <Item From="Template:Nonfree" To="Template:Nonfree" />
-      <Item From="Template:Partial" To="Template:Partial" />
-      <Item From="Template:Pending" To="Template:Pending" />
       <Item From="Template:Refend" To="Template:Refend" />
       <Item From="Template:Template doc" To="Template:Documentation" Redirect="Template:Documentation" />
-      <Item From="Template:Won" To="Template:Won" />
-      <Item From="Template:Yes-No" To="Template:Yes-No" />
     </ItemTable>
     <ItemTable From="en" To="ru">
       <Item From="Template:Doc" To="Шаблон:Doc" Redirect="Template:Documentation" />
       <Item From="Template:Template doc" To="Template:Documentation" Redirect="Template:Documentation" />
     </ItemTable>
     <ItemTable From="ja" To="en">
+      <Item From="Template:?" To="Template:?" />
       <Item From="Template:Col-2" To="Template:Col-2" />
       <Item From="Template:Col-3" To="Template:Col-3" />
       <Item From="Template:Col-4" To="Template:Col-4" />
       <Item From="Template:Documentation" To="Template:Documentation" />
       <Item From="Template:Multicol-break" To="Template:Multicol-break" />
       <Item From="Template:Multicol-end" To="Template:Multicol-end" />
-      <Item From="Template:Nom" To="Template:Nom" />
-      <Item From="Template:Pending" To="Template:Pending" />
       <Item From="Template:Refend" To="Template:Refend" />
       <Item From="Template:Template doc" To="Template:Documentation" Redirect="Template:Documentation" />
-      <Item From="Template:Won" To="Template:Won" />
     </ItemTable>
   </ItemTables>
   <HeadingTable>
index 73f4965..fdf5d79 100644 (file)
@@ -89,6 +89,8 @@
     <Compile Include="Parsers\MediaWikiVariable.cs" />
     <Compile Include="Parsers\MediaWikiVariableParser.cs" />
     <Compile Include="Parsers\MediaWikiRedirectParser.cs" />
+    <Compile Include="Parsers\MediaWikiPreparser.cs" />
+    <Compile Include="Utilities\EndPeriodException.cs" />
     <Compile Include="Websites\MediaWiki.cs" />
     <Compile Include="Parsers\MediaWikiLink.cs" />
     <Compile Include="Websites\MediaWikiPage.cs" />
diff --git a/WptscsTest/Data/MediaWiki/en/Template_Partial.xml b/WptscsTest/Data/MediaWiki/en/Template_Partial.xml
new file mode 100644 (file)
index 0000000..bf982cb
--- /dev/null
@@ -0,0 +1,60 @@
+<mediawiki xmlns="http://www.mediawiki.org/xml/export-0.5/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.mediawiki.org/xml/export-0.5/ http://www.mediawiki.org/xml/export-0.5.xsd" version="0.5" xml:lang="en">
+  <siteinfo>
+    <sitename>Wikipedia</sitename>
+    <base>http://en.wikipedia.org/wiki/Main_Page</base>
+    <generator>MediaWiki 1.18wmf1</generator>
+    <case>first-letter</case>
+    <namespaces>
+      <namespace key="-2" case="first-letter">Media</namespace>
+      <namespace key="-1" case="first-letter">Special</namespace>
+      <namespace key="0" case="first-letter" />
+      <namespace key="1" case="first-letter">Talk</namespace>
+      <namespace key="2" case="first-letter">User</namespace>
+      <namespace key="3" case="first-letter">User talk</namespace>
+      <namespace key="4" case="first-letter">Wikipedia</namespace>
+      <namespace key="5" case="first-letter">Wikipedia talk</namespace>
+      <namespace key="6" case="first-letter">File</namespace>
+      <namespace key="7" case="first-letter">File talk</namespace>
+      <namespace key="8" case="first-letter">MediaWiki</namespace>
+      <namespace key="9" case="first-letter">MediaWiki talk</namespace>
+      <namespace key="10" case="first-letter">Template</namespace>
+      <namespace key="11" case="first-letter">Template talk</namespace>
+      <namespace key="12" case="first-letter">Help</namespace>
+      <namespace key="13" case="first-letter">Help talk</namespace>
+      <namespace key="14" case="first-letter">Category</namespace>
+      <namespace key="15" case="first-letter">Category talk</namespace>
+      <namespace key="100" case="first-letter">Portal</namespace>
+      <namespace key="101" case="first-letter">Portal talk</namespace>
+      <namespace key="108" case="first-letter">Book</namespace>
+      <namespace key="109" case="first-letter">Book talk</namespace>
+    </namespaces>
+  </siteinfo>
+  <page>
+    <title>Template:Partial</title>
+    <id>2499676</id>
+    <revision>
+      <id>477680063</id>
+      <timestamp>2012-02-19T09:07:03Z</timestamp>
+      <contributor>
+        <username>Honeplus</username>
+        <id>1416502</id>
+      </contributor>
+      <minor/>
+      <comment>change ja</comment>
+      <text xml:space="preserve" bytes="479">&lt;noinclude&gt;{| class=&quot;wikitable&quot;
+|-
+|&lt;/noinclude&gt;style=&quot;background: #FFB; color: black; vertical-align: middle; text-align: {{{align|center}}}; {{{style|}}}&quot; class=&quot;partial table-partial&quot;|{{{1|Partial}}}&lt;noinclude&gt;
+|}
+{{Documentation|Template:Table cell templates/doc}}
+
+&lt;!-- interwikis &amp; categories --&gt;
+[[ca:Plantilla:Cel·la parcial]]
+[[ja:Template:Partial]]
+[[hu:Sablon:Részlegesen]]
+[[pl:Szablon:Tabela-częściowo]]
+[[sl:Predloga:Deloma]]
+[[tr:Şablon:Kısmen]]
+&lt;/noinclude&gt;</text>
+    </revision>
+  </page>
+</mediawiki>
diff --git a/WptscsTest/Data/MediaWiki/en/Template_Table cell templates_doc.xml b/WptscsTest/Data/MediaWiki/en/Template_Table cell templates_doc.xml
new file mode 100644 (file)
index 0000000..d54af88
--- /dev/null
@@ -0,0 +1,208 @@
+<mediawiki xmlns="http://www.mediawiki.org/xml/export-0.5/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.mediawiki.org/xml/export-0.5/ http://www.mediawiki.org/xml/export-0.5.xsd" version="0.5" xml:lang="en">
+  <siteinfo>
+    <sitename>Wikipedia</sitename>
+    <base>http://en.wikipedia.org/wiki/Main_Page</base>
+    <generator>MediaWiki 1.18wmf1</generator>
+    <case>first-letter</case>
+    <namespaces>
+      <namespace key="-2" case="first-letter">Media</namespace>
+      <namespace key="-1" case="first-letter">Special</namespace>
+      <namespace key="0" case="first-letter" />
+      <namespace key="1" case="first-letter">Talk</namespace>
+      <namespace key="2" case="first-letter">User</namespace>
+      <namespace key="3" case="first-letter">User talk</namespace>
+      <namespace key="4" case="first-letter">Wikipedia</namespace>
+      <namespace key="5" case="first-letter">Wikipedia talk</namespace>
+      <namespace key="6" case="first-letter">File</namespace>
+      <namespace key="7" case="first-letter">File talk</namespace>
+      <namespace key="8" case="first-letter">MediaWiki</namespace>
+      <namespace key="9" case="first-letter">MediaWiki talk</namespace>
+      <namespace key="10" case="first-letter">Template</namespace>
+      <namespace key="11" case="first-letter">Template talk</namespace>
+      <namespace key="12" case="first-letter">Help</namespace>
+      <namespace key="13" case="first-letter">Help talk</namespace>
+      <namespace key="14" case="first-letter">Category</namespace>
+      <namespace key="15" case="first-letter">Category talk</namespace>
+      <namespace key="100" case="first-letter">Portal</namespace>
+      <namespace key="101" case="first-letter">Portal talk</namespace>
+      <namespace key="108" case="first-letter">Book</namespace>
+      <namespace key="109" case="first-letter">Book talk</namespace>
+    </namespaces>
+  </siteinfo>
+  <page>
+    <title>Template:Table cell templates/doc</title>
+    <id>25941487</id>
+    <revision>
+      <id>471811634</id>
+      <timestamp>2012-01-17T04:53:15Z</timestamp>
+      <contributor>
+        <username>Tomchen1989</username>
+        <id>11110763</id>
+      </contributor>
+      <minor/>
+      <comment>/* Templates in this series */</comment>
+      <text xml:space="preserve" bytes="8034">&lt;noinclude&gt;{{Documentation subpage}}&lt;/noinclude&gt;
+
+The templates in this series are designed to be used in a [[Help:Table|table]] to make a cell with text in that cell, with an appropriately colored background.  They are commonly used in [[:Category:Comparisons|comparison tables]].
+
+For example, &lt;code&gt;{{Tl|yes}}&lt;/code&gt; makes a cell with a green background. The text in the cell is taken from the [[Help:Template#Parameters|first parameter]]; &lt;code&gt;&lt;nowiki&gt;{{&lt;/nowiki&gt;yes|Sure}}&lt;/code&gt; would output &quot;Sure&quot; otherwise it defaults to &quot;Yes&quot;. Most templates allow authors to override the default text in this way, some require text put after the template call and some also need a vertical bar in between: &lt;code&gt;&lt;nowiki&gt;{{&lt;/nowiki&gt;''table cell template''}} ''text''&lt;/code&gt; or &lt;code&gt;&lt;nowiki&gt;{{&lt;/nowiki&gt;''table cell template''}} | ''text''&lt;/code&gt;. This information, the colors and default texts are found in the table below.
+
+If you want to use other attributes for the table cells, e.g. &lt;code&gt;colspan&lt;/code&gt; or &lt;code&gt;rowspan&lt;/code&gt;, they need to be put before the template call and there must be no vertical bar &lt;code&gt;|&lt;/code&gt; in between them:
+
+{| class=wikitable align=right
+!{{Yes}} ||{{No}} ||rowspan=2 {{n/a}}
+|-
+|colspan=2 {{Yes-No}}
+|}
+&lt;pre&gt;...
+!{{Yes}} ||{{No}} ||rowspan=2 {{n/a}}
+|-
+|colspan=2 {{Yes-No}}
+...
+&lt;/pre&gt;
+
+=== Templates in this series ===
+
+{| class=&quot;wikitable&quot;
+|+ Table cell templates
+! Class&lt;ref name=&quot;table-cell-templates-table-css&quot;&gt;The HTML class of table cell templates may be referenced in a [[m:help:User style|user stylesheet]] to change appearance.&lt;/ref&gt; !! Template !! Preview !! Preview, alternative
+|-
+|rowspan=2| &lt;code&gt;table-rh&lt;/code&gt; || {{Tl|rh}} | Row header || {{rh}} | Row header ||
+|-
+| {{Tl|rh2}}&lt;ref name=&quot;table-cell-templates-table-after+pipe&quot;&gt;Does not take a parameter; the content should be placed after the template call, separated by a pipe (|) character.&lt;/ref&gt; | Row header || {{rh2}} | Row header ||
+|-
+| &lt;code&gt;table-yes&lt;/code&gt; || {{Tl|yes}}, {{Tlp|yes|&lt;nowiki&gt;Alternative [[Plain text|text]]&lt;/nowiki&gt;}} || {{yes}} ||{{yes|Alternative [[Plain text|text]]}}
+|-
+|rowspan=3| &lt;code&gt;table-no&lt;/code&gt; || {{Tl|no}}, {{Tlp|no|&lt;nowiki&gt;Alternative [[Plain text|text]]&lt;/nowiki&gt;}} || {{no}} ||{{no|Alternative [[Plain text|text]]}}
+|-
+| {{Tl|coming soon}} || {{coming soon}} ||
+|-
+| {{Tl|bad}}, {{Tlp|bad|F}} || {{bad}} || {{bad|F}}
+|-
+| &lt;code&gt;table-siteactive&lt;/code&gt; || {{Tl|Site active}} || {{site active}} || 
+|-
+| &lt;code&gt;table-siteinactive&lt;/code&gt; || {{Tl|Site inactive}} || {{site inactive}} || 
+|-
+|rowspan=7| &lt;code&gt;table-yes2&lt;/code&gt; || {{Tl|yes2}}&lt;ref name=&quot;table-cell-templates-table-after&quot;/&gt; Text || {{yes2}}Text ||
+|-
+| {{Tl|won}}, {{Tlp|won|5}} || {{won}} || {{won|5}}
+|-
+| &lt;nowiki&gt;{{won|place=1}} or =Gold&lt;/nowiki&gt; || {{won|place=1}} ||
+|-
+| &lt;nowiki&gt;{{won|place=2}} or =Silver&lt;/nowiki&gt; || {{won|place=2}} ||
+|-
+| &lt;nowiki&gt;{{won|place=3}} or =Bronze&lt;/nowiki&gt; || {{won|place=3}} ||
+|-
+| &lt;nowiki&gt;{{won|text=white|color=blue}} or =#1122CC&lt;/nowiki&gt; || {{won|text=white|color=blue}} ||
+|-
+| {{Tl|good}}, {{Tlp|good|A}} || {{good}} || {{good|A}}
+|-
+|rowspan=3| &lt;code&gt;table-no2&lt;/code&gt; || {{Tl|no2}}&lt;ref name=&quot;table-cell-templates-table-after&quot;&gt;Does not take a parameter; the content should be placed after the template call.&lt;/ref&gt; Text || {{no2}} Text || 
+|-
+| {{Tl|nom}}, {{Tlp|nom|5}} || {{nom}} || {{nom|5}}
+|-
+| {{Tl|sho}} || {{sho}} || 
+|-
+|rowspan=4| &lt;code&gt;table-partial&lt;/code&gt; || {{Tl|partial}} || {{partial}} || 
+|-
+| {{Tl|yes-No}} || {{Yes-No}} || 
+|-
+| {{Tl|okay}}, {{Tlp|okay|C}} || {{okay}} || {{okay|C}}
+|-
+| {{Tl|some}} || {{some}} || 
+|-
+| &lt;code&gt;table-any&lt;/code&gt; || {{Tl|any}} || {{any}} || 
+|-
+|rowspan=2| &lt;code&gt;table-na&lt;/code&gt; || {{Tl|n/a}} || {{n/a}} || 
+|-
+| {{Tl|BLACK}} || {{BLACK}} ||
+|-
+|rowspan=2| &lt;code&gt;table-unknown&lt;/code&gt; || {{Tl|dunno}} || {{dunno}} ||
+|-
+| {{Tl|Unknown}} || {{Unknown}} ||
+|-
+| &lt;code&gt;table-depends&lt;/code&gt; || {{Tl|Depends}}, {{Tlp|Depends|&lt;nowiki&gt;Alternative [[Plain text|text]]&lt;/nowiki&gt;}} || {{Depends}}|| {{Depends|Alternative [[Plain text|text]]}}
+|-
+| &lt;code&gt;table-included&lt;/code&gt; || {{Tl|Included}} || {{Included}} || 
+|-
+|rowspan=2| &lt;code&gt;table-dropped&lt;/code&gt; || {{Tl|dropped}} || {{dropped}} ||
+|-
+| {{Tl|terminated}} || {{terminated}} ||
+|-
+|rowspan=2| &lt;code&gt;table-beta&lt;/code&gt; || {{Tl|beta}} || {{beta}} ||
+|-
+| {{Tl|table-experimental}}, {{Tlp|table-experimental|&lt;nowiki&gt;Alternative [[Plain text|text]]&lt;/nowiki&gt;}} || {{table-experimental}}||{{table-experimental|Alternative [[Plain text|text]]}}
+|-
+| &lt;code&gt;table-free&lt;/code&gt; || {{Tl|free}}, {{Tlp|free|&lt;nowiki&gt;Alternative [[Plain text|text]]&lt;/nowiki&gt;}} || {{free}} ||{{free|Alternative [[Plain text|text]]}}
+|-
+| &lt;code&gt;table-nonfree&lt;/code&gt; || {{Tl|nonfree}}, {{Tlp|nonfree|&lt;nowiki&gt;Alternative [[Plain text|text]]&lt;/nowiki&gt;}} || {{nonfree}}|| {{nonfree|Alternative [[Plain text|text]]}}
+|-
+| &lt;code&gt;table-proprietary&lt;/code&gt; || {{Tl|proprietary}}, {{Tlp|proprietary|&lt;nowiki&gt;Alternative [[Plain text|text]]&lt;/nowiki&gt;}} ||{{proprietary}} || {{proprietary|Alternate [[Plain text|text]]}}
+|-
+| &lt;code&gt;table-needs&lt;/code&gt; || {{Tl|needs}}, {{Tlp|needs|&lt;nowiki&gt;Alternative [[Plain text|text]]&lt;/nowiki&gt;}} || {{needs}} ||{{needs|Alternative [[Plain text|text]]}}
+|-
+| ''no class'' || {{Tl|incorrect}}, {{Tlp|incorrect|&lt;nowiki&gt;Alternative [[Plain text|text]]&lt;/nowiki&gt;}}  || {{incorrect}} ||{{incorrect|Alternative [[Plain text|text]]}}
+|-
+| ''no class'' || {{Tl|no result}}, {{Tlp|no result|N.R.}} || {{No result}} || {{No result|N.R.}}
+|-
+| ''no class'' || {{Tl|pending}} || {{Pending}} || 
+|-
+| &lt;code&gt;table-nightly&lt;/code&gt; || {{Tl|nightly}}, {{Tlp|nightly|&lt;nowiki&gt;Alternative [[Plain text|text]]&lt;/nowiki&gt;}} || {{nightly}}|| {{nightly|Alternative [[Plain text|text]]}}
+|-
+| &lt;code&gt;table-release-candidate&lt;/code&gt; || {{Tl|release-candidate}}, {{Tlp|release-candidate|&lt;nowiki&gt;Alternative [[Plain text|text]]&lt;/nowiki&gt;}} || {{release-candidate}}|| {{release-candidate|Alternative [[Plain text|text]]}}
+|-
+| ''no class'' || {{Tl|?}}&lt;ref name=&quot;table-cell-templates-table-noparam&quot;&gt;Does not take a parameter; not really a table cell template at all, but here for completeness&lt;/ref&gt; || {{?}} || 
+|-
+| ''no class'' || {{Tl|unofficial}} || {{Unofficial}} || 
+|-
+| ''no class'' || {{Tl|usually}} || {{Usually}} ||
+|-
+| ''no class'' || {{Tl|rarely}} || {{Rarely}} || 
+|}
+
+=== Code ===
+
+Common code to most if not all the templates in this series:
+:&lt;code&gt;{{Table cell templates|class=''automatic''|text=''text''|bg=#''abcdef''}}&lt;/code&gt;
+
+Code specific to this template:
+:&lt;code&gt;{{ {{BASEPAGENAME}} }}&lt;/code&gt;
+
+To make a new table cell template you can use:
+&lt;source lang=text&gt;{{subst:Table cell templates| text = default text | bg = background color | class = a class name without prefix | align = 
+standard horizontal alignment}}&lt;/source&gt;
+
+You should leave out the &lt;code&gt;align&lt;/code&gt; parameter and often the &lt;code&gt;class&lt;/code&gt; parameter is unnecessary, too.
+
+Add the new template to the table in the [[Template:Table cell templates/doc|common documentation]] afterwards. Please consider reusing one of the other templates and please choose the color sensibly.
+
+If you find a table cell template that does not take a parameter and you want to be able to change the text in the cell, ''do not'' duplicate the template! Instead, edit the template and change the text to a default parameter substitution.  For example, if a template's text is &lt;code&gt;Dropped&lt;/code&gt;, change that to &lt;code&gt;&lt;nowiki&gt;{{{1|Dropped}}}&lt;/nowiki&gt;&lt;/code&gt;.
+
+=== See also ===
+
+* {{Tl|multiplayer}}
+* {{Tl|GPL-lic}}
+* {{Tl|chg}}: calculate and display changes between two values
+
+=== Notes ===
+
+{{Reflist}}
+
+&lt;!-- ADD CATEGORIES BELOW THIS LINE --&gt;
+[[Category:Table cell templates]]
+&lt;noinclude&gt;
+&lt;!-- ADD INTERWIKIS BELOW THIS LINE --&gt;
+[[es:Plantilla:Celda de tabla de plantillas]]
+[[ca:Plantilla:Plantilles per les cel·les de les taules]]
+[[eo:Ŝablono:Jes ne]]
+[[hsb:Předłoha:Haj ně]]
+[[ja:Template:Table cell templates]]
+[[ko:틀:표 셀 틀]]
+[[ru:Шаблон:Победа]]
+[[sh:Šablon:Da-ne]]
+[[tr:Şablon:Adaylık]]
+[[zh:Template:Table cell templates]]
+&lt;/noinclude&gt;</text>
+    </revision>
+  </page>
+</mediawiki>
index fe90c23..4bb5add 100644 (file)
@@ -13,9 +13,7 @@ namespace Honememo.Wptscs.Logics
     using System;
     using System.Collections.Generic;
     using System.IO;
-    using System.Reflection;
     using Honememo.Parsers;
-    using Honememo.Tests;
     using Honememo.Utilities;
     using Honememo.Wptscs.Models;
     using Honememo.Wptscs.Parsers;
@@ -24,10 +22,10 @@ namespace Honememo.Wptscs.Logics
     using NUnit.Framework;
 
     /// <summary>
-    /// MediaWikiTranslatorのテストクラスです。
+    /// <see cref="MediaWikiTranslator"/>のテストクラスです。
     /// </summary>
     [TestFixture]
-    public class MediaWikiTranslatorTest
+    class MediaWikiTranslatorTest
     {
         #region 定数
 
@@ -77,7 +75,7 @@ namespace Honememo.Wptscs.Logics
         #region 各処理のメソッドテストケース
 
         /// <summary>
-        /// ReplaceLinkメソッドテストケース。
+        /// <see cref="MediaWikiTranslator.ReplaceLink"/>メソッドテストケース。
         /// </summary>
         [Test]
         public void TestReplaceLink()
@@ -185,7 +183,7 @@ namespace Honememo.Wptscs.Logics
         }
 
         /// <summary>
-        /// ReplaceLinkメソッドテストケース(サブページ)。
+        /// <see cref="MediaWikiTranslator.ReplaceLink"/>メソッドテストケース(サブページ)。
         /// </summary>
         [Test]
         public void TestReplaceLinkSubpage()
@@ -199,15 +197,15 @@ namespace Honememo.Wptscs.Logics
 
             // 全て指定したサブページ
             // ※ 以下オブジェクトを毎回作り直しているのは、更新されてしまうケースがあるため
-            parent = new MediaWikiPage(translator.From, "Template:Citation needed");
+            parent = new MediaWikiPage(translator.From, "Template:Table cell templates");
             link = new MediaWikiLink();
-            link.Title = "Template:Citation needed/Doc";
-            Assert.AreEqual("[[Template:要出典|Template:Citation needed/Doc]]", translator.ReplaceLink(link, parent).ToString());
+            link.Title = "Template:Table cell templates/doc";
+            Assert.AreEqual("[[Template:Table cell templates|Template:Table cell templates/doc]]", translator.ReplaceLink(link, parent).ToString());
 
             // サブページ(子)
             link = new MediaWikiLink();
             link.Title = "/Doc";
-            Assert.AreEqual("[[Template:要出典|/Doc]]", translator.ReplaceLink(link, parent).ToString());
+            Assert.AreEqual("[[Template:Table cell templates|/Doc]]", translator.ReplaceLink(link, parent).ToString());
 
             // サブページ(親)、処理対象外
             link = new MediaWikiLink();
@@ -215,14 +213,14 @@ namespace Honememo.Wptscs.Logics
             Assert.AreEqual("[[../]]", translator.ReplaceLink(link, parent).ToString());
 
             // サブページ(兄弟)
-            parent = new MediaWikiPage(translator.From, "Template:Citation needed/xxx");
+            parent = new MediaWikiPage(translator.From, "Template:Table cell templates/xxx");
             link = new MediaWikiLink();
             link.Title = "../Doc";
-            Assert.AreEqual("[[Template:要出典|../Doc]]", translator.ReplaceLink(link, parent).ToString());
+            Assert.AreEqual("[[Template:Table cell templates|../Doc]]", translator.ReplaceLink(link, parent).ToString());
         }
 
         /// <summary>
-        /// ReplaceLinkメソッドテストケース(カテゴリ)。
+        /// <see cref="MediaWikiTranslator.ReplaceLink"/>メソッドテストケース(カテゴリ)。
         /// </summary>
         [Test]
         public void TestReplaceLinkCategory()
@@ -271,7 +269,7 @@ namespace Honememo.Wptscs.Logics
         }
 
         /// <summary>
-        /// ReplaceLinkメソッドテストケース(ファイル)。
+        /// <see cref="MediaWikiTranslator.ReplaceLink"/>メソッドテストケース(ファイル)。
         /// </summary>
         [Test]
         public void TestReplaceLinkFile()
@@ -309,7 +307,7 @@ namespace Honememo.Wptscs.Logics
         }
 
         /// <summary>
-        /// ReplaceLinkメソッドテストケース(仮リンク)。
+        /// <see cref="MediaWikiTranslator.ReplaceLink"/>メソッドテストケース(仮リンク)。
         /// </summary>
         [Test]
         public void TestReplaceLinkLinkInterwiki()
@@ -367,7 +365,7 @@ namespace Honememo.Wptscs.Logics
         }
 
         /// <summary>
-        /// ReplaceTemplateメソッドテストケース。
+        /// <see cref="MediaWikiTranslator.ReplaceTemplate"/>メソッドテストケース。
         /// </summary>
         [Test]
         public void TestReplaceTemplate()
@@ -413,7 +411,7 @@ namespace Honememo.Wptscs.Logics
         }
 
         /// <summary>
-        /// ReplaceTemplateメソッドテストケース(入れ子)。
+        /// <see cref="MediaWikiTranslator.ReplaceTemplate"/>メソッドテストケース(入れ子)。
         /// </summary>
         [Test]
         public void TestReplaceTemplateNested()
@@ -463,7 +461,7 @@ namespace Honememo.Wptscs.Logics
         }
 
         /// <summary>
-        /// ReplaceHeadingメソッドテストケース。
+        /// <see cref="MediaWikiTranslator.ReplaceHeading"/>メソッドテストケース。
         /// </summary>
         [Test]
         public void TestReplaceHeading()
@@ -505,7 +503,7 @@ namespace Honememo.Wptscs.Logics
         }
 
         /// <summary>
-        /// ReplaceHeadingメソッドテストケース(入れ子)。
+        /// <see cref="MediaWikiTranslator.ReplaceHeading"/>メソッドテストケース(入れ子)。
         /// </summary>
         [Test]
         public void TestReplaceHeadingNested()
@@ -818,9 +816,9 @@ namespace Honememo.Wptscs.Logics
         #region テスト用クラス
 
         /// <summary>
-        /// MediaWikiTranslatorテスト用のクラスです。
+        /// <see cref="MediaWikiTranslator"/>テスト用のクラスです。
         /// </summary>
-        public class TestMediaWikiTranslator : MediaWikiTranslator
+        private class TestMediaWikiTranslator : MediaWikiTranslator
         {
             #region 非公開メソッドテスト用のオーラーライドメソッド
 
index c0c1556..9dc931d 100644 (file)
@@ -17,15 +17,15 @@ namespace Honememo.Wptscs.Logics
     using NUnit.Framework;
 
     /// <summary>
-    /// Translatorのテストクラスです。
+    /// <see cref="Translator"/>のテストクラスです。
     /// </summary>
     [TestFixture]
-    public class TranslatorTest
+    class TranslatorTest
     {
         #region プロパティテストケース
 
         /// <summary>
-        /// ItemTableプロパティテストケース。
+        /// <see cref="Translator.ItemTable"/>プロパティテストケース。
         /// </summary>
         [Test]
         public void TestItemTable()
@@ -39,7 +39,7 @@ namespace Honememo.Wptscs.Logics
         }
 
         /// <summary>
-        /// HeadingTableプロパティテストケース。
+        /// <see cref="Translator.HeadingTable"/>プロパティテストケース。
         /// </summary>
         [Test]
         public void TestHeadingTable()
@@ -53,7 +53,7 @@ namespace Honememo.Wptscs.Logics
         }
 
         /// <summary>
-        /// Logプロパティテストケース。
+        /// <see cref="Translator.Log"/>プロパティテストケース。
         /// </summary>
         [Test]
         public void TestLog()
@@ -64,7 +64,7 @@ namespace Honememo.Wptscs.Logics
 
             // 更新時にLogUpdateイベントが実行されること
             int count = 0;
-            translator.LogUpdate += new EventHandler((object sender, EventArgs e) => { ++count; });
+            translator.LogUpdated += new EventHandler((object sender, EventArgs e) => { ++count; });
             Assert.AreEqual(0, count);
             translator.Logger.AddMessage("ログ");
             Assert.AreEqual(1, count);
@@ -75,7 +75,7 @@ namespace Honememo.Wptscs.Logics
         }
 
         /// <summary>
-        /// Textプロパティテストケース。
+        /// <see cref="Translator.Text"/>プロパティテストケース。
         /// </summary>
         [Test]
         public void TestText()
@@ -92,7 +92,7 @@ namespace Honememo.Wptscs.Logics
         }
 
         /// <summary>
-        /// CancellationPendingプロパティテストケース。
+        /// <see cref="Translator.CancellationPending"/>プロパティテストケース。
         /// </summary>
         [Test]
         public void TestCancellationPending()
@@ -107,7 +107,7 @@ namespace Honememo.Wptscs.Logics
         }
 
         /// <summary>
-        /// Fromプロパティテストケース。
+        /// <see cref="Translator.From"/>プロパティテストケース。
         /// </summary>
         [Test]
         public void TestFrom()
@@ -121,7 +121,7 @@ namespace Honememo.Wptscs.Logics
         }
 
         /// <summary>
-        /// Toプロパティテストケース。
+        /// <see cref="Translator.To"/>プロパティテストケース。
         /// </summary>
         [Test]
         public void TestTo()
@@ -139,7 +139,7 @@ namespace Honememo.Wptscs.Logics
         #region 静的メソッドテストケース
 
         /// <summary>
-        /// Createメソッドテストケース。
+        /// <see cref="Translator.Create"/>メソッドテストケース。
         /// </summary>
         [Test]
         public void TestCreate()
@@ -161,7 +161,7 @@ namespace Honememo.Wptscs.Logics
         }
 
         /// <summary>
-        /// Createメソッドテストケース(未対応のトランスレータクラス)。
+        /// <see cref="Translator.Create"/>メソッドテストケース(未対応のトランスレータクラス)。
         /// </summary>
         [Test]
         [ExpectedException(typeof(NotImplementedException))]
@@ -175,7 +175,7 @@ namespace Honememo.Wptscs.Logics
         }
 
         /// <summary>
-        /// Createメソッドテストケース(トランスレータクラス以外の指定)。
+        /// <see cref="Translator.Create"/>メソッドテストケース(トランスレータクラス以外の指定)。
         /// </summary>
         [Test]
         [ExpectedException(typeof(InvalidCastException))]
@@ -193,7 +193,7 @@ namespace Honememo.Wptscs.Logics
         #region publicメソッドテストケース
 
         /// <summary>
-        /// Runメソッドテストケース。
+        /// <see cref="Translator.Run"/>メソッドテストケース。
         /// </summary>
         [Test]
         public void TestRun()
@@ -233,7 +233,7 @@ namespace Honememo.Wptscs.Logics
         }
 
         /// <summary>
-        /// Runメソッドテストケース(必須パラメータ未設定)。
+        /// <see cref="Translator.Run"/>メソッドテストケース(必須パラメータ未設定)。
         /// </summary>
         [Test]
         [ExpectedException(typeof(InvalidOperationException))]
@@ -244,7 +244,7 @@ namespace Honememo.Wptscs.Logics
         }
 
         /// <summary>
-        /// Runメソッドテストケース(ping成功)。
+        /// <see cref="Translator.Run"/>メソッドテストケース(ping成功)。
         /// </summary>
         [Test]
         public void TestRunPing()
@@ -259,7 +259,7 @@ namespace Honememo.Wptscs.Logics
         }
 
         /// <summary>
-        /// Runメソッドテストケース(ping失敗)。
+        /// <see cref="Translator.Run"/>メソッドテストケース(ping失敗)。
         /// </summary>
         [Test]
         [ExpectedException(typeof(ApplicationException))]
@@ -279,9 +279,9 @@ namespace Honememo.Wptscs.Logics
         #region モッククラス
 
         /// <summary>
-        /// Translatorテスト用のモッククラスです。
+        /// <see cref="Translator"/>テスト用のモッククラスです。
         /// </summary>
-        public class TranslatorMock : Translator
+        private class TranslatorMock : Translator
         {
             #region テスト支援用プロパティ
 
@@ -350,9 +350,9 @@ namespace Honememo.Wptscs.Logics
         }
 
         /// <summary>
-        /// Translatorテスト用のモッククラスです。
+        /// <see cref="Translator"/>テスト用のモッククラスです。
         /// </summary>
-        public class TranslatorIgnoreMock : Translator
+        private class TranslatorIgnoreMock : Translator
         {
             #region コンストラクタ
 
@@ -380,9 +380,9 @@ namespace Honememo.Wptscs.Logics
         }
 
         /// <summary>
-        /// Translatorテスト用のモッククラスです。
+        /// <see cref="Translator"/>テスト用のモッククラスです。
         /// </summary>
-        public class WebsiteMock : Website
+        private class WebsiteMock : Website
         {
             #region ダミーメソッド
 
index a81ad85..c5633b1 100644 (file)
@@ -18,15 +18,15 @@ namespace Honememo.Wptscs.Parsers
     using NUnit.Framework;
 
     /// <summary>
-    /// MediaWikiHeadingParserのテストクラスです。
+    /// <see cref="MediaWikiHeadingParser"/>のテストクラスです。
     /// </summary>
     [TestFixture]
-    public class MediaWikiHeadingParserTest
+    class MediaWikiHeadingParserTest
     {
         #region private変数
 
         /// <summary>
-        /// 前処理・後処理で生成/解放される言語別のMediaWikiParser
+        /// 前処理・後処理で生成/解放される言語別の<see cref="MediaWikiParser"/>
         /// </summary>
         private IDictionary<string, MediaWikiParser> mediaWikiParsers = new Dictionary<string, MediaWikiParser>();
 
@@ -37,21 +37,21 @@ namespace Honememo.Wptscs.Parsers
         /// <summary>
         /// テストの前処理。
         /// </summary>
+        /// <remarks><see cref="MediaWikiParser.Dispose"/>が必要な<see cref="MediaWikiParser"/>の生成。</remarks>
         [TestFixtureSetUp]
         public void SetUpBeforeClass()
         {
-            // Disposeが必要なMediaWikiParserの生成/解放
             this.mediaWikiParsers["en"] = new MediaWikiParser(new MockFactory().GetMediaWiki("en"));
         }
 
         /// <summary>
         /// テストの後処理。
         /// </summary>
+        /// <remarks><see cref="MediaWikiParser.Dispose"/>が必要な<see cref="MediaWikiParser"/>の解放。</remarks>
         [TestFixtureTearDown]
         public void TearDownAfterClass()
         {
-            // Disposeが必要なMediaWikiParserの生成/解放
-            foreach (MediaWikiParser parser in this.mediaWikiParsers.Values)
+            foreach (IDisposable parser in this.mediaWikiParsers.Values)
             {
                 parser.Dispose();
             }
@@ -79,7 +79,7 @@ namespace Honememo.Wptscs.Parsers
         #region インタフェース実装メソッドテストケース
 
         /// <summary>
-        /// TryParseメソッドテストケース。
+        /// <see cref="MediaWikiHeadingParser.TryParse"/>メソッドテストケース。
         /// </summary>
         [Test]
         public void TestTryParse()
@@ -150,10 +150,16 @@ namespace Honememo.Wptscs.Parsers
             Assert.AreEqual("{{lang\n|ja|見出し}}", heading[1].ToString());
             Assert.IsInstanceOf(typeof(MediaWikiTemplate), heading[1]);
             Assert.AreEqual(" ", heading[2].ToString());
+
+            // 空・null
+            Assert.IsFalse(parser.TryParse(String.Empty, out element));
+            Assert.IsNull(element);
+            Assert.IsFalse(parser.TryParse(null, out element));
+            Assert.IsNull(element);
         }
 
         /// <summary>
-        /// TryParseメソッドテストケース(コメント)。
+        /// <see cref="MediaWikiHeadingParser.TryParse"/>メソッドテストケース(コメント)。
         /// </summary>
         [Test]
         public void TestTryParseComment()
index da65a1d..6c553ae 100644 (file)
@@ -18,15 +18,15 @@ namespace Honememo.Wptscs.Parsers
     using NUnit.Framework;
 
     /// <summary>
-    /// MediaWikiLinkParserのテストクラスです。
+    /// <see cref="MediaWikiLinkParser"/>のテストクラスです。
     /// </summary>
     [TestFixture]
-    public class MediaWikiLinkParserTest
+    class MediaWikiLinkParserTest
     {
         #region private変数
 
         /// <summary>
-        /// 前処理・後処理で生成/解放される言語別のMediaWikiParser
+        /// 前処理・後処理で生成/解放される言語別の<see cref="MediaWikiParser"/>
         /// </summary>
         private IDictionary<string, MediaWikiParser> mediaWikiParsers = new Dictionary<string, MediaWikiParser>();
 
@@ -37,10 +37,10 @@ namespace Honememo.Wptscs.Parsers
         /// <summary>
         /// テストの前処理。
         /// </summary>
+        /// <remarks><see cref="MediaWikiParser.Dispose"/>が必要な<see cref="MediaWikiParser"/>の生成。</remarks>
         [TestFixtureSetUp]
         public void SetUpBeforeClass()
         {
-            // Disposeが必要なMediaWikiParserの生成/解放
             this.mediaWikiParsers["en"] = new MediaWikiParser(new MockFactory().GetMediaWiki("en"));
             this.mediaWikiParsers["ja"] = new MediaWikiParser(new MockFactory().GetMediaWiki("ja"));
         }
@@ -48,11 +48,11 @@ namespace Honememo.Wptscs.Parsers
         /// <summary>
         /// テストの後処理。
         /// </summary>
+        /// <remarks><see cref="MediaWikiParser.Dispose"/>が必要な<see cref="MediaWikiParser"/>の解放。</remarks>
         [TestFixtureTearDown]
         public void TearDownAfterClass()
         {
-            // Disposeが必要なMediaWikiParserの生成/解放
-            foreach (MediaWikiParser parser in this.mediaWikiParsers.Values)
+            foreach (IDisposable parser in this.mediaWikiParsers.Values)
             {
                 parser.Dispose();
             }
@@ -65,7 +65,7 @@ namespace Honememo.Wptscs.Parsers
         #region インタフェース実装メソッドテストケース
 
         /// <summary>
-        /// TryParseメソッドテストケース(基本的な構文)。
+        /// <see cref="MediaWikiLinkParser.TryParse"/>メソッドテストケース(基本的な構文)。
         /// </summary>
         [Test]
         public void TestTryParseBasic()
@@ -124,7 +124,7 @@ namespace Honememo.Wptscs.Parsers
         }
 
         /// <summary>
-        /// TryParseメソッドテストケース(NGパターン)。
+        /// <see cref="MediaWikiLinkParser.TryParse"/>メソッドテストケース(NGパターン)。
         /// </summary>
         [Test]
         public void TestTryParseNg()
@@ -156,10 +156,14 @@ namespace Honememo.Wptscs.Parsers
             Assert.IsFalse(parser.TryParse("[[test{title]]", out element));
             Assert.IsFalse(parser.TryParse("[[test}title]]", out element));
             Assert.IsFalse(parser.TryParse("[[testtitle\n]]", out element));
+
+            // 空・null
+            Assert.IsFalse(parser.TryParse(String.Empty, out element));
+            Assert.IsFalse(parser.TryParse(null, out element));
         }
 
         /// <summary>
-        /// TryParseメソッドテストケース(入れ子)。
+        /// <see cref="MediaWikiLinkParser.TryParse"/>メソッドテストケース(入れ子)。
         /// </summary>
         [Test]
         public void TestTryParseNested()
@@ -182,7 +186,7 @@ namespace Honememo.Wptscs.Parsers
         }
 
         /// <summary>
-        /// TryParseメソッドテストケース(名前空間)。
+        /// <see cref="MediaWikiLinkParser.TryParse"/>メソッドテストケース(名前空間)。
         /// </summary>
         [Test]
         public void TestTryParseNamespace()
@@ -233,7 +237,7 @@ namespace Honememo.Wptscs.Parsers
         }
 
         /// <summary>
-        /// TryParseメソッドテストケース(サブページ)。
+        /// <see cref="MediaWikiLinkParser.TryParse"/>メソッドテストケース(サブページ)。
         /// </summary>
         [Test]
         public void TestTryParseSubpage()
@@ -262,7 +266,7 @@ namespace Honememo.Wptscs.Parsers
         }
 
         /// <summary>
-        /// TryParseメソッドテストケース(ウィキ間リンク)。
+        /// <see cref="MediaWikiLinkParser.TryParse"/>メソッドテストケース(ウィキ間リンク)。
         /// </summary>
         [Test]
         public void TestTryParseInterwiki()
index 0fd155b..bd54e3b 100644 (file)
@@ -17,17 +17,17 @@ namespace Honememo.Wptscs.Parsers
     using NUnit.Framework;
 
     /// <summary>
-    /// MediaWikiNowikiParserのテストクラスです。
+    /// <see cref="MediaWikiNowikiParser"/>のテストクラスです。
     /// </summary>
     [TestFixture]
-    public class MediaWikiNowikiParserTest
+    class MediaWikiNowikiParserTest
     {
         #region private変数
 
         /// <summary>
-        /// 前処理・後処理で生成/解放される言語別のMediaWikiParser
+        /// 前処理・後処理で生成/解放される<see cref="XmlParser"/>
         /// </summary>
-        private IDictionary<string, MediaWikiParser> mediaWikiParsers = new Dictionary<string, MediaWikiParser>();
+        private XmlParser xmlParser;
 
         #endregion
 
@@ -36,26 +36,21 @@ namespace Honememo.Wptscs.Parsers
         /// <summary>
         /// テストの前処理。
         /// </summary>
+        /// <remarks><see cref="XmlParser.Dispose"/>が必要な<see cref="XmlParser"/>の生成。</remarks>
         [TestFixtureSetUp]
         public void SetUpBeforeClass()
         {
-            // Disposeが必要なMediaWikiParserの生成/解放
-            this.mediaWikiParsers["en"] = new MediaWikiParser(new MockFactory().GetMediaWiki("en"));
+            this.xmlParser = new XmlParser();
         }
 
         /// <summary>
         /// テストの後処理。
         /// </summary>
+        /// <remarks><see cref="XmlParser.Dispose"/>が必要な<see cref="XmlParser"/>の解放。</remarks>
         [TestFixtureTearDown]
         public void TearDownAfterClass()
         {
-            // Disposeが必要なMediaWikiParserの生成/解放
-            foreach (MediaWikiParser parser in this.mediaWikiParsers.Values)
-            {
-                parser.Dispose();
-            }
-
-            this.mediaWikiParsers.Clear();
+            this.xmlParser.Dispose();
         }
 
         #endregion
@@ -63,56 +58,97 @@ namespace Honememo.Wptscs.Parsers
         #region インスタンス実装メソッドテストケース
 
         /// <summary>
-        /// TryParseメソッドテストケース
+        /// <see cref="MediaWikiNowikiParser.TryParse"/>メソッドテストケース(OKケース)
         /// </summary>
         [Test]
         public void TestTryParse()
         {
             IElement element;
-            MediaWikiNowikiParser parser = new MediaWikiNowikiParser(this.mediaWikiParsers["en"]);
+            XmlElement xml;
+            MediaWikiNowikiParser parser = new MediaWikiNowikiParser(this.xmlParser);
+
+            // 基本動作、nowiki区間は再帰的に処理されない
+            Assert.IsTrue(parser.TryParse("<nowiki>[[test]]</nowiki>", out element));
+            Assert.AreEqual("<nowiki>[[test]]</nowiki>", element.ToString());
+            Assert.IsInstanceOf(typeof(XmlElement), element);
+            xml = (XmlElement)element;
+            Assert.IsInstanceOf(typeof(XmlTextElement), xml[0]);
+            Assert.AreEqual("[[test]]", xml[0].ToString());
+            Assert.AreEqual(1, xml.Count);
+
+            Assert.IsTrue(parser.TryParse("<noWiki>{{!}}<nowiki>nowikiサンプルのつもり</nowiki>{{!}}</nowiki>", out element));
+            Assert.AreEqual("<noWiki>{{!}}<nowiki>nowikiサンプルのつもり</nowiki>", element.ToString());
+            Assert.IsInstanceOf(typeof(XmlElement), element);
+            xml = (XmlElement)element;
+            Assert.IsInstanceOf(typeof(XmlTextElement), xml[0]);
+            Assert.AreEqual("{{!}}<nowiki>nowikiサンプルのつもり", xml[0].ToString());
+            Assert.AreEqual(1, xml.Count);
+
+            Assert.IsTrue(parser.TryParse("<nowiki>{{!}}&lt;nowiki&gt;nowikiサンプル&lt;/nowiki&gt;{{!}}</nowiki>", out element));
+            Assert.AreEqual("<nowiki>{{!}}&lt;nowiki&gt;nowikiサンプル&lt;/nowiki&gt;{{!}}</nowiki>", element.ToString());
+            Assert.IsInstanceOf(typeof(XmlElement), element);
+            xml = (XmlElement)element;
+            Assert.IsInstanceOf(typeof(XmlTextElement), xml[0]);
+            Assert.AreEqual("{{!}}&lt;nowiki&gt;nowikiサンプル&lt;/nowiki&gt;{{!}}", xml[0].ToString());
+            Assert.AreEqual(1, xml.Count);
 
             // nowikiは大文字小文字区別せず動作、閉じタグが無くても機能する
             // (その判断はMediaWikiNowikiParserではなくMediaWikiParserでの設定次第によるものだが)
             // 属性値などが指定されていても機能する
             // nowiki区間ではコメントも機能しない
-            Assert.IsTrue(parser.TryParse("<nowiki>[[test]]</nowiki>", out element));
-            Assert.AreEqual("<nowiki>[[test]]</nowiki>", element.ToString());
             Assert.IsTrue(parser.TryParse("<NOWIKI>[[test]]</NOWIKI>", out element));
             Assert.AreEqual("<NOWIKI>[[test]]</NOWIKI>", element.ToString());
+
             Assert.IsTrue(parser.TryParse("<Nowiki>[[test]]</noWiki>", out element));
             Assert.AreEqual("<Nowiki>[[test]]</noWiki>", element.ToString());
+
             Assert.IsTrue(parser.TryParse("<nowiki>[[test]]</nowiki></nowiki>", out element));
             Assert.AreEqual("<nowiki>[[test]]</nowiki>", element.ToString());
+
             Assert.IsTrue(parser.TryParse("<nowiki>[[test]]nowiki", out element));
             Assert.AreEqual("<nowiki>[[test]]nowiki", element.ToString());
+
             Assert.IsTrue(parser.TryParse("<nowiki>\n\n[[test]]\r\n</nowiki>", out element));
             Assert.AreEqual("<nowiki>\n\n[[test]]\r\n</nowiki>", element.ToString());
+
             Assert.IsTrue(parser.TryParse("<nowiki><!--[[test]]--></nowiki>", out element));
             Assert.AreEqual("<nowiki><!--[[test]]--></nowiki>", element.ToString());
+
             Assert.IsTrue(parser.TryParse("<nowiki><!--<nowiki>[[test]]</nowiki>--></nowiki>", out element));
             Assert.AreEqual("<nowiki><!--<nowiki>[[test]]</nowiki>", element.ToString());
+
             Assert.IsTrue(parser.TryParse("<nowiki><!--[[test]]", out element));
             Assert.AreEqual("<nowiki><!--[[test]]", element.ToString());
+
             Assert.IsTrue(parser.TryParse("<nowiki attr=\"Value\">[[test]]</nowiki>", out element));
             Assert.AreEqual("<nowiki attr=\"Value\">[[test]]</nowiki>", element.ToString());
-            Assert.IsFalse(parser.TryParse("<nowik>[[test]]</nowik>", out element));
-            Assert.IsNull(element);
-            Assert.IsFalse(parser.TryParse("<nowiki[[test]]</nowiki>", out element));
-            Assert.IsNull(element);
         }
 
         /// <summary>
-        /// TryParseメソッドテストケース(nowiki以外のタグ)。
+        /// <see cref="MediaWikiNowikiParser.TryParse"/>メソッドテストケース(NGケース)。
         /// </summary>
-        /// <remarks>nowiki以外の場合に変な動きをしているようなバグがあったので。</remarks>
+        /// <remarks>nowiki以外のタグの場合に変な動きをしているようなバグがあったためその確認も行う。</remarks>
         [Test]
         public void TestTryParseNg()
         {
             IElement element;
-            MediaWikiNowikiParser parser = new MediaWikiNowikiParser(this.mediaWikiParsers["en"]);
+            MediaWikiNowikiParser parser = new MediaWikiNowikiParser(this.xmlParser);
+
+            Assert.IsFalse(parser.TryParse("<nowik>[[test]]</nowik>", out element));
+            Assert.IsNull(element);
+
+            Assert.IsFalse(parser.TryParse("<nowiki[[test]]</nowiki>", out element));
+            Assert.IsNull(element);
+
+            Assert.IsFalse(parser.TryParse(String.Empty, out element));
+            Assert.IsNull(element);
+
+            Assert.IsFalse(parser.TryParse(null, out element));
+            Assert.IsNull(element);
 
             Assert.IsFalse(parser.TryParse("<ref name=\"oscars.org\">{{cite web |url=http://www.oscars.org/awards/academyawards/legacy/ceremony/59th-winners.html |title=The 59th Academy Awards (1987) Nominees and Winners |accessdate=2011-07-23|work=oscars.org}}</ref> | owner = [[Discovery Communications|Discovery Communications, Inc.]] | CEO = David Zaslav | headquarters = [[Silver Spring, Maryland]] | country = Worldwide | language = English | sister names = [[TLC (TV channel)|TLC]]<br>[[Animal Planet]]<br>[[OWN: Oprah Winfrey Network]]<br>[[Planet Green]]<br>", out element));
             Assert.IsNull(element);
+
             Assert.IsFalse(parser.TryParse("<br>[[Animal Planet]]<br>[[OWN: Oprah Winfrey Network]]<br>[[Planet Green]]", out element));
             Assert.IsNull(element);
         }
index 620f3c5..ef1f453 100644 (file)
@@ -17,30 +17,67 @@ namespace Honememo.Wptscs.Parsers
     using NUnit.Framework;
 
     /// <summary>
-    /// MediaWikiParserのテストクラスです。
+    /// <see cref="MediaWikiParser"/>のテストクラスです。
     /// </summary>
     [TestFixture]
-    public class MediaWikiParserTest
+    class MediaWikiParserTest
     {
-        //// TODO: いっぱい足りない
+        #region 公開プロパティテストケース
 
-        #region インスタンス実装メソッドテストケース
+        /// <summary>
+        /// <see cref="MediaWikiParser.Website"/>プロパティテストケース。
+        /// </summary>
+        [Test]
+        public void TestWebsite()
+        {
+            MediaWiki site = new MediaWiki(new Language("en"));
+            using (MediaWikiParser parser = new MediaWikiParser(site))
+            {
+                // コンストラクタで指定したオブジェクトが格納されていること
+                Assert.AreSame(site, parser.Website);
+
+                // 設定すればそのオブジェクトが入ること
+                site = new MediaWiki(new Language("ja"));
+                parser.Website = site;
+                Assert.AreSame(site, parser.Website);
+            }
+        }
 
         /// <summary>
-        /// TryParseメソッドテストケース
+        /// <see cref="MediaWikiParser.Website"/>プロパティテストケース(null)
         /// </summary>
         [Test]
-        public void TestTryParse()
+        [ExpectedException(typeof(ArgumentNullException))]
+        public void TestWebsiteNull()
         {
+            using (MediaWikiParser parser = new MediaWikiParser(new MockFactory().GetMediaWiki("en")))
+            {
+                parser.Website = null;
+            }
+        }
+
+        #endregion
+
+        #region ITextParserインタフェース実装メソッドテストケース
+
+        /// <summary>
+        /// <see cref="MediaWikiParser.TryParseToEndCondition"/>
+        /// メソッドテストケース(実際のデータを想定)。
+        /// </summary>
+        [Test]
+        public void TestTryParseToEndCondition()
+        {
+            string text = "'''Article Name''' is [[xxx]]\r\n==test head==\r\n<p>test</p><nowiki>[[test]]</nowiki><!--comment-->{{reflist}}";
             IElement element;
-            ListElement list;
             using (MediaWikiParser parser = new MediaWikiParser(new MockFactory().GetMediaWiki("en")))
             {
-                Assert.IsTrue(parser.TryParse("'''Article Name''' is [[xxx]]\r\n==test head==\r\n<p>test</p><nowiki>[[test]]</nowiki><!--comment-->{{reflist}}", out element));
+                // 実際に想定されるようなデータ
+                Assert.IsTrue(parser.TryParseToEndCondition(text, null, out element));
             }
 
+            Assert.AreEqual(text, element.ToString());
             Assert.IsInstanceOf(typeof(ListElement), element);
-            list = (ListElement)element;
+            ListElement list = (ListElement)element;
             Assert.AreEqual(8, list.Count);
             Assert.AreEqual("'''Article Name''' is ", list[0].ToString());
             Assert.AreEqual("[[xxx]]", list[1].ToString());
@@ -53,25 +90,82 @@ namespace Honememo.Wptscs.Parsers
         }
 
         /// <summary>
-        /// TryParseメソッドテストケース(テンプレートページ実データ使用)。
+        /// <see cref="MediaWikiParser.TryParseToEndCondition"/>
+        /// メソッドテストケース(その他のケース)。
+        /// </summary>
+        [Test]
+        public void TestTryParseToEndConditionEmpty()
+        {
+            IElement element;
+            using (MediaWikiParser parser = new MediaWikiParser(new MockFactory().GetMediaWiki("en")))
+            {
+                // 空文字列、一応解析成功となる
+                Assert.IsTrue(parser.TryParseToEndCondition(String.Empty, null, out element));
+                Assert.AreEqual(String.Empty, element.ToString());
+                Assert.IsInstanceOf(typeof(TextElement), element);
+
+                // nullは解析失敗
+                Assert.IsFalse(parser.TryParseToEndCondition(null, null, out element));
+                Assert.IsNull(element);
+            }
+        }
+
+        /// <summary>
+        /// <see cref="MediaWikiParser.TryParseToEndCondition"/>
+        /// メソッドテストケース(終了条件)。
+        /// </summary>
+        [Test]
+        public void TestTryParseToEndConditionCondition()
+        {
+            // 親クラスにあった終了条件で停止する動作が継承先でも動作していること
+            string text = "'''Article Name''' is [[xxx]]\r\n==test head==\r\n<p>test</p><nowiki>[[test]]</nowiki><!--comment-->{{reflist}}";
+            IElement element;
+            using (MediaWikiParser parser = new MediaWikiParser(new MockFactory().GetMediaWiki("en")))
+            {
+                Assert.IsTrue(parser.TryParseToEndCondition(text, (string s, int index) => s[index] == '/', out element));
+            }
+
+            Assert.AreEqual("'''Article Name''' is [[xxx]]\r\n==test head==\r\n<p>test<", element.ToString());
+            Assert.IsInstanceOf(typeof(ListElement), element);
+            ListElement list = (ListElement)element;
+            Assert.AreEqual(5, list.Count);
+        }
+
+        /// <summary>
+        /// <see cref="MediaWikiParser.TryParseToEndCondition"/>
+        /// メソッドテストケース(テンプレートページ実データ使用)。
         /// </summary>
         /// <remarks>
-        /// Ver 1.11にて無限ループの不具合が発生していたデータ。
-        /// ä¸­èº«ã\81«ã\81¤ã\81\84ã\81¦ã\81¯ã\81»ã\81¼å\87¦ç\90\86ã\81§ã\81\8dã\81ªã\81\84é¡\9eã\81®ã\82\82ã\81®ã\81 ã\81\8cã\80\81ç\84¡é\99\90ã\83«ã\83¼ã\83\97ã\81«ã\81ªã\82\89ã\81ªã\81\84ことだけ検証。
+        /// Ver 1.11にて解析失敗時のリトライにより極端に時間がかかっていたデータ。
+        /// ä¸­èº«ã\81«ã\81¤ã\81\84ã\81¦ã\81¯ã\81»ã\81¼å\87¦ç\90\86ã\81§ã\81\8dã\81ªã\81\84é¡\9eã\81®ã\82\82ã\81®ã\81 ã\81\8cã\80\81ç\8f¾å®\9fç\9a\84ã\81ªæ\99\82é\96\93ã\81§è§£æ\9e\90ã\81\8cçµ\82ã\82\8fã\82\8bことだけ検証。
         /// </remarks>
         [Test, Timeout(20000)]
-        public void TestTryParseTemplateContext()
+        public void TestTryParseToEndConditionTemplateContext()
         {
             IElement element;
             using (MediaWikiParser parser = new MediaWikiParser(new MockFactory().GetMediaWiki("en")))
             {
                 string text = parser.Website.GetPage("Template:context").Text;
-                Assert.IsTrue(parser.TryParse(text, out element));
+                Assert.IsTrue(parser.TryParseToEndCondition(text, null, out element));
                 Assert.IsInstanceOf(typeof(ListElement), element);
                 Assert.AreEqual(text, element.ToString());
             }
         }
 
+        /// <summary>
+        /// <see cref="MediaWikiParser.TryParseToEndCondition"/>
+        /// メソッドテストケース(Dispose)。
+        /// </summary>
+        [Test]
+        [ExpectedException(typeof(ObjectDisposedException))]
+        public void TestTryParseToEndConditionDispose()
+        {
+            MediaWikiParser parser = new MediaWikiParser(new MockFactory().GetMediaWiki("en"));
+            parser.Dispose();
+            IElement result;
+            parser.TryParseToEndCondition(String.Empty, null, out result);
+        }
+
         #endregion
     }
 }
diff --git a/WptscsTest/Parsers/MediaWikiPreparserTest.cs b/WptscsTest/Parsers/MediaWikiPreparserTest.cs
new file mode 100644 (file)
index 0000000..a04f064
--- /dev/null
@@ -0,0 +1,269 @@
+// ================================================================================================
+// <summary>
+//      MediaWikiPreparserのテストクラスソース。</summary>
+//
+// <copyright file="MediaWikiPreparserTest.cs" company="honeplusのメモ帳">
+//      Copyright (C) 2012 Honeplus. All rights reserved.</copyright>
+// <author>
+//      Honeplus</author>
+// ================================================================================================
+
+namespace Honememo.Wptscs.Parsers
+{
+    using System;
+    using Honememo.Parsers;
+    using Honememo.Wptscs.Models;
+    using Honememo.Wptscs.Websites;
+    using NUnit.Framework;
+
+    /// <summary>
+    /// <see cref="MediaWikiPreparser"/>のテストクラスです。
+    /// </summary>
+    [TestFixture]
+    class MediaWikiPreparserTest
+    {
+        #region 定数
+
+        /// <summary>
+        /// 複数のテストケースで使用するテストテキスト。
+        /// </summary>
+        /// <remarks>タグの大文字小文字は区別されないはずのため、意図的に混入させている。</remarks>
+        private static readonly string TestData = "This template is [[xxx]]<br />\r\n"
+            + "<noWiki><nowiki>sample</nowiki></nowiki>\r\n"
+            + "<inclUdeonly><p>include text<nowiki><includeonly>sample</includeonly></nowiki></p></includeonly>\r\n"
+            + "<noInclude>[[ja:Template:sample/doc]]<!--noinclude only--></noinclude>\r\n"
+            + "<!-- <includeonly>include only comment</includeonly> -->";
+
+        #endregion
+
+        #region IParserインタフェーステストケース
+
+        // ※ 2012年2月現在、IParser, ITextParserの各メソッド実装は親クラス側で行われており、
+        //    改造部分はどこかでやればテストされるのでそれで割愛
+
+        /// <summary>
+        /// <see cref="IParser.Parse"/>メソッドテストトケース。
+        /// </summary>
+        [Test]
+        public void TestParse()
+        {
+            IElement element;
+            XmlElement xml;
+            using (MediaWikiPreparser parser = new MediaWikiPreparser())
+            {
+                element = parser.Parse(TestData);
+            }
+
+            // 解析だけであればincludeonly等の処理は行われない、元の文字列が保持される
+            Assert.AreEqual(TestData, element.ToString());
+
+            // includeonly, noinclude, nowiki, コメントのみ特別な要素として認識する
+            Assert.IsInstanceOf(typeof(ListElement), element);
+            ListElement list = (ListElement)element;
+            Assert.AreEqual("This template is [[xxx]]<br />\r\n", list[0].ToString());
+            Assert.AreEqual("<noWiki><nowiki>sample</nowiki>", list[1].ToString());
+            Assert.AreEqual("</nowiki>\r\n", list[2].ToString());
+            Assert.AreEqual("<inclUdeonly><p>include text<nowiki><includeonly>sample</includeonly></nowiki></p></includeonly>", list[3].ToString());
+            Assert.AreEqual("\r\n", list[4].ToString());
+            Assert.AreEqual("<noInclude>[[ja:Template:sample/doc]]<!--noinclude only--></noinclude>", list[5].ToString());
+            Assert.AreEqual("\r\n", list[6].ToString());
+            Assert.AreEqual("<!-- <includeonly>include only comment</includeonly> -->", list[7].ToString());
+            Assert.AreEqual(8, list.Count);
+
+            // 各要素の確認
+            Assert.IsInstanceOf(typeof(TextElement), list[0]);
+
+            // nowikiとコメントは再帰的に解析されない
+            Assert.IsInstanceOf(typeof(XmlElement), list[1]);
+            xml = (XmlElement)list[1];
+            Assert.AreEqual("<nowiki>sample", xml[0].ToString());
+            Assert.IsInstanceOf(typeof(XmlTextElement), xml[0]);
+            Assert.IsInstanceOf(typeof(XmlCommentElement), list[7]);
+
+            // includeonly, noincludeは再帰的に処理
+            Assert.IsInstanceOf(typeof(XmlElement), list[3]);
+            xml = (XmlElement)list[3];
+            Assert.AreEqual("<p>include text", xml[0].ToString());
+            Assert.AreEqual("<nowiki><includeonly>sample</includeonly></nowiki>", xml[1].ToString());
+            Assert.IsInstanceOf(typeof(XmlElement), xml[1]);
+            Assert.AreEqual("</p>", xml[2].ToString());
+            Assert.AreEqual(3, xml.Count);
+
+            Assert.IsInstanceOf(typeof(XmlElement), list[5]);
+            xml = (XmlElement)list[5];
+            Assert.AreEqual("[[ja:Template:sample/doc]]", xml[0].ToString());
+            Assert.IsInstanceOf(typeof(TextElement), xml[0]);
+            Assert.AreEqual("<!--noinclude only-->", xml[1].ToString());
+            Assert.IsInstanceOf(typeof(XmlCommentElement), xml[1]);
+            Assert.AreEqual(2, xml.Count);
+        }
+
+        #endregion
+
+        #region 公開メソッドテストケース
+
+        /// <summary>
+        /// <see cref="MediaWikiPreparser.FilterByInclude"/>メソッドテストケース。
+        /// </summary>
+        /// <remarks>
+        /// <see cref="TestParse"/>と同じデータを使うため、そちらのテストが通っていることを前提とする。
+        /// </remarks>
+        [Test]
+        public void TestFilterByInclude()
+        {
+            IElement element;
+            ListElement list;
+            using (MediaWikiPreparser parser = new MediaWikiPreparser())
+            {
+                element = parser.Parse(TestData);
+                parser.FilterByInclude(ref element);
+            }
+
+            // includeonlyが展開され、noinclude, コメントが削除される
+            Assert.IsInstanceOf(typeof(ListElement), element);
+            list = (ListElement)element;
+            Assert.AreEqual("This template is [[xxx]]<br />\r\n", list[0].ToString());
+            Assert.AreEqual("<noWiki><nowiki>sample</nowiki>", list[1].ToString());
+            Assert.AreEqual("</nowiki>\r\n", list[2].ToString());
+            Assert.AreEqual("<p>include text<nowiki><includeonly>sample</includeonly></nowiki></p>", list[3].ToString());
+            Assert.AreEqual("\r\n", list[4].ToString());
+            Assert.AreEqual("\r\n", list[5].ToString());
+            Assert.AreEqual(6, list.Count);
+
+            // 各要素の確認
+            Assert.IsInstanceOf(typeof(TextElement), list[0]);
+            Assert.IsInstanceOf(typeof(XmlElement), list[1]);
+
+            // includeonlyはListElementに置き換わる
+            Assert.IsInstanceOf(typeof(ListElement), list[3]);
+            list = (ListElement)list[3];
+            Assert.AreEqual("<p>include text", list[0].ToString());
+            Assert.AreEqual("<nowiki><includeonly>sample</includeonly></nowiki>", list[1].ToString());
+            Assert.IsInstanceOf(typeof(XmlElement), list[1]);
+            Assert.AreEqual("</p>", list[2].ToString());
+            Assert.AreEqual(3, list.Count);
+        }
+
+        /// <summary>
+        /// <see cref="MediaWikiPreparser.FilterByInclude"/>メソッドテストケース(null)。
+        /// </summary>
+        [Test]
+        [ExpectedException(typeof(ArgumentNullException))]
+        public void TestFilterByIncludeNull()
+        {
+            IElement element = null;
+            using (MediaWikiPreparser parser = new MediaWikiPreparser())
+            {
+                parser.FilterByInclude(ref element);
+            }
+        }
+
+        /// <summary>
+        /// <see cref="MediaWikiPreparser.FilterByNoinclude"/>メソッドテストケース。
+        /// </summary>
+        /// <remarks>
+        /// <see cref="TestParse"/>と同じデータを使うため、そちらのテストが通っていることを前提とする。
+        /// </remarks>
+        [Test]
+        public void TestFilterByNoinclude()
+        {
+            IElement element;
+            ListElement list;
+            using (MediaWikiPreparser parser = new MediaWikiPreparser())
+            {
+                element = parser.Parse(TestData);
+                parser.FilterByNoinclude(ref element);
+            }
+
+            // noincludeが展開され、includeonly, コメントが削除される
+            Assert.IsInstanceOf(typeof(ListElement), element);
+            list = (ListElement)element;
+            Assert.AreEqual("This template is [[xxx]]<br />\r\n", list[0].ToString());
+            Assert.AreEqual("<noWiki><nowiki>sample</nowiki>", list[1].ToString());
+            Assert.AreEqual("</nowiki>\r\n", list[2].ToString());
+            Assert.AreEqual("\r\n", list[3].ToString());
+            Assert.AreEqual("[[ja:Template:sample/doc]]", list[4].ToString());
+            Assert.AreEqual("\r\n", list[5].ToString());
+            Assert.AreEqual(6, list.Count);
+
+            // 各要素の確認
+            Assert.IsInstanceOf(typeof(TextElement), list[0]);
+            Assert.IsInstanceOf(typeof(XmlElement), list[1]);
+
+            // noincludeはListElementに置き換わる
+            Assert.IsInstanceOf(typeof(ListElement), list[4]);
+            list = (ListElement)list[4];
+            Assert.AreEqual("[[ja:Template:sample/doc]]", list[0].ToString());
+            Assert.IsInstanceOf(typeof(TextElement), list[0]);
+            Assert.AreEqual(1, list.Count);
+        }
+
+        /// <summary>
+        /// <see cref="MediaWikiPreparser.FilterByNoinclude"/>メソッドテストケース(null)。
+        /// </summary>
+        [Test]
+        [ExpectedException(typeof(ArgumentNullException))]
+        public void TestFilterByNoincludeNull()
+        {
+            IElement element = null;
+            using (MediaWikiPreparser parser = new MediaWikiPreparser())
+            {
+                parser.FilterByNoinclude(ref element);
+            }
+        }
+
+        #endregion
+
+        #region 静的メソッドテストケース
+
+        /// <summary>
+        /// <see cref="MediaWikiPreparser.PreprocessByInclude"/>メソッドテストケース。
+        /// </summary>
+        [Test]
+        public void TestPreprocessByInclude()
+        {
+            // Parse→FilterByIncludeした結果をToStringしたものが返る
+            Assert.AreEqual(
+                "This template is [[xxx]]<br />\r\n<noWiki><nowiki>sample</nowiki></nowiki>\r\n"
+                + "<p>include text<nowiki><includeonly>sample</includeonly></nowiki></p>\r\n\r\n",
+                MediaWikiPreparser.PreprocessByInclude(TestData));
+            Assert.AreEqual(String.Empty, MediaWikiPreparser.PreprocessByInclude(String.Empty));
+        }
+
+        /// <summary>
+        /// <see cref="MediaWikiPreparser.PreprocessByInclude"/>メソッドテストケース(null)。
+        /// </summary>
+        [Test]
+        [ExpectedException(typeof(ArgumentNullException))]
+        public void TestPreprocessByIncludeNull()
+        {
+            MediaWikiPreparser.PreprocessByInclude(null);
+        }
+
+        /// <summary>
+        /// <see cref="MediaWikiPreparser.PreprocessByNoinclude"/>メソッドテストケース。
+        /// </summary>
+        [Test]
+        public void TestPreprocessByNoinclude()
+        {
+            // Parse→FilterByNoincludeした結果をToStringしたものが返る
+            Assert.AreEqual(
+                "This template is [[xxx]]<br />\r\n<noWiki><nowiki>sample</nowiki></nowiki>\r\n\r\n"
+                + "[[ja:Template:sample/doc]]\r\n",
+                MediaWikiPreparser.PreprocessByNoinclude(TestData));
+            Assert.AreEqual(String.Empty, MediaWikiPreparser.PreprocessByNoinclude(String.Empty));
+        }
+
+        /// <summary>
+        /// <see cref="MediaWikiPreparser.PreprocessByNoinclude"/>メソッドテストケース(null)。
+        /// </summary>
+        [Test]
+        [ExpectedException(typeof(ArgumentNullException))]
+        public void TestPreprocessByNoincludeNull()
+        {
+            MediaWikiPreparser.PreprocessByNoinclude(null);
+        }
+
+        #endregion
+    }
+}
index c535cd6..031b23c 100644 (file)
@@ -17,15 +17,15 @@ namespace Honememo.Wptscs.Parsers
     using NUnit.Framework;
 
     /// <summary>
-    /// MediaWikiRedirectParserのテストクラスです。
+    /// <see cref="MediaWikiRedirectParser"/>のテストクラスです。
     /// </summary>
     [TestFixture]
-    public class MediaWikiRedirectParserTest
+    class MediaWikiRedirectParserTest
     {
         #region インスタンス実装メソッドテストケース
 
         /// <summary>
-        /// TryParseメソッドテストケース。
+        /// <see cref="MediaWikiRedirectParser.TryParse"/>メソッドテストケース。
         /// </summary>
         [Test]
         public void TestTryParse()
@@ -55,6 +55,12 @@ namespace Honememo.Wptscs.Parsers
                 // enで日本語の転送書式
                 Assert.IsFalse(parser.TryParse("#転送 [[Test]]", out element));
                 Assert.IsNull(element);
+
+                // 空文字列・null
+                Assert.IsFalse(parser.TryParse(String.Empty, out element));
+                Assert.IsNull(element);
+                Assert.IsFalse(parser.TryParse(null, out element));
+                Assert.IsNull(element);
             }
 
             using (MediaWikiRedirectParser parser = new MediaWikiRedirectParser(new MockFactory().GetMediaWiki("ja")))
index 2bb5083..0e19047 100644 (file)
@@ -18,15 +18,15 @@ namespace Honememo.Wptscs.Parsers
     using NUnit.Framework;
 
     /// <summary>
-    /// MediaWikiTemplateParserのテストクラスです。
+    /// <see cref="MediaWikiTemplateParser"/>のテストクラスです。
     /// </summary>
     [TestFixture]
-    public class MediaWikiTemplateParserTest
+    class MediaWikiTemplateParserTest
     {
         #region private変数
 
         /// <summary>
-        /// 前処理・後処理で生成/解放される言語別のMediaWikiParser
+        /// 前処理・後処理で生成/解放される言語別の<see cref="MediaWikiParser"/>
         /// </summary>
         private IDictionary<string, MediaWikiParser> mediaWikiParsers = new Dictionary<string, MediaWikiParser>();
 
@@ -37,10 +37,10 @@ namespace Honememo.Wptscs.Parsers
         /// <summary>
         /// テストの前処理。
         /// </summary>
+        /// <remarks><see cref="MediaWikiParser.Dispose"/>が必要な<see cref="MediaWikiParser"/>の生成。</remarks>
         [TestFixtureSetUp]
         public void SetUpBeforeClass()
         {
-            // Disposeが必要なMediaWikiParserの生成/解放
             this.mediaWikiParsers["en"] = new MediaWikiParser(new MockFactory().GetMediaWiki("en"));
             this.mediaWikiParsers["ja"] = new MediaWikiParser(new MockFactory().GetMediaWiki("ja"));
         }
@@ -48,11 +48,11 @@ namespace Honememo.Wptscs.Parsers
         /// <summary>
         /// テストの後処理。
         /// </summary>
+        /// <remarks><see cref="MediaWikiParser.Dispose"/>が必要な<see cref="MediaWikiParser"/>の解放。</remarks>
         [TestFixtureTearDown]
         public void TearDownAfterClass()
         {
-            // Disposeが必要なMediaWikiParserの生成/解放
-            foreach (MediaWikiParser parser in this.mediaWikiParsers.Values)
+            foreach (IDisposable parser in this.mediaWikiParsers.Values)
             {
                 parser.Dispose();
             }
@@ -65,7 +65,7 @@ namespace Honememo.Wptscs.Parsers
         #region インタフェース実装メソッドテストケース
 
         /// <summary>
-        /// TryParseメソッドテストケース(基本的な構文)。
+        /// <see cref="MediaWikiTemplateParser.TryParse"/>メソッドテストケース(基本的な構文)。
         /// </summary>
         [Test]
         public void TestTryParseBasic()
@@ -141,7 +141,7 @@ namespace Honememo.Wptscs.Parsers
         }
 
         /// <summary>
-        /// TryParseメソッドテストケース(NGパターン)。
+        /// <see cref="MediaWikiTemplateParser.TryParse"/>メソッドテストケース(NGパターン)。
         /// </summary>
         [Test]
         public void TestTryParseNg()
@@ -175,10 +175,14 @@ namespace Honememo.Wptscs.Parsers
             Assert.IsFalse(parser.TryParse("{{test]title}}", out element));
             Assert.IsFalse(parser.TryParse("{{test{title}}", out element));
             Assert.IsFalse(parser.TryParse("{{test}title}}", out element));
+
+            // 空・null
+            Assert.IsFalse(parser.TryParse(String.Empty, out element));
+            Assert.IsFalse(parser.TryParse(null, out element));
         }
 
         /// <summary>
-        /// TryParseメソッドテストケース(入れ子)。
+        /// <see cref="MediaWikiTemplateParser.TryParse"/>メソッドテストケース(入れ子)。
         /// </summary>
         [Test]
         public void TestTryParseNested()
@@ -207,7 +211,7 @@ namespace Honememo.Wptscs.Parsers
         }
 
         /// <summary>
-        /// TryParseメソッドテストケース(サブページ)。
+        /// <see cref="MediaWikiTemplateParser.TryParse"/>メソッドテストケース(サブページ)。
         /// </summary>
         [Test]
         public void TestTryParseSubpage()
@@ -236,7 +240,7 @@ namespace Honememo.Wptscs.Parsers
         }
 
         /// <summary>
-        /// TryParseメソッドテストケース(実データ複雑なinfobox)。
+        /// <see cref="MediaWikiTemplateParser.TryParse"/>メソッドテストケース(実データ複雑なinfobox)。
         /// </summary>
         /// <remarks>使用データは[[:en:Discovery Channel]](2012年1月17日 14:07:11(UTC))より抜粋。</remarks>
         [Test]
@@ -303,7 +307,7 @@ namespace Honememo.Wptscs.Parsers
         }
 
         /// <summary>
-        /// TryParseメソッドテストケース(実データ複雑なテンプレートの一部)。
+        /// <see cref="MediaWikiTemplateParser.TryParse"/>メソッドテストケース(実データ複雑なテンプレートの一部)。
         /// </summary>
         /// <remarks>
         /// 使用データはWiktionaryの[[:en:Template:context]](2011-08-29T20:15:35Z)より抜粋。
index 92e07ef..ab07751 100644 (file)
@@ -18,15 +18,15 @@ namespace Honememo.Wptscs.Parsers
     using NUnit.Framework;
 
     /// <summary>
-    /// MediaWikiVariableParserのテストクラスです。
+    /// <see cref="MediaWikiVariableParser"/>のテストクラスです。
     /// </summary>
     [TestFixture]
-    public class MediaWikiVariableParserTest
+    class MediaWikiVariableParserTest
     {
         #region private変数
 
         /// <summary>
-        /// 前処理・後処理で生成/解放される言語別のMediaWikiParser
+        /// 前処理・後処理で生成/解放される言語別の<see cref="MediaWikiParser"/>
         /// </summary>
         private IDictionary<string, MediaWikiParser> mediaWikiParsers = new Dictionary<string, MediaWikiParser>();
 
@@ -37,10 +37,10 @@ namespace Honememo.Wptscs.Parsers
         /// <summary>
         /// テストの前処理。
         /// </summary>
+        /// <remarks><see cref="MediaWikiParser.Dispose"/>が必要な<see cref="MediaWikiParser"/>の生成。</remarks>
         [TestFixtureSetUp]
         public void SetUpBeforeClass()
         {
-            // Disposeが必要なMediaWikiParserの生成/解放
             this.mediaWikiParsers["en"] = new MediaWikiParser(new MockFactory().GetMediaWiki("en"));
             this.mediaWikiParsers["ja"] = new MediaWikiParser(new MockFactory().GetMediaWiki("ja"));
         }
@@ -48,11 +48,11 @@ namespace Honememo.Wptscs.Parsers
         /// <summary>
         /// テストの後処理。
         /// </summary>
+        /// <remarks><see cref="MediaWikiParser.Dispose"/>が必要な<see cref="MediaWikiParser"/>の解放。</remarks>
         [TestFixtureTearDown]
         public void TearDownAfterClass()
         {
-            // Disposeが必要なMediaWikiParserの生成/解放
-            foreach (MediaWikiParser parser in this.mediaWikiParsers.Values)
+            foreach (IDisposable parser in this.mediaWikiParsers.Values)
             {
                 parser.Dispose();
             }
@@ -65,7 +65,7 @@ namespace Honememo.Wptscs.Parsers
         #region インタフェース実装メソッドテストケース
 
         /// <summary>
-        /// TryParseメソッドテストケース(基本的な構文)。
+        /// <see cref="MediaWikiVariableParser.TryParse"/>メソッドテストケース(基本的な構文)。
         /// </summary>
         [Test]
         public void TestTryParseBasic()
@@ -101,7 +101,7 @@ namespace Honememo.Wptscs.Parsers
         }
 
         /// <summary>
-        /// TryParseメソッドテストケース(NGパターン)。
+        /// <see cref="MediaWikiVariableParser.TryParse"/>メソッドテストケース(NGパターン)。
         /// </summary>
         [Test]
         public void TestTryParseNg()
@@ -126,10 +126,14 @@ namespace Honememo.Wptscs.Parsers
 
             // テンプレートリンクタグ
             Assert.IsFalse(parser.TryParse("{{変数名}}", out element));
+
+            // 空・null
+            Assert.IsFalse(parser.TryParse(String.Empty, out element));
+            Assert.IsFalse(parser.TryParse(null, out element));
         }
 
         /// <summary>
-        /// TryParseメソッドテストケース(入れ子)。
+        /// <see cref="MediaWikiVariableParser.TryParse"/>メソッドテストケース(入れ子)。
         /// </summary>
         [Test]
         public void TestTryParseNested()
index 7092b0d..6535861 100644 (file)
@@ -13,17 +13,16 @@ namespace Honememo.Wptscs.Websites
     using System;
     using System.Collections.Generic;
     using Honememo.Parsers;
-    using Honememo.Tests;
     using Honememo.Utilities;
     using Honememo.Wptscs.Models;
     using Honememo.Wptscs.Parsers;
     using NUnit.Framework;
 
     /// <summary>
-    /// MediaWikiPageのテストクラスです。
+    /// <see cref="MediaWikiPage"/>のテストクラスです。
     /// </summary>
     [TestFixture]
-    public class MediaWikiPageTest
+    class MediaWikiPageTest
     {
         #region コンストラクタテストケース
 
@@ -34,7 +33,7 @@ namespace Honememo.Wptscs.Websites
         public void TestConstructorWebsiteTitleTextTimestamp()
         {
             DateTime t = DateTime.Now;
-            MediaWiki s = new DummySite(new Language("en"));
+            MediaWiki s = new MediaWiki(new Language("en"));
             MediaWikiPage page = new MediaWikiPage(s, "TestTitle", "TestText", t);
             Assert.AreSame(s, page.Website);
             Assert.AreEqual("TestTitle", page.Title);
@@ -48,7 +47,7 @@ namespace Honememo.Wptscs.Websites
         [Test]
         public void TestConstructorWebsiteTitleText()
         {
-            MediaWiki s = new DummySite(new Language("en"));
+            MediaWiki s = new MediaWiki(new Language("en"));
             MediaWikiPage page = new MediaWikiPage(s, "TestTitle", "TestText");
             Assert.AreEqual(s, page.Website);
             Assert.AreEqual("TestTitle", page.Title);
@@ -62,7 +61,7 @@ namespace Honememo.Wptscs.Websites
         [Test]
         public void TestConstructorWebsiteTitle()
         {
-            MediaWiki s = new DummySite(new Language("en"));
+            MediaWiki s = new MediaWiki(new Language("en"));
             MediaWikiPage page = new MediaWikiPage(s, "TestTitle");
             Assert.AreEqual(s, page.Website);
             Assert.AreEqual("TestTitle", page.Title);
@@ -87,7 +86,7 @@ namespace Honememo.Wptscs.Websites
         [ExpectedException(typeof(ArgumentException))]
         public void TestConstructorTitleBlank()
         {
-            new MediaWikiPage(new DummySite(new Language("en")), "  ");
+            new MediaWikiPage(new MediaWiki(new Language("en")), "  ");
         }
 
         #endregion
@@ -95,7 +94,7 @@ namespace Honememo.Wptscs.Websites
         #region プロパティテストケース
 
         /// <summary>
-        /// Redirectプロパティテストケース(正常系)。
+        /// <see cref="MediaWikiPage.Redirect"/>プロパティテストケース(正常系)。
         /// </summary>
         [Test]
         public void TestRedirect()
@@ -117,7 +116,7 @@ namespace Honememo.Wptscs.Websites
         }
 
         /// <summary>
-        /// Redirectプロパティテストケース(Text未設定)。
+        /// <see cref="MediaWikiPage.Redirect"/>プロパティテストケース(Text未設定)。
         /// </summary>
         [Test]
         [ExpectedException(typeof(InvalidOperationException))]
@@ -126,47 +125,19 @@ namespace Honememo.Wptscs.Websites
             MediaWikiLink dummy = new MediaWikiPage(new MockFactory().GetMediaWiki("en"), "TestTitle").Redirect;
         }
 
-        /// <summary>
-        /// Elementプロパティテストケース(正常系)。
-        /// </summary>
-        [Test]
-        public void TestElement()
-        {
-            IElement element = new MediaWikiPage(new MockFactory().GetMediaWiki("en"), "TestTitle", "'''Title''' is [[xxx]].").Element;
-            Assert.IsNotNull(element);
-            Assert.AreEqual("'''Title''' is [[xxx]].", element.ToString());
-            Assert.IsInstanceOf(typeof(ListElement), element);
-            ListElement list = (ListElement)element;
-            Assert.AreEqual(3, list.Count);
-            Assert.AreEqual("'''Title''' is ", list[0].ToString());
-            Assert.AreEqual("[[xxx]]", list[1].ToString());
-            Assert.IsInstanceOf(typeof(MediaWikiLink), list[1]);
-            Assert.AreEqual(".", list[2].ToString());
-        }
-
-        /// <summary>
-        /// Elementプロパティテストケース(Text未設定)。
-        /// </summary>
-        [Test]
-        [ExpectedException(typeof(InvalidOperationException))]
-        public void TestElementTextNull()
-        {
-            IElement dummy = new MediaWikiPage(new MockFactory().GetMediaWiki("en"), "TestTitle").Element;
-        }
-
         #endregion
 
         #region 公開メソッドテストケース
 
         /// <summary>
-        /// GetInterlanguageメソッドテストケース(通常ページ)。
+        /// <see cref="MediaWikiPage.GetInterlanguage"/>メソッドテストケース(通常ページ)。
         /// </summary>
         [Test]
         public void TestGetInterlanguage()
         {
             // 普通のページ
             MediaWikiPage page = new MediaWikiPage(
-                new DummySite(new Language("en")),
+                new MediaWiki(new Language("en")),
                 "TestTitle",
                 "TestText\n [[ja:テストページ]]<nowiki>[[zh:試験]]</nowiki><!--[[ru:test]]-->[[fr:Test_Fr]]");
             Assert.AreEqual("[[ja:テストページ]]", page.GetInterlanguage("ja").ToString());
@@ -177,7 +148,7 @@ namespace Honememo.Wptscs.Websites
         }
 
         /// <summary>
-        /// GetInterlanguageメソッドテストケース(通常ページ実データ使用)。
+        /// <see cref="MediaWikiPage.GetInterlanguage"/>メソッドテストケース(通常ページ実データ使用)。
         /// </summary>
         [Test, Timeout(20000)]
         public void TestGetInterlanguageDiscoveryChannel()
@@ -191,7 +162,7 @@ namespace Honememo.Wptscs.Websites
         }
 
         /// <summary>
-        /// GetInterlanguageメソッドテストケース(テンプレートページ実データ使用)。
+        /// <see cref="MediaWikiPage.GetInterlanguage"/>メソッドテストケース(テンプレートページ実データ使用)。
         /// </summary>
         [Test, Timeout(20000)]
         public void TestGetInterlanguagePlanetboxBegin()
@@ -203,7 +174,7 @@ namespace Honememo.Wptscs.Websites
         }
 
         /// <summary>
-        /// GetInterlanguageメソッドテストケース(Template:Documentation使用ページ)。
+        /// <see cref="MediaWikiPage.GetInterlanguage"/>メソッドテストケース(Template:Documentation使用ページ)。
         /// </summary>
         [Test]
         public void TestGetInterlanguageDocumentation()
@@ -222,12 +193,23 @@ namespace Honememo.Wptscs.Websites
         }
 
         /// <summary>
-        /// IsRedirectメソッドテストケース。
+        /// <see cref="MediaWikiPage.GetInterlanguage"/>メソッドテストケース(Template:Documentationにnoincludeで囲まれた言語間リンクが存在)。
+        /// </summary>
+        [Test]
+        public void TestGetInterlanguagePartial()
+        {
+            MediaWikiPage page = (MediaWikiPage)new MockFactory().GetMediaWiki("en").GetPage("Template:Partial");
+            Assert.AreEqual("[[ja:Template:Partial]]", page.GetInterlanguage("ja").ToString());
+            Assert.IsNull(page.GetInterlanguage("ru"));
+        }
+
+        /// <summary>
+        /// <see cref="MediaWikiPage.IsRedirect"/>メソッドテストケース。
         /// </summary>
         [Test]
         public void TestIsRedirect()
         {
-            MediaWiki site = new DummySite(new Language("en"));
+            MediaWiki site = new MediaWiki(new Language("en"));
             MediaWikiPage page = new MediaWikiPage(site, "TestTitle", "#REDIRECT [[Test Redirect]]");
             Assert.IsTrue(page.IsRedirect());
             Assert.AreEqual("Test Redirect", page.Redirect.Title);
@@ -250,12 +232,12 @@ namespace Honememo.Wptscs.Websites
         }
 
         /// <summary>
-        /// Normalizeメソッドテストケース。
+        /// <see cref="MediaWikiPage.Normalize"/>メソッドテストケース。
         /// </summary>
         [Test]
         public void TestNormalize()
         {
-            MediaWiki site = new DummySite(new Language("en"));
+            MediaWiki site = new MediaWiki(new Language("en"));
             MediaWikiPage page = new MediaWikiPage(site, "A/b/c");
 
             // サブページの正規化
@@ -285,33 +267,26 @@ namespace Honememo.Wptscs.Websites
         // 非公開メソッドについてはprotected以上、またはやりたい部分だけ実施
 
         /// <summary>
-        /// ValidateIncompleteメソッドテストケース(正常系)。
+        /// <see cref="MediaWikiPage.ValidateIncomplete"/>メソッドテストケース(正常系)。
         /// </summary>
         [Test]
         public void TestValidateIncomplete()
         {
-            // 正常系は例外が発生しなければOK
-            PrivateAccessor<MediaWikiPage> acc = new PrivateAccessor<MediaWikiPage>(
-                new MediaWikiPage(
-                    new MediaWiki(new Language("en")),
-                    "TestTitle",
-                    "TestText"));
-            acc.SetMethod("ValidateIncomplete", new Type[0]);
-            acc.Invoke(new object[0]);
+            // Textが空の場合例外発生、正常系は例外が発生しなければOK
+            MediaWikiPageMock page = new MediaWikiPageMock(new MediaWiki(new Language("en")), "TestTitle");
+            page.Text = "TestText";
+            page.ValidateIncomplete();
         }
 
         /// <summary>
-        /// ValidateIncompleteメソッドテストケース(異常系)。
+        /// <see cref="MediaWikiPage.ValidateIncomplete"/>メソッドテストケース(異常系)。
         /// </summary>
         [Test]
         [ExpectedException(typeof(InvalidOperationException))]
         public void TestValidateIncompleteNg()
         {
-            // 正常系は例外が発生しなければOK
-            PrivateAccessor<MediaWikiPage> acc = new PrivateAccessor<MediaWikiPage>(
-                new MediaWikiPage(new MediaWiki(new Language("en")), "TestTitle"));
-            acc.SetMethod("ValidateIncomplete");
-            acc.Invoke();
+            // Textが空の場合例外発生
+            new MediaWikiPageMock(new MediaWiki(new Language("en")), "TestTitle").ValidateIncomplete();
         }
 
         #endregion
@@ -319,9 +294,9 @@ namespace Honememo.Wptscs.Websites
         #region モッククラス
 
         /// <summary>
-        /// MediaWikiテスト用のモッククラスです。
+        /// <see cref="MediaWikiPage"/>テスト用のモッククラスです。
         /// </summary>
-        public class DummySite : MediaWiki
+        private class DummySite : MediaWiki
         {
             #region コンストラクタ
 
@@ -339,7 +314,7 @@ namespace Honememo.Wptscs.Websites
             #region ダミーメソッド
 
             /// <summary>
-            /// ページを取得。
+            /// ページを取得。<paramref name="title"/>に応じてテスト用の結果を返す。
             /// </summary>
             /// <param name="title">ページタイトル。</param>
             /// <returns>取得したページ。</returns>
@@ -360,6 +335,61 @@ namespace Honememo.Wptscs.Websites
             #endregion
         }
 
+        /// <summary>
+        /// <see cref="MediaWikiPage"/>テスト用のモッククラスです。
+        /// </summary>
+        private class MediaWikiPageMock : MediaWikiPage
+        {
+            #region コンストラクタ
+
+            /// <summary>
+            /// コンストラクタ。
+            /// ページの本文, タイムスタンプには<c>null</c>を設定。
+            /// </summary>
+            /// <param name="website">ページが所属するウェブサイト。</param>
+            /// <param name="title">ページタイトル。</param>
+            public MediaWikiPageMock(MediaWiki website, string title)
+                : base(website, title)
+            {
+            }
+
+            #endregion
+
+            #region 非公開プロパティテスト用のオーラーライドプロパティ
+
+            /// <summary>
+            /// ページの本文。
+            /// </summary>
+            public new string Text
+            {
+                get
+                {
+                    return base.Text;
+                }
+
+                set
+                {
+                    base.Text = value;
+                }
+            }
+
+            #endregion
+
+            #region 非公開メソッドテスト用のオーラーライドメソッド
+
+            /// <summary>
+            /// オブジェクトがメソッドの実行に不完全な状態でないか検証する。
+            /// 不完全な場合、例外をスローする。
+            /// </summary>
+            /// <exception cref="InvalidOperationException">オブジェクトは不完全。</exception>
+            public new void ValidateIncomplete()
+            {
+                base.ValidateIncomplete();
+            }
+
+            #endregion
+        }
+
         #endregion
     }
 }
index 008c7dd..3637932 100644 (file)
@@ -57,6 +57,7 @@
     <Compile Include="Parsers\MediaWikiTemplateTest.cs" />
     <Compile Include="Parsers\MediaWikiHeadingTest.cs" />
     <Compile Include="Parsers\MediaWikiVariableTest.cs" />
+    <Compile Include="Parsers\MediaWikiPreparserTest.cs" />
     <Compile Include="Websites\MediaWikiPageTest.cs" />
     <Compile Include="Websites\PageTest.cs" />
     <Compile Include="Models\MockFactory.cs" />
     <Content Include="Data\MediaWiki\en\Template_context.xml">
       <CopyToOutputDirectory>Always</CopyToOutputDirectory>
     </Content>
+    <Content Include="Data\MediaWiki\en\Template_Partial.xml">
+      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+    </Content>
     <Content Include="Data\MediaWiki\en\Template_Planetbox begin.xml">
       <CopyToOutputDirectory>Always</CopyToOutputDirectory>
     </Content>
+    <Content Include="Data\MediaWiki\en\Template_Table cell templates_doc.xml">
+      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+    </Content>
     <Content Include="Data\MediaWiki\en\_api.xml">
       <CopyToOutputDirectory>Always</CopyToOutputDirectory>
     </Content>