/*
 * このソースコードは blanco Frameworkによって自動生成されています。
 */
package blanco.sample.csv.util;

import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;

import blanco.sample.csv.io.BlancoCsvIOException;

/**
 * blancoCsvで利用する実行時ユーティリティクラス
 *
 * このファイルはblancoIgにより自動生成されています。
 */
public class BlancoCsvRuntimeUtil {
    /**
     * 与えられたReaderから 与えられたデリミタを使ってトークンを取り出します
     *
     * @param argReader 与えられたリーダ
     * @param delimiter デリミタ
     * @param isEndOfLine 行の終端であるのかどうかフラグ
     * @return 取り出されたトークン。もはやリーダが空の場合にはnullを返します。
     * @throws BlancoCsvIOException 項目数が足りないなど与えられた文字列に関する例外が発生した場合。
     * @throws IOException 入出力例外が発生した場合。
     */
    public static String readToken(final Reader argReader, final char delimiter, final boolean isEndOfLine) throws BlancoCsvIOException, IOException {
        final StringBuffer buffer = new StringBuffer();
        for (;;) {
            final int iRead = argReader.read();
            if (iRead < 0) {
                // 終端に到達。
                if (isEndOfLine == false) {
                    throw new BlancoCsvIOException("行の終端でないのにも関わらずデリミタが現れずに行が終了してしまいました。");
                }
                break;
            }
            if (iRead == delimiter) {
                if (isEndOfLine) {
                    throw new BlancoCsvIOException("行の終端であるべきにも関わらずデリミタが現れました。");
                }
                break;
            } else {
                buffer.append((char) iRead);
            }
        }
        return buffer.toString();
    }

    /**
     * 与えられたReaderから ダブルクオート処理付きで 与えられたデリミタを使ってトークンを取り出します
     *
     * @param argReader 与えられたリーダ
     * @param delimiter デリミタ
     * @param isEndOfLine 行の終端であるのかどうかフラグ
     * @return 取り出されたトークン。もはやリーダが空の場合にはnullを返します。
     * @throws BlancoCsvIOException ダブルクオートの不一致など与えられた文字列に関する例外が発生した場合。
     * @throws IOException 入出力例外が発生した場合。
     */
    public static String readTokenWithQuote(final Reader argReader, final char delimiter, final boolean isEndOfLine) throws BlancoCsvIOException, IOException {
        final StringBuffer buffer = new StringBuffer();
        boolean isStringStarted = false;
        boolean isStringEnded = false;
        for (;;) {
            int iRead = argReader.read();
            if (iRead < 0) {
                // 終端に到達。
                if (isEndOfLine == false) {
                    throw new BlancoCsvIOException("行の終端でないのにも関わらずデリミタが現れずに行が終了してしまいました。");
                }
                break;
            }
            if (isStringStarted == false) {
                if (iRead != '"') {
                    throw new BlancoCsvIOException("ダブルクオート無しで文字列が開始されました。");
                }
                // ダブルクオーテーションは読み飛ばします。
                isStringStarted = true;
            } else if (isStringEnded) {
                if (iRead == delimiter) {
                    // 文字列終端に到達しました。
                    if (isEndOfLine) {
                        throw new BlancoCsvIOException("行の終端であるべきにも関わらずデリミタが現れました。");
                    }
                    break;
                }
                throw new BlancoCsvIOException("ダブルクオートによる文字列終了より後に文字列が与えられました。");
            } else {
                // 通常の文字列エリア
                if (iRead == '"') {
                    // ダブルクオートが重ねられたものでないかどうかチェックします。
                    argReader.mark(1);
                    if (argReader.read() == '"') {
                        // エスケープされたダブルクオート
                        // 2つでひとつなので、ひとつは読み捨てます。
                        buffer.append((char) iRead);
                    } else {
                        // ダブルクオートによるエスケープではありませんでした。
                        // これは終端を意味しています。
                        argReader.reset();
                        isStringEnded = true;
                    }
                } else {
                    buffer.append((char) iRead);
                }
            }
        }
        if (isStringStarted == false) {
            throw new BlancoCsvIOException("ダブルクオートが必要な文字列であるのにダブルクオートによる開始が現れませんでした。");
        }
        if (isStringEnded == false) {
            throw new BlancoCsvIOException("ダブルクオートが必要な文字列であるのにダブルクオートによる終了が現れませんでした。");
        }
        return buffer.toString();
    }

    /**
     * ダブルクオートエスケープ処理付きの文字列を取得します
     *
     * @param value 書き出したい文字列
     * @return ダブルクオートエスケープ処理付きの文字列
     * @throws IOException 入出力例外などが発生した場合。
     */
    public static String getTokenWithQuote(final String value) throws IOException {
        final StringWriter writer = new StringWriter();
        writer.write("\"");

        final StringReader reader = new StringReader(value);
        for (;;) {
            final int iRead = reader.read();
            if (iRead < 0) {
                // 終端に到達。
                break;
            }
            if (iRead == '\"') {
                // ダブルクオートは二重で書き出します。
                writer.write("\"\"");
            } else {
                writer.write((char) iRead);
            }
        }

        writer.write("\"");
        writer.close();
        return writer.toString();
    }
}
