2 * Shift_JIS decode notifier
4 * License : The MIT License
5 * Copyright(c) 2018 olyutorskii
8 package jp.sourceforge.jindolf.parser;
10 import io.bitbucket.olyutorskii.jiocema.DecodeNotifier;
11 import java.io.IOException;
12 import java.nio.ByteBuffer;
13 import java.nio.charset.CoderResult;
16 * Shift_JISバイト列のデコードエラーに特化した、
17 * {@link DecodeNotifier}の派生クラス。
19 * <p>Javaランタイムの細かな仕様差異による
20 * デコードエラー出現パターンゆらぎの正規化を行う。
24 * <li>バイト列 [0xff:0x32]や[0x81:0xfd]を
25 * 2バイト長Unmapエラーとして検出してしまう
28 * <li>バイト列 [0x85,0x40](未割り当て9区の文字) を、
29 * 1バイトのエラーと文字@に分離してしまうJavaランタイムへの対処。
31 * <li>バイト列 [0x80:0x41]先頭を1バイト長Unmapエラーとして検出してしまう
36 * <p>TODO: 1.7系ランタイムによっては
37 * [0x81, 0x7f]が「÷」にデコードされる場合がある問題が未解決。
39 * @see https://en.wikipedia.org/wiki/Shift_JIS
40 * @see sun.nio.cs.ext.SJIS
42 public class SjisNotifier extends DecodeNotifier{
44 private static final String MSGFORM_SJBUFLEN =
45 "input buffer length must be 2 or more for Shift_JIS";
51 * <p>デコーダにはShift_JIS用ランタイムが用いられる。
53 * <p>バッファサイズはデフォルト値が用いられる。
55 * @see DecodeNotifier#DEFSZ_BYTEBUF
56 * @see DecodeNotifier#DEFSZ_CHARBUF
58 public SjisNotifier(){
59 this(DEFSZ_BYTEBUF, DEFSZ_CHARBUF);
66 * <p>デコーダにはShift_JIS用ランタイムが用いられる。
68 * @param inbufSz 入力バッファサイズ。
69 * シフトJIS上位下位のため2以上を指定しなければならない。
70 * @param outbufSz 出力バッファサイズ。
71 * サロゲートペア格納のため2以上を指定しなければならない。
72 * @throws IllegalArgumentException 不適切なバッファサイズ
74 public SjisNotifier(int inbufSz, int outbufSz)
75 throws IllegalArgumentException {
76 super(ShiftJis.CHARSET.newDecoder(), inbufSz, outbufSz);
79 throw new IllegalArgumentException(MSGFORM_SJBUFLEN);
87 * Javaランタイムの差異によるシフトJISデコードエラーの揺らぎを正規化する。
91 * <li>2バイト長のUnmapエラーがシフトJISの形式を満たさない場合、
92 * 1バイト長のMalformedエラーに修正する。
94 * <li>2バイト長でない、もしくはUnmapでないエラー時に、
95 * 入力バッファ未読部先頭がシフトJISの形式を満たす場合、
96 * 2バイト長のUnmapエラーに修正する。
99 * 入力バッファ未読部先頭がシフトJISの形式を満たさない場合、
100 * 1バイト長のMalformedエラーに修正する。
104 * <p>必要に応じて1バイト以上の追加先読みを行う。
108 * @param errInfo {@inheritDoc}
109 * @return {@inheritDoc}
110 * @throws IOException {@inheritDoc}
113 protected CoderResult modifyErrorResult(CoderResult errInfo)
115 boolean unmapSingle = false;
116 boolean unmapDouble = false;
117 if(errInfo.isUnmappable()){
118 int errorLength = errInfo.length();
120 case 1: unmapSingle = true; break;
121 case 2: unmapDouble = true; break;
127 return modifyUnmapDoubleError(errInfo);
130 boolean detectSjis = false;
131 if(fillDoubleBytes()){
137 CoderResult newResult;
139 newResult = CoderResult.unmappableForLength(2);
140 }else if(unmapSingle){
141 newResult = CoderResult.malformedForLength(1);
150 * modify 2-byte unmap decode error.
152 * <p>if 2-bytes sequence is invalid Shift_JIS,
153 * single-byte malformed error will be return.
155 * <p>Yes, [81:fd] must not be Unmap-error.
157 * @param errInfo original error information
158 * @return modified error information
160 private CoderResult modifyUnmapDoubleError(CoderResult errInfo){
161 assert errInfo.isUnmappable();
162 assert errInfo.length() == 2;
164 CoderResult newResult;
168 newResult = CoderResult.malformedForLength(1);
175 * 入力バッファ未読部長が2バイト以上になるまで入力を進める。
177 * @return 未読部長が2バイト未満の段階で入力が終了したらfalse
178 * @throws IOException 入力エラー
180 private boolean fillDoubleBytes() throws IOException{
181 ByteBuffer inbuffer = getByteBuffer();
182 while(inbuffer.remaining() < 2){
183 if( ! hasMoreInput()) return false;
190 * 入力バッファ未読部先頭がシフトJISの2バイト長文字形式か判定する。
192 * <p>入力バッファ未読部の長さが2未満の場合は常に偽となる。
194 * <p>文字集合がJIS X0208に収まるか否かのUnmap判定は行わない。
196 * @return シフトJISの2バイト長文字形式ならtrue
198 private boolean isSjisHeadErr(){
199 ByteBuffer inbuffer = getByteBuffer();
201 if(inbuffer.remaining() < 2) return false;
202 int currPos = inbuffer.position();
203 int nextPos = currPos + 1;
205 byte curr = inbuffer.get(currPos);
206 byte next = inbuffer.get(nextPos);
209 result = ShiftJis.isShiftJIS(curr, next);