/*
- * abstract content builder
+ * content builder
*
* License : The MIT License
* Copyright(c) 2009 olyutorskii
package jp.sourceforge.jindolf.parser;
+import io.bitbucket.olyutorskii.jiocema.CharDecodeListener;
+import io.bitbucket.olyutorskii.jiocema.DecodeBreakException;
import java.nio.charset.CharsetDecoder;
/**
- * {@link DecodedContent}取得用抽象デコードハンドラ。
+ * 文字列デコード通知から
+ * {@link DecodedContent}を生成するためのデコードリスナ。
*/
-public abstract class ContentBuilder implements DecodeHandler{
+public class ContentBuilder implements CharDecodeListener{
+
+ private static final int DEF_CAPA_SZ = 128;
+
/** 文字列内容。 */
private final DecodedContent content;
/**
* コンストラクタ。
- * 長さ0で空の{@link DecodedContent}がセットされる。
+ */
+ public ContentBuilder(){
+ this(DEF_CAPA_SZ);
+ }
+
+ /**
+ * コンストラクタ。
+ *
* @param capacity 初期容量
* @throws NegativeArraySizeException 容量指定が負。
*/
- protected ContentBuilder(int capacity) throws NegativeArraySizeException{
+ public ContentBuilder(int capacity) throws NegativeArraySizeException{
super();
this.content = new DecodedContent(capacity);
return;
}
+
+ /**
+ * デコード結果の{@link DecodedContent}を取得する。
+ *
+ * @return デコード結果文字列
+ */
+ public DecodedContent getContent(){
+ return this.content;
+ }
+
/**
- * デコード処理の初期化。
+ * {@inheritDoc}
+ *
+ * @param decoder {@inheritDoc}
+ * @throws DecodeBreakException {@inheritDoc}
*/
- protected void init(){
+ @Override
+ public void startDecoding(CharsetDecoder decoder)
+ throws DecodeBreakException{
+ this.content.init();
return;
}
/**
- * エラー情報をフラッシュする。
+ * {@inheritDoc}
+ *
+ * @throws DecodeBreakException {@inheritDoc}
*/
- protected void flushError(){
+ @Override
+ public void endDecoding()
+ throws DecodeBreakException{
+ // NOTHING
return;
}
/**
* {@inheritDoc}
- * @param decoder {@inheritDoc}
- * @throws DecodeException {@inheritDoc}
+ *
+ * @param byteArray {@inheritDoc}
+ * @param offset {@inheritDoc}
+ * @param length {@inheritDoc}
*/
@Override
- public void startDecoding(CharsetDecoder decoder)
- throws DecodeException{
- init();
+ public void rawBytes(byte[] byteArray, int offset, int length){
+ // NOTHING
return;
}
/**
* {@inheritDoc}
- * @throws DecodeException {@inheritDoc}
+ *
+ * @param charArray {@inheritDoc}
+ * @param offset {@inheritDoc}
+ * @param length {@inheritDoc}
+ * @throws DecodeBreakException {@inheritDoc}
*/
@Override
- public void endDecoding()
- throws DecodeException{
- flushError();
+ public void charContent(char[] charArray, int offset, int length)
+ throws DecodeBreakException{
+ getContent().append(charArray, offset, length);
return;
}
/**
- * デコード結果の{@link DecodedContent}を取得する。
- * @return デコード結果文字列
+ * {@inheritDoc}
+ *
+ * @param errorArray {@inheritDoc}
+ * @param offset {@inheritDoc}
+ * @param length {@inheritDoc}
+ * @throws DecodeBreakException {@inheritDoc}
*/
- public DecodedContent getContent(){
- return this.content;
+ @Override
+ public void malformedError(byte[] errorArray, int offset, int length)
+ throws DecodeBreakException {
+ decodingError(errorArray, offset, length);
+ return;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @param errorArray {@inheritDoc}
+ * @param offset {@inheritDoc}
+ * @param length {@inheritDoc}
+ * @throws DecodeBreakException {@inheritDoc}
+ */
+ @Override
+ public void unmapError(byte[] errorArray, int offset, int length)
+ throws DecodeBreakException {
+ decodingError(errorArray, offset, length);
+ return;
+ }
+
+ /**
+ * デコードエラーの受信。
+ *
+ * <p>エラーを構成する各バイトから一つずつエラー情報を生成する。
+ *
+ * @param errorArray エラーの含まれるバイト倍列
+ * @param offset オフセット
+ * @param length 長さ
+ */
+ private void decodingError(byte[] errorArray, int offset, int length){
+ DecodedContent text = getContent();
+
+ int limit = offset + length;
+ for(int bpos = offset; bpos < limit; bpos++){
+ byte bVal = errorArray[bpos];
+ text.addDecodeError(bVal);
+ }
+
+ return;
}
}
package jp.sourceforge.jindolf.parser;
+import io.bitbucket.olyutorskii.jiocema.DecodeBreakException;
+
/**
- * "Shift_JIS"エンコーディング用デコードハンドラ。
- * {@link SjisDecoder}からの通知に従い、
- * {@link DecodedContent}へとデコードする。
+ * Shift_JIS 文字列のデコード通知から
+ * {@link DecodedContent}を生成するためのデコードリスナ。
+ *
+ * <p>2バイト系Unmapエラーを普通のデコードエラーと区別する。
*/
public class ContentBuilderSJ extends ContentBuilder{
private static final int DEF_BUF_SZ = 128;
- private boolean hasByte1st;
- private byte byte1st;
-
-
/**
* コンストラクタ。
- * 長さ0で空の{@link DecodedContent}がセットされる。
*/
public ContentBuilderSJ(){
this(DEF_BUF_SZ);
/**
* コンストラクタ。
- * 長さ0で空の{@link DecodedContent}がセットされる。
+ *
* @param capacity 初期容量
* @throws NegativeArraySizeException 容量指定が負。
*/
public ContentBuilderSJ(int capacity) throws NegativeArraySizeException{
super(capacity);
- initImpl();
- return;
- }
-
- /**
- * デコード処理の初期化下請。
- */
- private void initImpl(){
- getContent().init();
- this.hasByte1st = false;
- this.byte1st = 0x00;
- return;
- }
-
- /**
- * デコード処理の初期化。
- */
- @Override
- protected void init(){
- initImpl();
return;
}
- /**
- * エラー情報をフラッシュする。
- */
- @Override
- protected void flushError(){
- if(this.hasByte1st){
- getContent().addDecodeError(this.byte1st);
- this.hasByte1st = false;
- }
- return;
- }
-
- /**
- * {@inheritDoc}
- * @param seq {@inheritDoc}
- * @throws DecodeException {@inheritDoc}
- */
- @Override
- public void charContent(CharSequence seq)
- throws DecodeException{
- flushError();
- getContent().append(seq);
- return;
- }
/**
* {@inheritDoc}
+ *
* @param errorArray {@inheritDoc}
* @param offset {@inheritDoc}
* @param length {@inheritDoc}
- * @throws DecodeException {@inheritDoc}
+ * @throws DecodeBreakException {@inheritDoc}
*/
@Override
- public void decodingError(byte[] errorArray, int offset, int length)
- throws DecodeException{
- DecodedContent text = getContent();
-
- switch(length){
- case 1:
- text.addDecodeError(errorArray[0]);
- return;
- case 2:
- text.addDecodeError(errorArray[0], errorArray[1]);
- return;
- default:
- break;
- }
+ public void unmapError(byte[] errorArray, int offset, int length)
+ throws DecodeBreakException {
+ assert length == 2;
- int limit = offset + length;
- for(int bpos = offset; bpos < limit; bpos++){
- byte bval = errorArray[bpos];
+ byte b0 = errorArray[offset];
+ byte b1 = errorArray[offset + 1];
- if(this.hasByte1st){
- if(ShiftJis.isShiftJIS2ndByte(bval)){ // 文字集合エラー
- text.addDecodeError(this.byte1st, bval);
- this.hasByte1st = false;
- }else if(ShiftJis.isShiftJIS1stByte(bval)){
- text.addDecodeError(this.byte1st);
- this.byte1st = bval;
- this.hasByte1st = true;
- }else{
- text.addDecodeError(this.byte1st);
- text.addDecodeError(bval);
- this.hasByte1st = false;
- }
- }else{
- if(ShiftJis.isShiftJIS1stByte(bval)){
- this.byte1st = bval;
- this.hasByte1st = true;
- }else{
- text.addDecodeError(bval);
- }
- }
- }
+ DecodedContent text = getContent();
+ text.addDecodeError(b0, b1);
return;
}
+++ /dev/null
-/*
- * content builder for UTF-8
- *
- * License : The MIT License
- * Copyright(c) 2010 olyutorskii
- */
-
-package jp.sourceforge.jindolf.parser;
-
-/**
- * "UTF-8"エンコーディング用デコードハンドラ。
- * {@link StreamDecoder}からの通知に従い、
- * {@link DecodedContent}へとデコードする。
- */
-public class ContentBuilderUCS2 extends ContentBuilder{
-
- private static final int DEF_BUF_SZ = 128;
-
-
- /**
- * コンストラクタ。
- * 長さ0で空の{@link DecodedContent}がセットされる。
- */
- public ContentBuilderUCS2(){
- this(DEF_BUF_SZ);
- return;
- }
-
- /**
- * コンストラクタ。
- * 長さ0で空の{@link DecodedContent}がセットされる。
- * @param capacity 初期容量
- * @throws NegativeArraySizeException 容量指定が負。
- */
- public ContentBuilderUCS2(int capacity)
- throws NegativeArraySizeException{
- super(capacity);
- initImpl();
- return;
- }
-
-
- /**
- * デコード処理の初期化下請。
- */
- private void initImpl(){
- this.getContent().init();
- return;
- }
-
- /**
- * デコード処理の初期化。
- */
- @Override
- protected void init(){
- initImpl();
- return;
- }
-
- /**
- * {@inheritDoc}
- * @param seq {@inheritDoc}
- * @throws DecodeException {@inheritDoc}
- */
- @Override
- public void charContent(CharSequence seq)
- throws DecodeException{
- flushError();
- getContent().append(seq);
- return;
- }
-
- /**
- * {@inheritDoc}
- * @param errorArray {@inheritDoc}
- * @param offset {@inheritDoc}
- * @param length {@inheritDoc}
- * @throws DecodeException {@inheritDoc}
- */
- @Override
- public void decodingError(byte[] errorArray, int offset, int length)
- throws DecodeException{
- int limit = offset + length;
-
- for(int bpos = offset; bpos < limit; bpos++){
- byte bval = errorArray[bpos];
- getContent().addDecodeError(bval);
- }
-
- return;
- }
-
-}
+++ /dev/null
-/*
- * decode exception
- *
- * License : The MIT License
- * Copyright(c) 2009 olyutorskii
- */
-
-package jp.sourceforge.jindolf.parser;
-
-/**
- * デコード異常系情報。
- * {@link DecodeHandler}の各メソッドは、この例外をスローすることで
- * デコード処理の即時停止を{@link StreamDecoder}に指示することができる。
- * デコード元(バイトストリーム)の中のエラー発生位置と
- * デコード先(CharSequence)の中のエラー発生位置を保持することができる。
- * いずれの値も、エラー発生位置が不明な場合は負の値が設定される。
- */
-@SuppressWarnings("serial")
-public class DecodeException extends Exception{
-
- private static final char CH_SP = '\u0020';
-
-
- private final int bytePos;
- private final int charPos;
-
- /**
- * コンストラクタ。
- */
- public DecodeException(){
- this(null);
- return;
- }
-
- /**
- * コンストラクタ。
- * @param message メッセージ
- */
- public DecodeException(String message){
- this(message, -1, -1);
- return;
- }
-
- /**
- * コンストラクタ。
- * 位置情報が不明な場合は負の値を渡す。
- * @param bytePos デコード元エラー発生バイト位置
- * @param charPos デコード先エラー発生文字位置
- */
- public DecodeException(int bytePos, int charPos){
- this(null, bytePos, charPos);
- return;
- }
-
- /**
- * コンストラクタ。
- * 位置情報が不明な場合は負の値を渡す。
- * @param message メッセージ
- * @param bytePos デコード元エラー発生バイト位置
- * @param charPos デコード先エラー発生文字位置
- */
- public DecodeException(String message, int bytePos, int charPos){
- super(message);
- this.bytePos = bytePos;
- this.charPos = charPos;
- return;
- }
-
- /**
- * デコード元エラー発生位置を返す。
- * 単位はbyte単位。
- * @return エラー発生位置。不明な場合は負の値。
- */
- public int getBytePos(){
- return this.bytePos;
- }
-
- /**
- * デコード先エラー発生位置を返す。
- * 単位はchar単位。
- * @return エラー発生位置。不明な場合は負の値。
- */
- public int getCharPos(){
- return this.charPos;
- }
-
- /**
- * {@inheritDoc}
- * @return {@inheritDoc}
- */
- @Override
- public String getMessage(){
- StringBuilder result = new StringBuilder(20);
-
- String message = super.getMessage();
- if(message != null && message.length() > 0){
- result.append(message).append(CH_SP);
- }
-
- result.append("bytePos=").append(this.bytePos);
- result.append(CH_SP);
- result.append("charPos=").append(this.charPos);
-
- return result.toString();
- }
-
-}
+++ /dev/null
-/*
- * decode handler
- *
- * License : The MIT License
- * Copyright(c) 2009 olyutorskii
- */
-
-package jp.sourceforge.jindolf.parser;
-
-import java.nio.charset.CharsetDecoder;
-
-/**
- * 文字デコードハンドラ。
- * {@link StreamDecoder}により呼び出される。
- *
- * <p>デコード処理を通じてメソッドが呼ばれる順番は
- * {@link #startDecoding}が最初で
- * {@link #endDecoding}が最後。
- * その間、{@link #charContent}
- * または{@link #decodingError}が複数回呼ばれる。
- *
- * <p>各メソッドは、{@link DecodeException}をスローすることで
- * デコード処理を中止させることができる。
- */
-public interface DecodeHandler{
-
- /**
- * デコード処理開始の通知を受け取る。
- *
- * <p>渡された文字デコーダの各種設定を変更してはならない。
- *
- * @param decoder 文字デコーダ
- * @throws DecodeException デコードエラー
- */
- void startDecoding(CharsetDecoder decoder)
- throws DecodeException;
-
- /**
- * 正常にデコードした文字列の通知を受け取る。
- *
- * <p>seqの内容は、ハンドラ呼び出し元で随時変更されうる。
- * seqの内容を後々再利用するつもりなら、
- * 制御を呼び出し元に戻すまでの間に必要な箇所をコピーする必要がある。
- *
- * @param seq 文字列
- * @throws DecodeException デコードエラー
- */
- void charContent(CharSequence seq)
- throws DecodeException;
-
- /**
- * デコードエラーの通知を受け取る。
- *
- * <p>errorArrayの内容は、ハンドラ呼び出し元で随時変更されうる。
- * errorArrayの内容を後々再利用するつもりなら、
- * 制御を呼び出し元に戻すまでの間に必要な箇所をコピーする必要がある。
- *
- * @param errorArray エラーを引き起こした入力バイトシーケンス。
- * @param offset errorArrayに含まれるエラーの開始位置。
- * @param length errorArrayに含まれるエラーのバイト長。
- * @throws DecodeException デコードエラー
- */
- void decodingError(byte[] errorArray, int offset, int length)
- throws DecodeException;
-
- /**
- * デコード処理終了の通知を受け取る。
- *
- * @throws DecodeException デコードエラー
- */
- void endDecoding()
- throws DecodeException;
-
-}
import java.util.RandomAccess;
/**
- * ShiftJISデコードエラー情報を含む再利用可能な文字列。
- * デコードエラーを起こした箇所は代替文字{@link #ALTCHAR}で置き換えられる。
+ * デコードエラー情報を含む再利用可能な文字列。
+ *
+ * <p>デコードエラーを起こした箇所は代替文字{@link #ALTCHAR}で置き換えられる。
* マルチスレッドには非対応。
- * UCS-4コードポイントには未対応。
*/
public class DecodedContent
implements CharSequence,
int startPos, int endPos)
throws IndexOutOfBoundsException{
if(seq == null){
- this.rawContent.append(NULLTEXT, startPos, endPos);
+ this.rawContent.append(NULLTEXT);
}else if(seq instanceof DecodedContent){
append((DecodedContent) seq, startPos, endPos);
}else{
/**
* 文字列を追加する。
+ *
+ * @param str 追加される文字
+ * @param offset 追加される最初の char のインデックス
+ * @param len 追加される char の数
+ * @return thisオブジェクト
+ * @throws IndexOutOfBoundsException 範囲指定が不正。
+ */
+ public DecodedContent append(char[] str, int offset, int len)
+ throws IndexOutOfBoundsException{
+ if(str == null){
+ this.rawContent.append(NULLTEXT);
+ }else{
+ this.rawContent.append(str, offset, len);
+ }
+
+ return this;
+ }
+
+ /**
+ * 文字列を追加する。
* @param source 追加する文字列
* @param startPos 開始位置
* @param endPos 終了位置
int startPos, int endPos)
throws IndexOutOfBoundsException{
if(source == null){
- return append(NULLTEXT, startPos, endPos);
+ return append(NULLTEXT);
}
int gap = startPos - this.rawContent.length();
}
/**
- * 代替文字とともにデコードエラーを追加する。
+ * 代替文字とともに2バイトからなるデコードエラーを追加する。
+ *
+ * <p>主にシフトJISのUnmapエラーを想定。
+ *
* @param b1st 1バイト目の値
* @param b2nd 2バイト目の値
*/
+++ /dev/null
-/*
- * stream decoder for Shift_JIS
- *
- * License : The MIT License
- * Copyright(c) 2009 olyutorskii
- */
-
-package jp.sourceforge.jindolf.parser;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.charset.CoderResult;
-
-/**
- * Shift_JISバイト列のデコードエラーに特化した、
- * {@link StreamDecoder}の派生クラス。
- *
- * <p>Java実行系の細かな仕様差異による
- * デコードエラー出現パターンゆらぎの正規化を行う。
- *
- * <p>0x5Cが{@literal U+005C}にデコードされるか
- * {@literal U+00A5}にデコードされるかはJava実行系の実装依存。
- *
- * @see <a href="http://www.iana.org/assignments/character-sets">
- * CHARACTER SETS</a>
- */
-public class SjisDecoder extends StreamDecoder{
-
- /**
- * コンストラクタ。
- */
- public SjisDecoder(){
- this(BYTEBUF_DEFSZ, CHARBUF_DEFSZ);
- return;
- }
-
- /**
- * コンストラクタ。
- *
- * @param inbufSz 入力バッファサイズ
- * @param outbufSz 出力バッファサイズ
- * @throws IllegalArgumentException バッファサイズが小さすぎる。
- */
- public SjisDecoder(int inbufSz, int outbufSz)
- throws IllegalArgumentException{
- super(ShiftJis.CHARSET.newDecoder(), inbufSz, outbufSz);
- return;
- }
-
-
- /**
- * 1バイトのエラーをUnmap系2バイトエラーに統合できないか試す。
- *
- * <p>必要に応じて1バイト以上の追加先読みを行う。
- *
- * <p>バイト列 {0x85,0x40}(未割り当て9区の文字) を、
- * 1バイトのエラーと文字@に分離するJava実行系への対処。
- *
- * @param result デコード異常系
- * @return 修正されたデコード異常系。修正がなければ引数と同じものを返す。
- * @throws IOException 追加入力エラー
- */
- private CoderResult modify1ByteError(CoderResult result)
- throws IOException {
- assert result.length() == 1;
-
- ByteBuffer inbuffer = getByteBuffer();
-
- if(inbuffer.remaining() < 2){
- fillByteBuffer();
- }
- // 入力バイト列の最後がこの1バイトエラーだった場合。
- if(inbuffer.remaining() < 2) return result;
-
- int currPos = inbuffer.position();
- int nextPos = currPos + 1;
-
- // 絶対的get
- byte curr = inbuffer.get(currPos);
- byte next = inbuffer.get(nextPos);
-
- CoderResult newResult;
- if( ShiftJis.isShiftJIS(curr, next) ){
- newResult = CoderResult.unmappableForLength(2);
- }else{
- newResult = result;
- }
-
- return newResult;
- }
-
- /**
- * 2バイトのエラーを1バイトに分割できないか試す。
- *
- * <p>※ バイト列"FF:32" のShift_JISデコードに際して、
- * 2バイト長のデコードエラーを返す1.6系実行系が存在する。
- *
- * @param result デコード異常系
- * @return 修正されたデコード異常系。修正がなければ引数と同じものを返す。
- */
- private CoderResult modify2ByteError(CoderResult result){
- assert result.length() == 2;
-
- ByteBuffer inbuffer = getByteBuffer();
- assert inbuffer.remaining() >= 2;
-
- int currPos = inbuffer.position();
- int nextPos = currPos + 1;
-
- byte curr = inbuffer.get(currPos); // 絶対的get
- byte next = inbuffer.get(nextPos);
-
- CoderResult newResult;
- if( ShiftJis.isShiftJIS(curr, next) ){
- newResult = result;
- }else{
- newResult = CoderResult.malformedForLength(1);
- }
-
- return newResult;
- }
-
- /**
- * {@inheritDoc}
- *
- * <p>シフトJISデコードエラー出現パターンの
- * ランタイム実装による差異を吸収する。
- *
- * @param result {@inheritDoc}
- * @return {@inheritDoc}
- * @throws IOException {@inheritDoc}
- */
- @Override
- protected CoderResult modifyErrorLength(CoderResult result)
- throws IOException{
- int errorLength = result.length();
-
- CoderResult newResult;
- switch (errorLength) {
- case 1:
- newResult = modify1ByteError(result);
- break;
- case 2:
- newResult = modify2ByteError(result);
- break;
- default:
- newResult = result;
- break;
- }
-
- return newResult;
- }
-
-}
--- /dev/null
+/*
+ * Shift_JIS decode notifier
+ *
+ * License : The MIT License
+ * Copyright(c) 2018 olyutorskii
+ */
+
+package jp.sourceforge.jindolf.parser;
+
+import io.bitbucket.olyutorskii.jiocema.DecodeNotifier;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.charset.CoderResult;
+
+/**
+ * Shift_JISバイト列のデコードエラーに特化した、
+ * {@link DecodeNotifier}の派生クラス。
+ *
+ * <p>Javaランタイムの細かな仕様差異による
+ * デコードエラー出現パターンゆらぎの正規化を行う。
+ *
+ * <ul>
+ *
+ * <li>バイト列 [0xff:0x32]や[0x81:0xfd]を
+ * 2バイト長Unmapエラーとして検出してしまう
+ * Javaランタイムへの対処。
+ *
+ * <li>バイト列 [0x85,0x40](未割り当て9区の文字) を、
+ * 1バイトのエラーと文字@に分離してしまうJavaランタイムへの対処。
+ *
+ * <li>バイト列 [0x80:0x41]先頭を1バイト長Unmapエラーとして検出してしまう
+ * Javaランタイムへの対処。
+ *
+ * </ul>
+ *
+ * <p>TODO: 1.7系ランタイムによっては
+ * [0x81, 0x7f]が「÷」にデコードされる場合がある問題が未解決。
+ *
+ * @see https://en.wikipedia.org/wiki/Shift_JIS
+ * @see sun.nio.cs.ext.SJIS
+ */
+public class SjisNotifier extends DecodeNotifier{
+
+ private static final String MSGFORM_SJBUFLEN =
+ "input buffer length must be 2 or more for Shift_JIS";
+
+
+ /**
+ * コンストラクタ。
+ *
+ * <p>デコーダにはShift_JIS用ランタイムが用いられる。
+ *
+ * <p>バッファサイズはデフォルト値が用いられる。
+ *
+ * @see DecodeNotifier#DEFSZ_BYTEBUF
+ * @see DecodeNotifier#DEFSZ_CHARBUF
+ */
+ public SjisNotifier(){
+ this(DEFSZ_BYTEBUF, DEFSZ_CHARBUF);
+ return;
+ }
+
+ /**
+ * コンストラクタ。
+ *
+ * <p>デコーダにはShift_JIS用ランタイムが用いられる。
+ *
+ * @param inbufSz 入力バッファサイズ。
+ * シフトJIS上位下位のため2以上を指定しなければならない。
+ * @param outbufSz 出力バッファサイズ。
+ * サロゲートペア格納のため2以上を指定しなければならない。
+ * @throws IllegalArgumentException 不適切なバッファサイズ
+ */
+ public SjisNotifier(int inbufSz, int outbufSz)
+ throws IllegalArgumentException {
+ super(ShiftJis.CHARSET.newDecoder(), inbufSz, outbufSz);
+
+ if(inbufSz < 2){
+ throw new IllegalArgumentException(MSGFORM_SJBUFLEN);
+ }
+
+ return;
+ }
+
+
+ /**
+ * Javaランタイムの差異によるシフトJISデコードエラーの揺らぎを正規化する。
+ *
+ * <ul>
+ *
+ * <li>2バイト長のUnmapエラーがシフトJISの形式を満たさない場合、
+ * 1バイト長のMalformedエラーに修正する。
+ *
+ * <li>2バイト長でない、もしくはUnmapでないエラー時に、
+ * 入力バッファ未読部先頭がシフトJISの形式を満たす場合、
+ * 2バイト長のUnmapエラーに修正する。
+ *
+ * <li>1バイト長のUnmapエラーで
+ * 入力バッファ未読部先頭がシフトJISの形式を満たさない場合、
+ * 1バイト長のMalformedエラーに修正する。
+ *
+ * </ul>
+ *
+ * <p>必要に応じて1バイト以上の追加先読みを行う。
+ *
+ * <p>{@inheritDoc}
+ *
+ * @param errInfo {@inheritDoc}
+ * @return {@inheritDoc}
+ * @throws IOException {@inheritDoc}
+ */
+ @Override
+ protected CoderResult modifyErrorResult(CoderResult errInfo)
+ throws IOException {
+ boolean unmapSingle = false;
+ boolean unmapDouble = false;
+ if(errInfo.isUnmappable()){
+ int errorLength = errInfo.length();
+ switch(errorLength){
+ case 1: unmapSingle = true; break;
+ case 2: unmapDouble = true; break;
+ default: break;
+ }
+ }
+
+ if(unmapDouble){
+ return modifyUnmapDoubleError(errInfo);
+ }
+
+ boolean detectSjis = false;
+ if(fillDoubleBytes()){
+ if(isSjisHeadErr()){
+ detectSjis = true;
+ }
+ }
+
+ CoderResult newResult;
+ if(detectSjis){
+ newResult = CoderResult.unmappableForLength(2);
+ }else if(unmapSingle){
+ newResult = CoderResult.malformedForLength(1);
+ }else{
+ newResult = errInfo;
+ }
+
+ return newResult;
+ }
+
+ /**
+ * modify 2-byte unmap decode error.
+ *
+ * <p>if 2-bytes sequence is invalid Shift_JIS,
+ * single-byte malformed error will be return.
+ *
+ * <p>Yes, [81:fd] must not be Unmap-error.
+ *
+ * @param errInfo original error information
+ * @return modified error information
+ */
+ private CoderResult modifyUnmapDoubleError(CoderResult errInfo){
+ assert errInfo.isUnmappable();
+ assert errInfo.length() == 2;
+
+ CoderResult newResult;
+ if(isSjisHeadErr()){
+ newResult = errInfo;
+ }else{
+ newResult = CoderResult.malformedForLength(1);
+ }
+
+ return newResult;
+ }
+
+ /**
+ * 入力バッファ未読部長が2バイト以上になるまで入力を進める。
+ *
+ * @return 未読部長が2バイト未満の段階で入力が終了したらfalse
+ * @throws IOException 入力エラー
+ */
+ private boolean fillDoubleBytes() throws IOException{
+ ByteBuffer inbuffer = getByteBuffer();
+ while(inbuffer.remaining() < 2){
+ if( ! hasMoreInput()) return false;
+ supplyInputBytes();
+ }
+ return true;
+ }
+
+ /**
+ * 入力バッファ未読部先頭がシフトJISの2バイト長文字形式か判定する。
+ *
+ * <p>入力バッファ未読部の長さが2未満の場合は常に偽となる。
+ *
+ * <p>文字集合がJIS X0208に収まるか否かのUnmap判定は行わない。
+ *
+ * @return シフトJISの2バイト長文字形式ならtrue
+ */
+ private boolean isSjisHeadErr(){
+ ByteBuffer inbuffer = getByteBuffer();
+
+ if(inbuffer.remaining() < 2) return false;
+ int currPos = inbuffer.position();
+ int nextPos = currPos + 1;
+
+ byte curr = inbuffer.get(currPos);
+ byte next = inbuffer.get(nextPos);
+
+ boolean result;
+ result = ShiftJis.isShiftJIS(curr, next);
+
+ return result;
+ }
+
+}
+++ /dev/null
-/*
- * stream decoder
- *
- * License : The MIT License
- * Copyright(c) 2009 olyutorskii
- */
-
-package jp.sourceforge.jindolf.parser;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.ByteBuffer;
-import java.nio.CharBuffer;
-import java.nio.channels.Channels;
-import java.nio.channels.ReadableByteChannel;
-import java.nio.charset.CharsetDecoder;
-import java.nio.charset.CoderResult;
-import java.nio.charset.CodingErrorAction;
-import java.util.Arrays;
-
-/**
- * バイトストリームを入力とする文字列デコーダ。
- *
- * <p>入力バイトストリームから文字列(charシーケンス)をデコードし、
- * デコード結果およびデコード異常系を
- * 文字デコードハンドラ<code>{@link DecodeHandler}</code>に通知する。
- *
- * <p>このクラスは、「制御の反転」(Inversion of Control)を用いて
- * <code>{@link java.nio.charset.CharsetDecoder}</code>呼び出しの
- * 煩雑さを隠蔽するために設計された。
- *
- * <p>このクラスは、
- * デコード異常系詳細を察知できない
- * <code>{@link java.io.InputStreamReader}</code>の
- * 代替品として設計された。
- *
- * <p>マルチスレッドからの同一インスタンスへの操作は非対応。
- *
- * @see java.nio.charset.CharsetDecoder
- */
-public class StreamDecoder{
-
- /** デフォルト入力バッファサイズ(={@value}bytes)。 */
- public static final int BYTEBUF_DEFSZ = 4 * 1024;
- /** デフォルト出力バッファサイズ(={@value}chars)。 */
- public static final int CHARBUF_DEFSZ = 4 * 1024;
-
- private static final int DEF_ERRBUFLEN = 4;
-
-
- private final CharsetDecoder decoder;
-
- private final ByteBuffer byteBuffer;
- private final CharBuffer charBuffer;
-
- private ReadableByteChannel channel;
-
- private DecodeHandler decodeHandler;
-
- private byte[] errorData = new byte[DEF_ERRBUFLEN];
-
-
- /**
- * コンストラクタ。
- *
- * バッファサイズは入出力ともデフォルト値が用いられる。
- *
- * @param decoder 文字列デコーダ。
- * 異常系に関するアクション応答設定は変更される。
- * @throws NullPointerException デコーダにnullを渡した。
- */
- public StreamDecoder(CharsetDecoder decoder) throws NullPointerException{
- this(decoder, BYTEBUF_DEFSZ, CHARBUF_DEFSZ);
- return;
- }
-
- /**
- * コンストラクタ。
- *
- * @param decoder 文字列デコーダ。
- * 異常系に関するアクション応答設定は変更される。
- * @param inbufSz 入力バッファサイズ(byte単位)
- * @param outbufSz 出力バッファサイズ(char単位)
- * @throws NullPointerException デコーダにnullを渡した。
- * @throws IllegalArgumentException バッファサイズが0以下。
- */
- public StreamDecoder(CharsetDecoder decoder,
- int inbufSz,
- int outbufSz )
- throws NullPointerException,
- IllegalArgumentException {
- super();
-
- if(decoder == null) throw new NullPointerException();
-
- if(inbufSz <= 0 || outbufSz <= 0){
- throw new IllegalArgumentException();
- }
-
- this.decoder = decoder;
- this.byteBuffer = ByteBuffer.allocate(inbufSz);
- this.charBuffer = CharBuffer.allocate(outbufSz);
- this.channel = null;
-
- initDecoderImpl();
-
- return;
- }
-
-
- /**
- * デコーダの初期化下請。
- */
- private void initDecoderImpl(){
- this.byteBuffer.clear().flip();
- this.charBuffer.clear();
-
- this.decoder.onMalformedInput (CodingErrorAction.REPORT);
- this.decoder.onUnmappableCharacter(CodingErrorAction.REPORT);
- this.decoder.reset();
-
- Arrays.fill(this.errorData, (byte) 0x00);
-
- return;
- }
-
- /**
- * デコーダの初期化。
- */
- protected void initDecoder(){
- initDecoderImpl();
- return;
- }
-
- /**
- * デコードハンドラの設定。
- *
- * <p>デコード結果はここで指定したハンドラに通知される。
- *
- * <p>nullオブジェクトを指定することも可能だが、
- * その場合デコード時に例外を起こす。
- *
- * @param decodeHandler デコードハンドラ
- */
- public void setDecodeHandler(DecodeHandler decodeHandler){
- this.decodeHandler = decodeHandler;
- return;
- }
-
- /**
- * 入力バッファを返す。
- *
- * @return 入力バッファ
- */
- protected ByteBuffer getByteBuffer(){
- return this.byteBuffer;
- }
-
- /**
- * 出力バッファを返す。
- *
- * @return 出力バッファ
- */
- protected CharBuffer getCharBuffer(){
- return this.charBuffer;
- }
-
- /**
- * チャネルからの入力を読み進め入力バッファに詰め込む。
- *
- * <p>前回デコード処理の読み残しはバッファ前方に詰め直される。
- *
- * <p>入力バッファに空きがない状態で呼んではいけない。
- *
- * @return 入力バイト数。
- * 入力末端に達したときは負の値。
- * ※入力バッファに空きがありチャネルがブロックモードの場合、
- * 返り値0はありえない。
- * @throws java.io.IOException 入力エラー
- */
- protected int fillByteBuffer() throws IOException{
- this.byteBuffer.compact();
- assert this.byteBuffer.hasRemaining();
-
- int length = this.channel.read(this.byteBuffer);
- assert length != 0;
-
- this.byteBuffer.flip();
-
- return length;
- }
-
- /**
- * 出力バッファの全出力を読み進め、
- * デコードハンドラに文字列を通知する。
- *
- * <p>出力バッファはクリアされる。
- *
- * <p>既に出力バッファが空だった場合、何もしない。
- *
- * @throws DecodeException ハンドラによるデコードエラー
- */
- protected void notifyText() throws DecodeException{
- if(this.charBuffer.position() <= 0){
- return;
- }
-
- this.charBuffer.flip();
- this.decodeHandler.charContent(this.charBuffer);
- this.charBuffer.clear();
-
- return;
- }
-
- /**
- * デコードハンドラにデコードエラーを通知する。
- *
- * @param result デコード結果
- * @throws DecodeException ハンドラによるデコードエラー
- * @throws IOException 入力エラー
- */
- protected void notifyError(CoderResult result)
- throws IOException,
- DecodeException{
- int length = chopErrorSequence(result);
- this.decodeHandler.decodingError(this.errorData, 0, length);
- return;
- }
-
- /**
- * 入力バッファからデコードエラーの原因となったバイト列を読み進める。
- *
- * <p>{@link #errorData}の先頭にバイト列がコピーされ、
- * バイト長が返される。
- *
- * <p>入力バッファはエラーの長さの分だけ読み進められる。
- *
- * @param result デコードエラー
- * @return 原因バイト列の長さ
- * @throws IOException 入力エラー。
- * ※このメソッドを継承する場合、必要に応じて先読みをしてもよいし、
- * その結果生じたIO例外を投げてもよい。
- */
- protected int chopErrorSequence(CoderResult result) throws IOException{
- int errorLength = result.length();
- reassignErrorData(errorLength);
- this.byteBuffer.get(this.errorData, 0, errorLength); // 相対get
- return errorLength;
- }
-
- /**
- * デコードエラー格納配列の再アサイン。
- *
- * <p>旧配列の内容は保持される。
- * 決して縮小することは無い。
- *
- * @param size 再アサイン量。バイト長。
- */
- protected void reassignErrorData(int size){
- int oldLength = this.errorData.length;
- if(oldLength >= size) return;
-
- int newSize = size;
- if(oldLength * 2 > newSize) newSize = oldLength * 2;
-
- byte[] newData = Arrays.copyOf(this.errorData, newSize);
- this.errorData = newData;
-
- return;
- }
-
- /**
- * バイト入力ストリームを文字列デコードする。
- *
- * <p>デコード作業の状況に応じてハンドラへの各種通知が行われる。
- *
- * <p>入力ストリーム末端に到達するとデコード作業は終わり、
- * 入力ストリームは閉じられる。
- *
- * @param istream 入力ストリーム
- * @throws IOException 入力エラー
- * @throws DecodeException ハンドラによるデコードエラー
- */
- public void decode(InputStream istream)
- throws IOException,
- DecodeException {
- // このチャネルは必ずブロックモードのはず
- this.channel = Channels.newChannel(istream);
-
- try{
- decodeChannel();
- }finally{
- this.channel.close();
- this.channel = null;
- }
-
- return;
- }
-
- /**
- * 内部チャネルのデコードを開始する。
- *
- * @throws IOException 入力エラー
- * @throws DecodeException ハンドラによるデコードエラー
- */
- protected void decodeChannel()
- throws IOException,
- DecodeException {
- initDecoder();
-
- this.decodeHandler.startDecoding(this.decoder);
-
- int ioLength;
- boolean isEndOfInput;
-
- ioLength = fillByteBuffer();
- isEndOfInput = ioLength < 0;
-
- for(;;){
- CoderResult decodeResult =
- this.decoder.decode(this.byteBuffer,
- this.charBuffer,
- isEndOfInput);
- // デコードエラー出現
- if(decodeResult.isError()){
- notifyText();
- decodeResult = modifyErrorLength(decodeResult);
- notifyError(decodeResult);
- continue;
- }
-
- // 出力バッファが一杯
- if(decodeResult.isOverflow()){
- notifyText();
- continue;
- }
-
- assert decodeResult.isUnderflow();
-
- // デコード掃き出し開始
- if(isEndOfInput){
- break;
- }
-
- // 入力バッファのデータが不足
- checkInfLoop();
- ioLength = fillByteBuffer();
- isEndOfInput = ioLength < 0;
- }
-
- notifyText();
-
- CoderResult flushResult;
- do{
- flushResult = this.decoder.flush(this.charBuffer);
- assert ! flushResult.isError();
- notifyText();
- }while( ! flushResult.isUnderflow() );
-
- this.decodeHandler.endDecoding();
-
- return;
- }
-
- /**
- * エラーの長さ(バイト列長)を修正する。
- *
- * <p>文字コード毎の事情に特化した異常系実装を目的とする。
- * デフォルト実装では引数をそのまま返す。
- *
- * <p>バイト列を先読みすることで、
- * さらに長いエラー情報を再構成してもよい。
- *
- * <p>エラー情報を前方に縮小することで、
- * エラーとして扱われるはずだったバイト列後部は
- * 再度デコード処理の対象として扱われる。
- *
- * @param result 修正元エラー情報。
- * @return 修正後エラー情報。引数と同じ場合もありうる。
- * (修正がない場合など)
- * @throws IOException バイト列読み込みエラー
- */
- protected CoderResult modifyErrorLength(CoderResult result)
- throws IOException{
- CoderResult newResult = result;
- return newResult;
- }
-
- /**
- * 不適切な入力バッファサイズ由来の無限ループを検出し例外を投げる。
- *
- * <p>検出しなければ何もしない。
- *
- * @throws DecodeException 無限ループが検出された
- */
- private void checkInfLoop() throws DecodeException{
- if(this.byteBuffer.position() == 0){
- int bufSz = this.byteBuffer.capacity();
- String csName = this.decoder.charset().name();
-
- StringBuilder text = new StringBuilder();
- text.append("too small input buffer (");
- text.append(bufSz).append("bytes) for ");
- text.append(csName);
-
- throw new DecodeException(text.toString());
- }
- return;
- }
-
-}
* <pre>
* {@code
* InputStream is = .....
- * StreamDecoder decoder = new SjisDecoder();
+ * DecodeNotifier decoder = new DecodeNotifier(...);
* ContentBuilder builder = new ContentBuilder();
- * decoder.setDecodeHandler(builder);
+ * decoder.setCharDecodeListener(builder);
* try{
* decoder.decode(is);
* }catch(IOException e){
* // ERROR!
- * }catch(DecodeException e){
+ * }catch(DecodeBreakException e){
* // ERROR!
* }
* DecodedContent content = builder.getContent();
--- /dev/null
+/*
+ */
+
+package jp.sourceforge.jindolf.parser;
+
+import java.io.ByteArrayInputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * テスト用byte列シーケンスを生成する。
+ */
+class Bseq {
+
+ private Bseq(){
+ }
+
+ static ByteArrayInputStream byteStream(int... array) {
+ byte[] ba = new byte[array.length];
+ int idx = 0;
+ for (int iVal : array) {
+ byte bVal = (byte) (iVal & 255);
+ ba[idx++] = bVal;
+ }
+ return new ByteArrayInputStream(ba);
+ }
+
+ static ByteArrayInputStream byteIs(CharSequence seq) {
+ byte[] bs = byteArray(seq);
+ ByteArrayInputStream result = new ByteArrayInputStream(bs);
+ return result;
+ }
+
+ static byte[] byteArray(CharSequence seq) {
+ byte[] result;
+ List<Byte> byteList = new ArrayList<>();
+ int length = seq.length();
+ for (int pos = 0; pos < length; pos++) {
+ int val = 0;
+ char ch = seq.charAt(pos);
+ if ('0' <= ch && ch <= '9') {
+ val += ch - '0';
+ } else if ('a' <= ch && ch <= 'f') {
+ val += ch - 'a' + 10;
+ } else if ('A' <= ch && ch <= 'F') {
+ val += ch - 'A' + 10;
+ } else {
+ continue;
+ }
+ pos++;
+ if (pos >= length) {
+ break;
+ }
+ val *= 16;
+ ch = seq.charAt(pos);
+ if ('0' <= ch && ch <= '9') {
+ val += ch - '0';
+ } else if ('a' <= ch && ch <= 'f') {
+ val += ch - 'a' + 10;
+ } else if ('A' <= ch && ch <= 'F') {
+ val += ch - 'A' + 10;
+ } else {
+ continue;
+ }
+ byteList.add((byte) val);
+ }
+ result = new byte[byteList.size()];
+ for (int pos = 0; pos < result.length; pos++) {
+ result[pos] = byteList.get(pos);
+ }
+ return result;
+ }
+
+}
package jp.sourceforge.jindolf.parser;
+import io.bitbucket.olyutorskii.jiocema.DecodeBreakException;
+import io.bitbucket.olyutorskii.jiocema.DecodeNotifier;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.List;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
public void tearDown() {
}
- public static byte[] byteArray(CharSequence seq){
- byte[] result;
-
- List<Byte> byteList = new ArrayList<Byte>();
-
- int length = seq.length();
- for(int pos = 0; pos < length; pos++){
- int val = 0;
-
- char ch = seq.charAt(pos);
-
- if('0' <= ch && ch <= '9'){
- val += ch - '0';
- }else if('a' <= ch && ch <= 'f'){
- val += ch - 'a' + 10;
- }else if('A' <= ch && ch <= 'F'){
- val += ch - 'A' + 10;
- }else{
- continue;
- }
-
- pos++;
- if(pos >= length) break;
-
- val *= 16;
- ch = seq.charAt(pos);
-
- if('0' <= ch && ch <= '9'){
- val += ch - '0';
- }else if('a' <= ch && ch <= 'f'){
- val += ch - 'a' + 10;
- }else if('A' <= ch && ch <= 'F'){
- val += ch - 'A' + 10;
- }else{
- continue;
- }
-
- byteList.add((byte)val);
- }
-
- result = new byte[byteList.size()];
-
- for(int pos = 0; pos < result.length; pos++){
- result[pos] = byteList.get(pos);
- }
-
- return result;
- }
/**
* Test of SjisDecoder & ContentBuilder.
* @throws java.io.IOException
- * @throws jp.sourceforge.jindolf.parser.DecodeException
+ * @throws DecodeBreakException
*/
@Test
- public void testDecoding() throws IOException, DecodeException{
+ public void testDecoding() throws IOException, DecodeBreakException{
System.out.println("Decoding");
- SjisDecoder decoder;
+ DecodeNotifier decoder;
ContentBuilderSJ builder;
byte[] bdata;
InputStream istream;
DecodedContent content;
- decoder = new SjisDecoder();
+ decoder = new SjisNotifier();
builder = new ContentBuilderSJ();
- decoder.setDecodeHandler(builder);
+ decoder.setCharDecodeListener(builder);
- bdata = byteArray("20:41:42:43:7e");
+ bdata = Bseq.byteArray("20:41:42:43:7e");
istream = new ByteArrayInputStream(bdata);
decoder.decode(istream);
content = builder.getContent();
assertEquals(" ABC~", content.toString());
assertFalse(content.hasDecodeError());
- bdata = byteArray("");
+ bdata = Bseq.byteArray("");
istream = new ByteArrayInputStream(bdata);
decoder.decode(istream);
content = builder.getContent();
assertEquals("", content.toString());
assertFalse(content.hasDecodeError());
- bdata = byteArray("00:0A:0D:1F");
+ bdata = Bseq.byteArray("00:0A:0D:1F");
istream = new ByteArrayInputStream(bdata);
decoder.decode(istream);
content = builder.getContent();
assertEquals("\u0000\n\r\u001f", content.toString());
assertFalse(content.hasDecodeError());
- bdata = byteArray("A1:B1:B2:B3:DF");
+ bdata = Bseq.byteArray("A1:B1:B2:B3:DF");
istream = new ByteArrayInputStream(bdata);
decoder.decode(istream);
content = builder.getContent();
assertEquals("。アイウ゚", content.toString());
assertFalse(content.hasDecodeError());
- bdata = byteArray("8140:82A0:82A2:82A4:889F:EAA4");
+ bdata = Bseq.byteArray("8140:82A0:82A2:82A4:889F:EAA4");
istream = new ByteArrayInputStream(bdata);
decoder.decode(istream);
content = builder.getContent();
assertEquals("\u3000あいう亜熙", content.toString());
assertFalse(content.hasDecodeError());
- bdata = byteArray("5c");
+ bdata = Bseq.byteArray("5c");
istream = new ByteArrayInputStream(bdata);
decoder.decode(istream);
content = builder.getContent();
assertNotSame("\u00a5", content.toString());
assertFalse(content.hasDecodeError());
- bdata = byteArray("8d5c");
+ bdata = Bseq.byteArray("8d5c");
istream = new ByteArrayInputStream(bdata);
decoder.decode(istream);
content = builder.getContent();
/**
* Test of unmappable character.
* @throws java.io.IOException
- * @throws jp.sourceforge.jindolf.parser.DecodeException
+ * @throws DecodeBreakException
*/
@Test
- public void testUnmap() throws IOException, DecodeException{
+ public void testUnmap() throws IOException, DecodeBreakException{
System.out.println("Unmap");
- SjisDecoder decoder;
+ SjisNotifier decoder;
ContentBuilderSJ builder;
byte[] bdata;
InputStream istream;
DecodedContent content;
DecodeErrorInfo einfo;
- decoder = new SjisDecoder();
+ decoder = new SjisNotifier();
builder = new ContentBuilderSJ();
- decoder.setDecodeHandler(builder);
+ decoder.setCharDecodeListener(builder);
- bdata = byteArray("41:8540:42"); // 9区
+ bdata = Bseq.byteArray("41:8540:42"); // 9区
istream = new ByteArrayInputStream(bdata);
decoder.decode(istream);
content = builder.getContent();
einfo = content.getDecodeErrorList().get(0);
assertUnmapError(einfo, 1, 0x85, 0x40);
- bdata = byteArray("41:8740:42"); // 13区
+ bdata = Bseq.byteArray("41:8740:42"); // 13区
istream = new ByteArrayInputStream(bdata);
decoder.decode(istream);
content = builder.getContent();
einfo = content.getDecodeErrorList().get(0);
assertUnmapError(einfo, 1, 0x87, 0x40);
- bdata = byteArray("41:8840:42"); // 15区
+ bdata = Bseq.byteArray("41:8840:42"); // 15区
istream = new ByteArrayInputStream(bdata);
decoder.decode(istream);
content = builder.getContent();
einfo = content.getDecodeErrorList().get(0);
assertUnmapError(einfo, 1, 0x88, 0x40);
- bdata = byteArray("41:EB40:42"); // 85区
+ bdata = Bseq.byteArray("41:EB40:42"); // 85区
istream = new ByteArrayInputStream(bdata);
decoder.decode(istream);
content = builder.getContent();
einfo = content.getDecodeErrorList().get(0);
assertUnmapError(einfo, 1, 0xEB, 0x40);
- bdata = byteArray("41:ED40:42"); // 89区
+ bdata = Bseq.byteArray("41:ED40:42"); // 89区
istream = new ByteArrayInputStream(bdata);
decoder.decode(istream);
content = builder.getContent();
einfo = content.getDecodeErrorList().get(0);
assertUnmapError(einfo, 1, 0xED, 0x40);
- bdata = byteArray("41:EEFC:42"); // 92区
+ bdata = Bseq.byteArray("41:EEFC:42"); // 92区
istream = new ByteArrayInputStream(bdata);
decoder.decode(istream);
content = builder.getContent();
einfo = content.getDecodeErrorList().get(0);
assertUnmapError(einfo, 1, 0xEE, 0xFC);
- bdata = byteArray("41:EF9F:42"); // 94区
+ bdata = Bseq.byteArray("41:EF9F:42"); // 94区
istream = new ByteArrayInputStream(bdata);
decoder.decode(istream);
content = builder.getContent();
/**
* Test of malformed character.
* @throws java.io.IOException
- * @throws jp.sourceforge.jindolf.parser.DecodeException
+ * @throws DecodeBreakException
*/
@Test
- public void testMalform() throws IOException, DecodeException{
+ public void testMalform() throws IOException, DecodeBreakException{
System.out.println("Malform");
- SjisDecoder decoder;
+ SjisNotifier decoder;
ContentBuilderSJ builder;
byte[] bdata;
InputStream istream;
DecodedContent content;
DecodeErrorInfo einfo;
- decoder = new SjisDecoder();
+ decoder = new SjisNotifier();
builder = new ContentBuilderSJ();
- decoder.setDecodeHandler(builder);
+ decoder.setCharDecodeListener(builder);
- bdata = byteArray("31:FD:FE:FF:32");
+ bdata = Bseq.byteArray("31:FD:FE:FF:32");
istream = new ByteArrayInputStream(bdata);
decoder.decode(istream);
content = builder.getContent();
einfo = content.getDecodeErrorList().get(2);
assertMalformError(einfo, 3, 0xff);
- bdata = byteArray("31:82:32:33");
+ bdata = Bseq.byteArray("31:82:32:33");
istream = new ByteArrayInputStream(bdata);
decoder.decode(istream);
content = builder.getContent();
einfo = content.getDecodeErrorList().get(0);
assertMalformError(einfo, 1, 0x82);
- bdata = byteArray("31:32:33:82");
+ bdata = Bseq.byteArray("31:32:33:82");
istream = new ByteArrayInputStream(bdata);
decoder.decode(istream);
content = builder.getContent();
/**
* Test of Bounds buffering.
* @throws java.io.IOException
- * @throws jp.sourceforge.jindolf.parser.DecodeException
+ * @throws DecodeBreakException
*/
@Test
- public void testBounds() throws IOException, DecodeException{
+ public void testBounds() throws IOException, DecodeBreakException{
System.out.println("Bounds");
- SjisDecoder decoder;
+ SjisNotifier decoder;
ContentBuilderSJ builder;
byte[] bdata;
InputStream istream;
DecodedContent content;
DecodeErrorInfo einfo;
- decoder = new SjisDecoder(5, 5);
+ decoder = new SjisNotifier(5, 5);
builder = new ContentBuilderSJ();
- decoder.setDecodeHandler(builder);
+ decoder.setCharDecodeListener(builder);
- bdata = byteArray("31:32:33:34:88" + "9F:35");
+ bdata = Bseq.byteArray("31:32:33:34:88" + "9F:35");
istream = new ByteArrayInputStream(bdata);
decoder.decode(istream);
content = builder.getContent();
assertFalse(content.hasDecodeError());
assertEquals(0, content.getDecodeErrorList().size());
- bdata = byteArray("31:32:33:34:82" + "35:36");
+ bdata = Bseq.byteArray("31:32:33:34:82" + "35:36");
istream = new ByteArrayInputStream(bdata);
decoder.decode(istream);
content = builder.getContent();
einfo = content.getDecodeErrorList().get(0);
assertMalformError(einfo, 4, 0x82);
- bdata = byteArray("31:32:33:34:87" + "40:35");
+ bdata = Bseq.byteArray("31:32:33:34:87" + "40:35");
istream = new ByteArrayInputStream(bdata);
decoder.decode(istream);
content = builder.getContent();
einfo = content.getDecodeErrorList().get(0);
assertUnmapError(einfo, 4, 0x87, 0x40);
- decoder = new SjisDecoder(5, 3);
+ decoder = new SjisNotifier(5, 3);
builder = new ContentBuilderSJ();
- decoder.setDecodeHandler(builder);
+ decoder.setCharDecodeListener(builder);
- bdata = byteArray("31:32:33:34:35:36");
+ bdata = Bseq.byteArray("31:32:33:34:35:36");
istream = new ByteArrayInputStream(bdata);
decoder.decode(istream);
content = builder.getContent();
package jp.sourceforge.jindolf.parser;
+import io.bitbucket.olyutorskii.jiocema.DecodeBreakException;
+import io.bitbucket.olyutorskii.jiocema.DecodeNotifier;
import java.io.ByteArrayInputStream;
+import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
-import java.util.ArrayList;
import java.util.List;
import org.junit.After;
import org.junit.AfterClass;
/**
*
*/
-public class ContentBuilderUCS2Test {
+public class ContentBuilderTest {
- public ContentBuilderUCS2Test() {
+ public ContentBuilderTest() {
}
@BeforeClass
public void tearDown() {
}
- public static byte[] byteArray(CharSequence seq){
- byte[] result;
-
- List<Byte> byteList = new ArrayList<>();
-
- int length = seq.length();
- for(int pos = 0; pos < length; pos++){
- int val = 0;
-
- char ch = seq.charAt(pos);
-
- if('0' <= ch && ch <= '9'){
- val += ch - '0';
- }else if('a' <= ch && ch <= 'f'){
- val += ch - 'a' + 10;
- }else if('A' <= ch && ch <= 'F'){
- val += ch - 'A' + 10;
- }else{
- continue;
- }
-
- pos++;
- if(pos >= length) break;
-
- val *= 16;
- ch = seq.charAt(pos);
-
- if('0' <= ch && ch <= '9'){
- val += ch - '0';
- }else if('a' <= ch && ch <= 'f'){
- val += ch - 'a' + 10;
- }else if('A' <= ch && ch <= 'F'){
- val += ch - 'A' + 10;
- }else{
- continue;
- }
-
- byteList.add((byte)val);
- }
-
- result = new byte[byteList.size()];
-
- for(int pos = 0; pos < result.length; pos++){
- result[pos] = byteList.get(pos);
- }
-
- return result;
- }
/**
* Test of UTF8
+ * @throws Exception
*/
@Test
public void testUTF8() throws Exception {
Charset cs = Charset.forName("UTF-8");
CharsetDecoder cd;
- ContentBuilderUCS2 cb;
- StreamDecoder decoder;
+ ContentBuilder cb;
+ DecodeNotifier decoder;
byte[] bdata;
InputStream is;
DecodedContent content;
cd = cs.newDecoder();
- decoder = new StreamDecoder(cd);
- cb = new ContentBuilderUCS2();
- decoder.setDecodeHandler(cb);
- bdata = byteArray("41:42:43");
+ decoder = new DecodeNotifier(cd);
+ cb = new ContentBuilder();
+ decoder.setCharDecodeListener(cb);
+ bdata = Bseq.byteArray("41:42:43");
is = new ByteArrayInputStream(bdata);
decoder.decode(is);
content = cb.getContent();
cd = cs.newDecoder();
- decoder = new StreamDecoder(cd);
- cb = new ContentBuilderUCS2();
- decoder.setDecodeHandler(cb);
- bdata = byteArray("41:EFBCA2:43");
+ decoder = new DecodeNotifier(cd);
+ cb = new ContentBuilder();
+ decoder.setCharDecodeListener(cb);
+ bdata = Bseq.byteArray("41:EFBCA2:43");
is = new ByteArrayInputStream(bdata);
decoder.decode(is);
content = cb.getContent();
cd = cs.newDecoder();
- decoder = new StreamDecoder(cd);
- cb = new ContentBuilderUCS2();
- decoder.setDecodeHandler(cb);
- bdata = byteArray("41:FF:43");
+ decoder = new DecodeNotifier(cd);
+ cb = new ContentBuilder();
+ decoder.setCharDecodeListener(cb);
+ bdata = Bseq.byteArray("41:FF:43");
is = new ByteArrayInputStream(bdata);
decoder.decode(is);
content = cb.getContent();
/**
* Test of UTF16
+ * @throws Exception
*/
@Test
public void testUTF16() throws Exception {
Charset cs = Charset.forName("UTF-16");
CharsetDecoder cd;
- ContentBuilderUCS2 cb;
- StreamDecoder decoder;
+ ContentBuilder cb;
+ DecodeNotifier decoder;
byte[] bdata;
InputStream is;
DecodedContent content;
cd = cs.newDecoder();
- decoder = new StreamDecoder(cd);
- cb = new ContentBuilderUCS2();
- decoder.setDecodeHandler(cb);
- bdata = byteArray("0041:0042:0043");
+ decoder = new DecodeNotifier(cd);
+ cb = new ContentBuilder();
+ decoder.setCharDecodeListener(cb);
+ bdata = Bseq.byteArray("0041:0042:0043");
is = new ByteArrayInputStream(bdata);
decoder.decode(is);
content = cb.getContent();
cd = cs.newDecoder();
- decoder = new StreamDecoder(cd);
- cb = new ContentBuilderUCS2();
- decoder.setDecodeHandler(cb);
- bdata = byteArray("0041:FF22:0043");
+ decoder = new DecodeNotifier(cd);
+ cb = new ContentBuilder();
+ decoder.setCharDecodeListener(cb);
+ bdata = Bseq.byteArray("0041:FF22:0043");
is = new ByteArrayInputStream(bdata);
decoder.decode(is);
content = cb.getContent();
/**
* Test of UTF16 sequence error
+ * @throws Exception
*/
@Test
public void testUTF16_seq() throws Exception {
Charset cs = Charset.forName("UTF-16");
CharsetDecoder cd;
- ContentBuilderUCS2 cb;
- StreamDecoder decoder;
+ ContentBuilder cb;
+ DecodeNotifier decoder;
byte[] bdata;
InputStream is;
DecodedContent content;
DecodeErrorInfo einfo;
cd = cs.newDecoder();
- decoder = new StreamDecoder(cd);
- cb = new ContentBuilderUCS2();
- decoder.setDecodeHandler(cb);
- bdata = byteArray("0041:d800:0043:0044");
+ decoder = new DecodeNotifier(cd);
+ cb = new ContentBuilder();
+ decoder.setCharDecodeListener(cb);
+ bdata = Bseq.byteArray("0041:d800:0043:0044");
is = new ByteArrayInputStream(bdata);
decoder.decode(is);
content = cb.getContent();
cd = cs.newDecoder();
- decoder = new StreamDecoder(cd);
- cb = new ContentBuilderUCS2();
- decoder.setDecodeHandler(cb);
- bdata = byteArray("0041:0042:dc00:0044");
+ decoder = new DecodeNotifier(cd);
+ cb = new ContentBuilder();
+ decoder.setCharDecodeListener(cb);
+ bdata = Bseq.byteArray("0041:0042:dc00:0044");
is = new ByteArrayInputStream(bdata);
decoder.decode(is);
content = cb.getContent();
cd = cs.newDecoder();
- decoder = new StreamDecoder(cd);
- cb = new ContentBuilderUCS2();
- decoder.setDecodeHandler(cb);
- bdata = byteArray("0041:d800");
+ decoder = new DecodeNotifier(cd);
+ cb = new ContentBuilder();
+ decoder.setCharDecodeListener(cb);
+ bdata = Bseq.byteArray("0041:d800");
is = new ByteArrayInputStream(bdata);
decoder.decode(is);
content = cb.getContent();
/**
* Test of UTF16 mapping error
+ * @throws Exception
*/
@Test
public void testUTF16_nomap() throws Exception {
Charset cs = Charset.forName("UTF-16");
CharsetDecoder cd;
- ContentBuilderUCS2 cb;
- StreamDecoder decoder;
+ ContentBuilder cb;
+ DecodeNotifier decoder;
byte[] bdata;
InputStream is;
DecodedContent content;
cd = cs.newDecoder();
- decoder = new StreamDecoder(cd);
- cb = new ContentBuilderUCS2();
- decoder.setDecodeHandler(cb);
- bdata = byteArray("0041:d83d:dc11:0042");
+ decoder = new DecodeNotifier(cd);
+ cb = new ContentBuilder();
+ decoder.setCharDecodeListener(cb);
+ bdata = Bseq.byteArray("0041:d83d:dc11:0042");
is = new ByteArrayInputStream(bdata);
decoder.decode(is);
content = cb.getContent();
return;
}
+ @Test
+ public void testSheep() throws IOException, DecodeBreakException {
+ System.out.println("sheep");
+
+ Charset cs;
+ CharsetDecoder decoder;
+ ContentBuilder listener;
+
+ DecodeNotifier sd;
+ InputStream is;
+
+ cs = Charset.forName("UTF-8");
+ decoder = cs.newDecoder();
+
+ sd = new DecodeNotifier(decoder);
+
+ listener = new ContentBuilder();
+ sd.setCharDecodeListener(listener);
+
+ // SMP character U+1F411 [SHEEP]
+ // see https://ja.osdn.net/projects/jindolf/ticket/36356
+ is = Bseq.byteStream(0xf0, 0x9f, 0x90, 0x91);
+ sd.decode(is);
+ assertEquals("\ud83d\udc11", listener.getContent().toString());
+
+ return;
+ }
+
}
+++ /dev/null
-/*
- * License : The MIT License
- * Copyright(c) 2009 olyutorskii
- */
-
-package jp.sourceforge.jindolf.parser;
-
-import org.junit.After;
-import org.junit.AfterClass;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.Test;
-
-import static org.junit.Assert.*;
-
-/**
- */
-public class DecodeExceptionTest {
-
- public DecodeExceptionTest() {
- }
-
- @BeforeClass
- public static void setUpClass() throws Exception{
- }
-
- @AfterClass
- public static void tearDownClass() throws Exception{
- }
-
- @Before
- public void setUp() {
- }
-
- @After
- public void tearDown() {
- }
-
- /**
- * Test of getBytePos method, of class DecodeException.
- */
- @Test
- public void testGetBytePos(){
- System.out.println("getBytePos");
-
- DecodeException ex;
-
- ex = new DecodeException();
- assertTrue(0 > ex.getBytePos());
-
- ex = new DecodeException("abc");
- assertTrue(0 > ex.getBytePos());
-
- ex = new DecodeException(10, 11);
- assertEquals(10, ex.getBytePos());
-
- ex = new DecodeException("abc", 10, 11);
- assertEquals(10, ex.getBytePos());
-
- return;
- }
-
- /**
- * Test of getCharPos method, of class DecodeException.
- */
- @Test
- public void testGetCharPos(){
- System.out.println("getCharPos");
-
- DecodeException ex;
-
- ex = new DecodeException();
- assertTrue(0 > ex.getCharPos());
-
- ex = new DecodeException("abc");
- assertTrue(0 > ex.getCharPos());
-
- ex = new DecodeException(10, 11);
- assertEquals(11, ex.getCharPos());
-
- ex = new DecodeException("abc", 10, 11);
- assertEquals(11, ex.getCharPos());
-
- return;
- }
-
- /**
- * Test of getMessage method, of class DecodeException.
- */
- @Test
- public void testGetMessage(){
- System.out.println("getMessage");
-
- DecodeException ex;
-
- ex = new DecodeException();
- assertEquals("bytePos=-1 charPos=-1", ex.getMessage());
-
- ex = new DecodeException("abc");
- assertEquals("abc bytePos=-1 charPos=-1", ex.getMessage());
-
- ex = new DecodeException(10, 11);
- assertEquals("bytePos=10 charPos=11", ex.getMessage());
-
- ex = new DecodeException("abc", 10, 11);
- assertEquals("abc bytePos=10 charPos=11", ex.getMessage());
-
- return;
- }
-
-}
assertEquals("abc", content.toString());
try{
- new DecodedContent(-1);
+ Object o = new DecodedContent(-1);
fail();
}catch(NegativeArraySizeException e){
}catch(Throwable e){
}
/**
+ * Test of append method, of class DecodedContent.
+ */
+ @Test
+ public void testAppend_3args_3(){
+ System.out.println("append");
+
+ DecodedContent content;
+
+ content = new DecodedContent();
+ content.append("abc");
+ assertEquals("abc", content.toString());
+
+ char[] seq = {'1','2','3','4','5',};
+ content.append(seq, 1, 3);
+ assertEquals("abc234", content.toString());
+
+ return;
+ }
+
+ /**
* Test of addDecodeError method, of class DecodedContent.
*/
@Test
+++ /dev/null
-/*
- */
-
-package jp.sourceforge.jindolf.parser;
-
-import java.io.ByteArrayInputStream;
-import java.io.InputStream;
-import java.nio.charset.CharsetDecoder;
-import java.util.ArrayList;
-import java.util.List;
-import org.junit.After;
-import org.junit.AfterClass;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.Test;
-
-import static org.junit.Assert.*;
-
-/**
- *
- */
-public class SjisDecoderTest {
-
- public SjisDecoderTest() {
- }
-
- @BeforeClass
- public static void setUpClass() {
- }
-
- @AfterClass
- public static void tearDownClass() {
- }
-
- @Before
- public void setUp() {
- }
-
- @After
- public void tearDown() {
- }
-
- public static ByteArrayInputStream byteIs(CharSequence seq){
- byte[] bs = byteArray(seq);
- ByteArrayInputStream result = new ByteArrayInputStream(bs);
- return result;
- }
-
- public static byte[] byteArray(CharSequence seq){
- byte[] result;
-
- List<Byte> byteList = new ArrayList<>();
-
- int length = seq.length();
- for(int pos = 0; pos < length; pos++){
- int val = 0;
-
- char ch = seq.charAt(pos);
-
- if('0' <= ch && ch <= '9'){
- val += ch - '0';
- }else if('a' <= ch && ch <= 'f'){
- val += ch - 'a' + 10;
- }else if('A' <= ch && ch <= 'F'){
- val += ch - 'A' + 10;
- }else{
- continue;
- }
-
- pos++;
- if(pos >= length) break;
-
- val *= 16;
- ch = seq.charAt(pos);
-
- if('0' <= ch && ch <= '9'){
- val += ch - '0';
- }else if('a' <= ch && ch <= 'f'){
- val += ch - 'a' + 10;
- }else if('A' <= ch && ch <= 'F'){
- val += ch - 'A' + 10;
- }else{
- continue;
- }
-
- byteList.add((byte)val);
- }
-
- result = new byte[byteList.size()];
-
- for(int pos = 0; pos < result.length; pos++){
- result[pos] = byteList.get(pos);
- }
-
- return result;
- }
-
- /**
- * Test of class SjisDecoder.
- * @throws Exception
- */
- @Test
- public void testSjisDecoder() throws Exception {
- SjisDecoder sjd;
- InputStream is;
- TestHandler handler;
-
- handler = new TestHandler();
-
- sjd = new SjisDecoder(10, 10);
- sjd.setDecodeHandler(handler);
- is = byteIs("414243");
- handler.clear();
- sjd.decode(is);
- assertEquals("[ST][CH]ABC[EN]", handler.toString());
-
- sjd = new SjisDecoder(10, 2);
- sjd.setDecodeHandler(handler);
- is = byteIs("414243");
- handler.clear();
- sjd.decode(is);
- assertEquals("[ST][CH]AB[CH]C[EN]", handler.toString());
-
- sjd = new SjisDecoder(2, 10);
- sjd.setDecodeHandler(handler);
- is = byteIs("82a0:82a1");
- handler.clear();
- sjd.decode(is);
- assertEquals("[ST][CH]あぃ[EN]", handler.toString());
-
- sjd = new SjisDecoder(2, 10);
- sjd.setDecodeHandler(handler);
- is = byteIs("41:82a0:82a1");
- handler.clear();
- sjd.decode(is);
- assertEquals("[ST][CH]Aあぃ[EN]", handler.toString());
-
- sjd = new SjisDecoder(1, 10);
- sjd.setDecodeHandler(handler);
- is = byteIs("82a0:82a1");
- handler.clear();
- try{
- sjd.decode(is);
- fail();
- }catch(DecodeException e){
- // GOOD
- }
-
- sjd = new SjisDecoder(4, 10);
- sjd.setDecodeHandler(handler);
- is = byteIs("41:8540:42");
- handler.clear();
- sjd.decode(is);
- assertEquals("[ST][CH]A[ER]8540[CH]B[EN]", handler.toString());
-
- sjd = new SjisDecoder(4, 10);
- sjd.setDecodeHandler(handler);
- is = byteIs("414243:8540:44");
- handler.clear();
- sjd.decode(is);
- assertEquals("[ST][CH]ABC[ER]8540[CH]D[EN]", handler.toString());
-
- sjd = new SjisDecoder(4, 10);
- sjd.setDecodeHandler(handler);
- is = byteIs("414243:85");
- handler.clear();
- sjd.decode(is);
- assertEquals("[ST][CH]ABC[ER]85[EN]", handler.toString());
-
- sjd = new SjisDecoder(4, 10);
- sjd.setDecodeHandler(handler);
- is = byteIs("41:ff32:42");
- handler.clear();
- sjd.decode(is);
- assertEquals("[ST][CH]A[ER]ff[CH]2B[EN]", handler.toString());
-
- sjd = new SjisDecoder(4, 10);
- sjd.setDecodeHandler(handler);
- is = byteIs("414243:ff32:44");
- handler.clear();
- sjd.decode(is);
- assertEquals("[ST][CH]ABC[ER]ff[CH]2D[EN]", handler.toString());
-
- return;
- }
-
- static class TestHandler implements DecodeHandler{
-
- private final StringBuilder text = new StringBuilder();
-
- @Override
- public void startDecoding(CharsetDecoder decoder) throws DecodeException {
- this.text.append("[ST]");
- }
-
- @Override
- public void endDecoding() throws DecodeException {
- this.text.append("[EN]");
- }
-
- @Override
- public void charContent(CharSequence seq) throws DecodeException {
- this.text.append("[CH]");
- this.text.append(seq);
- }
-
- @Override
- public void decodingError(byte[] errorArray, int offset, int length) throws DecodeException {
- this.text.append("[ER]");
- for(int ct = 0; ct < length;ct++){
- int val = errorArray[offset + ct] & 0xff;
- if(val <= 0xf) this.text.append('0');
- this.text.append(Integer.toHexString(val));
- }
- }
-
- public void clear(){
- text.setLength(0);
- }
-
- @Override
- public String toString(){
- return text.toString();
- }
-
- }
-
-}
--- /dev/null
+/*
+ */
+
+package jp.sourceforge.jindolf.parser;
+
+import io.bitbucket.olyutorskii.jiocema.DecodeNotifier;
+import java.io.InputStream;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ *
+ */
+public class SjisNotifierTest {
+
+
+
+ public SjisNotifierTest() {
+ }
+
+ @BeforeClass
+ public static void setUpClass() {
+ }
+
+ @AfterClass
+ public static void tearDownClass() {
+ }
+
+ @Before
+ public void setUp() {
+ }
+
+ @After
+ public void tearDown() {
+ }
+
+ @Test
+ public void testConstructor() throws Exception{
+ DecodeNotifier decoder;
+
+ decoder = new SjisNotifier();
+ assert decoder == decoder;
+
+ decoder = new SjisNotifier(2, 2);
+ assert decoder == decoder;
+
+ try{
+ decoder = new SjisNotifier(1, 2);
+ fail();
+ }catch(IllegalArgumentException e){
+ assertEquals("input buffer length must be 2 or more for Shift_JIS", e.getMessage());
+ }
+
+ try{
+ decoder = new SjisNotifier(2, 1);
+ fail();
+ }catch(IllegalArgumentException e){
+ assertEquals("output buffer length must be 2 or more for surrogate pair", e.getMessage());
+ }
+
+ assert decoder == decoder;
+
+ return;
+ }
+
+ @Test
+ public void testJisX0201() throws Exception{
+ DecodeNotifier decoder;
+ TestListener lst;
+ InputStream is;
+
+ decoder = new SjisNotifier();
+
+ lst = new TestListenerRW();
+ decoder.setCharDecodeListener(lst);
+
+ // test JISX0201 ASCII decoding
+
+ lst.clear();
+ is = Bseq.byteStream(0x00, 0x1f, 0x20, 0x21, 0x7f);
+ decoder.decode(is);
+ assertEquals("[ST][RW]001f20217f[CH]\u0000\u001f\u0020\u0021\u007f[EN]", lst.toString());
+
+ // test JISX0201 Hankaku-Katakana decoding
+
+ lst.clear();
+ is = Bseq.byteStream(0xa1, 0xb1, 0xdf);
+ decoder.decode(is);
+ assertEquals("[ST][RW]a1b1df[CH]\uff61\uff71\uff9f[EN]", lst.toString());
+
+ return;
+ }
+
+ @Test
+ public void testJisX0208() throws Exception{
+ DecodeNotifier decoder;
+ TestListener lst;
+ InputStream is;
+
+ decoder = new SjisNotifier();
+
+ lst = new TestListenerRW();
+ decoder.setCharDecodeListener(lst);
+
+ // test JISX0208 ASCII decoding
+
+ lst.clear();
+ is = Bseq.byteStream(0x88, 0x9f, 0xea, 0xa4);
+ decoder.decode(is);
+ assertEquals("[ST][RW]889feaa4[CH]\u4e9c\u7199[EN]", lst.toString());
+
+ return;
+ }
+
+ @Test
+ public void testInvalid1st() throws Exception{
+ DecodeNotifier decoder;
+ TestListener lst;
+ InputStream is;
+
+ decoder = new SjisNotifier();
+
+ lst = new TestListenerRW();
+ decoder.setCharDecodeListener(lst);
+
+ // test invalid 1st character decoding
+ // 0x80, 0xa0, 0xf0 - 0xff
+
+ lst.clear();
+ is = Bseq.byteStream(0x80);
+ decoder.decode(is);
+ assertEquals("[ST][ME]80[EN]", lst.toString());
+
+ lst.clear();
+ is = Bseq.byteStream(0x80, 0x41);
+ decoder.decode(is);
+ assertEquals("[ST][ME]80[RW]41[CH]A[EN]", lst.toString());
+
+ // 0x80+あ
+ lst.clear();
+ is = Bseq.byteStream(0x80, 0x82, 0xa0);
+ decoder.decode(is);
+ assertEquals("[ST][ME]80[RW]82a0[CH]\u3042[EN]", lst.toString());
+
+ lst.clear();
+ is = Bseq.byteStream(0x80, 0x80);
+ decoder.decode(is);
+ assertEquals("[ST][ME]80[ME]80[EN]", lst.toString());
+
+ lst.clear();
+ is = Bseq.byteStream(0xa0);
+ decoder.decode(is);
+ assertEquals("[ST][ME]a0[EN]", lst.toString());
+
+ lst.clear();
+ is = Bseq.byteStream(0xf0);
+ decoder.decode(is);
+ assertEquals("[ST][ME]f0[EN]", lst.toString());
+
+ lst.clear();
+ is = Bseq.byteStream(0xff);
+ decoder.decode(is);
+ assertEquals("[ST][ME]ff[EN]", lst.toString());
+
+ lst.clear();
+ is = Bseq.byteStream(0xfd, 0xfe, 0xff);
+ decoder.decode(is);
+ assertEquals("[ST][ME]fd[ME]fe[ME]ff[EN]", lst.toString());
+
+ lst.clear();
+ is = Bseq.byteStream(0xff, 0x32);
+ decoder.decode(is);
+ assertEquals("[ST][ME]ff[RW]32[CH]2[EN]", lst.toString());
+
+ lst.clear();
+ is = Bseq.byteStream(0x41, 0x42, 0x43, 0xff, 0x32);
+ decoder.decode(is);
+ assertEquals("[ST][RW]414243[CH]ABC[ME]ff[RW]32[CH]2[EN]", lst.toString());
+
+ return;
+ }
+
+ @Test
+ public void testInvalid2nd() throws Exception{
+ DecodeNotifier decoder;
+ TestListener lst;
+ InputStream is;
+
+ decoder = new SjisNotifier();
+
+ lst = new TestListenerRW();
+ decoder.setCharDecodeListener(lst);
+
+ // test invalid 2nd character decoding
+ // 0x00 - 0x3f, 0x7f, 0xfd - 0xff
+
+ lst.clear();
+ is = Bseq.byteStream(0x81, 0x00);
+ decoder.decode(is);
+ assertEquals("[ST][ME]81[RW]00[CH]\u0000[EN]", lst.toString());
+
+ lst.clear();
+ is = Bseq.byteStream(0x81, 0x3f);
+ decoder.decode(is);
+ assertEquals("[ST][ME]81[RW]3f[CH]\u003f[EN]", lst.toString());
+
+ lst.clear();
+ is = Bseq.byteStream(0x81, 0x7f);
+ decoder.decode(is);
+// assertEquals("[ST][RW]817f[CH]\u00f7[EN]", lst.toString()); // 1.7
+// assertEquals("[ST][ME]81[RW]7f[CH]\u007f[EN]", lst.toString()); // 1.8
+
+ lst.clear();
+ is = Bseq.byteStream(0x81, 0xfd);
+ decoder.decode(is);
+ assertEquals("[ST][ME]81[ME]fd[EN]", lst.toString());
+
+ lst.clear();
+ is = Bseq.byteStream(0x81, 0xfe);
+ decoder.decode(is);
+ assertEquals("[ST][ME]81[ME]fe[EN]", lst.toString());
+
+ lst.clear();
+ is = Bseq.byteStream(0x81, 0xff);
+ decoder.decode(is);
+ assertEquals("[ST][ME]81[ME]ff[EN]", lst.toString());
+
+ return;
+ }
+
+ @Test
+ public void testUnmap() throws Exception{
+ DecodeNotifier decoder;
+ TestListener lst;
+ InputStream is;
+
+ decoder = new SjisNotifier(4, 4);
+
+ lst = new TestListenerRW();
+ decoder.setCharDecodeListener(lst);
+
+ // test unmap error
+
+ lst.clear();
+ is = Bseq.byteStream(0x85, 0x40);
+ decoder.decode(is);
+ assertEquals("[ST][UE]8540[EN]", lst.toString());
+
+ lst.clear();
+ is = Bseq.byteStream(0x41, 0x42, 0x43, 0x85, 0x40);
+ decoder.decode(is);
+ assertEquals("[ST][RW]414243[CH]ABC[UE]8540[EN]", lst.toString());
+
+ return;
+ }
+
+ @Test
+ public void testSomeMethod() throws Exception{
+ DecodeNotifier decoder;
+ TestListener lst;
+ InputStream is;
+
+ // test decoding
+
+ decoder = new SjisNotifier();
+
+ lst = new TestListenerRW();
+ decoder.setCharDecodeListener(lst);
+
+ lst.clear();
+ is = Bseq.byteStream();
+ decoder.decode(is);
+ assertEquals("[ST][EN]", lst.toString());
+
+ lst.clear();
+ is = Bseq.byteStream(0x41);
+ decoder.decode(is);
+ assertEquals("[ST][RW]41[CH]A[EN]", lst.toString());
+
+ return;
+ }
+
+}
+++ /dev/null
-/*
- */
-
-package jp.sourceforge.jindolf.parser;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.charset.Charset;
-import java.nio.charset.CharsetDecoder;
-import org.junit.After;
-import org.junit.AfterClass;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.Test;
-
-import static org.junit.Assert.*;
-
-/**
- *
- */
-public class StreamDecoderTest {
-
- public StreamDecoderTest() {
- }
-
- @BeforeClass
- public static void setUpClass() {
- }
-
- @AfterClass
- public static void tearDownClass() {
- }
-
- @Before
- public void setUp() {
- }
-
- @After
- public void tearDown() {
- }
-
- /**
- * Test of constructor method, of class StreamDecoder.
- * @throws IOException
- * @throws DecodeException
- */
- @Test
- public void testConstructor() throws IOException, DecodeException {
- System.out.println("constructor");
-
- Charset cs;
- CharsetDecoder decoder;
- StreamDecoder sd;
-
- try{
- sd = new StreamDecoder(null);
- fail();
- }catch(NullPointerException e){
- // GOOD
- }
-
- cs = Charset.forName("US-ASCII");
- decoder = cs.newDecoder();
-
- try{
- sd = new StreamDecoder(decoder, 0, 100);
- fail();
- }catch(IllegalArgumentException e){
- // GOOD
- }
-
- try{
- sd = new StreamDecoder(decoder, 100, 0);
- fail();
- }catch(IllegalArgumentException e){
- // GOOD
- }
-
- return;
- }
-
- /**
- * Test of decode method, of class StreamDecoder.
- * @throws IOException
- * @throws DecodeException
- */
- @Test
- public void testDecode() throws IOException, DecodeException {
- System.out.println("decode");
-
- Charset cs;
- CharsetDecoder decoder;
-
- StreamDecoder sd;
- InputStream is;
- TestHandler handler;
-
- cs = Charset.forName("US-ASCII");
-
- decoder = cs.newDecoder();
- sd = new StreamDecoder(decoder);
- is = new ByteArrayInputStream(new byte[]{});
-
- try{
- sd.decode(is);
- fail();
- }catch(NullPointerException e){
- // GOOD
- }
-
- handler = new TestHandler();
- sd.setDecodeHandler(handler);
-
- is = byteStream();
- handler.clear();
- sd.decode(is);
- assertEquals("[ST][EN]", handler.toString());
-
- is = byteStream(0x41, 0x42, 0x43);
- handler.clear();
- sd.decode(is);
- assertEquals("[ST][CH]ABC[EN]", handler.toString());
-
- is = byteStream(0x0d, 0x0a, 0x7f);
- handler.clear();
- sd.decode(is);
- assertEquals("[ST][CH]\r\n\u007f[EN]", handler.toString());
-
- is = byteStream(0x7e, 0x7f, 0x80, 0xfe, 0xff);
- handler.clear();
- sd.decode(is);
- assertEquals("[ST][CH]\u007e\u007f[ER]80[ER]fe[ER]ff[EN]", handler.toString());
-
- is = byteStream(0x41, 0x42, 0x80, 0x43);
- handler.clear();
- sd.decode(is);
- assertEquals("[ST][CH]AB[ER]80[CH]C[EN]", handler.toString());
-
- decoder = cs.newDecoder();
- sd = new StreamDecoder(decoder, 4, 100);
- sd.setDecodeHandler(handler);
- is = byteStream(0x41, 0x42, 0x43, 0x44, 0x45);
- handler.clear();
- sd.decode(is);
- assertEquals("[ST][CH]ABCDE[EN]", handler.toString());
-
- decoder = cs.newDecoder();
- sd = new StreamDecoder(decoder, 100, 4);
- sd.setDecodeHandler(handler);
- is = byteStream(0x41, 0x42, 0x43, 0x44, 0x45);
- handler.clear();
- sd.decode(is);
- assertEquals("[ST][CH]ABCDE[EN]", handler.toString());
-
- decoder = cs.newDecoder();
- sd = new StreamDecoder(decoder, 4, 4);
- sd.setDecodeHandler(handler);
- is = byteStream(0x41, 0x42, 0x43, 0x44, 0x45);
- handler.clear();
- sd.decode(is);
- assertEquals("[ST][CH]ABCDE[EN]", handler.toString());
-
- return;
- }
-
- /**
- * Test of decode method, of class StreamDecoder.
- * @throws IOException
- * @throws DecodeException
- */
- @Test
- public void testDecodeSJ() throws IOException, DecodeException {
- System.out.println("decode");
-
- Charset cs;
- CharsetDecoder decoder;
-
- StreamDecoder sd;
- InputStream is;
- TestHandler handler;
-
- cs = ShiftJis.CHARSET;
-
- handler = new TestHandler();
-
- decoder = cs.newDecoder();
- sd = new StreamDecoder(decoder, 4, 4);
- sd.setDecodeHandler(handler);
- is = byteStream(0x41, 0x82, 0xa0, 0x44, 0x45);
- handler.clear();
- sd.decode(is);
- assertEquals("[ST][CH]AあDE[EN]", handler.toString());
-
- is = byteStream(0x41, 0x82, 0xf2, 0x44, 0x45);
- handler.clear();
- sd.decode(is);
- assertEquals("[ST][CH]A[ER]82[ER]f2[CH]DE[EN]", handler.toString());
-
- // malform error
- // WARNING: some JDK 1.6 implements make 2byte error 0xff32
- is = byteStream(0x41, 0xff, 0x32, 0x44, 0x45);
- handler.clear();
- sd.decode(is);
- assertEquals("[ST][CH]A[ER]ff[CH]2DE[EN]", handler.toString());
-
- // malform error
- is = byteStream(0x41, 0x81, 0xfd, 0x44, 0x45);
- handler.clear();
- sd.decode(is);
- assertEquals("[ST][CH]A[ER]81[ER]fd[CH]DE[EN]", handler.toString());
-
- // malform error
- is = byteStream(0x41, 0xa0, 0x80, 0x44, 0x45);
- handler.clear();
- sd.decode(is);
- assertEquals("[ST][CH]A[ER]a0[ER]80[CH]DE[EN]", handler.toString());
-
- is = byteStream(0x41, 0x82, 0xa0, 0x82, 0xa2, 0x82, 0xa4);
- handler.clear();
- sd.decode(is);
- assertEquals("[ST][CH]Aあいう[EN]", handler.toString());
-
- // unmap error
- is = byteStream(0x41, 0x82, 0xa0, 0x82, 0xf2, 0x82, 0xa4);
- handler.clear();
- sd.decode(is);
- assertEquals("[ST][CH]Aあ[ER]82[ER]f2[CH]う[EN]", handler.toString());
-
- // flush & error
- is = byteStream(0x41, 0x42, 0x43, 0x44, 0x45, 0x82, 0xf2);
- handler.clear();
- sd.decode(is);
- assertEquals("[ST][CH]ABCDE[ER]82[ER]f2[EN]", handler.toString());
-
- }
-
- /**
- * Test of decode method, of class StreamDecoder.
- * @throws IOException
- * @throws DecodeException
- */
- @Test
- public void testDecodeUCS4() throws IOException, DecodeException {
- System.out.println("decode");
-
- Charset cs;
- CharsetDecoder decoder;
-
- StreamDecoder sd;
- InputStream is;
- TestHandler handler;
-
- cs = Charset.forName("UTF-8");
-
- handler = new TestHandler();
-
- decoder = cs.newDecoder();
- sd = new StreamDecoder(decoder, 4, 4);
- sd.setDecodeHandler(handler);
- is = byteStream(0x41, 0x42, 0xe3, 0x81, 0x82, 0x46);
- handler.clear();
- sd.decode(is);
- assertEquals("[ST][CH]ABあF[EN]", handler.toString());
-
- // malformed
- is = byteStream(0x41, 0x42, 0xc2, 0xc0, 0x45);
- handler.clear();
- sd.decode(is);
- assertEquals("[ST][CH]AB[ER]c2[ER]c0[CH]E[EN]", handler.toString());
-
- // SMP character U+1F411 [SHEEP]
- is = byteStream(0x41, 0x42, 0xf0, 0x9f, 0x90, 0x91, 0x47);
- handler.clear();
- sd.decode(is);
- assertEquals("[ST][CH]AB\ud83d\udc11G[EN]", handler.toString());
-
- sd = new StreamDecoder(decoder, 3, 4);
- sd.setDecodeHandler(handler);
- is = byteStream(0x41, 0x42, 0xf0, 0x9f, 0x90, 0x91, 0x47);
- handler.clear();
- try{
- sd.decode(is);
- fail();
- }catch(DecodeException e){
- assertEquals("too small input buffer (3bytes) for UTF-8 bytePos=-1 charPos=-1", e.getMessage());
- }
-
- }
-
- static ByteArrayInputStream byteStream(int... array){
- byte[] ba = new byte[array.length];
-
- int idx = 0;
- for(int iVal : array){
- byte bVal = (byte)(iVal & 0xff);
- ba[idx++] = bVal;
- }
-
- return new ByteArrayInputStream(ba);
- }
-
- static class TestHandler implements DecodeHandler{
-
- private final StringBuilder text = new StringBuilder();
- private boolean notch = true;
-
- @Override
- public void startDecoding(CharsetDecoder decoder) throws DecodeException {
- this.text.append("[ST]");
- this.notch = true;
- }
-
- @Override
- public void endDecoding() throws DecodeException {
- this.text.append("[EN]");
- this.notch = true;
- }
-
- @Override
- public void charContent(CharSequence seq) throws DecodeException {
- if(this.notch){
- this.text.append("[CH]");
- }
- this.text.append(seq);
- this.notch = false;
- }
-
- @Override
- public void decodingError(byte[] errorArray, int offset, int length) throws DecodeException {
- for(int ct = 0; ct < length;ct++){
- this.text.append("[ER]");
- int val = errorArray[offset + ct] & 0xff;
- if(val <= 0xf) this.text.append('0');
- this.text.append(Integer.toHexString(val));
- }
- this.notch = true;
- }
-
- public void clear(){
- text.setLength(0);
- this.notch = true;
- }
-
- @Override
- public String toString(){
- return text.toString();
- }
-
- }
-
-}
--- /dev/null
+/*
+ * License : The MIT License
+ * Copyright(c) 2018 olyutorskii
+ */
+
+package jp.sourceforge.jindolf.parser;
+
+import io.bitbucket.olyutorskii.jiocema.CharDecodeListener;
+import io.bitbucket.olyutorskii.jiocema.DecodeBreakException;
+import java.nio.charset.CharsetDecoder;
+
+/**
+ * Test listener for {@link CharDecodeListener}
+ */
+class TestListener implements CharDecodeListener{
+
+ private final StringBuilder text = new StringBuilder();
+
+
+ @Override
+ public void startDecoding(CharsetDecoder decoder)
+ throws DecodeBreakException {
+ append("[ST]");
+ return;
+ }
+
+ @Override
+ public void endDecoding() throws DecodeBreakException {
+ append("[EN]");
+ return;
+ }
+
+ @Override
+ public void charContent(char[] charArray, int offset, int length)
+ throws DecodeBreakException {
+ append("[CH]");
+ this.text.append(charArray, offset, length);
+ return;
+ }
+
+ @Override
+ public void rawBytes(byte[] byteArray, int offset, int length)
+ throws DecodeBreakException {
+ // NOTHING
+ return;
+ }
+
+ @Override
+ public void malformedError(byte[] errorArray, int offset, int length)
+ throws DecodeBreakException {
+ append("[ME]");
+ dumpHex(errorArray, offset, length);
+ return;
+ }
+
+ @Override
+ public void unmapError(byte[] errorArray, int offset, int length)
+ throws DecodeBreakException {
+ append("[UE]");
+ dumpHex(errorArray, offset, length);
+ return;
+ }
+
+ protected void append(CharSequence seq){
+ this.text.append(seq);
+ return;
+ }
+
+ protected void dumpHex(byte[] errorArray, int offset, int length){
+ for(int ct = 0; ct < length; ct++){
+ dumpHex(errorArray[offset + ct]);
+ }
+ return;
+ }
+
+ private void dumpHex(byte bVal){
+ int val = bVal & 0xff;
+ if(val <= 0xf) this.text.append('0');
+ append(Integer.toHexString(val));
+ return;
+ }
+
+ public void clear(){
+ text.setLength(0);
+ return;
+ }
+
+ @Override
+ public String toString(){
+ return text.toString();
+ }
+
+}
--- /dev/null
+/*
+ * License : The MIT License
+ * Copyright(c) 2018 olyutorskii
+ */
+
+package jp.sourceforge.jindolf.parser;
+
+import io.bitbucket.olyutorskii.jiocema.DecodeBreakException;
+
+/**
+ * Test listener for {@link CharDecodeListener} with Raw-bytes
+ */
+class TestListenerRW extends TestListener{
+
+ @Override
+ public void rawBytes(byte[] byteArray, int offset, int length)
+ throws DecodeBreakException {
+ append("[RW]");
+ dumpHex(byteArray, offset, length);
+ return;
+ }
+
+}
package sample;
+import io.bitbucket.olyutorskii.jiocema.DecodeBreakException;
+import io.bitbucket.olyutorskii.jiocema.DecodeNotifier;
import java.io.FileInputStream;
-import jp.sourceforge.jindolf.parser.*;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.TreeMap;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
+import jp.sourceforge.jindolf.parser.*;
/**
* サンプルのパーサ
*/
public class SampleParser{
- private static final CharsetDecoder ud;
+ private static final CharsetDecoder CHD_UTF8;
static{
- ud = Charset.forName("UTF-8").newDecoder();
+ CHD_UTF8 = Charset.forName("UTF-8").newDecoder();
}
private SampleParser(){
}
public static SortedMap<String, ZipEntry> createEntryMap(ZipFile file){
- TreeMap<String, ZipEntry> result = new TreeMap<String, ZipEntry>();
+ TreeMap<String, ZipEntry> result = new TreeMap<>();
Enumeration<? extends ZipEntry> list = file.entries();
while(list.hasMoreElements()){
}
public static DecodedContent contentFromStream(InputStream istream)
- throws IOException, DecodeException{
- StreamDecoder decoder = new StreamDecoder(ud);
- ContentBuilderUCS2 builder = new ContentBuilderUCS2();
+ throws IOException, DecodeBreakException{
+ DecodeNotifier decoder = new DecodeNotifier(CHD_UTF8);
+ ContentBuilder builder = new ContentBuilder();
- decoder.setDecodeHandler(builder);
+ decoder.setCharDecodeListener(builder);
decoder.decode(istream);
public static void parseStream(InputStream istream)
throws IOException,
- DecodeException,
+ DecodeBreakException,
HtmlParseException{
DecodedContent content = contentFromStream(istream);
public static void main(String[] args)
throws IOException,
- DecodeException,
+ DecodeBreakException,
HtmlParseException {
if(args.length == 0){
System.out.println(