OSDN Git Service

from subversion repository
[jindolf/JinParser.git] / src / main / java / jp / sourceforge / jindolf / parser / EntityConverter.java
1 /*\r
2  * entity converter\r
3  *\r
4  * Copyright(c) 2009 olyutorskii\r
5  * $Id: EntityConverter.java 894 2009-11-04 07:26:59Z olyutorskii $\r
6  */\r
7 \r
8 package jp.sourceforge.jindolf.parser;\r
9 \r
10 import java.util.regex.Matcher;\r
11 import java.util.regex.Pattern;\r
12 \r
13 /**\r
14  * 人狼BBSで用いられる4種類のXHTML文字実体参照の\r
15  * 解決を伴う{@link DecodedContent}の切り出しを行う。\r
16  * 文字実体参照は{@code > < " &}が対象。\r
17  * U+005C(バックスラッシュ)をU+00A5(円通貨)に直す処理も行われる。\r
18  * ※ 人狼BBSはShift_JIS(⊃JISX0201)で運営されているので、\r
19  * バックスラッシュは登場しないはず。\r
20  * ※ が、バックスラッシュを生成するShift_JISデコーダは存在する。\r
21  * マルチスレッドには非対応。\r
22  */\r
23 public class EntityConverter{\r
24 \r
25     private static final String[][] XCHG_TABLE = {\r
26         {">",   ">"},\r
27         {"&lt;",   "<"},\r
28         {"&quot;", "\""},\r
29         {"&amp;",  "&"},\r
30         {"\u005c\u005c", "\u00a5"},\r
31     };\r
32 \r
33     private static final Pattern XCHG_PATTERN;\r
34 \r
35     static{\r
36         StringBuilder regex = new StringBuilder();\r
37         for(String[] xchg : XCHG_TABLE){\r
38             String xchgFrom = xchg[0];\r
39             if(regex.length() > 0) regex.append('|');\r
40             regex.append('(')\r
41                  .append(Pattern.quote(xchgFrom))\r
42                  .append(')');\r
43             assert xchgFrom.indexOf(DecodedContent.ALTCHAR) < 0;\r
44         }\r
45         XCHG_PATTERN = Pattern.compile(regex.toString());\r
46     }\r
47 \r
48     private final Matcher matcher = XCHG_PATTERN.matcher("");\r
49 \r
50     /**\r
51      * コンストラクタ。\r
52      */\r
53     public EntityConverter(){\r
54         super();\r
55         return;\r
56     }\r
57 \r
58     /**\r
59      * 実体参照の変換を行う。\r
60      * @param content 変換元文書\r
61      * @return 切り出された変換済み文書\r
62      */\r
63     public DecodedContent convert(DecodedContent content){\r
64         return append(null, content, 0, content.length());\r
65     }\r
66 \r
67     /**\r
68      * 実体参照の変換を行う。\r
69      * @param content 変換元文書\r
70      * @param range 範囲指定\r
71      * @return 切り出された変換済み文書\r
72      * @throws IndexOutOfBoundsException 位置指定に不正があった\r
73      */\r
74     public DecodedContent convert(DecodedContent content, SeqRange range)\r
75             throws IndexOutOfBoundsException{\r
76         return append(null, content, range.getStartPos(), range.getEndPos());\r
77     }\r
78 \r
79     /**\r
80      * 実体参照の変換を行う。\r
81      * @param content 変換元文書\r
82      * @param startPos 開始位置\r
83      * @param endPos 終了位置\r
84      * @return 切り出された変換済み文書\r
85      * @throws IndexOutOfBoundsException 位置指定に不正があった\r
86      */\r
87     public DecodedContent convert(DecodedContent content,\r
88                                    int startPos, int endPos)\r
89             throws IndexOutOfBoundsException{\r
90         return append(null, content, startPos, endPos);\r
91     }\r
92 \r
93     /**\r
94      * 実体参照の変換を行い既存のDecodedContentに追加を行う。\r
95      * @param target 追加先文書。nullなら新たな文書が用意される。\r
96      * @param content 変換元文書\r
97      * @return targetもしくは新規に用意された文書\r
98      * @throws IndexOutOfBoundsException 位置指定に不正があった\r
99      */\r
100     public DecodedContent  append(DecodedContent target,\r
101                                    DecodedContent content)\r
102             throws IndexOutOfBoundsException{\r
103         return append(target, content, 0, content.length());\r
104     }\r
105 \r
106     /**\r
107      * 実体参照の変換を行い既存のDecodedContentに追加を行う。\r
108      * @param target 追加先文書。nullなら新たな文書が用意される。\r
109      * @param content 変換元文書\r
110      * @param range 範囲指定\r
111      * @return targetもしくは新規に用意された文書\r
112      * @throws IndexOutOfBoundsException 位置指定に不正があった\r
113      */\r
114     public DecodedContent  append(DecodedContent target,\r
115                                    DecodedContent content,\r
116                                    SeqRange range )\r
117             throws IndexOutOfBoundsException{\r
118         return append(target, content,\r
119                       range.getStartPos(), range.getEndPos());\r
120     }\r
121 \r
122     /**\r
123      * 実体参照の変換を行い既存のDecodedContentに追加を行う。\r
124      * @param target 追加先文書。nullなら新たな文書が用意される。\r
125      * @param content 変換元文書\r
126      * @param startPos 開始位置\r
127      * @param endPos 終了位置\r
128      * @return targetもしくは新規に用意された文書\r
129      * @throws IndexOutOfBoundsException 位置指定に不正があった\r
130      */\r
131     public DecodedContent append(DecodedContent target,\r
132                                   DecodedContent content,\r
133                                   int startPos, int endPos)\r
134             throws IndexOutOfBoundsException{\r
135         if(   startPos > endPos\r
136            || startPos < 0\r
137            || content.length() < endPos){\r
138             throw new IndexOutOfBoundsException();\r
139         }\r
140 \r
141         DecodedContent result;\r
142         if(target == null){\r
143             result = new DecodedContent(endPos - startPos);\r
144         }else{\r
145             result = target;\r
146         }\r
147 \r
148         this.matcher.reset(content.getRawContent());\r
149         this.matcher.region(startPos, endPos);\r
150 \r
151         int lastPos = startPos;\r
152         while(this.matcher.find()){\r
153             int group;\r
154             int matchStart = -1;\r
155             for(group = 1; group <= XCHG_TABLE.length; group++){\r
156                 matchStart = this.matcher.start(group);\r
157                 if(matchStart >= 0) break;\r
158             }\r
159             int matchEnd = this.matcher.end(group);\r
160 \r
161             result.append(content, lastPos, matchStart);\r
162 \r
163             String toStr = XCHG_TABLE[group - 1][1];\r
164             result.append(toStr);\r
165 \r
166             lastPos = matchEnd;\r
167         }\r
168         result.append(content, lastPos, endPos);\r
169 \r
170         this.matcher.reset("");\r
171 \r
172         return result;\r
173     }\r
174 \r
175 }\r