OSDN Git Service

#27621 言語間リンク解析の前処理の対象に<onlyinclude>タグを追加,
authorhoneplus <honeplus@users.osdn.me>
Sat, 3 Mar 2012 18:17:29 +0000 (18:17 +0000)
committerhoneplus <honeplus@users.osdn.me>
Sat, 3 Mar 2012 18:17:29 +0000 (18:17 +0000)
テンプレートの変換で言語間リンク先がテンプレート名前空間でない場合も名前空間名をカットしてしまっていたのを修正

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

Wptscs/Logics/MediaWikiTranslator.cs
Wptscs/Parsers/MediaWikiPreparser.cs
Wptscs/Readme.txt
WptscsTest/Parsers/MediaWikiPreparserTest.cs

index f772cc5..8f91a34 100644 (file)
@@ -449,8 +449,12 @@ namespace Honememo.Wptscs.Logics
             else
             {
                 // 言語間リンクが存在する場合、そちらを指すように置換
-                // : より前の部分を削除して出力(: が無いときは-1+1で0から)
-                template.Title = interWiki.Substring(interWiki.IndexOf(':') + 1);
+                template.Title = interWiki;
+                if (new MediaWikiPage(this.To, interWiki).IsTemplate())
+                {
+                    // 言語間リンク先がテンプレートの場合、: より前の部分を削除
+                    template.Title = interWiki.Substring(interWiki.IndexOf(':') + 1);
+                }
 
                 // | の後に内部リンクやテンプレートが書かれている場合があるので、再帰的に処理する
                 template.PipeTexts = this.ReplaceElements(template.PipeTexts, parent);
index d5f839f..49c931d 100644 (file)
@@ -20,7 +20,7 @@ namespace Honememo.Wptscs.Parsers
     /// </summary>
     /// <remarks>
     /// <para>
-    /// MediaWikiのページに対して、コメント, includeonly, noincludeの解析を行います。
+    /// MediaWikiのページに対して、コメント, includeonly, noinclude, onlyincludeの解析を行います。
     /// </para>
     /// <para>
     /// <see cref="IParser.Parse"/>等では上記要素を解析した結果を返します。
@@ -46,6 +46,11 @@ namespace Honememo.Wptscs.Parsers
         /// </summary>
         private static readonly string NoincludeTag = "noinclude";
 
+        /// <summary>
+        /// onlyincludeタグ。
+        /// </summary>
+        private static readonly string OnlyincludeTag = "onlyinclude";
+
         #endregion
 
         #region コンストラクタ
@@ -55,12 +60,20 @@ namespace Honememo.Wptscs.Parsers
         /// </summary>
         public MediaWikiPreparser()
         {
-            // コメント, nowiki, includeonly, noincludeのみを処理対象とする
+            // コメント, nowiki, includeonly, noinclude, onlyincludeのみを処理対象とする
             this.Parsers = new IParser[]
             {
                 new XmlCommentElementParser(),
                 new MediaWikiNowikiParser(this),
-                new XmlElementParser(this) { Targets = new string[] { IncludeonlyTag, NoincludeTag } }
+                new XmlElementParser(this)
+                {
+                    Targets = new string[]
+                    {
+                        IncludeonlyTag,
+                        NoincludeTag,
+                        OnlyincludeTag
+                    }
+                }
             };
         }
 
@@ -77,7 +90,14 @@ namespace Honememo.Wptscs.Parsers
         /// <exception cref="ArgumentNullException"><c>null</c>が指定された場合。</exception>
         public static string PreprocessByNoinclude(string text)
         {
-            return PreprocessTag(text, false);
+            IElement element;
+            using (MediaWikiPreparser parser = new MediaWikiPreparser())
+            {
+                element = parser.Parse(text);
+                parser.FilterByNoinclude(ref element);
+            }
+
+            return ObjectUtils.ToString(element);
         }
 
         /// <summary>
@@ -89,7 +109,14 @@ namespace Honememo.Wptscs.Parsers
         /// <exception cref="ArgumentNullException"><c>null</c>が指定された場合。</exception>
         public static string PreprocessByInclude(string text)
         {
-            return PreprocessTag(text, true);
+            IElement element;
+            using (MediaWikiPreparser parser = new MediaWikiPreparser())
+            {
+                element = parser.Parse(text);
+                parser.FilterByInclude(ref element);
+            }
+
+            return ObjectUtils.ToString(element);
         }
 
         #endregion
@@ -97,34 +124,48 @@ namespace Honememo.Wptscs.Parsers
         #region 公開メソッド
 
         /// <summary>
-        /// 渡された解析結果内のnoinclude要素の展開と、includeonly, コメント要素の除去を行う。
+        /// 渡された解析結果内のnoinclude, onlyinclude要素の展開と、includeonly, コメント要素の除去を行う。
         /// </summary>
         /// <param name="element">
         /// 要素を展開/除去する解析結果。
+        /// noinclude, onlyincludeの<see cref="XmlElement"/>
+        /// の場合、内部要素の展開を、includeonlyまたは<see cref="XmlCommentElement"/>
+        /// 要素の場合、<c>null</c>への置き換えを行う。
         /// <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)
         {
+            Validate.NotNull(element, "element");
             this.FilterTag(ref element, false);
         }
 
         /// <summary>
-        /// 渡された解析結果内のincludeonly要素の展開と、noinclude, コメント要素の除去を行う。
+        /// 渡された解析結果内のincludeonly, onlyinclude要素の展開と、noinclude, コメント要素の除去を行う。
         /// </summary>
         /// <param name="element">
         /// 要素を展開/除去する解析結果。
+        /// onlyincludeの<see cref="XmlElement"/>が存在する場合、それ以外の要素を除去する。
+        /// 存在しない場合、includeonlyの<see cref="XmlElement"/>
+        /// であれば、内部要素の展開を、noincludeまたは<see cref="XmlCommentElement"/>
+        /// 要素の場合、<c>null</c>への置き換えを行う。
         /// <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)
         {
+            // ※ 2012年3月現在のWikipediaで実験して確認。
+            //    onlyincludeとnoincludeの入れ子ではonlyincludeが優先
+            //    (noincludeに囲まれたonlyincludeであっても、
+            //    中身が出力される)ため、先にonlyincludeの処理を行う。
+            if (this.ContainsOnlyinclude(Validate.NotNull(element, "element")))
+            {
+                element = this.FilterOnlyincludeTagByInclude(element);
+            }
+
+            // 残ったタグの展開/除去を行う
             this.FilterTag(ref element, true);
         }
 
@@ -133,38 +174,105 @@ namespace Honememo.Wptscs.Parsers
         #region 内部処理用メソッド
 
         /// <summary>
-        /// æ\8c\87å®\9aã\81\95ã\82\8cã\81\9fã\83\91ã\83©ã\83¡ã\83¼ã\82¿ã\81«å¿\9cã\81\98ã\81\9fã\80\81ã\83\9aã\83¼ã\82¸è§£æ\9e\90ç\94¨ã\81®å\89\8då\87¦ç\90\86ã\82\92è¡\8cã\81\86
+        /// æ¸¡ã\81\95ã\82\8cã\81\9f解æ\9e\90çµ\90æ\9e\9cå\86\85ã\81«onlyincludeè¦\81ç´ ã\81\8cå\90«ã\81¾ã\82\8cã\82\8bã\81\8bã\82\92確èª\8dã\81\99ã\82\8b
         /// </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)
+        /// <param name="element">探索する解析結果。</param>
+        /// <returns>含まれる場合<c>true</c>。</returns>
+        private bool ContainsOnlyinclude(IElement element)
         {
-            IElement element;
-            using (MediaWikiPreparser parser = new MediaWikiPreparser())
+            if (element is XmlElement)
             {
-                element = parser.Parse(text);
-                parser.FilterTag(ref element, include);
+                // XML/HTML要素の場合、タグ名を確認
+                if (((XmlElement)element).Name.ToLower() == OnlyincludeTag)
+                {
+                    // onlyincludeが存在
+                    return true;
+                }
             }
 
-            return ObjectUtils.ToString(element);
+            if (element is IEnumerable<IElement>)
+            {
+                // 中身を持つ要素の場合、再帰的に探索
+                // ※ XML/HTML要素も含む
+                foreach (IElement e in (IEnumerable<IElement>)element)
+                {
+                    if (this.ContainsOnlyinclude(e))
+                    {
+                        return true;
+                    }
+                }
+            }
+
+            return false;
+        }
+
+        /// <summary>
+        /// 渡された解析結果内のonlyinclude要素以外の除去を行う。
+        /// </summary>
+        /// <param name="element">要素を絞り込む解析結果。</param>
+        /// <returns>onlyincludeを含む要素、含まない要素の場合<c>null</c>。</returns>
+        /// <remarks>
+        /// onlyinclude要素の中身の展開は本メソッドでは行わない。
+        /// そちらについては、本メソッド実行後に
+        /// <see cref="FilterTag"/>を呼び出すことで行う。
+        /// </remarks>
+        private IElement FilterOnlyincludeTagByInclude(IElement element)
+        {
+            if (element is XmlElement)
+            {
+                // XML/HTML要素の場合、タグの種類を見て対応
+                XmlElement xml = (XmlElement)element;
+                if (((XmlElement)element).Name.ToLower() == OnlyincludeTag)
+                {
+                    // onlyincludeは残す
+                    return element;
+                }
+            }
+
+            if (element is IEnumerable<IElement>)
+            {
+                // onlyinclude以外の中身を持つ要素の場合、
+                // onlyincludeのみを残したリストに置き換え
+                // ※ もともとの要素が持っていた情報も除去する
+                ListElement list = new ListElement();
+                foreach (IElement e in (IEnumerable<IElement>)element)
+                {
+                    IElement inner = this.FilterOnlyincludeTagByInclude(e);
+                    if (inner != null)
+                    {
+                        list.Add(inner);
+                    }
+                }
+
+                if (list.Count > 0)
+                {
+                    return list;
+                }
+            }
+
+            // onlyinclude以外の要素は全て削除する
+            return null;
         }
 
         /// <summary>
-        /// 渡された解析結果から、指定に応じてincludeonly, noinclude、またコメント要素を展開/除去する。
+        /// 渡された解析結果から、指定に応じてincludeonly, noinclude, onlyinclude、またコメント要素を展開/除去する。
         /// </summary>
         /// <param name="element">
         /// 要素を展開/除去する解析結果。
         /// <see cref="ListElement"/>の場合その内部の上記要素が更新/除去される。
-        /// includeonly, noincludeの<see cref="XmlElement"/>の場合、更新または<c>null</c>への置き換えを、
+        /// includeonly, noinclude, onlyincludeの<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>
+        /// <remarks>
+        /// onlyincludeでインクルードの場合その外側の要素は全て無効になるが、
+        /// その処理は<see cref="FilterOnlyincludeTagByInclude"/>
+        /// で事前に行われるものと想定。このメソッドは展開のみを行う。
+        /// </remarks>
         private void FilterTag(ref IElement element, bool include)
         {
-            if (Validate.NotNull(element, "element") is XmlCommentElement)
+            if (element is XmlCommentElement)
             {
                 // XMLコメントは除去
                 element = null;
@@ -176,9 +284,11 @@ namespace Honememo.Wptscs.Parsers
                 XmlElement xml = (XmlElement)element;
                 string name = xml.Name.ToLower();
                 if ((include && name == IncludeonlyTag)
-                    || (!include && name == NoincludeTag))
+                    || (!include && name == NoincludeTag)
+                    || name == OnlyincludeTag)
                 {
-                    // インクルードでincludeonly、非インクルードでnoincludeは中身を展開
+                    // インクルードでincludeonly、非インクルードでnoinclude,
+                    // またはonlyincludeの場合、中身を展開
                     // ※ このリストは、この後↓でもう一度処理される
                     ListElement list = new ListElement();
                     list.AddRange(xml);
index 71ba2c7..2941192 100644 (file)
@@ -166,7 +166,7 @@ Ver1.11  2012/02/19 英語リソースの追加(ツールチップは除く)
 
 Ver1.20  2012/03/xx Wiktionary/Wikitravelでの動作に対応。
                     設定の切り替え/追加機能を追加。
-                    言語間リンク取得時に<includeonly>, <noinclude>を考慮するよう修正。
+                    言語間リンク取得時に<includeonly>, <noinclude>, <onlyinclude>を考慮するよう修正。
                     見出しの置き換えに改行区切りで複数の語句を登録できるよう改良。
                     非常に複雑なテンプレートで極端に時間がかかっていたのを改善。
                     サーバーへのアクセス時に自動でRefererを設定するよう変更。
index d3231d6..e57a445 100644 (file)
@@ -25,15 +25,21 @@ namespace Honememo.Wptscs.Parsers
         #region 定数
 
         /// <summary>
-        /// 複数のテストケースで使用するテストテキスト。
+        /// 複数のテストケースで使用するテストテキスト(onlyinclude無し)
         /// </summary>
         /// <remarks>タグの大文字小文字は区別されないはずのため、意図的に混入させている。</remarks>
-        private static readonly string TestData = "This template is [[xxx]]<br />\r\n"
+        private static readonly string TestDataWithoutOnlyinclude = "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> -->";
 
+        /// <summary>
+        /// 複数のテストケースで使用するテストテキスト。
+        /// </summary>
+        private static readonly string TestData = TestDataWithoutOnlyinclude
+            + "<onlyinclude><noinclude>インクルード時は</noinclude>ここしか</onlyinclude>, <onlyinclude>有効にならない</onlyinclude>";
+
         #endregion
 
         #region IParserインタフェーステストケース
@@ -51,11 +57,11 @@ namespace Honememo.Wptscs.Parsers
             XmlElement xml;
             using (MediaWikiPreparser parser = new MediaWikiPreparser())
             {
-                element = parser.Parse(TestData);
+                element = parser.Parse(TestDataWithoutOnlyinclude);
             }
 
             // 解析だけであればincludeonly等の処理は行われない、元の文字列が保持される
-            Assert.AreEqual(TestData, element.ToString());
+            Assert.AreEqual(TestDataWithoutOnlyinclude, element.ToString());
 
             // includeonly, noinclude, nowiki, コメントのみ特別な要素として認識する
             Assert.IsInstanceOf(typeof(ListElement), element);
@@ -98,6 +104,38 @@ namespace Honememo.Wptscs.Parsers
             Assert.AreEqual(2, xml.Count);
         }
 
+        /// <summary>
+        /// <see cref="IParser.Parse"/>メソッドテストトケース(onlyinclude)。
+        /// </summary>
+        [Test]
+        public void TestParseOnlyinclude()
+        {
+            IElement element;
+            XmlElement xml;
+            using (MediaWikiPreparser parser = new MediaWikiPreparser())
+            {
+                element = parser.Parse(TestData);
+            }
+
+            // onlyincludeが存在するケース、解析時点では特に他のタグと同じ扱い
+            // ※ 前半部分はTestParseと同じデータなので割愛
+            Assert.AreEqual(TestData, element.ToString());
+            Assert.IsInstanceOf(typeof(ListElement), element);
+            ListElement list = (ListElement)element;
+            Assert.AreEqual("<onlyinclude><noinclude>インクルード時は</noinclude>ここしか</onlyinclude>", list[8].ToString());
+            Assert.AreEqual(", ", list[9].ToString());
+            Assert.AreEqual("<onlyinclude>有効にならない</onlyinclude>", list[10].ToString());
+            Assert.AreEqual(11, list.Count);
+
+            // onlyincludeも再帰的に処理
+            Assert.IsInstanceOf(typeof(XmlElement), list[8]);
+            xml = (XmlElement)list[8];
+            Assert.AreEqual("<noinclude>インクルード時は</noinclude>", xml[0].ToString());
+            Assert.IsInstanceOf(typeof(XmlElement), xml[0]);
+            Assert.AreEqual("ここしか", xml[1].ToString());
+            Assert.AreEqual(2, xml.Count);
+        }
+
         #endregion
 
         #region 公開メソッドテストケース
@@ -115,7 +153,7 @@ namespace Honememo.Wptscs.Parsers
             ListElement list;
             using (MediaWikiPreparser parser = new MediaWikiPreparser())
             {
-                element = parser.Parse(TestData);
+                element = parser.Parse(TestDataWithoutOnlyinclude);
                 parser.FilterByInclude(ref element);
             }
 
@@ -145,6 +183,36 @@ namespace Honememo.Wptscs.Parsers
         }
 
         /// <summary>
+        /// <see cref="MediaWikiPreparser.FilterByInclude"/>メソッドテストケース(onlyinclude)。
+        /// </summary>
+        /// <remarks>
+        /// <see cref="TestParseOnlyinclude"/>と同じデータを使うため、そちらのテストが通っていることを前提とする。
+        /// </remarks>
+        [Test]
+        public void TestFilterByIncludeOnlyinclude()
+        {
+            IElement element;
+            ListElement list;
+            using (MediaWikiPreparser parser = new MediaWikiPreparser())
+            {
+                element = parser.Parse(TestData);
+                parser.FilterByInclude(ref element);
+            }
+
+            // onlyincludeが存在する場合、その外側は全て削除され、タグが展開される
+            // ※ onlyincludeの内部にnoinclude等が存在する場合、それはそれで通常と同様処理される
+            Assert.IsInstanceOf(typeof(ListElement), element);
+            list = (ListElement)element;
+            Assert.AreEqual("ここしか", list[0].ToString());
+            Assert.AreEqual("有効にならない", list[1].ToString());
+            Assert.AreEqual(2, list.Count);
+
+            // onlyincludeはListElementに置き換わる
+            Assert.IsInstanceOf(typeof(ListElement), list[0]);
+            Assert.IsInstanceOf(typeof(ListElement), list[1]);
+        }
+
+        /// <summary>
         /// <see cref="MediaWikiPreparser.FilterByInclude"/>メソッドテストケース(null)。
         /// </summary>
         [Test]
@@ -168,34 +236,45 @@ namespace Honememo.Wptscs.Parsers
         public void TestFilterByNoinclude()
         {
             IElement element;
-            ListElement list;
+            ListElement innerList;
             using (MediaWikiPreparser parser = new MediaWikiPreparser())
             {
                 element = parser.Parse(TestData);
                 parser.FilterByNoinclude(ref element);
             }
 
-            // noincludeが展開され、includeonly, コメントが削除される
+            // noinclude, onlyincludeが展開され、includeonly, コメントが削除される
             Assert.IsInstanceOf(typeof(ListElement), element);
-            list = (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("\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.AreEqual("インクルード時はここしか", list[6].ToString());
+            Assert.AreEqual(", ", list[7].ToString());
+            Assert.AreEqual("有効にならない", list[8].ToString());
+            Assert.AreEqual(9, list.Count);
 
             // 各要素の確認
             Assert.IsInstanceOf(typeof(TextElement), list[0]);
             Assert.IsInstanceOf(typeof(XmlElement), list[1]);
 
-            // noincludeはListElementに置き換わる
+            // noinclude, onlyincludeは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);
+            innerList = (ListElement)list[4];
+            Assert.AreEqual("[[ja:Template:sample/doc]]", innerList[0].ToString());
+            Assert.IsInstanceOf(typeof(TextElement), innerList[0]);
+            Assert.AreEqual(1, innerList.Count);
+
+            Assert.IsInstanceOf(typeof(ListElement), list[6]);
+            innerList = (ListElement)list[6];
+            Assert.AreEqual("インクルード時は", innerList[0].ToString());
+            Assert.IsInstanceOf(typeof(ListElement), innerList[0]);
+            Assert.AreEqual("ここしか", innerList[1].ToString());
+            Assert.IsInstanceOf(typeof(TextElement), innerList[1]);
+            Assert.AreEqual(2, innerList.Count);
         }
 
         /// <summary>
@@ -226,6 +305,9 @@ namespace Honememo.Wptscs.Parsers
             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(TestDataWithoutOnlyinclude));
+            Assert.AreEqual(
+                "ここしか有効にならない",
                 MediaWikiPreparser.PreprocessByInclude(TestData));
             Assert.AreEqual(String.Empty, MediaWikiPreparser.PreprocessByInclude(String.Empty));
         }
@@ -249,7 +331,7 @@ namespace Honememo.Wptscs.Parsers
             // 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",
+                + "[[ja:Template:sample/doc]]\r\nインクルード時はここしか, 有効にならない",
                 MediaWikiPreparser.PreprocessByNoinclude(TestData));
             Assert.AreEqual(String.Empty, MediaWikiPreparser.PreprocessByNoinclude(String.Empty));
         }