OSDN Git Service

split decoder to Jiocema
authorOlyutorskii <olyutorskii@users.osdn.me>
Tue, 20 Mar 2018 16:43:02 +0000 (01:43 +0900)
committerOlyutorskii <olyutorskii@users.osdn.me>
Tue, 20 Mar 2018 16:43:02 +0000 (01:43 +0900)
21 files changed:
src/main/java/jp/sourceforge/jindolf/parser/ContentBuilder.java
src/main/java/jp/sourceforge/jindolf/parser/ContentBuilderSJ.java
src/main/java/jp/sourceforge/jindolf/parser/ContentBuilderUCS2.java [deleted file]
src/main/java/jp/sourceforge/jindolf/parser/DecodeException.java [deleted file]
src/main/java/jp/sourceforge/jindolf/parser/DecodeHandler.java [deleted file]
src/main/java/jp/sourceforge/jindolf/parser/DecodedContent.java
src/main/java/jp/sourceforge/jindolf/parser/SjisDecoder.java [deleted file]
src/main/java/jp/sourceforge/jindolf/parser/SjisNotifier.java [new file with mode: 0644]
src/main/java/jp/sourceforge/jindolf/parser/StreamDecoder.java [deleted file]
src/main/java/jp/sourceforge/jindolf/parser/package-info.java
src/test/java/jp/sourceforge/jindolf/parser/Bseq.java [new file with mode: 0644]
src/test/java/jp/sourceforge/jindolf/parser/ContentBuilderSJTest.java
src/test/java/jp/sourceforge/jindolf/parser/ContentBuilderTest.java [moved from src/test/java/jp/sourceforge/jindolf/parser/ContentBuilderUCS2Test.java with 67% similarity]
src/test/java/jp/sourceforge/jindolf/parser/DecodeExceptionTest.java [deleted file]
src/test/java/jp/sourceforge/jindolf/parser/DecodedContentTest.java
src/test/java/jp/sourceforge/jindolf/parser/SjisDecoderTest.java [deleted file]
src/test/java/jp/sourceforge/jindolf/parser/SjisNotifierTest.java [new file with mode: 0644]
src/test/java/jp/sourceforge/jindolf/parser/StreamDecoderTest.java [deleted file]
src/test/java/jp/sourceforge/jindolf/parser/TestListener.java [new file with mode: 0644]
src/test/java/jp/sourceforge/jindolf/parser/TestListenerRW.java [new file with mode: 0644]
src/test/java/sample/SampleParser.java

index f3e3b61..bcb42ee 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * abstract content builder
+ * content builder
  *
  * License : The MIT License
  * Copyright(c) 2009 olyutorskii
@@ -7,12 +7,18 @@
 
 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;
