OSDN Git Service

modify pmd warnings
[jindolf/JinParser.git] / src / main / java / jp / sourceforge / jindolf / parser / TalkParser.java
1 /*
2  * talk-part parser
3  *
4  * License : The MIT License
5  * Copyright(c) 2009 olyutorskii
6  */
7
8 package jp.sourceforge.jindolf.parser;
9
10 import java.util.regex.Pattern;
11 import jp.sourceforge.jindolf.corelib.TalkType;
12
13 /**
14  * 人狼BBSシステムが出力する各発言箇所のパーサ。
15  * パース進行に従い{@link TalkHandler}の各種メソッドが呼び出される。
16  */
17 @SuppressWarnings("PMD.FieldDeclarationsShouldBeAtStartOfClass")
18 public class TalkParser extends AbstractParser{
19
20     private TalkHandler talkHandler;
21
22     private final SeqRange rangepool_1 = new SeqRange();
23
24     /**
25      * コンストラクタ。
26      * @param parent 親パーサ
27      */
28     public TalkParser(ChainedParser parent){
29         super(parent);
30         return;
31     }
32
33     /**
34      * {@link TalkHandler}ハンドラを登録する。
35      * @param talkHandler ハンドラ
36      */
37     public void setTalkHandler(TalkHandler talkHandler){
38         this.talkHandler = talkHandler;
39         return;
40     }
41
42     /**
43      * 各Avatarの個別の発言をパースする。
44      * 最初のAタグは既にパース済みとする。
45      * @param talkNo 白発言番号
46      * @param nameRange Aタグのname属性値の範囲
47      * @throws HtmlParseException パースエラー
48      */
49     public void parseTalk(int talkNo, SeqRange nameRange)
50             throws HtmlParseException{
51         this.talkHandler.startTalk();
52
53         this.talkHandler.talkNo(talkNo);
54         this.talkHandler.talkId(getContent(), nameRange);
55
56         parseName();
57         parseTime();
58         parseIcon();
59         parseType();
60         parseText();
61         parseTail();
62
63         this.talkHandler.endTalk();
64
65         return;
66     }
67
68     private static final Pattern AVATARNAME_PATTERN =
69             compile("([^<]*)");
70
71     /**
72      * 発言者名をパースする。
73      * @throws HtmlParseException パースエラー
74      */
75     private void parseName() throws HtmlParseException{
76         setContextErrorMessage("lost dialog avatar-name");
77
78         SeqRange avatarRange = this.rangepool_1;
79
80         lookingAtAffirm(AVATARNAME_PATTERN);
81         avatarRange.setLastMatchedGroupRange(getMatcher(), 1);
82         shrinkRegion();
83
84         this.talkHandler.talkAvatar(getContent(), avatarRange);
85
86         return;
87     }
88
89     private static final Pattern TALKTIME_PATTERN =
90             compile(
91                  "</a>"
92                 +SP_I
93                 +"<span class=\"time\">"
94                 +"(?:(午前\u0020)|(午後\u0020))?"
95                 +"([0-9][0-9]?)(?:時\u0020|\\:)"
96                 +"([0-9][0-9]?)分?\u0020"
97                 +"</span>"
98                 +SP_I
99                 +"<table\u0020[^>]*>"
100                 +SP_I
101                 +"(?:<tbody>)?"
102                 +SP_I
103                 +"<tr>"
104             );
105
106     /**
107      * 発言時刻をパースする。
108      * @throws HtmlParseException パースエラー
109      */
110     private void parseTime() throws HtmlParseException{
111         setContextErrorMessage("lost dialog time");
112
113         lookingAtAffirm(TALKTIME_PATTERN);
114         int hour = parseGroupedInt(3);
115         int minute = parseGroupedInt(4);
116         if(isGroupMatched(2)){  // 午後指定
117             hour = (hour + 12) % 24;
118         }
119         shrinkRegion();
120         sweepSpace();
121
122         this.talkHandler.talkTime(hour, minute);
123
124         return;
125     }
126
127     private static final Pattern IMGSRC_PATTERN =
128             compile(
129                   "<td\u0020[^>]*><img\u0020src=\"([^\"]*)\"></td>"
130                  +SP_I
131                  +"<td\u0020[^>]*><img\u0020[^>]*></td>"
132             );
133
134     /**
135      * アイコンのURLをパースする。
136      * @throws HtmlParseException パースエラー
137      */
138     private void parseIcon() throws HtmlParseException{
139         setContextErrorMessage("lost icon url");
140
141         SeqRange urlRange = this.rangepool_1;
142
143         lookingAtAffirm(IMGSRC_PATTERN);
144         urlRange.setLastMatchedGroupRange(getMatcher(), 1);
145         shrinkRegion();
146         sweepSpace();
147
148         this.talkHandler.talkIconUrl(getContent(), urlRange);
149
150         return;
151     }
152
153     private static final Pattern TALKDIC_PATTERN =
154             compile(
155                  "<td>" +SP_I+ "<div(?:\u0020[^>]*)?>"
156                 +SP_I
157                 +"<div class=\"mes_"
158                 +"(?:(say)|(think)|(whisper)|(groan))"
159                 +"_body1\">"
160             );
161
162     /**
163      * 発言種別をパースする。
164      * @throws HtmlParseException パースエラー
165      */
166     private void parseType() throws HtmlParseException{
167         setContextErrorMessage("lost dialog type");
168
169         lookingAtAffirm(TALKDIC_PATTERN);
170         TalkType type;
171         if(isGroupMatched(1)){
172             type = TalkType.PUBLIC;
173         }else if(isGroupMatched(2)){
174             type = TalkType.PRIVATE;
175         }else if(isGroupMatched(3)){
176             type = TalkType.WOLFONLY;
177         }else if(isGroupMatched(4)){
178             type = TalkType.GRAVE;
179         }else{
180             assert false;
181             throw buildParseException();
182         }
183         shrinkRegion();
184
185         this.talkHandler.talkType(type);
186
187         return;
188     }
189
190     private static final Pattern TEXT_PATTERN =
191             compile("([^<>]+)|(<br />)|(<a href=\"[^\"]*\">)|(</a>)");
192
193     /**
194      * 発言テキストをパースする。
195      * 前後のホワイトスペースは無視しない。
196      * @throws HtmlParseException パースエラー
197      */
198     private void parseText() throws HtmlParseException{
199         setContextErrorMessage("lost dialog text");
200
201         SeqRange textRange = this.rangepool_1;
202
203         while(lookingAtProbe(TEXT_PATTERN)){
204             if(isGroupMatched(1)){
205                 textRange.setLastMatchedGroupRange(getMatcher(), 1);
206                 this.talkHandler.talkText(getContent(), textRange);
207             }else if(isGroupMatched(2)){      // <br />
208                 this.talkHandler.talkBreak();
209             }else if(isGroupMatched(3)){      // <a>
210                 // IGNORE
211                 assert true;
212             }else if(isGroupMatched(4)){      // </a>
213                 // IGNORE
214                 assert true;
215             }else{
216                 assert false;
217                 throw buildParseException();
218             }
219             shrinkRegion();
220         }
221
222         return;
223     }
224
225     private static final Pattern TAIL_PATTERN =
226             compile(
227                        "</div>"  // F1603 2d21:12 ペーター発言には注意
228                 +SP_I+ "</div>"
229                 +SP_I+ "</td>"
230                 +SP_I+ "</tr>"
231                 +SP_I+ "(?:</tbody>)?"
232                 +SP_I+ "</table>"
233             );
234
235     /**
236      * 発言末尾をパースする。
237      * @throws HtmlParseException パースエラー
238      */
239     private void parseTail() throws HtmlParseException{
240         setContextErrorMessage("lost dialog termination");
241
242         lookingAtAffirm(TAIL_PATTERN);
243         shrinkRegion();
244         sweepSpace();
245
246         return;
247     }
248
249 }