package jp.sourceforge.mikutoga.parser;
-import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
/**
* 文字デコーダー。
- * <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;
/**
*/
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;
}
/**
}
/**
- * バイト列を読み込み文字列へデコーディングする。
- * @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();
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;
}
}