OSDN Git Service

#30244 Visual Studio 2012 Express for Windows Desktop, StyleCop 4.7, WiX 3.6 に合わせたソース...
[wptscs/wpts.git] / Wptscs / Parsers / MediaWikiHeadingParser.cs
1 // ================================================================================================
2 // <summary>
3 //      MediaWikiの見出しを解析するパーサークラスソース</summary>
4 //
5 // <copyright file="MediaWikiHeadingParser.cs" company="honeplusのメモ帳">
6 //      Copyright (C) 2012 Honeplus. All rights reserved.</copyright>
7 // <author>
8 //      Honeplus</author>
9 // ================================================================================================
10
11 namespace Honememo.Wptscs.Parsers
12 {
13     using System;
14     using System.Collections.Generic;
15     using System.Text;
16     using Honememo.Parsers;
17     using Honememo.Utilities;
18
19     /// <summary>
20     /// MediaWikiの見出しを解析するパーサークラスです。
21     /// </summary>
22     public class MediaWikiHeadingParser : AbstractParser
23     {
24         #region private変数
25
26         /// <summary>
27         /// このパーサーが参照する<see cref="MediaWikiParser"/>。
28         /// </summary>
29         private MediaWikiParser parser;
30
31         #endregion
32         
33         #region コンストラクタ
34
35         /// <summary>
36         /// 指定された<see cref="MediaWikiParser"/>を元に見出しを解析するためのパーサーを作成する。
37         /// </summary>
38         /// <param name="parser">このパーサーが参照する<see cref="MediaWikiParser"/>。</param>
39         public MediaWikiHeadingParser(MediaWikiParser parser)
40         {
41             this.parser = parser;
42         }
43
44         #endregion
45
46         #region インタフェース実装メソッド
47
48         /// <summary>
49         /// 渡されたテキストをMediaWikiの見出し(==関連項目==みたいなの)として解析する。
50         /// </summary>
51         /// <param name="s">行頭からの文字列。</param>
52         /// <param name="result">解析した見出し。</param>
53         /// <returns>解析に成功した場合<c>true</c>。</returns>
54         /// <remarks>
55         /// 見出しは行単位で有効になるため、行頭からの文字列を渡す必要がある。
56         /// (ただし、--&lt;==見出し== みたいな事もできるので、その場合は=の開始部分から。)
57         /// </remarks>
58         public override bool TryParse(string s, out IElement result)
59         {
60             // 入力値確認、空の場合は即終了
61             result = null;
62             if (string.IsNullOrEmpty(s))
63             {
64                 return false;
65             }
66
67             // 始まりの = の数を数える
68             // ※ 構文はWikipediaのプレビューで色々試して確認、足りなかったり間違ってたりするかも・・・
69             // TODO: Wikipediaでは <!--test-->=<!--test-->=関連項目<!--test-->==<!--test--> みたいなのでも認識するが、2012年1月現在未対応
70             //      (昔は対応していたが、その過程でコメントが失われれるつくりになっており、
71             //        Parser周りを整理した際に情報を取りこぼさないことを最優先としたため取り止め。)
72             int startCount = 0;
73             for (int i = 0; i < s.Length; i++)
74             {
75                 if (s[i] == MediaWikiHeading.DelimiterStart)
76                 {
77                     ++startCount;
78                 }
79                 else
80                 {
81                     break;
82                 }
83             }
84
85             // = で始まる行ではない場合、処理対象外
86             if (startCount < 1)
87             {
88                 return false;
89             }
90
91             // 始まりの = の次の文字から、行の終わりまでを解析
92             // (=={{lang\n|ja|見出し}}== みたいに何かの中にある改行はOK。Wikipediaでも認識された)
93             IElement element;
94             this.parser.TryParseToDelimiter(StringUtils.Substring(s, startCount), out element, "\r", "\n");
95
96             // 終わりの = の数を確認
97             // ※ この処理だと中身の無い行(====とか)は弾かれてしまうが、どうせ処理できないので許容する
98             string substr = element.ToString().TrimEnd();
99             int endCount = 0;
100             for (int i = substr.Length - 1; i >= 0; i--)
101             {
102                 if (substr[i] == MediaWikiHeading.DelimiterEnd)
103                 {
104                     ++endCount;
105                 }
106                 else
107                 {
108                     break;
109                 }
110             }
111
112             // = で終わる行ではない場合、処理対象外
113             if (endCount < 1)
114             {
115                 return false;
116             }
117
118             // 始まりと終わり、=の少ないほうにあわせる(==test===とか用の処理)
119             int level = startCount;
120             if (startCount > endCount)
121             {
122                 level = endCount;
123             }
124
125             // 確定した見出しの階層から、見出し内部の文字列を抽出。内部要素を再帰的に探索する
126             // ※ 二重処理になってしまうが、後ろの = を取り除くと微妙にややこしいことになりそうだったので
127             //    見出しは処理件数も少なく、深い再帰もないはずなので、影響ない・・・はず
128             IElement innerElement;
129             if (!this.parser.TryParse(substr.Substring(0, substr.Length - level), out innerElement))
130             {
131                 return false;
132             }
133
134             // 解析に成功した場合、結果を出力値に設定
135             result = this.MakeElement(innerElement, level, s.Substring(0, startCount + element.ToString().Length));
136             return true;
137         }
138         
139         #endregion
140
141         #region 内部処理用メソッド
142
143         /// <summary>
144         /// 見出しタグを解析した結果から、MediaWiki見出し要素を生成する。
145         /// </summary>
146         /// <param name="innerElement">見出しタグ上の見出し部分の要素。</param>
147         /// <param name="level">見出しの階層。</param>
148         /// <param name="parsedString">解析した見出しタグの文字列。</param>
149         /// <returns>生成した見出し要素。</returns>
150         private MediaWikiHeading MakeElement(IElement innerElement, int level, string parsedString)
151         {
152             MediaWikiHeading heading = new MediaWikiHeading();
153
154             // 解析した見出しの素のテキストを保存
155             heading.ParsedString = parsedString;
156
157             // 見出しの階層を保存
158             heading.Level = level;
159
160             // 内部要素については、結果がリストの場合マージ、それ以外はそのままElementに代入
161             if (innerElement.GetType() == typeof(ListElement))
162             {
163                 heading.AddRange((ListElement)innerElement);
164             }
165             else
166             {
167                 heading.Add(innerElement);
168             }
169
170             return heading;
171         }
172
173         #endregion
174     }
175 }