@@ -20,59 +26,135 @@ public abstract class ContentBuilder implements DecodeHandler{
 
     /**
      * コンストラクタ。
-     * 長さ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;
     }
 
 }
index 50c3cc5..393c825 100644 (file)
@@ -7,23 +7,21 @@
 
 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);
@@ -32,109 +30,34 @@ public class ContentBuilderSJ extends ContentBuilder{
 
     /**
      * コンストラクタ。
-     * 長さ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;
     }
diff --git a/src/main/java/jp/sourceforge/jindolf/parser/ContentBuilderUCS2.java b/src/main/java/jp/sourceforge/jindolf/parser/ContentBuilderUCS2.java
deleted file mode 100644 (file)
index 5ab7832..0000000
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * 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;
-    }
-
-}
diff --git a/src/main/java/jp/sourceforge/jindolf/parser/DecodeException.java b/src/main/java/jp/sourceforge/jindolf/parser/DecodeException.java
deleted file mode 100644 (file)
index 85d0f54..0000000
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * 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();
-    }
-
-}
diff --git a/src/main/java/jp/sourceforge/jindolf/parser/DecodeHandler.java b/src/main/java/jp/sourceforge/jindolf/parser/DecodeHandler.java
deleted file mode 100644 (file)
index 7d12898..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * 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;
-
-}
index 1ced535..55604b9 100644 (file)
@@ -13,10 +13,10 @@ import java.util.List;
 import java.util.RandomAccess;
 
 /**
- * ShiftJISデコードエラー情報を含む再利用可能な文字列。
- * デコードエラーを起こした箇所は代替文字{@link #ALTCHAR}で置き換えられる。
+ * デコードエラー情報を含む再利用可能な文字列。
+ *
+ * <p>デコードエラーを起こした箇所は代替文字{@link #ALTCHAR}で置き換えられる。
  * マルチスレッドには非対応。
- * UCS-4コードポイントには未対応。
  */
 public class DecodedContent
         implements CharSequence,
@@ -381,7 +381,7 @@ public class DecodedContent
                                   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{
@@ -393,6 +393,26 @@ public class DecodedContent
 
     /**
      * 文字列を追加する。
+     *
+     * @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 終了位置
@@ -403,7 +423,7 @@ public class DecodedContent
                                   int startPos, int endPos)
             throws IndexOutOfBoundsException{
         if(source == null){
-            return append(NULLTEXT, startPos, endPos);
+            return append(NULLTEXT);
         }
 
         int gap = startPos - this.rawContent.length();
@@ -460,7 +480,10 @@ public class DecodedContent
     }
 
     /**
-     * 代替文字とともにデコードエラーを追加する。
+     * 代替文字とともに2バイトからなるデコードエラーを追加する。
+     *
+     * <p>主にシフトJISのUnmapエラーを想定。
+     *
      * @param b1st 1バイト目の値
      * @param b2nd 2バイト目の値
      */
diff --git a/src/main/java/jp/sourceforge/jindolf/parser/SjisDecoder.java b/src/main/java/jp/sourceforge/jindolf/parser/SjisDecoder.java
deleted file mode 100644 (file)
index 3353baa..0000000
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * 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;
-    }
-
-}
diff --git a/src/main/java/jp/sourceforge/jindolf/parser/SjisNotifier.java b/src/main/java/jp/sourceforge/jindolf/parser/SjisNotifier.java
new file mode 100644 (file)
index 0000000..78aa99e
--- /dev/null
@@ -0,0 +1,214 @@
+/*
+ * 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;
+    }
+
+}
diff --git a/src/main/java/jp/sourceforge/jindolf/parser/StreamDecoder.java b/src/main/java/jp/sourceforge/jindolf/parser/StreamDecoder.java
deleted file mode 100644 (file)
index 405ca06..0000000
+++ /dev/null
@@ -1,411 +0,0 @@
-/*
- * 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;
-    }
-
-}
index 1066d51..8c25401 100644 (file)
  * <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();
diff --git a/src/test/java/jp/sourceforge/jindolf/parser/Bseq.java b/src/test/java/jp/sourceforge/jindolf/parser/Bseq.java
new file mode 100644 (file)
index 0000000..04af3e3
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ */
+
+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;
+    }
+
+}
index 2638bd7..4370744 100644 (file)
@@ -5,11 +5,11 @@
 
 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;
