OSDN Git Service

XML出力改善
[mikutoga/TogaGem.git] / src / main / java / jp / sourceforge / mikutoga / parser / TextDecoder.java
index 1a8044a..1b3419b 100644 (file)
@@ -7,7 +7,6 @@
 
 package jp.sourceforge.mikutoga.parser;
 
-import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.nio.CharBuffer;
 import java.nio.charset.Charset;
@@ -17,32 +16,28 @@ import java.nio.charset.CodingErrorAction;
 
 /**
  * 文字デコーダー。
- * <p>あらかじめ長さが既知であるバイト列をMMD入力ソースから読み取り、
- * デコーディング結果を返す。
+ * <p>あらかじめ長さが既知であるバイト列を読み取り、
+ * 文字列へのデコード結果を返す。
  * <p>デコード対象のバイト列が全てメモリ上に展開されるので、
  * 巨大なテキストのデコードには不適当。
- * <p>å\85¥å\8a\9bã\83\90ã\82¤ã\83\88å\80¤0x00以é\99\8dã\82\92ã\83\87ã\82³ã\83¼ã\83\87ã\82£ã\83³ã\82°の対象から外す
+ * <p>å\85¥å\8a\9bã\83\90ã\82¤ã\83\88å\80¤0x00以é\99\8dã\82\92ã\83\87ã\82³ã\83¼ã\83\89å\87¦ç\90\86の対象から外す
  * 「ゼロチョップモード」を備える。
  * デフォルトではゼロチョップモードはオフ。
- * ã\82¼ã\83­ã\83\81ã\83§ã\83\83ã\83\97ã\83¢ã\83¼ã\83\89ã\81¯UTF16ã\81ªã\81©ã\81®ã\83\87ã\82³ã\83¼ã\83\87ã\82£ã\83³ã\82°æ\99\82ã\81«ä½¿ã\81£ã\81¦ã\82\82æ\84\8få\91³ã\81\8cç\84¡い。
+ * ã\82¼ã\83­ã\83\81ã\83§ã\83\83ã\83\97ã\83¢ã\83¼ã\83\89ã\81¯UTF16ã\81ªã\81©ã\81®ã\83\87ã\82³ã\83¼ã\83\87ã\82£ã\83³ã\82°æ\99\82ã\81«ä½¿ã\81£ã\81¦ã\81¯ã\81ªã\82\89ã\81ªい。
  */
 public class TextDecoder {
 
     /** デコード作業用入力バッファ長のデフォルト。バイト単位。 */
-    public static final int BYTEBUF_SZ = 512;
-
-    /** バッファ成長率。 */
-    private static final double WIDEN_RATE = 1.5;
+    public static final int DEF_BUFSZ = 512;
 
 
     private final CharsetDecoder decoder;
 
-    private boolean chopZero = false;
-
     private byte[] byteArray;
     private ByteBuffer byteBuffer;  // byteArrayの別ビュー
     private CharBuffer charBuffer;
-    private CharBuffer roBuffer;    // charBufferの閲覧用ビュー
+
+    private boolean chopZero = false;
 
 
     /**
@@ -60,36 +55,40 @@ public class TextDecoder {
      */
     public TextDecoder(CharsetDecoder decoder){
         super();
+
+        if(decoder == null) throw new NullPointerException();
+
         this.decoder = decoder;
-        this.decoder.onMalformedInput(CodingErrorAction.REPORT);
+        this.decoder.onMalformedInput     (CodingErrorAction.REPORT);
         this.decoder.onUnmappableCharacter(CodingErrorAction.REPORT);
+
         return;
     }
 
     /**
-     * æ\8c\87å®\9aã\81\95ã\82\8cã\81\9fã\82µã\82¤ã\82ºã\81§æ\96\87å­\97ã\83\87ã\82³ã\83¼ã\83\89ç\94¨バッファを用意する。
+     * æ\8c\87å®\9aã\81\95ã\82\8cã\81\9fã\83\90ã\82¤ã\83\88é\95·ã\82\92æº\80ã\81\9fã\81\99ã\80\81ã\83\87ã\82³ã\83¼ã\83\89ç\94¨å\85¥å\8a\9bバッファを用意する。
      * 既存バッファで足りなければ新たに確保し直す。
-     * @param newSize バッファ長さ。単位はバイト数。
+     * <p>内部用出力用バッファも同時に適切な長さで確保される。
+     * @param newSize 新たなバッファ長。単位はバイト数。
+     * @return 入力バッファ。指定バイト長より長いかもしれない。他用厳禁
      */
-    protected void prepareBuffer(int newSize){
+    public byte[] prepareBuffer(int newSize){
         if(this.byteArray != null && this.byteArray.length >= newSize){
-            return;
+            return this.byteArray;
         }
 
-        int rounded = (int)( newSize * WIDEN_RATE );
-        if(rounded < BYTEBUF_SZ) rounded = BYTEBUF_SZ;
+        int rounded = newSize;
+        if(rounded < DEF_BUFSZ) rounded = DEF_BUFSZ;
 
         this.byteArray = new byte[rounded];
         this.byteBuffer = ByteBuffer.wrap(this.byteArray);
 
         float maxCharsPerByte = this.decoder.maxCharsPerByte();
         int maxChars =
-                (int)( this.byteBuffer.capacity() * maxCharsPerByte ) + 1;
+                (int)( this.byteArray.length * maxCharsPerByte ) + 1;
         this.charBuffer = CharBuffer.allocate(maxChars);
 
-        this.roBuffer = this.charBuffer.asReadOnlyBuffer();
-
-        return;
+        return this.byteArray;
     }
 
     /**
@@ -132,26 +131,51 @@ public class TextDecoder {
     }
 
     /**
-     * バイト列を読み込み文字列へデコーディングする。
-     * @param is 入力ストリーム
-     * @param byteSize 読み込みバイトサイズ
-     * @return 内部に保持されるデコード結果。
-     * 次回呼び出しまでに結果の適切なコピーがなされなければならない。
-     * @throws MmdEofException 意図しないファイル末端
-     * @throws MmdFormatException 矛盾したバイトシーケンス
-     * もしくは未定義文字
-     * @throws IOException 入力エラー
+     * 指定配列を内部にコピーした後、デコード処理を行う。
+     * @param basePos エラー情報に含まれるストリーム位置
+     * @param buf 入力バッファ
+     * @return デコードされた文字列
+     * @throws MmdFormatException デコード異常
      */
-    public CharBuffer parseString(MmdInputStream is, int byteSize)
-            throws MmdEofException, MmdFormatException, IOException{
-        prepareBuffer(byteSize);
+    public String decode(long basePos, byte[] buf)
+            throws MmdFormatException {
+        String result = decode(basePos, buf, 0, buf.length);
+        return result;
+    }
 
-        int readSize = is.read(this.byteArray, 0, byteSize);
-        if(readSize != byteSize){
-            throw new MmdEofException(is.getPosition());
+    /**
+     * 指定配列の一部を内部にコピーした後、デコード処理を行う。
+     * @param basePos エラー情報に含まれるストリーム位置
+     * @param buf 入力バッファ
+     * @param off 位置オフセット
+     * @param byteLen バイト長
+     * @return デコードされた文字列
+     * @throws MmdFormatException デコード異常
+     * @throws IndexOutOfBoundsException 不正な位置指定。
+     */
+    public String decode(long basePos, byte[] buf, int off, int byteLen)
+            throws MmdFormatException, IndexOutOfBoundsException {
+        prepareBuffer(byteLen);
+        System.arraycopy(buf, off, this.byteArray, 0, byteLen);
+        String result = decode(basePos, byteLen);
+        return result;
+    }
+
+    /**
+     * 内部バッファのデコード処理を行う。
+     * @param basePos エラー情報に含まれるストリーム位置
+     * @param byteLen バイト長
+     * @return デコードされた文字列
+     * @throws MmdFormatException デコード異常
+     * @throws IndexOutOfBoundsException 不正なバイト長。
+     */
+    public String decode(long basePos, int byteLen)
+            throws MmdFormatException, IndexOutOfBoundsException {
+        if(this.byteArray.length < byteLen){
+            throw new IndexOutOfBoundsException();
         }
 
-        this.byteBuffer.rewind().limit(byteSize);
+        this.byteBuffer.rewind().limit(byteLen);
         chopZeroTermed();
 
         this.charBuffer.clear();
@@ -159,21 +183,22 @@ public class TextDecoder {
         this.decoder.reset();
         CoderResult decResult =
                 this.decoder.decode(this.byteBuffer, this.charBuffer, true);
+
         if(decResult.isError()){
+            String errMsg;
             if(decResult.isUnmappable()){
-                throw new MmdFormatException("unmapped character",
-                                             is.getPosition() );
+                errMsg = "unmapped character";
             }else{
-                throw new MmdFormatException("illegal character encoding",
-                                             is.getPosition() );
+                errMsg = "illegal character encoding";
             }
-        }else if(decResult.isOverflow()){
-            assert false;
+            long errPos = basePos + decResult.length();
+            throw new MmdFormatException(errMsg, errPos);
         }
 
-        this.roBuffer.rewind().limit(this.charBuffer.position());
+        assert ! decResult.isOverflow();
 
-        return this.roBuffer;
+        String result = this.charBuffer.flip().toString();
+        return result;
     }
 
 }