• R/O
  • SSH

JinParser: Commit

JinParserライブラリは、CGIゲーム「人狼BBS」のクライアント制作者向けに作られたJavaライブラリです。


Commit MetaInfo

Revision83725f5a1e4ef7b68fba3e1974f8ef0e13648124 (tree)
Zeit2018-04-04 22:21:25
Autor <olyutorskii@user...>

Log Message

default とマージ

Ändern Zusammenfassung

Diff

diff -r 4652c2fd346d -r 83725f5a1e4e .hgtags
--- a/.hgtags Sun Mar 25 12:23:15 2018 +0900
+++ b/.hgtags Wed Apr 04 22:21:25 2018 +0900
@@ -8,3 +8,5 @@
88 682e3b49529fcf52154ec3607722c81288dae6aa release-1.408.6
99 3e47d5b2b0ff68b6dc9f1cb69b0f7c1ae3fb256e release-1.409.2
1010 06d9e4c356ac9e9ad7035fa9f67571e17908c22f release-1.409.4
11+2bd32e0430338494ea75777c63c43ded9d1d5fff release-2.101.2
12+4f6d22016f169cebd17acb92b8f46fb10441f379 release-2.101.4
diff -r 4652c2fd346d -r 83725f5a1e4e CHANGELOG.txt
--- a/CHANGELOG.txt Sun Mar 25 12:23:15 2018 +0900
+++ b/CHANGELOG.txt Wed Apr 04 22:21:25 2018 +0900
@@ -4,8 +4,11 @@
44 JinParser 変更履歴
55
66
7-2.101.X (20XX-XX-XX)
8- ・文字デコード処理をJioCemaに分離。
7+2.101.4 (2018-04-04)
8+ ・assemblyファイルにconfig/ディレクトリが指定されない不備を修正。
9+
10+2.101.2 (2018-04-04)
11+ ・文字デコード処理をJioCemaライブラリに分離。
912 ・OSDN社の事情を鑑みパッケージ名とgroupIdを変更。
1013 ・XHTMLに依存しない部分をパッケージ分離。
1114
diff -r 4652c2fd346d -r 83725f5a1e4e pom.xml
--- a/pom.xml Sun Mar 25 12:23:15 2018 +0900
+++ b/pom.xml Wed Apr 04 22:21:25 2018 +0900
@@ -16,7 +16,7 @@
1616 <groupId>jp.osdn.jindolf</groupId>
1717 <artifactId>jinparser</artifactId>
1818
19- <version>2.101.1-SNAPSHOT</version>
19+ <version>2.101.4</version>
2020
2121 <packaging>jar</packaging>
2222 <name>JinParser</name>
@@ -132,6 +132,7 @@
132132 <groupId>io.bitbucket.olyutorskii</groupId>
133133 <artifactId>jiocema</artifactId>
134134 <version>1.101.2</version>
135+ <scope>compile</scope>
135136 </dependency>
136137
137138 </dependencies>
diff -r 4652c2fd346d -r 83725f5a1e4e src/assembly/src.xml
--- a/src/assembly/src.xml Sun Mar 25 12:23:15 2018 +0900
+++ b/src/assembly/src.xml Wed Apr 04 22:21:25 2018 +0900
@@ -35,6 +35,10 @@
3535 <directory>${project.basedir}/src</directory>
3636 <useDefaultExcludes>true</useDefaultExcludes>
3737 </fileSet>
38+ <fileSet>
39+ <directory>${project.basedir}/config</directory>
40+ <useDefaultExcludes>true</useDefaultExcludes>
41+ </fileSet>
3842 </fileSets>
3943
4044 </assembly>
diff -r 4652c2fd346d -r 83725f5a1e4e src/main/java/jp/osdn/jindolf/parser/EntityConverter.java
--- a/src/main/java/jp/osdn/jindolf/parser/EntityConverter.java Sun Mar 25 12:23:15 2018 +0900
+++ b/src/main/java/jp/osdn/jindolf/parser/EntityConverter.java Wed Apr 04 22:21:25 2018 +0900
@@ -7,6 +7,9 @@
77
88 package jp.osdn.jindolf.parser;
99
10+import java.util.Arrays;
11+import java.util.Collections;
12+import java.util.List;
1013 import java.util.regex.Matcher;
1114 import java.util.regex.Pattern;
1215 import jp.osdn.jindolf.parser.content.DecodedContent;
@@ -18,7 +21,7 @@
1821 * <p>文字実体参照は{@code &gt; &lt; &quot; &amp;}が対象。
1922 *
2023 * <p>U+005C(バックスラッシュ)をU+00A5(円通貨)に直す処理も行われる。
21- * ※ 人狼BBSはShift_JIS(⊃JISX0201)で運営されているので、
24+ * ※ 人狼BBS(F国以前)はShift_JIS(⊃JISX0201)で運営されているので、
2225 * バックスラッシュは登場しないはず。
2326 * ※ が、バックスラッシュを生成するShift_JISデコーダは存在する。
2427 *
@@ -41,16 +44,27 @@
4144
4245 private static final String UCS4_PATTERN = "[\\x{10000}-\\x{10ffff}]";
4346
44- private static final RegexRep[] VALUES_CACHE = RegexRep.values();
47+ private static final RepInfo GT = new RepInfo("&gt;", ">");
48+ private static final RepInfo LT = new RepInfo("&lt;", "<");
49+ private static final RepInfo AMP = new RepInfo("&amp;", "&");
50+ private static final RepInfo QUAT = new RepInfo("&quot;", DQ_STR);
51+ private static final RepInfo BS = new RepInfo(BS_PATTERN, YEN_STR);
52+ private static final RepInfo UCS4 = new RepInfo(UCS4_PATTERN, "?");
4553
4654
47- private final Matcher matcher = RegexRep.buildMatcher();
55+ private final MultiMatcher multiMatcher = new MultiMatcher();
56+
57+ {
58+ this.multiMatcher.putRepInfo(GT, LT, AMP, QUAT, BS, UCS4);
59+ }
60+
4861 private final boolean replaceSmp;
4962
5063
5164 /**
5265 * コンストラクタ。
53- * SMP面文字の代替処理は行われない。
66+ *
67+ * <p>SMP面文字の代替処理は行われない。
5468 */
5569 public EntityConverter(){
5670 this(false);
@@ -59,6 +73,7 @@
5973
6074 /**
6175 * コンストラクタ。
76+ *
6277 * @param replaceSmp SMP面文字を代替処理するならtrue
6378 */
6479 public EntityConverter(boolean replaceSmp){
@@ -69,163 +84,287 @@
6984
7085
7186 /**
72- * 実体参照の変換を行う。
73- * @param content 変換元文書
87+ * XHTML文字実体参照の変換を行う。
88+ *
89+ * @param srcContent 変換元文書
7490 * @return 切り出された変換済み文書
7591 */
76- public DecodedContent convert(DecodedContent content){
92+ public DecodedContent convert(DecodedContent srcContent){
7793 int startPos = 0;
78- int endPos = content.length();
79- return append(null, content, startPos, endPos);
94+ int endPos = srcContent.length();
95+ return append(null, srcContent, startPos, endPos);
8096 }
8197
8298 /**
83- * 実体参照の変換を行う。
84- * @param content 変換元文書
99+ * XHTML文字実体参照の変換を行う。
100+ *
101+ * @param srcContent 変換元文書
85102 * @param range 範囲指定
86103 * @return 切り出された変換済み文書
87104 * @throws IndexOutOfBoundsException 位置指定に不正があった
88105 */
89- public DecodedContent convert(DecodedContent content, SeqRange range)
106+ public DecodedContent convert(DecodedContent srcContent, SeqRange range)
90107 throws IndexOutOfBoundsException{
91108 int startPos = range.getStartPos();
92109 int endPos = range.getEndPos();
93- return append(null, content, startPos, endPos);
110+ return append(null, srcContent, startPos, endPos);
94111 }
95112
96113 /**
97- * 実体参照の変換を行う。
98- * @param content 変換元文書
114+ * XHTML文字実体参照の変換を行う。
115+ *
116+ * @param srcContent 変換元文書
99117 * @param startPos 開始位置
100118 * @param endPos 終了位置
101119 * @return 切り出された変換済み文書
102120 * @throws IndexOutOfBoundsException 位置指定に不正があった
103121 */
104- public DecodedContent convert(DecodedContent content,
105- int startPos, int endPos)
122+ public DecodedContent convert(DecodedContent srcContent,
123+ int startPos, int endPos)
106124 throws IndexOutOfBoundsException{
107- return append(null, content, startPos, endPos);
108- }
109-
110- /**
111- * 実体参照の変換を行い既存のDecodedContentに追加を行う。
112- * @param target 追加先文書。nullなら新たな文書が用意される。
113- * @param content 変換元文書
114- * @return targetもしくは新規に用意された文書
115- * @throws IndexOutOfBoundsException 位置指定に不正があった
116- */
117- public DecodedContent append(DecodedContent target,
118- DecodedContent content)
119- throws IndexOutOfBoundsException{
120- int startPos = 0;
121- int endPos = content.length();
122- return append(target, content, startPos, endPos);
125+ return append(null, srcContent, startPos, endPos);
123126 }
124127
125128 /**
126- * 実体参照の変換を行い既存のDecodedContentに追加を行う。
127- * @param target 追加先文書。nullなら新たな文書が用意される。
128- * @param content 変換元文書
129- * @param range 範囲指定
130- * @return targetもしくは新規に用意された文書
129+ * XHTML文字実体参照の変換を行い既存のDecodedContentに追加を行う。
130+ *
131+ * @param dstContent 追加先文書。nullなら新たな文書が用意される。
132+ * @param srcContent 変換元文書
133+ * @return dstContentもしくは新規に用意された文書
131134 * @throws IndexOutOfBoundsException 位置指定に不正があった
132135 */
133- public DecodedContent append(DecodedContent target,
134- DecodedContent content,
135- SeqRange range )
136+ public DecodedContent append(DecodedContent dstContent,
137+ DecodedContent srcContent)
138+ throws IndexOutOfBoundsException{
139+ int startPos = 0;
140+ int endPos = srcContent.length();
141+ return append(dstContent, srcContent, startPos, endPos);
142+ }
143+
144+ /**
145+ * XHTML文字実体参照の変換を行い既存のDecodedContentに追加を行う。
146+ *
147+ * @param dstContent 追加先文書。nullなら新たな文書が用意される。
148+ * @param srcContent 変換元文書
149+ * @param range 範囲指定
150+ * @return dstContentもしくは新規に用意された文書
151+ * @throws IndexOutOfBoundsException 位置指定に不正があった
152+ */
153+ public DecodedContent append(DecodedContent dstContent,
154+ DecodedContent srcContent,
155+ SeqRange range )
136156 throws IndexOutOfBoundsException{
137157 int startPos = range.getStartPos();
138158 int endPos = range.getEndPos();
139- return append(target, content, startPos, endPos);
159+ return append(dstContent, srcContent, startPos, endPos);
140160 }
141161
142162 /**
143- * 実体参照の変換を行い既存のDecodedContentに追加を行う。
144- * @param target 追加先文書。nullなら新たな文書が用意される。
145- * @param content 変換元文書
163+ * XHTML文字実体参照の変換を行い既存のDecodedContentに追加を行う。
164+ *
165+ * @param dstContent 追加先文書。nullなら新たな文書が用意される。
166+ * @param srcContent 変換元文書
146167 * @param startPos 開始位置
147168 * @param endPos 終了位置
148- * @return targetもしくは新規に用意された文書
169+ * @return dstContentもしくは新規に用意された文書
149170 * @throws IndexOutOfBoundsException 位置指定に不正があった
150171 */
151- public DecodedContent append(DecodedContent target,
152- DecodedContent content,
153- int startPos, int endPos)
172+ public DecodedContent append(DecodedContent dstContent,
173+ DecodedContent srcContent,
174+ int startPos, int endPos)
154175 throws IndexOutOfBoundsException{
155176 if( startPos > endPos
156177 || startPos < 0
157- || content.length() < endPos){
178+ || srcContent.length() < endPos){
158179 throw new IndexOutOfBoundsException();
159180 }
160181
161182 DecodedContent result;
162- if(target == null){
183+ if(dstContent == null){
163184 int length = endPos - startPos;
164185 result = new DecodedContent(length);
165186 }else{
166- result = target;
187+ result = dstContent;
167188 }
168189
169- this.matcher.reset(content.getRawContent());
170- this.matcher.region(startPos, endPos);
190+ CharSequence rawContent = srcContent.getRawContent();
191+ this.multiMatcher.setText(rawContent, startPos, endPos);
171192
172193 int copiedPos = startPos;
173- while(this.matcher.find()){
174- int group = -1;
175- int matchStart = -1;
176- String altTxt = "";
177- for(RegexRep rr : VALUES_CACHE){
178- group = rr.getGroupNo();
179- matchStart = this.matcher.start(group);
180- if(matchStart >= 0){
181- if(rr == RegexRep.UCS4 && ! this.replaceSmp){
182- altTxt = this.matcher.group(group);
183- }else{
184- altTxt = rr.getAltTxt();
185- }
186- break;
187- }
194+
195+ for(;;){
196+ RepInfo repInfo = this.multiMatcher.multiFind();
197+ if(repInfo == null) break;
198+ if(repInfo == UCS4 && ! this.replaceSmp){
199+ continue;
188200 }
189- assert group >= 1;
190- int matchEnd = this.matcher.end(group);
191201
192- result.append(content, copiedPos, matchStart);
202+ int matchStart = this.multiMatcher.getMatchStart();
203+ int matchEnd = this.multiMatcher.getMatchEnd();
204+ result.append(srcContent, copiedPos, matchStart);
205+
206+ String altTxt = repInfo.getAltTxt();
193207 result.append(altTxt);
194208
195209 copiedPos = matchEnd;
196210 }
197- result.append(content, copiedPos, endPos);
198211
199- this.matcher.reset("");
212+ result.append(srcContent, copiedPos, endPos);
200213
201214 return result;
202215 }
203216
204217
205218 /**
206- * 文字列置換リスト。
219+ * 同時に複数の正規表現をOR探索するマッチャ。
207220 */
208- private static enum RegexRep{
221+ private static class MultiMatcher{
209222
210- GT ("&gt;", ">"),
211- LT ("&lt;", "<"),
212- AMP ("&amp;", "&"),
213- QUAT ("&quot;", DQ_STR),
214- BS (BS_PATTERN, YEN_STR),
215- UCS4 (UCS4_PATTERN, "?"),
216- ;
223+ private static final char REGEX_OR = '|';
224+ private static final char REGEX_GRPOPEN = '(';
225+ private static final char REGEX_GRPCLOSE = ')';
217226
218227
228+ private List<RepInfo> repInfoList;
229+ private Pattern orPattern;
230+
231+ private Matcher matcher;
232+
233+ private int matchStart = -1;
234+ private int matchEnd = -1;
235+
236+
237+ /**
238+ * コンストラクタ。
239+ */
240+ MultiMatcher(){
241+ super();
242+ return;
243+ }
244+
245+
246+ /**
247+ * 置換情報を設定する。
248+ *
249+ * <p>先頭の置換情報の方が優先的にマッチングされる。
250+ *
251+ * @param infos 置換情報並び
252+ */
253+ void putRepInfo(RepInfo... infos){
254+ List<RepInfo> list;
255+ list = Arrays.asList(infos);
256+ list = Collections.unmodifiableList(list);
257+ this.repInfoList = list;
258+
259+ StringBuilder orRegex = new StringBuilder();
260+ for(RepInfo repInfo : this.repInfoList){
261+ String regex = repInfo.getRegex();
262+
263+ if(orRegex.length() != 0) orRegex.append(REGEX_OR);
264+ orRegex.append(REGEX_GRPOPEN);
265+ orRegex.append(regex);
266+ orRegex.append(REGEX_GRPCLOSE);
267+ }
268+ this.orPattern = Pattern.compile(orRegex.toString());
269+
270+ this.matcher = this.orPattern.matcher("");
271+ this.matchStart = -1;
272+ this.matchEnd = -1;
273+
274+ return;
275+ }
276+
277+ /**
278+ * 走査対象を設定する。
279+ *
280+ * @param seq 対象文字列
281+ * @param startPos 走査開始位置
282+ * @param endPos 走査終了位置
283+ * @throws IllegalStateException 置換情報が未設定
284+ */
285+ void setText(CharSequence seq, int startPos, int endPos){
286+ if(this.matcher == null) throw new IllegalStateException();
287+
288+ this.matcher.reset(seq);
289+ this.matcher.region(startPos, endPos);
290+
291+ this.matchStart = -1;
292+ this.matchEnd = -1;
293+
294+ return;
295+ }
296+
297+ /**
298+ * マッチ開始位置を返す。
299+ *
300+ * @return 開始位置
301+ */
302+ int getMatchStart(){
303+ return this.matchStart;
304+ }
305+
306+ /**
307+ * マッチ終了位置を返す。
308+ *
309+ * @return 終了位置
310+ */
311+ int getMatchEnd(){
312+ return this.matchEnd;
313+ }
314+
315+ /**
316+ * 同時に複数の正規表現とのマッチングを試みるための走査を行う。
317+ *
318+ * <p>マッチングに伴いマッチ開始位置と終了位置が更新される。
319+ *
320+ * @return 最初にマッチングした正規表現。
321+ * マッチングしなければnullを返す。
322+ * @throws IllegalStateException 置換情報が未設定
323+ */
324+ RepInfo multiFind(){
325+ if(this.repInfoList == null || this.matcher == null){
326+ throw new IllegalStateException();
327+ }
328+
329+ if( ! this.matcher.find()) return null;
330+
331+ RepInfo result = null;
332+
333+ int group = 1;
334+ for(RepInfo rc : this.repInfoList){
335+ this.matchStart = this.matcher.start(group);
336+ this.matchEnd = this.matcher.end(group);
337+ if(this.matchStart >= 0){
338+ result = rc;
339+ break;
340+ }
341+ group++;
342+ }
343+
344+ return result;
345+ }
346+
347+ }
348+
349+
350+ /**
351+ * 文字列置換設定。
352+ */
353+ private static class RepInfo{
354+
219355 private final String regex;
220356 private final String altTxt;
221357
222358
223359 /**
224360 * コンストラクタ。
361+ *
362+ * <p>正規表現文字列に前方参照グループ記号()を含めてはならない。
363+ *
225364 * @param regex 置換元パターン正規表現
226365 * @param altTxt 置換文字列。
227366 */
228- RegexRep(String regex, String altTxt){
367+ RepInfo(String regex, String altTxt){
229368 this.regex = regex;
230369 this.altTxt = altTxt;
231370 return;
@@ -233,51 +372,23 @@
233372
234373
235374 /**
236- * 全正規表現をOR連結したパターンを生成する。
237- * @return パターン
375+ * 正規表現文字列を返す。
376+ *
377+ * @return 正規表現文字列
238378 */
239- private static Pattern buildPattern(){
240- StringBuilder orRegex = new StringBuilder();
241-
242- for(RegexRep rr : values()){
243- if(orRegex.length() > 0) orRegex.append('|');
244- orRegex.append('(');
245- orRegex.append(rr.regex);
246- orRegex.append(')');
247- }
248-
249- Pattern result = Pattern.compile(orRegex.toString());
250- return result;
379+ String getRegex(){
380+ return this.regex;
251381 }
252382
253383 /**
254- * マッチャを生成する。
255- * @return マッチャ
256- */
257- private static Matcher buildMatcher(){
258- Pattern pattern = buildPattern();
259- Matcher result = pattern.matcher("");
260- return result;
261- }
262-
263-
264- /**
265384 * 置換文字列を返す。
385+ *
266386 * @return 置換文字列
267387 */
268- private String getAltTxt(){
388+ String getAltTxt(){
269389 return this.altTxt;
270390 }
271391
272- /**
273- * パターン内において占めるグループ番号を返す。
274- * @return グループ番号
275- */
276- private int getGroupNo(){
277- int group = ordinal() + 1;
278- return group;
279- }
280-
281392 }
282393
283394 }
diff -r 4652c2fd346d -r 83725f5a1e4e src/main/java/jp/osdn/jindolf/parser/content/ContentBuilder.java
--- a/src/main/java/jp/osdn/jindolf/parser/content/ContentBuilder.java Sun Mar 25 12:23:15 2018 +0900
+++ b/src/main/java/jp/osdn/jindolf/parser/content/ContentBuilder.java Wed Apr 04 22:21:25 2018 +0900
@@ -102,7 +102,16 @@
102102 @Override
103103 public void charContent(char[] charArray, int offset, int length)
104104 throws DecodeBreakException{
105+ if(length > 0){
106+ char ch1st = charArray[0];
107+ assert ! Character.isLowSurrogate(ch1st);
108+
109+ char chLast = charArray[length - 1];
110+ assert ! Character.isHighSurrogate(chLast);
111+ }
112+
105113 getContent().append(charArray, offset, length);
114+
106115 return;
107116 }
108117
diff -r 4652c2fd346d -r 83725f5a1e4e src/main/java/jp/osdn/jindolf/parser/content/DecodeErrorInfo.java
--- a/src/main/java/jp/osdn/jindolf/parser/content/DecodeErrorInfo.java Sun Mar 25 12:23:15 2018 +0900
+++ b/src/main/java/jp/osdn/jindolf/parser/content/DecodeErrorInfo.java Wed Apr 04 22:21:25 2018 +0900
@@ -1,5 +1,5 @@
11 /*
2- * invalid Shift_JIS decoding information
2+ * invalid character decoding information
33 *
44 * License : The MIT License
55 * Copyright(c) 2009 olyutorskii
@@ -8,12 +8,19 @@
88 package jp.osdn.jindolf.parser.content;
99
1010 import java.util.Comparator;
11+import java.util.List;
12+import java.util.RandomAccess;
1113
1214 /**
13- * 不正な Shift_JIS デコードの情報。
15+ * 文字デコード異常系の発生により
16+ * {@link DecodedContent}に代替文字とエラーバイトが埋め込まれた事実を
17+ * 記録する。
18+ *
19+ * <p>エラーの原因となったバイト値に関する情報は、
1420 * 1バイトもしくは2バイトで構成される。
15- * 1バイトの場合はおそらくエンコーディングに関するエラー。
16- * 2バイトの場合はおそらく文字集合に関するエラー。
21+ * 2バイトの場合はおそらくシフトJISの文字集合に関するエラー。
22+ *
23+ * <p>{@link DecodedContent}内での代替文字出現位置をchar単位で保持する。
1724 */
1825 public class DecodeErrorInfo{
1926
@@ -21,23 +28,28 @@
2128 public static final Comparator<DecodeErrorInfo> POS_COMPARATOR =
2229 new PosComparator();
2330
31+ static final int BSEARCH_THRESHOLD = 16;
32+
33+
2434 private final int charPos;
2535 private final boolean has2ndFlag;
2636 private final byte rawByte1st;
2737 private final byte rawByte2nd;
2838
39+
2940 /**
3041 * 下請けコンストラクタ。
31- * @param charPos デコードエラーで置き換えられた文字列の開始位置
42+ *
43+ * @param charPos デコードエラーで置き換えられた代替文字の出現位置
3244 * @param has2ndFlag 2バイト目が有効ならtrueを渡す。
33- * @param rawByte1st デコードエラーを引き起こした最初のバイト値
45+ * @param rawByte1st デコードエラーを引き起こした1番目のバイト値
3446 * @param rawByte2nd デコードエラーを引き起こした2番目のバイト値
3547 * @throws IndexOutOfBoundsException charPosが負
3648 */
3749 private DecodeErrorInfo(int charPos,
38- boolean has2ndFlag,
39- byte rawByte1st,
40- byte rawByte2nd)
50+ boolean has2ndFlag,
51+ byte rawByte1st,
52+ byte rawByte2nd)
4153 throws IndexOutOfBoundsException{
4254 if(charPos < 0) throw new IndexOutOfBoundsException();
4355
@@ -51,14 +63,15 @@
5163
5264 /**
5365 * コンストラクタ。
54- * @param charPos デコードエラーで置き換えられた文字列の開始位置
55- * @param rawByte1st デコードエラーを引き起こした最初のバイト値
66+ *
67+ * @param charPos デコードエラーで置き換えられた代替文字の出現位置
68+ * @param rawByte1st デコードエラーを引き起こした1番目のバイト値
5669 * @param rawByte2nd デコードエラーを引き起こした2番目のバイト値
5770 * @throws IndexOutOfBoundsException charPosが負
5871 */
5972 public DecodeErrorInfo(int charPos,
60- byte rawByte1st,
61- byte rawByte2nd)
73+ byte rawByte1st,
74+ byte rawByte2nd)
6275 throws IndexOutOfBoundsException{
6376 this(charPos, true, rawByte1st, rawByte2nd);
6477 return;
@@ -66,27 +79,122 @@
6679
6780 /**
6881 * コンストラクタ。
69- * @param charPos デコードエラーで置き換えられた文字列の開始位置
82+ *
83+ * @param charPos デコードエラーで置き換えられた代替文字の出現位置
7084 * @param rawByte1st デコードエラーを引き起こしたバイト値
7185 * @throws IndexOutOfBoundsException charPosが負
7286 */
7387 public DecodeErrorInfo(int charPos,
74- byte rawByte1st)
88+ byte rawByte1st)
7589 throws IndexOutOfBoundsException{
7690 this(charPos, false, rawByte1st, (byte) 0x00);
7791 return;
7892 }
7993
94+
8095 /**
81- * デコードエラーで置き換えられた文字列の開始位置を返す。
82- * @return デコードエラーで置き換えられた文字列の開始位置
96+ * 与えられた文字位置を含むか、またはそれ以降で最も小さな位置情報を持つ
97+ * デコードエラーのインデックス位置を返す。※リニアサーチ版。
98+ *
99+ * @param errList デコードエラーのリスト
100+ * @param charPos 代替文字位置
101+ * @return 0から始まるリスト内の位置。
102+ * 文字位置の一致するデコードエラーがなければ
103+ * リストへの挿入ポイントが返る。
104+ */
105+ public static int lsearchErrorIndex(List<DecodeErrorInfo> errList,
106+ int charPos){
107+ int idx = 0;
108+ for(DecodeErrorInfo einfo : errList){
109+ int errCharPos = einfo.getCharPosition();
110+ if(charPos <= errCharPos) break;
111+ idx++;
112+ }
113+
114+ return idx;
115+ }
116+
117+ /**
118+ * 与えられた文字位置を含むか、またはそれ以降で最も小さな位置情報を持つ
119+ * デコードエラーのインデックス位置を返す。※バイナリサーチ版。
120+ *
121+ * @param errList デコードエラーのリスト
122+ * @param charPos 代替文字位置
123+ * @return 0から始まるリスト内の位置。
124+ * 文字位置の一致するデコードエラーがなければ
125+ * リストへの挿入ポイントが返る。
126+ */
127+ public static int bsearchErrorIndex(List<DecodeErrorInfo> errList,
128+ int charPos){
129+ int floorIdx = 0;
130+ int roofIdx = errList.size() - 1;
131+
132+ while(floorIdx <= roofIdx){
133+ int gapHalf = (roofIdx - floorIdx) / 2; // 切り捨て
134+ int midIdx = floorIdx + gapHalf;
135+ DecodeErrorInfo einfo = errList.get(midIdx);
136+ int errCharPos = einfo.getCharPosition();
137+
138+ if (errCharPos < charPos) floorIdx = midIdx + 1;
139+ else if(errCharPos > charPos) roofIdx = midIdx - 1;
140+ else return midIdx;
141+ }
142+
143+ return floorIdx;
144+ }
145+
146+ /**
147+ * 与えられた文字位置を含むか、またはそれ以降で最も小さな位置情報を持つ
148+ * デコードエラーのインデックス位置を返す。
149+ *
150+ * <p>ランダムアクセスの可否、および要素数の増減に応じて
151+ * リニアサーチとバイナリサーチを使い分ける。
152+ *
153+ * @param errList デコードエラーのリスト
154+ * @param charPos 代替文字位置
155+ * @return 0から始まるリスト内の位置。
156+ * 文字位置の一致するデコードエラーがなければ
157+ * リストへの挿入ポイントが返る。
158+ */
159+ public static int searchErrorIndex(List<DecodeErrorInfo> errList,
160+ int charPos){
161+ int result;
162+
163+ boolean useLinear;
164+ if(errList instanceof RandomAccess){
165+ if(errList.size() < BSEARCH_THRESHOLD){
166+ useLinear = true;
167+ }else{
168+ useLinear = false;
169+ }
170+ }else{
171+ useLinear = true;
172+ }
173+
174+ if(useLinear){
175+ // linear-search
176+ result = lsearchErrorIndex(errList, charPos);
177+ }else{
178+ // binary-search
179+ result = bsearchErrorIndex(errList, charPos);
180+ }
181+
182+ return result;
183+ }
184+
185+
186+ /**
187+ * エラー代替文字の出現位置を返す。
188+ *
189+ * @return 代替文字の開始位置
83190 */
84191 public int getCharPosition(){
85192 return this.charPos;
86193 }
87194
88195 /**
89- * 2バイト目の情報を持つか判定する。
196+ * 2バイト目のエラー情報を持つか判定する。
197+ *
90198 * @return 2バイト目の情報を持つならtrue
91199 */
92200 public boolean has2nd(){
@@ -94,7 +202,8 @@
94202 }
95203
96204 /**
97- * 1バイト目の値を返す。
205+ * 1バイト目のエラーバイト値を返す。
206+ *
98207 * @return 1バイト目の値
99208 */
100209 public byte getRawByte1st(){
@@ -102,7 +211,8 @@
102211 }
103212
104213 /**
105- * 2バイト目の値を返す。
214+ * 2バイト目のエラーバイト値を返す。
215+ *
106216 * @return 2バイト目の値
107217 * @throws IllegalStateException 2バイト目の情報を把持していないとき
108218 */
@@ -113,27 +223,26 @@
113223
114224 /**
115225 * 出現位置のみが違う複製オブジェクトを生成する。
226+ *
116227 * @param gap 出現位置から引きたい値。正の値なら文字開始位置に向かう。
117228 * @return 複製オブジェクト
118229 * @throws IndexOutOfBoundsException 再計算された出現位置が負
119230 */
120231 public DecodeErrorInfo createGappedClone(int gap)
121232 throws IndexOutOfBoundsException{
122- DecodeErrorInfo result;
123-
124233 int newPos = this.charPos - gap;
125- if(this.has2ndFlag){
126- result = new DecodeErrorInfo(newPos,
127- this.rawByte1st, this.rawByte2nd);
128- }else{
129- result = new DecodeErrorInfo(newPos, this.rawByte1st);
130- }
234+
235+ DecodeErrorInfo result;
236+ result = new DecodeErrorInfo(newPos,
237+ this.has2ndFlag,
238+ this.rawByte1st, this.rawByte2nd);
131239
132240 return result;
133241 }
134242
135243 /**
136244 * {@inheritDoc}
245+ *
137246 * @return {@inheritDoc}
138247 */
139248 @Override
@@ -148,8 +257,8 @@
148257 result.append(hex);
149258
150259 if(this.has2ndFlag){
260+ result.append(':');
151261 hex = Integer.toHexString(this.rawByte2nd & 0xff);
152- result.append(':');
153262 if(hex.length() <= 1) result.append('0');
154263 result.append(hex);
155264 }
@@ -160,6 +269,7 @@
160269 /**
161270 * 出現位置で順序づける比較子。
162271 */
272+ @SuppressWarnings("serial")
163273 private static final class PosComparator
164274 implements Comparator<DecodeErrorInfo> {
165275
@@ -173,22 +283,26 @@
173283
174284 /**
175285 * {@inheritDoc}
286+ *
176287 * @param info1 {@inheritDoc}
177288 * @param info2 {@inheritDoc}
178289 * @return {@inheritDoc}
179290 */
180291 @Override
181292 public int compare(DecodeErrorInfo info1, DecodeErrorInfo info2){
182- int pos1;
183- int pos2;
293+ if (info1 == info2) return 0;
294+ else if(info1 == null) return -1;
295+ else if(info2 == null) return 1;
184296
185- if(info1 == null) pos1 = -1;
186- else pos1 = info1.charPos;
297+ int pos1 = info1.getCharPosition();
298+ int pos2 = info2.getCharPosition();
187299
188- if(info2 == null) pos2 = -1;
189- else pos2 = info2.charPos;
300+ int result;
301+ if (pos1 < pos2) result = -1;
302+ else if(pos1 > pos2) result = 1;
303+ else result = 0;
190304
191- return pos1 - pos2;
305+ return result;
192306 }
193307
194308 }
diff -r 4652c2fd346d -r 83725f5a1e4e src/main/java/jp/osdn/jindolf/parser/content/DecodedContent.java
--- a/src/main/java/jp/osdn/jindolf/parser/content/DecodedContent.java Sun Mar 25 12:23:15 2018 +0900
+++ b/src/main/java/jp/osdn/jindolf/parser/content/DecodedContent.java Wed Apr 04 22:21:25 2018 +0900
@@ -13,10 +13,11 @@
1313 import java.util.RandomAccess;
1414
1515 /**
16- * デコードエラー情報を含む再利用可能な文字列。
16+ * デコードエラー情報と共に追記可能な可変文字列。
1717 *
1818 * <p>デコードエラーを起こした箇所は代替文字{@link #ALTCHAR}で置き換えられる。
19- * マルチスレッドには非対応。
19+ *
20+ * <p>マルチスレッドには非対応。
2021 */
2122 public class DecodedContent
2223 implements CharSequence,
@@ -24,48 +25,50 @@
2425
2526 /**
2627 * 代替文字。
27- * {@literal HTMLで使うなら < や > や & や " や ' はやめて!}
28+ *
29+ * <p>{@literal HTMLで使うなら < や > や & や " や ' は避けた方が無難}
2830 */
2931 public static final char ALTCHAR = '?';
3032
33+ private static final List<DecodeErrorInfo> EMPTY_LIST;
3134 private static final String NULLTEXT = "null";
3235
33- private static final List<DecodeErrorInfo> EMPTY_LIST =
34- Collections.emptyList();
35-
36- private static final int BSEARCH_THRESHOLD = 16;
37-
3836 static{
39- assert ALTCHAR != '<';
40- assert ALTCHAR != '>';
41- assert ALTCHAR != '&';
42- assert ALTCHAR != '"';
43- assert ALTCHAR != '\'';
44- assert ALTCHAR != '\\';
37+ List<DecodeErrorInfo> emptyList;
38+ emptyList = Collections.emptyList();
39+ emptyList = Collections.unmodifiableList(emptyList);
40+ EMPTY_LIST = emptyList;
41+
42+ assert createErrorList() instanceof RandomAccess;
4543 }
4644
4745
4846 private final StringBuilder rawContent = new StringBuilder();
49-
50- private List<DecodeErrorInfo> decodeError;
47+ private List<DecodeErrorInfo> errList;
5148
5249
5350 /**
5451 * コンストラクタ。
52+ *
53+ * <p>長さ0の文字列が反映され、デコードエラー総数は0件となる。
5554 */
5655 public DecodedContent(){
57- this("");
56+ super();
57+ initImpl();
5858 return;
5959 }
6060
6161 /**
6262 * コンストラクタ。
63+ *
64+ * <p>引数の文字列が反映され、デコードエラー総数は0件となる。
65+ *
66+ * <p>nullが渡されると文字列"null"として解釈される。
67+ *
6368 * @param seq 初期化文字列
64- * @throws NullPointerException 引数がnull
6569 */
66- public DecodedContent(CharSequence seq) throws NullPointerException{
70+ public DecodedContent(CharSequence seq){
6771 super();
68- if(seq == null) throw new NullPointerException();
6972 initImpl();
7073 this.rawContent.append(seq);
7174 return;
@@ -73,6 +76,13 @@
7376
7477 /**
7578 * コンストラクタ。
79+ *
80+ * <p>長さ0の文字列が反映され、デコードエラー総数は0件となる。
81+ *
82+ * <p>文字数の初期容量を引数で指定する。
83+ * 文字列長が初期容量を超えるまでの間、
84+ * 文字列格納の再割り当てが起こらないことが期待される。
85+ *
7686 * @param capacity 文字数の初期容量
7787 * @throws NegativeArraySizeException 容量が負の値
7888 */
@@ -86,124 +96,114 @@
8696
8797
8898 /**
89- * 与えられた文字位置を含むか、またはそれ以降で最も小さな位置情報を持つ
90- * デコードエラーのインデックス位置を返す。※リニアサーチ版。
91- * @param errList デコードエラーのリスト
92- * @param startPos 文字位置
93- * @return 0から始まるリスト内の位置。
94- * 一致する文字位置がなければ挿入ポイント。
99+ * ギャップ情報が加味されたデコードエラー情報を、
100+ * 範囲指定込みで指定エラーリストに追加コピーする。
101+ *
102+ * <p>追加先エラーリストがnullだった場合、
103+ * 必要に応じてエラーリストが生成され
104+ * 戻り値となる場合がありうる。
105+ *
106+ * @param srcErrList 追加元エラーリスト
107+ * @param startCharPt 範囲開始位置
108+ * @param endCharPt 範囲終了位置
109+ * @param dstErrList 追加先エラーリスト。nullでもよい。
110+ * 追加元と異なるリストでなければならない。
111+ * @param gap ギャップ量
112+ * @return 引数targetErrorもしくは新規生成されたリストを返す。
113+ * なにもコピーされなければnullを返す。
114+ * @throws IllegalArgumentException 追加元リストと追加先リストが
115+ * 同一インスタンス
95116 */
96- protected static int lsearchErrorIndex(List<DecodeErrorInfo> errList,
97- int startPos){
98- // assert errList instanceof RandomAccess;
117+ static List<DecodeErrorInfo> appendGappedErrorInfo(
118+ List<DecodeErrorInfo> srcErrList,
119+ int startCharPt, int endCharPt,
120+ List<DecodeErrorInfo> dstErrList,
121+ int gap
122+ ){
123+ if(srcErrList == dstErrList) throw new IllegalArgumentException();
124+ if(startCharPt >= endCharPt) return dstErrList;
99125
100- int errSize = errList.size();
101-
102- int idx;
103- for(idx = 0; idx < errSize; idx++){
104- DecodeErrorInfo einfo = errList.get(idx);
105- int errPos = einfo.getCharPosition();
106- if(startPos <= errPos) break;
126+ int startErrorIdx =
127+ DecodeErrorInfo.searchErrorIndex(srcErrList, startCharPt);
128+ int errSize = srcErrList.size();
129+ if(startErrorIdx >= errSize){
130+ return null;
107131 }
108132
109- return idx;
110- }
133+ int endErrorIdx = calcEndIdx(srcErrList, endCharPt);
111134
112- /**
113- * 与えられた文字位置を含むか、またはそれ以降で最も小さな位置情報を持つ
114- * デコードエラーのインデックス位置を返す。※バイナリサーチ版。
115- * @param errList デコードエラーのリスト
116- * @param startPos 文字位置
117- * @return 0から始まるリスト内の位置。
118- * 一致する文字位置がなければ挿入ポイント。
119- */
120- protected static int bsearchErrorIndex(List<DecodeErrorInfo> errList,
121- int startPos){
122- // assert errList instanceof RandomAccess;
123-
124- int floor = 0;
125- int roof = errList.size() - 1;
126-
127- while(floor <= roof){
128- int gapHalf = (roof - floor) / 2; // 切り捨て
129- int midpoint = floor + gapHalf;
130- DecodeErrorInfo einfo = errList.get(midpoint);
131- int cmp = einfo.getCharPosition() - startPos;
132-
133- if (cmp < 0) floor = midpoint + 1;
134- else if(cmp > 0) roof = midpoint - 1;
135- else return midpoint;
135+ boolean hasLoop =
136+ (0 <= startErrorIdx) && (startErrorIdx <= endErrorIdx);
137+ if( ! hasLoop){
138+ return null;
136139 }
137140
138- return floor;
139- }
141+ List<DecodeErrorInfo> result;
142+ if(dstErrList == null) result = createErrorList();
143+ else result = dstErrList;
140144
141- /**
142- * 与えられた文字位置を含むか、またはそれ以降で最も小さな位置情報を持つ
143- * デコードエラーのインデックス位置を返す。
144- * 要素数の増減に応じてリニアサーチとバイナリサーチを使い分ける。
145- * @param errList デコードエラーのリスト
146- * @param startPos 文字位置
147- * @return 0から始まるリスト内の位置。
148- * 一致する文字位置がなければ挿入ポイント。
149- */
150- protected static int searchErrorIndex(List<DecodeErrorInfo> errList,
151- int startPos){
152- int result;
153-
154- int errSize = errList.size();
155- if(errSize < BSEARCH_THRESHOLD){
156- // linear-search
157- result = lsearchErrorIndex(errList, startPos);
158- }else{
159- // binary-search
160- result = bsearchErrorIndex(errList, startPos);
161- }
162-
145+ copyGappedErrorInfo(srcErrList,
146+ startErrorIdx, endErrorIdx,
147+ result, gap);
163148 return result;
164149 }
165150
166151 /**
167- * ギャップ情報が加味されたデコードエラー情報を、
168- * 範囲指定込みで指定エラーリストに追加転記する。
169- * 追加先エラーリストがnullだった場合、必要に応じてエラーリストが生成され
170- * 戻り値となる場合がありうる。
171- * @param sourceContent 元の文字列
172- * @param startPos 範囲開始位置
173- * @param endPos 範囲終了位置
174- * @param targetError 追加先エラーリスト。nullでもよい。
175- * @param gap ギャップ量
176- * @return 引数targetErrorもしくは新規生成されたリストを返す。
152+ * 文字列終了点からエラーリスト範囲の最終インデックスを求める。
153+ *
154+ * @param srcErrList エラーリスト
155+ * @param endCharPt 文字列終了点
156+ * @return リスト範囲の最終インデックス
177157 */
178- protected static List<DecodeErrorInfo>
179- appendGappedErrorInfo(DecodedContent sourceContent,
180- int startPos, int endPos,
181- List<DecodeErrorInfo> targetError,
182- int gap){
183- List<DecodeErrorInfo> sourceError = sourceContent.decodeError;
184- List<DecodeErrorInfo> result = targetError;
158+ private static int calcEndIdx(List<DecodeErrorInfo> srcErrList,
159+ int endCharPt){
160+ int lastCharPos = endCharPt - 1;
185161
186- int startErrorIdx = searchErrorIndex(sourceError, startPos);
187- int endErrorIdx = sourceError.size() - 1;
188- assert endErrorIdx >= 0;
162+ int endErrorIdx =
163+ DecodeErrorInfo.searchErrorIndex(srcErrList, lastCharPos);
189164
190- for(int index = startErrorIdx; index <= endErrorIdx; index++){
191- DecodeErrorInfo einfo = sourceError.get(index);
192- int pos = einfo.getCharPosition();
193- if(pos < startPos) continue;
194- if(pos >= endPos) break;
195- DecodeErrorInfo newInfo = einfo.createGappedClone(gap);
196- if(result == null){
197- result = createErrorList();
165+ int errSize = srcErrList.size();
166+ if(endErrorIdx >= errSize){
167+ endErrorIdx = errSize - 1;
168+ }else{
169+ DecodeErrorInfo lastErrorInfo = srcErrList.get(endErrorIdx);
170+
171+ boolean isLastErrorInfoOnLastPos =
172+ lastErrorInfo.getCharPosition() == lastCharPos;
173+ if( ! isLastErrorInfoOnLastPos){
174+ endErrorIdx--;
198175 }
199- result.add(newInfo);
200176 }
201177
202- return result;
178+ return endErrorIdx;
179+ }
180+
181+ /**
182+ * エラーリストの一部の範囲を、gapを加味して別リストに追加コピーする。
183+ *
184+ * @param srcErrList コピー元リスト
185+ * @param startErrorIdx コピー元の範囲開始インデックス
186+ * @param endErrorIdx コピー元の範囲終了インデックス
187+ * @param dstErrList コピー先リスト
188+ * @param gap 代替文字出現位置ギャップ量
189+ */
190+ private static void copyGappedErrorInfo(
191+ List<DecodeErrorInfo> srcErrList,
192+ int startErrorIdx, int endErrorIdx,
193+ List<DecodeErrorInfo> dstErrList,
194+ int gap
195+ ){
196+ for(int index = startErrorIdx; index <= endErrorIdx; index++){
197+ DecodeErrorInfo srcErrInfo = srcErrList.get(index);
198+ DecodeErrorInfo gappedInfo = srcErrInfo.createGappedClone(gap);
199+ dstErrList.add(gappedInfo);
200+ }
201+ return;
203202 }
204203
205204 /**
206205 * エラー格納用リストを生成する。
206+ *
207207 * @return リスト
208208 */
209209 private static List<DecodeErrorInfo> createErrorList(){
@@ -211,19 +211,28 @@
211211 return result;
212212 }
213213
214- static{
215- assert createErrorList() instanceof RandomAccess;
214+
215+ /**
216+ * 初期化。
217+ *
218+ * <p>長さ0の文字列&デコードエラー無しの状態になる。
219+ * コンストラクタで新インスタンスを作るより低コスト。
220+ */
221+ public void init(){
222+ initImpl();
223+ return;
216224 }
217225
218226 /**
219227 * 初期化下請け。
220- * 長さ0の文字列&デコードエラー無しの状態になる。
228+ *
229+ * <p>長さ0の文字列&デコードエラー無しの状態になる。
221230 */
222231 private void initImpl(){
223232 this.rawContent.setLength(0);
224233
225- if(this.decodeError != null){
226- this.decodeError.clear();
234+ if(this.errList != null){
235+ this.errList.clear();
227236 }
228237
229238 return;
@@ -231,7 +240,9 @@
231240
232241 /**
233242 * 事前にキャパシティを確保する。
234- * 指定されたキャパシティの範囲内で再割り当てが起きないことを保証する。
243+ *
244+ * <p>指定されたキャパシティの範囲内で再割り当てが起きないことを保証する。
245+ *
235246 * @param minimumCapacity キャラクタ単位のキャパシティ長。
236247 */
237248 public void ensureCapacity(int minimumCapacity){
@@ -240,39 +251,35 @@
240251 }
241252
242253 /**
243- * 初期化。
244- * 長さ0の文字列&デコードエラー無しの状態になる。
245- * コンストラクタで新インスタンスを作るより低コスト。
246- */
247- public void init(){
248- initImpl();
249- return;
250- }
251-
252- /**
253- * デコードエラーを含むか判定する。
254+ * デコードエラーを含むか否か判定する。
255+ *
254256 * @return デコードエラーを含むならtrue
255257 */
256258 public boolean hasDecodeError(){
257- if(this.decodeError == null) return false;
258- if(this.decodeError.isEmpty()) return false;
259+ if(this.errList == null) return false;
260+ if(this.errList.isEmpty()) return false;
259261 return true;
260262 }
261263
262264 /**
263265 * デコードエラーの一覧を取得する。
266+ *
264267 * @return デコードエラーの一覧
265268 */
266269 public List<DecodeErrorInfo> getDecodeErrorList(){
267270 if( ! hasDecodeError() ){
268271 return EMPTY_LIST;
269272 }
270- return Collections.unmodifiableList(this.decodeError);
273+ return Collections.unmodifiableList(this.errList);
271274 }
272275
273276 /**
274277 * 生の文字列を得る。
275- * 高速なCharSequenceアクセス用途。
278+ *
279+ * <p>戻り値からエラー情報は消される。
280+ *
281+ * <p>高速なCharSequenceアクセス用途。
282+ *
276283 * @return 生の文字列。
277284 */
278285 public CharSequence getRawContent(){
@@ -281,6 +288,9 @@
281288
282289 /**
283290 * 指定された位置の文字を変更する。
291+ *
292+ * <p>デコードエラーにより追加された代替文字の変更も可能。
293+ *
284294 * @param index 文字位置
285295 * @param ch 文字
286296 * @throws IndexOutOfBoundsException 不正な位置指定
@@ -293,6 +303,7 @@
293303
294304 /**
295305 * {@inheritDoc}
306+ *
296307 * @param index {@inheritDoc}
297308 * @return {@inheritDoc}
298309 */
@@ -303,6 +314,7 @@
303314
304315 /**
305316 * {@inheritDoc}
317+ *
306318 * @return {@inheritDoc}
307319 */
308320 @Override
@@ -312,36 +324,40 @@
312324
313325 /**
314326 * {@inheritDoc}
315- * @param start {@inheritDoc}
316- * @param end {@inheritDoc}
327+ *
328+ * @param startCharPt {@inheritDoc}
329+ * @param endCharPt {@inheritDoc}
317330 * @return {@inheritDoc}
318331 */
319332 @Override
320- public CharSequence subSequence(int start, int end){
321- return this.rawContent.subSequence(start, end);
333+ public CharSequence subSequence(int startCharPt, int endCharPt){
334+ return this.rawContent.subSequence(startCharPt, endCharPt);
322335 }
323336
324337 /**
325338 * 範囲指定されたサブコンテントを切り出す。
326- * サブコンテントにはデコードエラー情報が引き継がれる。
327- * @param start 開始位置
328- * @param end 終了位置
339+ *
340+ * <p>サブコンテントにはデコードエラー情報が引き継がれる。
341+ *
342+ * @param startCharPt 開始位置
343+ * @param endCharPt 終了位置
329344 * @return サブコンテント
330345 * @throws IndexOutOfBoundsException start または end が負の値の場合、
331346 * end が length() より大きい場合、
332347 * あるいは start が end より大きい場合
333348 */
334- public DecodedContent subContent(int start, int end)
349+ public DecodedContent subContent(int startCharPt, int endCharPt)
335350 throws IndexOutOfBoundsException{
336- int length = end - start;
351+ int length = endCharPt - startCharPt;
337352 if(length < 0) throw new IndexOutOfBoundsException();
338353 DecodedContent result = new DecodedContent(length);
339- result.append(this, start, end);
354+ result.append(this, startCharPt, endCharPt);
340355 return result;
341356 }
342357
343358 /**
344- * 文字を追加する。
359+ * 文字を末尾へ追加する。
360+ *
345361 * @param letter 追加する文字
346362 * @return thisオブジェクト
347363 */
@@ -352,125 +368,166 @@
352368 }
353369
354370 /**
355- * 文字列を追加する。
371+ * 文字列を末尾へ追加する。
372+ *
373+ * <p>nullが渡されると文字列"null"として解釈される。
374+ *
356375 * @param seq 追加する文字列
357376 * @return thisオブジェクト
358377 */
359378 @Override
360379 public DecodedContent append(CharSequence seq){
380+ DecodedContent result;
381+
361382 if(seq == null){
362- this.rawContent.append(NULLTEXT);
383+ result = append(NULLTEXT);
363384 }else if(seq instanceof DecodedContent){
364- append((DecodedContent) seq, 0, seq.length());
385+ DecodedContent content = (DecodedContent) seq;
386+ int seqLen = seq.length();
387+ result = append(content, 0, seqLen);
365388 }else{
366389 this.rawContent.append(seq);
390+ result = this;
367391 }
368- return this;
392+
393+ return result;
369394 }
370395
371396 /**
372- * 文字列を追加する。
397+ * 文字列を末尾へ追加する。
398+ *
399+ * <p>nullが渡されると文字列"null"として解釈される。
400+ *
373401 * @param seq 追加する文字列
374- * @param startPos 開始位置
375- * @param endPos 終了位置
402+ * @param startCharPt 開始位置
403+ * @param endCharPt 終了位置
376404 * @return thisオブジェクト
377405 * @throws IndexOutOfBoundsException 範囲指定が変。
378406 */
379407 @Override
380408 public DecodedContent append(CharSequence seq,
381- int startPos, int endPos)
409+ int startCharPt, int endCharPt)
382410 throws IndexOutOfBoundsException{
411+ DecodedContent result;
412+
383413 if(seq == null){
384- this.rawContent.append(NULLTEXT);
414+ result = append(NULLTEXT, startCharPt, endCharPt);
385415 }else if(seq instanceof DecodedContent){
386- append((DecodedContent) seq, startPos, endPos);
416+ result = append((DecodedContent) seq, startCharPt, endCharPt);
417+ }else if( startCharPt < 0
418+ || startCharPt > endCharPt
419+ || endCharPt > seq.length()){
420+ throw new IndexOutOfBoundsException();
421+ }else if(startCharPt == endCharPt){
422+ result = this;
387423 }else{
388- this.rawContent.append(seq, startPos, endPos);
424+ this.rawContent.append(seq, startCharPt, endCharPt);
425+ result = this;
426+ }
427+
428+ return result;
429+ }
430+
431+ /**
432+ * 文字列を末尾へ追加する。
433+ *
434+ * @param str 追加する文字配列
435+ * @param offset 追加される最初の char のインデックス
436+ * @param len 追加される char の数
437+ * @return thisオブジェクト
438+ * @throws IndexOutOfBoundsException 範囲指定が不正。
439+ * @see StringBuffer#append(char[], int, int)
440+ */
441+ public DecodedContent append(char[] str, int offset, int len)
442+ throws IndexOutOfBoundsException{
443+ this.rawContent.append(str, offset, len);
444+ return this;
445+ }
446+
447+ /**
448+ * 文字列を末尾へ追加する。
449+ *
450+ * <p>追加元のエラー情報は追加先へ引き継がれる。
451+ *
452+ * <p>nullが渡されると文字列"null"として解釈される。
453+ *
454+ * @param source 追加する文字列
455+ * @param startCharPt 開始位置
456+ * @param endCharPt 終了位置
457+ * @return thisオブジェクト
458+ * @throws IndexOutOfBoundsException 範囲指定が変。
459+ * @see Appendable#append(CharSequence, int, int)
460+ */
461+ public DecodedContent append(DecodedContent source,
462+ int startCharPt, int endCharPt)
463+ throws IndexOutOfBoundsException{
464+ if(source == null){
465+ return append(NULLTEXT, startCharPt, endCharPt);
466+ }
467+
468+ if( startCharPt < 0
469+ || startCharPt > endCharPt
470+ || endCharPt > source.length()){
471+ throw new IndexOutOfBoundsException();
472+ }else if(startCharPt == endCharPt){
473+ return this;
474+ }
475+
476+ int oldLength = this.rawContent.length();
477+
478+ this.rawContent.append(source.rawContent, startCharPt, endCharPt);
479+
480+ List<DecodeErrorInfo> srcErrList;
481+ if(source.hasDecodeError()){
482+ srcErrList = source.errList;
483+ }else{
484+ return this;
485+ }
486+
487+ List<DecodeErrorInfo> dstErrList;
488+ if(source == this) dstErrList = null;
489+ else dstErrList = this.errList;
490+
491+ int gap = startCharPt - oldLength;
492+
493+ dstErrList = appendGappedErrorInfo(srcErrList,
494+ startCharPt, endCharPt,
495+ dstErrList,
496+ gap);
497+
498+ if(dstErrList == null) return this;
499+ if(dstErrList == this.errList) return this;
500+
501+ if(this.errList == null){
502+ this.errList = dstErrList;
503+ }else{
504+ this.errList.addAll(dstErrList);
389505 }
390506
391507 return this;
392508 }
393509
394510 /**
395- * 文字列を追加する。
511+ * 代替文字とともにデコードエラーを末尾へ追加する。
396512 *
397- * @param str 追加される文字
398- * @param offset 追加される最初の char のインデックス
399- * @param len 追加される char の数
400- * @return thisオブジェクト
401- * @throws IndexOutOfBoundsException 範囲指定が不正。
402- */
403- public DecodedContent append(char[] str, int offset, int len)
404- throws IndexOutOfBoundsException{
405- if(str == null){
406- this.rawContent.append(NULLTEXT);
407- }else{
408- this.rawContent.append(str, offset, len);
409- }
410-
411- return this;
412- }
413-
414- /**
415- * 文字列を追加する。
416- * @param source 追加する文字列
417- * @param startPos 開始位置
418- * @param endPos 終了位置
419- * @return thisオブジェクト
420- * @throws IndexOutOfBoundsException 範囲指定が変。
421- */
422- public DecodedContent append(DecodedContent source,
423- int startPos, int endPos)
424- throws IndexOutOfBoundsException{
425- if(source == null){
426- return append(NULLTEXT);
427- }
428-
429- int gap = startPos - this.rawContent.length();
430-
431- this.rawContent.append(source.rawContent, startPos, endPos);
432-
433- if( ! source.hasDecodeError() ) return this;
434-
435- List<DecodeErrorInfo> targetErrorList;
436- if(source != this) targetErrorList = this.decodeError;
437- else targetErrorList = null;
438-
439- targetErrorList = appendGappedErrorInfo(source,
440- startPos, endPos,
441- targetErrorList,
442- gap);
443-
444- if(targetErrorList == null) return this;
445- if(targetErrorList == this.decodeError) return this;
446-
447- if(this.decodeError == null){
448- this.decodeError = targetErrorList;
449- }else{
450- this.decodeError.addAll(targetErrorList);
451- }
452-
453- return this;
454- }
455-
456- /**
457- * 代替文字とともにデコードエラーを追加する。
458- * ※呼び出し側は、追加されるデコードエラーの位置情報が
513+ * <p>※呼び出し側は、追加されるデコードエラーの位置情報が
459514 * 既存のデコードエラーよりも大きいことを保証しなければならない。
515+ *
460516 * @param errorInfo デコードエラー
461517 */
462- private void addDecodeError(DecodeErrorInfo errorInfo){
463- if(this.decodeError == null){
464- this.decodeError = createErrorList();
518+ void addDecodeError(DecodeErrorInfo errorInfo){
519+ if(this.errList == null){
520+ this.errList = createErrorList();
465521 }
466- this.decodeError.add(errorInfo);
522+ this.errList.add(errorInfo);
467523 this.rawContent.append(ALTCHAR);
468524 return;
469525 }
470526
471527 /**
472- * 代替文字とともにデコードエラーを追加する。
473- * @param b1st 1バイト目の値
528+ * 代替文字とともにデコードエラーを末尾へ追加する。
529+ *
530+ * @param b1st エラー1バイト目の値
474531 */
475532 public void addDecodeError(byte b1st){
476533 DecodeErrorInfo errInfo =
@@ -480,12 +537,12 @@
480537 }
481538
482539 /**
483- * 代替文字とともに2バイトからなるデコードエラーを追加する。
540+ * 代替文字とともに2バイトからなるデコードエラーを末尾へ追加する。
484541 *
485542 * <p>主にシフトJISのUnmapエラーを想定。
486543 *
487- * @param b1st 1バイト目の値
488- * @param b2nd 2バイト目の値
544+ * @param b1st エラー1バイト目の値
545+ * @param b2nd エラー2バイト目の値
489546 */
490547 public void addDecodeError(byte b1st, byte b2nd){
491548 DecodeErrorInfo errInfo =
@@ -496,6 +553,7 @@
496553
497554 /**
498555 * {@inheritDoc}
556+ *
499557 * @return {@inheritDoc}
500558 */
501559 @Override
diff -r 4652c2fd346d -r 83725f5a1e4e src/main/java/jp/osdn/jindolf/parser/content/SjisNotifier.java
--- a/src/main/java/jp/osdn/jindolf/parser/content/SjisNotifier.java Sun Mar 25 12:23:15 2018 +0900
+++ b/src/main/java/jp/osdn/jindolf/parser/content/SjisNotifier.java Wed Apr 04 22:21:25 2018 +0900
@@ -86,6 +86,7 @@
8686 /**
8787 * Javaランタイムの差異によるシフトJISデコードエラーの揺らぎを正規化する。
8888 *
89+ * <p>
8990 * <ul>
9091 *
9192 * <li>2バイト長のUnmapエラーがシフトJISの形式を満たさない場合、
diff -r 4652c2fd346d -r 83725f5a1e4e src/test/java/jp/osdn/jindolf/parser/content/ContentBuilderTest.java
--- a/src/test/java/jp/osdn/jindolf/parser/content/ContentBuilderTest.java Sun Mar 25 12:23:15 2018 +0900
+++ b/src/test/java/jp/osdn/jindolf/parser/content/ContentBuilderTest.java Wed Apr 04 22:21:25 2018 +0900
@@ -259,36 +259,6 @@
259259 return;
260260 }
261261
262- /**
263- * Test of UTF16 mapping error
264- * @throws Exception
265- */
266- @Test
267- public void testUTF16_nomap() throws Exception {
268- Charset cs = Charset.forName("UTF-16");
269-
270- CharsetDecoder cd;
271- ContentBuilder cb;
272- DecodeNotifier decoder;
273- byte[] bdata;
274- InputStream is;
275- DecodedContent content;
276-
277- cd = cs.newDecoder();
278- decoder = new DecodeNotifier(cd);
279- cb = new ContentBuilder();
280- decoder.setCharDecodeListener(cb);
281- bdata = Bseq.byteArray("0041:d83d:dc11:0042");
282- is = new ByteArrayInputStream(bdata);
283- decoder.decode(is);
284- content = cb.getContent();
285-
286- assertEquals(4, content.length());
287- assertEquals("A\ud83d\udc11B", content.toString());
288-
289- return;
290- }
291-
292262 @Test
293263 public void testSheep() throws IOException, DecodeBreakException {
294264 System.out.println("sheep");
diff -r 4652c2fd346d -r 83725f5a1e4e src/test/java/jp/osdn/jindolf/parser/content/DecodeErrorInfoTest.java
--- a/src/test/java/jp/osdn/jindolf/parser/content/DecodeErrorInfoTest.java Sun Mar 25 12:23:15 2018 +0900
+++ b/src/test/java/jp/osdn/jindolf/parser/content/DecodeErrorInfoTest.java Wed Apr 04 22:21:25 2018 +0900
@@ -5,6 +5,8 @@
55
66 package jp.osdn.jindolf.parser.content;
77
8+import java.util.ArrayList;
9+import java.util.List;
810 import org.junit.After;
911 import org.junit.AfterClass;
1012 import org.junit.Before;
@@ -17,6 +19,9 @@
1719 */
1820 public class DecodeErrorInfoTest {
1921
22+ private static final byte B0 = (byte)0x00;
23+
24+
2025 public DecodeErrorInfoTest() {
2126 }
2227
@@ -43,26 +48,34 @@
4348 public void testConstructor(){
4449 System.out.println("Constructor");
4550
46- new DecodeErrorInfo(99, (byte)0xfe);
47- new DecodeErrorInfo(999, (byte)0x87, (byte)0x40);
51+ DecodeErrorInfo info;
4852
49- new DecodeErrorInfo(0, (byte)0xfe);
50- new DecodeErrorInfo(0, (byte)0x87, (byte)0x40);
53+ info = new DecodeErrorInfo(99, (byte)0xfe);
54+ assertNotNull(info);
55+
56+ info = new DecodeErrorInfo(999, (byte)0x87, (byte)0x40);
57+ assertNotNull(info);
58+
59+ info = new DecodeErrorInfo(0, (byte)0xfe);
60+ assertNotNull(info);
61+
62+ info = new DecodeErrorInfo(0, (byte)0x87, (byte)0x40);
63+ assertNotNull(info);
5164
5265 try{
53- new DecodeErrorInfo(-1, (byte)0xfe);
66+ info = new DecodeErrorInfo(-1, (byte)0xfe);
5467 fail();
68+ info.hashCode();
5569 }catch(IndexOutOfBoundsException e){
56- }catch(Throwable e){
57- fail();
70+ // GOOD
5871 }
5972
6073 try{
61- new DecodeErrorInfo(-1, (byte)0x87, (byte)0x40);
74+ info = new DecodeErrorInfo(-1, (byte)0x87, (byte)0x40);
6275 fail();
76+ info.hashCode();
6377 }catch(IndexOutOfBoundsException e){
64- }catch(Throwable e){
65- fail();
78+ // GOOD
6679 }
6780
6881 return;
@@ -77,6 +90,9 @@
7790
7891 DecodeErrorInfo info;
7992
93+ info = new DecodeErrorInfo(0, (byte)0xfe);
94+ assertEquals(0, info.getCharPosition());
95+
8096 info = new DecodeErrorInfo(99, (byte)0xfe);
8197 assertEquals(99, info.getCharPosition());
8298
@@ -136,8 +152,7 @@
136152 info.getRawByte2nd();
137153 fail();
138154 }catch(IllegalStateException e){
139- }catch(Throwable e){
140- fail();
155+ // GOOD
141156 }
142157
143158 info = new DecodeErrorInfo(999, (byte)0x87, (byte)0x40);
@@ -183,18 +198,18 @@
183198 try{
184199 info = info.createGappedClone(100);
185200 fail();
201+ info.hashCode();
186202 }catch(IndexOutOfBoundsException e){
187- }catch(Throwable e){
188- fail();
203+ // GOOD
189204 }
190205
191206 info = new DecodeErrorInfo(999, (byte)0x87, (byte)0x40);
192207 try{
193208 info = info.createGappedClone(1000);
194209 fail();
210+ info.hashCode();
195211 }catch(IndexOutOfBoundsException e){
196- }catch(Throwable e){
197- fail();
212+ // GOOD
198213 }
199214
200215 return;
@@ -225,6 +240,163 @@
225240 }
226241
227242 /**
243+ * Test of lsearchErrorIndex method, of class DecodedContent.
244+ */
245+ @Test
246+ public void testLsearchErrorIndex(){
247+ System.out.println("lsearchErrorIndex");
248+
249+ List<DecodeErrorInfo> errList;
250+ int result;
251+
252+ errList = new ArrayList<>();
253+
254+ errList.clear();
255+ result = DecodeErrorInfo.lsearchErrorIndex(errList, -1);
256+ assertEquals(0, result);
257+ result = DecodeErrorInfo.lsearchErrorIndex(errList, 0);
258+ assertEquals(0, result);
259+ result = DecodeErrorInfo.lsearchErrorIndex(errList, 10);
260+ assertEquals(0, result);
261+
262+ errList.clear();
263+ errList.add(new DecodeErrorInfo(10, B0));
264+ result = DecodeErrorInfo.lsearchErrorIndex(errList, 9);
265+ assertEquals(0, result);
266+ result = DecodeErrorInfo.lsearchErrorIndex(errList, 10);
267+ assertEquals(0, result);
268+ result = DecodeErrorInfo.lsearchErrorIndex(errList, 11);
269+ assertEquals(1, result);
270+
271+ errList.clear();
272+ errList.add(new DecodeErrorInfo(10, B0));
273+ errList.add(new DecodeErrorInfo(20, B0));
274+ errList.add(new DecodeErrorInfo(30, B0));
275+ errList.add(new DecodeErrorInfo(40, B0));
276+ errList.add(new DecodeErrorInfo(50, B0));
277+ result = DecodeErrorInfo.lsearchErrorIndex(errList, 9);
278+ assertEquals(0, result);
279+ result = DecodeErrorInfo.lsearchErrorIndex(errList, 10);
280+ assertEquals(0, result);
281+ result = DecodeErrorInfo.lsearchErrorIndex(errList, 11);
282+ assertEquals(1, result);
283+ result = DecodeErrorInfo.lsearchErrorIndex(errList, 29);
284+ assertEquals(2, result);
285+ result = DecodeErrorInfo.lsearchErrorIndex(errList, 30);
286+ assertEquals(2, result);
287+ result = DecodeErrorInfo.lsearchErrorIndex(errList, 31);
288+ assertEquals(3, result);
289+ result = DecodeErrorInfo.lsearchErrorIndex(errList, 49);
290+ assertEquals(4, result);
291+ result = DecodeErrorInfo.lsearchErrorIndex(errList, 50);
292+ assertEquals(4, result);
293+ result = DecodeErrorInfo.lsearchErrorIndex(errList, 51);
294+ assertEquals(5, result);
295+ result = DecodeErrorInfo.lsearchErrorIndex(errList, 1000);
296+ assertEquals(5, result);
297+
298+ return;
299+ }
300+
301+ /**
302+ * Test of bsearchErrorIndex method, of class DecodedContent.
303+ */
304+ @Test
305+ public void testBsearchErrorIndex(){
306+ System.out.println("bsearchErrorIndex");
307+
308+ List<DecodeErrorInfo> errList;
309+ int result;
310+
311+ errList = new ArrayList<>();
312+
313+ errList.clear();
314+ result = DecodeErrorInfo.bsearchErrorIndex(errList, -1);
315+ assertEquals(0, result);
316+ result = DecodeErrorInfo.bsearchErrorIndex(errList, 0);
317+ assertEquals(0, result);
318+ result = DecodeErrorInfo.bsearchErrorIndex(errList, 10);
319+ assertEquals(0, result);
320+
321+ errList.clear();
322+ errList.add(new DecodeErrorInfo(10, B0));
323+ result = DecodeErrorInfo.bsearchErrorIndex(errList, 9);
324+ assertEquals(0, result);
325+ result = DecodeErrorInfo.bsearchErrorIndex(errList, 10);
326+ assertEquals(0, result);
327+ result = DecodeErrorInfo.bsearchErrorIndex(errList, 11);
328+ assertEquals(1, result);
329+
330+ errList.clear();
331+ errList.add(new DecodeErrorInfo(10, B0));
332+ errList.add(new DecodeErrorInfo(20, B0));
333+ errList.add(new DecodeErrorInfo(30, B0));
334+ errList.add(new DecodeErrorInfo(40, B0));
335+ errList.add(new DecodeErrorInfo(50, B0));
336+ result = DecodeErrorInfo.bsearchErrorIndex(errList, 9);
337+ assertEquals(0, result);
338+ result = DecodeErrorInfo.bsearchErrorIndex(errList, 10);
339+ assertEquals(0, result);
340+ result = DecodeErrorInfo.bsearchErrorIndex(errList, 11);
341+ assertEquals(1, result);
342+ result = DecodeErrorInfo.bsearchErrorIndex(errList, 29);
343+ assertEquals(2, result);
344+ result = DecodeErrorInfo.bsearchErrorIndex(errList, 30);
345+ assertEquals(2, result);
346+ result = DecodeErrorInfo.bsearchErrorIndex(errList, 31);
347+ assertEquals(3, result);
348+ result = DecodeErrorInfo.bsearchErrorIndex(errList, 49);
349+ assertEquals(4, result);
350+ result = DecodeErrorInfo.bsearchErrorIndex(errList, 50);
351+ assertEquals(4, result);
352+ result = DecodeErrorInfo.bsearchErrorIndex(errList, 51);
353+ assertEquals(5, result);
354+ result = DecodeErrorInfo.bsearchErrorIndex(errList, 1000);
355+ assertEquals(5, result);
356+
357+ return;
358+ }
359+
360+ /**
361+ * Test of searchErrorIndex method, of class DecodedContent.
362+ */
363+ @Test
364+ public void testSearchErrorIndex(){
365+ System.out.println("searchErrorIndex");
366+
367+ List<DecodeErrorInfo> errList;
368+ int result;
369+
370+ errList = new ArrayList<>();
371+
372+ errList.clear();
373+ for(int pos = 0; pos < 150; pos += 10){
374+ errList.add(new DecodeErrorInfo(pos, B0));
375+ }
376+ assertTrue(errList.size() < DecodeErrorInfo.BSEARCH_THRESHOLD);
377+ result = DecodeErrorInfo.searchErrorIndex(errList, 89);
378+ assertEquals(9, result);
379+ result = DecodeErrorInfo.searchErrorIndex(errList, 90);
380+ assertEquals(9, result);
381+ result = DecodeErrorInfo.searchErrorIndex(errList, 91);
382+ assertEquals(10, result);
383+
384+ errList.clear();
385+ for(int pos = 0; pos < 1500; pos += 10){
386+ errList.add(new DecodeErrorInfo(pos, B0));
387+ }
388+ assertTrue(errList.size() >= DecodeErrorInfo.BSEARCH_THRESHOLD);
389+ result = DecodeErrorInfo.searchErrorIndex(errList, 899);
390+ assertEquals(90, result);
391+ result = DecodeErrorInfo.searchErrorIndex(errList, 900);
392+ assertEquals(90, result);
393+ result = DecodeErrorInfo.searchErrorIndex(errList, 901);
394+ assertEquals(91, result);
395+
396+ return;
397+ }
398+
399+ /**
228400 * Test of POS_COMPARATOR.
229401 */
230402 @Test
@@ -234,16 +406,16 @@
234406 DecodeErrorInfo info;
235407 DecodeErrorInfo other;
236408
237- info = new DecodeErrorInfo(99, (byte)0xfe);
238- other = new DecodeErrorInfo(98, (byte)0xfe);
409+ info = new DecodeErrorInfo(99, (byte)0xf1);
410+ other = new DecodeErrorInfo(98, (byte)0xf2);
239411 assertTrue(DecodeErrorInfo.POS_COMPARATOR.compare(info, other) > 0);
240412
241- info = new DecodeErrorInfo(99, (byte)0xfe);
242- other = new DecodeErrorInfo(100, (byte)0xfe);
413+ info = new DecodeErrorInfo(99, (byte)0xf3);
414+ other = new DecodeErrorInfo(100, (byte)0xf4);
243415 assertTrue(DecodeErrorInfo.POS_COMPARATOR.compare(info, other) < 0);
244416
245- info = new DecodeErrorInfo(99, (byte)0xfe);
246- other = new DecodeErrorInfo(99, (byte)0xfe);
417+ info = new DecodeErrorInfo(99, (byte)0xf5);
418+ other = new DecodeErrorInfo(99, (byte)0xf6);
247419 assertTrue(DecodeErrorInfo.POS_COMPARATOR.compare(info, other) == 0);
248420
249421 assertTrue(DecodeErrorInfo.POS_COMPARATOR.compare(null, other) < 0);
diff -r 4652c2fd346d -r 83725f5a1e4e src/test/java/jp/osdn/jindolf/parser/content/DecodedContentTest.java
--- a/src/test/java/jp/osdn/jindolf/parser/content/DecodedContentTest.java Sun Mar 25 12:23:15 2018 +0900
+++ b/src/test/java/jp/osdn/jindolf/parser/content/DecodedContentTest.java Wed Apr 04 22:21:25 2018 +0900
@@ -69,6 +69,9 @@
6969 fail();
7070 }
7171
72+ content = new DecodedContent(null);
73+ assertEquals("null", content.toString());
74+
7275 return;
7376 }
7477
@@ -345,6 +348,7 @@
345348
346349 /**
347350 * Test of append method, of class DecodedContent.
351+ * @see DecodedContent#append(char)
348352 */
349353 @Test
350354 public void testAppend_char(){
@@ -353,57 +357,135 @@
353357 DecodedContent content;
354358
355359 content = new DecodedContent();
360+ assertEquals("", content.toString());
356361 content.append('a');
357362 assertEquals("a", content.toString());
358-
359- return;
360- }
361-
362- /**
363- * Test of append method, of class DecodedContent.
364- */
365- @Test
366- public void testAppend_CharSequence(){
367- System.out.println("append");
368-
369- DecodedContent content;
370-
371- content = new DecodedContent();
372- CharSequence seq = "abc";
373- content.append(seq);
374- assertEquals("abc", content.toString());
363+ content.append('b');
364+ assertEquals("ab", content.toString());
375365
376366 return;
377367 }
378368
379369 /**
380370 * Test of append method, of class DecodedContent.
371+ * @see DecodedContent#append(CharSequence)
381372 */
382373 @Test
383- public void testAppend_3args_1(){
374+ public void testAppend_CharSequence(){
384375 System.out.println("append");
385376
386377 DecodedContent content;
378+ CharSequence seq;
387379
388380 content = new DecodedContent();
389- content.append("abc");
381+ assertEquals("", content.toString());
382+ seq = "abc";
383+ content.append(seq);
390384 assertEquals("abc", content.toString());
391-
392- CharSequence seq = "12345";
393- content.append(seq, 1, 4);
394- assertEquals("abc234", content.toString());
385+ seq = "def";
386+ content.append(seq);
387+ assertEquals("abcdef", content.toString());
388+ seq = null;
389+ content.append(seq);
390+ assertEquals("abcdefnull", content.toString());
391+ content.append(new DecodedContent("dec"));
392+ assertEquals("abcdefnulldec", content.toString());
395393
396394 return;
397395 }
398396
399397 /**
400398 * Test of append method, of class DecodedContent.
399+ * @see DecodedContent#append(CharSequence txt, int start, int end)
401400 */
402401 @Test
403- public void testAppend_3args_2(){
402+ public void testAppend_3args_CharSeqintint(){
404403 System.out.println("append");
405404
406405 DecodedContent content;
406+ CharSequence seq;
407+
408+ content = new DecodedContent();
409+
410+ seq = "12345";
411+
412+ content.init();
413+ try{
414+ content.append(seq, -1, 3);
415+ fail();
416+ }catch(IndexOutOfBoundsException e){
417+ // GOOD
418+ }
419+
420+ content.init();
421+ content.append(seq, 0, 3);
422+ assertEquals("123", content.toString());
423+
424+ content.init();
425+ content.append(seq, 1, 3);
426+ assertEquals("23", content.toString());
427+
428+ content.init();
429+ content.append((CharSequence)new DecodedContent("PQR"), 1, 3);
430+ assertEquals("QR", content.toString());
431+
432+ content.init();
433+ try{
434+ content.append(seq, 3, 1);
435+ fail();
436+ }catch(IndexOutOfBoundsException e){
437+ // GOOD
438+ }
439+
440+ content.init();
441+ content.append(seq, 3, 3);
442+ assertEquals("", content.toString());
443+
444+ content.init();
445+ content.append(seq, 3, 5);
446+ assertEquals("45", content.toString());
447+
448+ content.init();
449+ try{
450+ content.append(seq, 3, 6);
451+ fail();
452+ }catch(IndexOutOfBoundsException e){
453+ // GOOD
454+ }
455+
456+ content.init();
457+ try{
458+ content.append(seq, 10, 10);
459+ fail();
460+ }catch(IndexOutOfBoundsException e){
461+ // GOOD
462+ }
463+
464+ // test runtime
465+ StringBuilder sb = new StringBuilder("");
466+ seq = null;
467+ sb.append(seq, 1, 2);
468+ assertEquals("u", sb.toString());
469+
470+ content.init();
471+ seq = null;
472+ content.append(seq, 1, 2);
473+ assertEquals("u", content.toString());
474+
475+ return;
476+ }
477+
478+ /**
479+ * Test of append method, of class DecodedContent.
480+ * @see DecodedContent#append(DecodedContent txt, int start, int end)
481+ */
482+ @Test
483+ public void testAppend_3args_Decodeintint(){
484+ System.out.println("append");
485+
486+ DecodedContent content;
487+ List<DecodeErrorInfo> errList;
488+ DecodeErrorInfo info;
407489
408490 content = new DecodedContent();
409491 content.append("abc");
@@ -415,53 +497,206 @@
415497 content.append(other, 1, 4);
416498 assertEquals("abc234", content.toString());
417499
418- content = new DecodedContent();
500+ content.init();
501+ try{
502+ content.append(other, -1, 3);
503+ fail();
504+ }catch(IndexOutOfBoundsException e){
505+ // GOOD
506+ }
507+
508+ content.init();
509+ content.append(other, 0, 3);
510+ assertEquals("123", content.toString());
511+
512+ content.init();
513+ content.append(other, 1, 3);
514+ assertEquals("23", content.toString());
515+
516+ content.init();
517+ try{
518+ content.append(other, 3, 1);
519+ fail();
520+ }catch(IndexOutOfBoundsException e){
521+ // GOOD
522+ }
523+
524+ content.init();
525+ content.append(other, 3, 3);
526+ assertEquals("", content.toString());
527+
528+ content.init();
529+ content.append(other, 3, 5);
530+ assertEquals("45", content.toString());
531+
532+ content.init();
533+ try{
534+ content.append(other, 3, 6);
535+ fail();
536+ }catch(IndexOutOfBoundsException e){
537+ // GOOD
538+ }
539+
540+ content.init();
541+ try{
542+ content.append(other, 10, 10);
543+ fail();
544+ }catch(IndexOutOfBoundsException e){
545+ // GOOD
546+ }
547+
548+ content.init();
419549 content.append("abc");
420550
421551 other = new DecodedContent();
552+ other.append('A');
422553 other.addDecodeError((byte)0x01);
554+ other.append('B');
423555 other.addDecodeError((byte)0x02);
424- other.addDecodeError((byte)0x03);
425- other.addDecodeError((byte)0x04);
426- other.addDecodeError((byte)0x05);
556+ other.append('C');
557+ assertEquals("A?B?C", other.toString());
427558
428559 content.append(other, 1, 4);
429- assertEquals("abc???", content.toString());
560+ assertEquals("abc?B?", content.toString());
430561
431- List<DecodeErrorInfo> list = content.getDecodeErrorList();
432- assertEquals(3, list.size());
562+ errList = content.getDecodeErrorList();
563+ assertEquals(2, errList.size());
433564
434- DecodeErrorInfo info;
565+ info = errList.get(0);
566+ assertEquals(3, info.getCharPosition());
567+ assertEquals((byte)0x01, info.getRawByte1st());
568+ info = errList.get(1);
569+ assertEquals(5, info.getCharPosition());
570+ assertEquals((byte)0x02, info.getRawByte1st());
435571
436- info = list.get(0);
572+ content.init();
573+ content.append(other, 0, 5);
574+ assertEquals("A?B?C", content.toString());
575+
576+ errList = content.getDecodeErrorList();
577+ assertEquals(2, errList.size());
578+
579+ info = errList.get(0);
580+ assertEquals(1, info.getCharPosition());
581+ assertEquals((byte)0x01, info.getRawByte1st());
582+ info = errList.get(1);
437583 assertEquals(3, info.getCharPosition());
438584 assertEquals((byte)0x02, info.getRawByte1st());
439- info = list.get(1);
440- assertEquals(4, info.getCharPosition());
441- assertEquals((byte)0x03, info.getRawByte1st());
442- info = list.get(2);
585+
586+ content.init();
587+ content.append("ABCDE");
588+ content.append(content, 1, 3);
589+ assertEquals("ABCDEBC", content.toString());
590+
591+ content.init();
592+ content.append('A');
593+ content.addDecodeError((byte)0x00);
594+ content.append('B');
595+ content.addDecodeError((byte)0x01);
596+ content.append('C');
597+ content.append(content, 1, 3);
598+ assertEquals("A?B?C?B", content.toString());
599+ errList = content.getDecodeErrorList();
600+ assertEquals(3, errList.size());
601+ info = errList.get(0);
602+ assertEquals(1, info.getCharPosition());
603+ assertEquals((byte)0x00, info.getRawByte1st());
604+ info = errList.get(1);
605+ assertEquals(3, info.getCharPosition());
606+ assertEquals((byte)0x01, info.getRawByte1st());
607+ info = errList.get(2);
443608 assertEquals(5, info.getCharPosition());
444- assertEquals((byte)0x04, info.getRawByte1st());
609+ assertEquals((byte)0x00, info.getRawByte1st());
610+
611+ CharSequence seq;
612+ // test runtime
613+ StringBuilder sb = new StringBuilder("");
614+ seq = null;
615+ sb.append(seq, 1, 2);
616+ assertEquals("u", sb.toString());
617+
618+ content.init();
619+ other = null;
620+ content.append(other, 1, 2);
621+ assertEquals("u", content.toString());
445622
446623 return;
447624 }
448625
449626 /**
450627 * Test of append method, of class DecodedContent.
628+ * @see DecodedContent#append(char[], int, int)
451629 */
452630 @Test
453- public void testAppend_3args_3(){
631+ public void testAppend_3args_charintint(){
454632 System.out.println("append");
455633
456634 DecodedContent content;
457635
636+ char[] seq = {'1','2','3','4','5',};
637+
458638 content = new DecodedContent();
459- content.append("abc");
460- assertEquals("abc", content.toString());
461639
462- char[] seq = {'1','2','3','4','5',};
640+
641+ content.init();
642+ try{
643+ content.append(seq, -1, 3);
644+ fail();
645+ }catch(IndexOutOfBoundsException e){
646+ // GOOD
647+ }
648+
649+ content.init();
650+ content.append(seq, 0, 3);
651+ assertEquals("123", content.toString());
652+
653+ content.init();
463654 content.append(seq, 1, 3);
464- assertEquals("abc234", content.toString());
655+ assertEquals("234", content.toString());
656+
657+
658+ try{
659+ content.append(seq, 3, -1);
660+ fail();
661+ }catch(IndexOutOfBoundsException e){
662+ // GOOD
663+ }
664+
665+ content.init();
666+ content.append(seq, 3, 0);
667+ assertEquals("", content.toString());
668+
669+ content.init();
670+ content.append(seq, 3, 1);
671+ assertEquals("4", content.toString());
672+
673+ content.init();
674+ content.append(seq, 3, 2);
675+ assertEquals("45", content.toString());
676+
677+ content.init();
678+ try{
679+ content.append(seq, 3, 3);
680+ fail();
681+ }catch(IndexOutOfBoundsException e){
682+ // GOOD
683+ }
684+
685+ // test runtime
686+ StringBuilder sb = new StringBuilder("A");
687+ try{
688+ sb.append((char[])null, 1, 2);
689+ fail();
690+ }catch(NullPointerException e){
691+ // GOOD
692+ }
693+
694+ try{
695+ content.append((char[])null, 1, 2);
696+ fail();
697+ }catch(NullPointerException e){
698+ // GOOD
699+ }
465700
466701 return;
467702 }
@@ -558,156 +793,106 @@
558793 }
559794
560795 /**
561- * Test of lsearchErrorIndex method, of class DecodedContent.
562- */
563- @Test
564- public void testLsearchErrorIndex(){
565- System.out.println("lsearchErrorIndex");
566-
567- List<DecodeErrorInfo> errList;
568- int result;
569-
570- errList = new ArrayList<>();
571- result = DecodedContent.lsearchErrorIndex(errList, 10);
572- assertEquals(0, result);
573-
574- errList.clear();
575- errList.add(new DecodeErrorInfo(5, (byte)0x00));
576- result = DecodedContent.lsearchErrorIndex(errList, 10);
577- assertEquals(1, result);
578-
579- errList.clear();
580- errList.add(new DecodeErrorInfo(10, (byte)0x00));
581- result = DecodedContent.lsearchErrorIndex(errList, 10);
582- assertEquals(0, result);
583-
584- errList.clear();
585- errList.add(new DecodeErrorInfo(15, (byte)0x00));
586- result = DecodedContent.lsearchErrorIndex(errList, 10);
587- assertEquals(0, result);
588-
589- errList.clear();
590- errList.add(new DecodeErrorInfo(4, (byte)0x00));
591- errList.add(new DecodeErrorInfo(5, (byte)0x00));
592- errList.add(new DecodeErrorInfo(14, (byte)0x00));
593- errList.add(new DecodeErrorInfo(15, (byte)0x00));
594- result = DecodedContent.lsearchErrorIndex(errList, 10);
595- assertEquals(2, result);
596-
597- errList.clear();
598- errList.add(new DecodeErrorInfo(4, (byte)0x00));
599- errList.add(new DecodeErrorInfo(5, (byte)0x00));
600- errList.add(new DecodeErrorInfo(10, (byte)0x00));
601- errList.add(new DecodeErrorInfo(14, (byte)0x00));
602- errList.add(new DecodeErrorInfo(15, (byte)0x00));
603- result = DecodedContent.lsearchErrorIndex(errList, 10);
604- assertEquals(2, result);
605-
606- return;
607- }
608-
609- /**
610- * Test of bsearchErrorIndex method, of class DecodedContent.
611- */
612- @Test
613- public void testBsearchErrorIndex(){
614- System.out.println("bsearchErrorIndex");
615-
616- List<DecodeErrorInfo> errList;
617- int result;
618-
619- errList = new ArrayList<>();
620- result = DecodedContent.bsearchErrorIndex(errList, 10);
621- assertEquals(0, result);
622-
623- errList.clear();
624- errList.add(new DecodeErrorInfo(5, (byte)0x00));
625- result = DecodedContent.bsearchErrorIndex(errList, 10);
626- assertEquals(1, result);
627-
628- errList.clear();
629- errList.add(new DecodeErrorInfo(10, (byte)0x00));
630- result = DecodedContent.bsearchErrorIndex(errList, 10);
631- assertEquals(0, result);
632-
633- errList.clear();
634- errList.add(new DecodeErrorInfo(15, (byte)0x00));
635- result = DecodedContent.bsearchErrorIndex(errList, 10);
636- assertEquals(0, result);
637-
638- errList.clear();
639- errList.add(new DecodeErrorInfo(4, (byte)0x00));
640- errList.add(new DecodeErrorInfo(5, (byte)0x00));
641- errList.add(new DecodeErrorInfo(14, (byte)0x00));
642- errList.add(new DecodeErrorInfo(15, (byte)0x00));
643- result = DecodedContent.bsearchErrorIndex(errList, 10);
644- assertEquals(2, result);
645-
646- errList.clear();
647- errList.add(new DecodeErrorInfo(4, (byte)0x00));
648- errList.add(new DecodeErrorInfo(5, (byte)0x00));
649- errList.add(new DecodeErrorInfo(10, (byte)0x00));
650- errList.add(new DecodeErrorInfo(14, (byte)0x00));
651- errList.add(new DecodeErrorInfo(15, (byte)0x00));
652- result = DecodedContent.bsearchErrorIndex(errList, 10);
653- assertEquals(2, result);
654- result = DecodedContent.bsearchErrorIndex(errList, 9);
655- assertEquals(2, result);
656- result = DecodedContent.bsearchErrorIndex(errList, 11);
657- assertEquals(3, result);
658-
659- return;
660- }
661-
662- /**
663- * Test of searchErrorIndex method, of class DecodedContent.
664- */
665- @Test
666- public void testSearchErrorIndex(){
667- System.out.println("searchErrorIndex");
668-
669- List<DecodeErrorInfo> errList;
670- int result;
671-
672- errList = new ArrayList<>();
673-
674- errList.clear();
675- for(int pos = 0; pos <= 1000; pos += 10){
676- errList.add(new DecodeErrorInfo(pos, (byte)0x00));
677- }
678- result = DecodedContent.searchErrorIndex(errList, 503);
679- assertEquals(51, result);
680-
681- errList.clear();
682- for(int pos = 0; pos <= 50; pos += 10){
683- errList.add(new DecodeErrorInfo(pos, (byte)0x00));
684- }
685- result = DecodedContent.searchErrorIndex(errList, 23);
686- assertEquals(3, result);
687-
688- return;
689- }
690-
691- /**
692796 * Test of appendGappedErrorInfo method, of class DecodedContent.
693797 */
694798 @Test
695799 public void testAppendGappedErrorInfo(){
696800 System.out.println("appendGappedErrorInfo");
697801
698- DecodedContent sourceContent;
699- sourceContent = new DecodedContent();
802+ List<DecodeErrorInfo> srcErrList = new ArrayList<>();
700803 for(int pos = 0; pos <= 50; pos += 10){
701- sourceContent.append("123456789");
702- sourceContent.addDecodeError((byte)0x00);
804+ DecodeErrorInfo info = new DecodeErrorInfo(pos, (byte)0x00);
805+ srcErrList.add(info);
703806 }
704807
705808 List<DecodeErrorInfo> result;
706- result = DecodedContent.appendGappedErrorInfo(sourceContent, 15, 35, null, -100);
809+ List<DecodeErrorInfo> target;
810+
811+ result = DecodedContent.appendGappedErrorInfo(srcErrList, 15, 35, null, -100);
707812 assertNotNull(result);
708813 assertEquals(2, result.size());
709- assertEquals(119, result.get(0).getCharPosition());
710- assertEquals(129, result.get(1).getCharPosition());
814+ assertEquals(120, result.get(0).getCharPosition());
815+ assertEquals(130, result.get(1).getCharPosition());
816+
817+ target = new ArrayList<>();
818+ result = DecodedContent.appendGappedErrorInfo(srcErrList, 15, 35, target, -100);
819+ assertSame(target, result);
820+ assertEquals(2, result.size());
821+ assertEquals(120, result.get(0).getCharPosition());
822+ assertEquals(130, result.get(1).getCharPosition());
823+
824+ try{
825+ DecodedContent.appendGappedErrorInfo(srcErrList, 15, 35, srcErrList, -100);
826+ fail();
827+ }catch(IllegalArgumentException e){
828+ // GOOD
829+ }
830+
831+
832+ result = DecodedContent.appendGappedErrorInfo(srcErrList, 10, 40, null, -100);
833+ assertNotNull(result);
834+ assertEquals(3, result.size());
835+ assertEquals(110, result.get(0).getCharPosition());
836+ assertEquals(120, result.get(1).getCharPosition());
837+ assertEquals(130, result.get(2).getCharPosition());
838+
839+ result = DecodedContent.appendGappedErrorInfo(srcErrList, 10, 41, null, -100);
840+ assertNotNull(result);
841+ assertEquals(4, result.size());
842+ assertEquals(110, result.get(0).getCharPosition());
843+ assertEquals(120, result.get(1).getCharPosition());
844+ assertEquals(130, result.get(2).getCharPosition());
845+ assertEquals(140, result.get(3).getCharPosition());
846+
847+ result = DecodedContent.appendGappedErrorInfo(srcErrList, 11, 40, null, -100);
848+ assertNotNull(result);
849+ assertEquals(2, result.size());
850+ assertEquals(120, result.get(0).getCharPosition());
851+ assertEquals(130, result.get(1).getCharPosition());
852+
853+ result = DecodedContent.appendGappedErrorInfo(srcErrList, 9, 40, null, -100);
854+ assertNotNull(result);
855+ assertEquals(3, result.size());
856+ assertEquals(110, result.get(0).getCharPosition());
857+ assertEquals(120, result.get(1).getCharPosition());
858+ assertEquals(130, result.get(2).getCharPosition());
859+
860+ result = DecodedContent.appendGappedErrorInfo(srcErrList, 10, 50, null, -100);
861+ assertNotNull(result);
862+ assertEquals(4, result.size());
863+ assertEquals(110, result.get(0).getCharPosition());
864+ assertEquals(120, result.get(1).getCharPosition());
865+ assertEquals(130, result.get(2).getCharPosition());
866+ assertEquals(140, result.get(3).getCharPosition());
867+
868+ result = DecodedContent.appendGappedErrorInfo(srcErrList, 10, 51, null, -100);
869+ assertNotNull(result);
870+ assertEquals(5, result.size());
871+ assertEquals(110, result.get(0).getCharPosition());
872+ assertEquals(120, result.get(1).getCharPosition());
873+ assertEquals(130, result.get(2).getCharPosition());
874+ assertEquals(140, result.get(3).getCharPosition());
875+ assertEquals(150, result.get(4).getCharPosition());
876+
877+ result = DecodedContent.appendGappedErrorInfo(srcErrList, 0, 0, null, -100);
878+ assertNull(result);
879+
880+ result = DecodedContent.appendGappedErrorInfo(srcErrList, 0, 1, null, -100);
881+ assertNotNull(result);
882+ assertEquals(1, result.size());
883+ assertEquals(100, result.get(0).getCharPosition());
884+
885+ result = DecodedContent.appendGappedErrorInfo(srcErrList, 10, 10, null, -100);
886+ assertNull(result);
887+
888+ result = DecodedContent.appendGappedErrorInfo(srcErrList, 15, 15, null, -100);
889+ assertNull(result);
890+
891+ result = DecodedContent.appendGappedErrorInfo(srcErrList, 15, 16, null, -100);
892+ assertNull(result);
893+
894+ result = DecodedContent.appendGappedErrorInfo(srcErrList, 60, 70, null, -100);
895+ assertNull(result);
711896
712897 return;
713898 }
diff -r 4652c2fd346d -r 83725f5a1e4e src/test/java/sample/SampleParser.java
--- a/src/test/java/sample/SampleParser.java Sun Mar 25 12:23:15 2018 +0900
+++ b/src/test/java/sample/SampleParser.java Wed Apr 04 22:21:25 2018 +0900
@@ -14,10 +14,7 @@
1414 import java.io.InputStream;
1515 import java.nio.charset.Charset;
1616 import java.nio.charset.CharsetDecoder;
17-import java.util.Collections;
1817 import java.util.Enumeration;
19-import java.util.SortedMap;
20-import java.util.TreeMap;
2118 import java.util.zip.ZipEntry;
2219 import java.util.zip.ZipFile;
2320 import jp.osdn.jindolf.parser.HtmlHandler;
@@ -27,37 +24,25 @@
2724 import jp.osdn.jindolf.parser.content.DecodedContent;
2825
2926 /**
30- * サンプルのパーサ
27+ * サンプルのパーサ。
28+ *
29+ * <p>F国以前(Shift_JIS)用
3130 */
3231 public class SampleParser{
3332
34- private static final CharsetDecoder CHD_UTF8;
33+ private static final Charset CS = Charset.forName("Shift_JIS");
3534
36- static{
37- CHD_UTF8 = Charset.forName("UTF-8").newDecoder();
38- }
3935
4036 private SampleParser(){
4137 super();
4238 return;
4339 }
4440
45- public static SortedMap<String, ZipEntry> createEntryMap(ZipFile file){
46- TreeMap<String, ZipEntry> result = new TreeMap<>();
4741
48- Enumeration<? extends ZipEntry> list = file.entries();
49- while(list.hasMoreElements()){
50- ZipEntry entry = list.nextElement();
51- String name = entry.getName();
52- result.put(name, entry);
53- }
54-
55- return Collections.unmodifiableSortedMap(result);
56- }
57-
58- public static DecodedContent contentFromStream(InputStream istream)
42+ private static DecodedContent contentFromStream(InputStream istream)
5943 throws IOException, DecodeBreakException{
60- DecodeNotifier decoder = new DecodeNotifier(CHD_UTF8);
44+ CharsetDecoder cd = CS.newDecoder();
45+ DecodeNotifier decoder = new DecodeNotifier(cd);
6146 ContentBuilder builder = new ContentBuilder();
6247
6348 decoder.setCharDecodeListener(builder);
@@ -69,7 +54,7 @@
6954 return content;
7055 }
7156
72- public static void parseContent(DecodedContent content)
57+ private static void parseContent(DecodedContent content)
7358 throws HtmlParseException{
7459 HtmlParser parser = new HtmlParser();
7560 HtmlHandler handler = new SampleHandler();
@@ -83,13 +68,57 @@
8368 return;
8469 }
8570
86- public static void parseStream(InputStream istream)
71+ private static void parseStream(InputStream istream)
8772 throws IOException,
8873 DecodeBreakException,
8974 HtmlParseException{
9075 DecodedContent content = contentFromStream(istream);
76+ parseContent(content);
77+ return;
78+ }
9179
92- parseContent(content);
80+ private static void modeStdin()
81+ throws IOException,DecodeBreakException,HtmlParseException{
82+ System.out.println(
83+ "標準入力から人狼BBSのXHTML文書の読み取りを"
84+ +"開始します...");
85+
86+ parseStream(System.in);
87+
88+ return;
89+ }
90+
91+ private static void modeZip(String zipFileName)
92+ throws IOException,DecodeBreakException,HtmlParseException{
93+ System.out.println(
94+ "ZIPアーカイブ内の*.htmlファイルから"
95+ +"人狼BBSのXHTML文書の読み取りを開始します...");
96+
97+ try(ZipFile zipFile = new ZipFile(zipFileName)){
98+ Enumeration<? extends ZipEntry> list = zipFile.entries();
99+ while(list.hasMoreElements()){
100+ ZipEntry entry = list.nextElement();
101+ String name = entry.getName();
102+ if( ! name.endsWith(".html") ) continue;
103+
104+ System.out.println(name + "のパースを開始...");
105+
106+ try(InputStream istream = zipFile.getInputStream(entry)){
107+ parseStream(istream);
108+ }
109+ }
110+ }
111+
112+ return;
113+ }
114+
115+ private static void modeFile(String fileName)
116+ throws IOException,DecodeBreakException,HtmlParseException{
117+ System.out.println(fileName + "のパースを開始...");
118+
119+ try(InputStream istream = new FileInputStream(fileName)){
120+ parseStream(istream);
121+ }
93122
94123 return;
95124 }
@@ -99,50 +128,20 @@
99128 DecodeBreakException,
100129 HtmlParseException {
101130 if(args.length == 0){
102- System.out.println(
103- "標準入力から人狼BBSのXHTML文書の読み取りを"
104- +"開始します...");
105-
106- parseStream(System.in);
107-
108- System.exit(0);
109-
131+ modeStdin();
110132 return;
111- }else if(args[0].endsWith(".zip")){
112- System.out.println(
113- "ZIPアーカイブ内の*.htmlファイルから"
114- +"人狼BBSのXHTML文書の読み取りを開始します...");
115-
116- ZipFile zipfile = new ZipFile(args[0]);
117-
118- SortedMap<String, ZipEntry> map = createEntryMap(zipfile);
119-
120- for(ZipEntry entry : map.values()){
121- String name = entry.getName();
122- if( ! name.endsWith(".html") ) continue;
133+ }
123134
124- System.out.println(name + "のパースを開始...");
125-
126- InputStream istream = zipfile.getInputStream(entry);
127- parseStream(istream);
128-
129- istream.close();
130- }
131-
132- zipfile.close();
135+ String fileName;
136+ fileName = args[0];
133137
134- System.exit(0);
135-
136- return;
138+ if(fileName.endsWith(".zip")){
139+ modeZip(fileName);
137140 }else{
138- System.out.println(args[0] + "のパースを開始...");
141+ modeFile(fileName);
142+ }
139143
140- InputStream istream = new FileInputStream(args[0]);
141- parseStream(istream);
142- istream.close();
143-
144- System.exit(0);
145- }
144+ System.exit(0);
146145
147146 return;
148147 }
Show on old repository browser