@@ -41,110 +41,62 @@ public class ContentBuilderSJTest {
     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();
@@ -152,7 +104,7 @@ public class ContentBuilderSJTest {
         assertNotSame("\u00a5", content.toString());
         assertFalse(content.hasDecodeError());
 
-        bdata = byteArray("8d5c");
+        bdata = Bseq.byteArray("8d5c");
         istream = new ByteArrayInputStream(bdata);
         decoder.decode(istream);
         content = builder.getContent();
@@ -175,24 +127,24 @@ public class ContentBuilderSJTest {
     /**
      * 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();
@@ -202,7 +154,7 @@ public class ContentBuilderSJTest {
         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();
@@ -212,7 +164,7 @@ public class ContentBuilderSJTest {
         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();
@@ -222,7 +174,7 @@ public class ContentBuilderSJTest {
         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();
@@ -232,7 +184,7 @@ public class ContentBuilderSJTest {
         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();
@@ -242,7 +194,7 @@ public class ContentBuilderSJTest {
         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();
@@ -252,7 +204,7 @@ public class ContentBuilderSJTest {
         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();
@@ -277,24 +229,24 @@ public class ContentBuilderSJTest {
     /**
      * 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();
@@ -308,7 +260,7 @@ public class ContentBuilderSJTest {
         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();
@@ -318,7 +270,7 @@ public class ContentBuilderSJTest {
         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();
@@ -334,24 +286,24 @@ public class ContentBuilderSJTest {
     /**
      * 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();
@@ -359,7 +311,7 @@ public class ContentBuilderSJTest {
         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();
@@ -369,7 +321,7 @@ public class ContentBuilderSJTest {
         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();
@@ -379,11 +331,11 @@ public class ContentBuilderSJTest {
         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();
@@ -5,11 +5,13 @@
 
 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;
@@ -22,9 +24,9 @@ import static org.junit.Assert.*;
 /**
  *
  */
-public class ContentBuilderUCS2Test {
+public class ContentBuilderTest {
 
-    public ContentBuilderUCS2Test() {
+    public ContentBuilderTest() {
     }
 
     @BeforeClass
@@ -43,65 +45,18 @@ public class ContentBuilderUCS2Test {
     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;
@@ -110,10 +65,10 @@ public class ContentBuilderUCS2Test {
 
 
         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();
@@ -124,10 +79,10 @@ public class ContentBuilderUCS2Test {
 
 
         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();
@@ -138,10 +93,10 @@ public class ContentBuilderUCS2Test {
 
 
         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();
@@ -161,24 +116,25 @@ public class ContentBuilderUCS2Test {
 
     /**
      * 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();
@@ -189,10 +145,10 @@ public class ContentBuilderUCS2Test {
 
 
         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();
@@ -207,14 +163,15 @@ public class ContentBuilderUCS2Test {
 
     /**
      * 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;
@@ -222,10 +179,10 @@ public class ContentBuilderUCS2Test {
         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();
@@ -254,10 +211,10 @@ public class ContentBuilderUCS2Test {
 
 
         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();
@@ -277,10 +234,10 @@ public class ContentBuilderUCS2Test {
 
 
         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();
@@ -304,23 +261,24 @@ public class ContentBuilderUCS2Test {
 
     /**
      * 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();
@@ -331,4 +289,32 @@ public class ContentBuilderUCS2Test {
         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;
+    }
+
 }
diff --git a/src/test/java/jp/sourceforge/jindolf/parser/DecodeExceptionTest.java b/src/test/java/jp/sourceforge/jindolf/parser/DecodeExceptionTest.java
deleted file mode 100644 (file)
index f0dde35..0000000
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * 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;
-    }
-
-}
index 096dc29..b3fe931 100644 (file)
@@ -62,7 +62,7 @@ public class DecodedContentTest {
         assertEquals("abc", content.toString());
 
         try{
-            new DecodedContent(-1);
+            Object o = new DecodedContent(-1);
             fail();
         }catch(NegativeArraySizeException e){
         }catch(Throwable e){
@@ -447,6 +447,26 @@ public class DecodedContentTest {
     }
 
     /**
+     * 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
diff --git a/src/test/java/jp/sourceforge/jindolf/parser/SjisDecoderTest.java b/src/test/java/jp/sourceforge/jindolf/parser/SjisDecoderTest.java
deleted file mode 100644 (file)
index 55271c8..0000000
+++ /dev/null
@@ -1,228 +0,0 @@
-/*
- */
-
-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();
-        }
-
-    }
-
-}
diff --git a/src/test/java/jp/sourceforge/jindolf/parser/SjisNotifierTest.java b/src/test/java/jp/sourceforge/jindolf/parser/SjisNotifierTest.java
new file mode 100644 (file)
index 0000000..d5dcdb0
--- /dev/null
@@ -0,0 +1,288 @@
+/*
+ */
+
+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;
+    }
+
+}
diff --git a/src/test/java/jp/sourceforge/jindolf/parser/StreamDecoderTest.java b/src/test/java/jp/sourceforge/jindolf/parser/StreamDecoderTest.java
deleted file mode 100644 (file)
index 5ab25da..0000000
+++ /dev/null
@@ -1,352 +0,0 @@
-/*
- */
-
-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();
-        }
-
-    }
-
-}
diff --git a/src/test/java/jp/sourceforge/jindolf/parser/TestListener.java b/src/test/java/jp/sourceforge/jindolf/parser/TestListener.java
new file mode 100644 (file)
index 0000000..0614273
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * 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();
+    }
+
+}
diff --git a/src/test/java/jp/sourceforge/jindolf/parser/TestListenerRW.java b/src/test/java/jp/sourceforge/jindolf/parser/TestListenerRW.java
new file mode 100644 (file)
index 0000000..021908b
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * 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;
+    }
+
+}
index 43ab984..60390ab 100644 (file)
@@ -7,8 +7,9 @@
 
 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;
@@ -19,16 +20,17 @@ import java.util.SortedMap;
 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(){
@@ -37,7 +39,7 @@ public class 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()){
@@ -50,11 +52,11 @@ public class SampleParser{
     }
 
     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);
 
@@ -79,7 +81,7 @@ public class SampleParser{
 
     public static void parseStream(InputStream istream)
             throws IOException,
-                   DecodeException,
+                   DecodeBreakException,
                    HtmlParseException{
         DecodedContent content = contentFromStream(istream);
 
@@ -90,7 +92,7 @@ public class SampleParser{
 
     public static void main(String[] args)
             throws IOException,
-                   DecodeException,
+                   DecodeBreakException,
                    HtmlParseException {
         if(args.length == 0){
             System.out.println(