/*
 * Copyright (c) 2009 OrangeSignal.com All rights reserved.
 *
 * これは Apache ライセンス Version 2.0 (以下、このライセンスと記述) に
 * 従っています。このライセンスに準拠する場合以外、このファイルを使用
 * してはなりません。このライセンスのコピーは以下から入手できます。
 *
 * http://www.apache.org/licenses/LICENSE-2.0.txt
 *
 * 適用可能な法律がある、あるいは文書によって明記されている場合を除き、
 * このライセンスの下で配布されているソフトウェアは、明示的であるか暗黙の
 * うちであるかを問わず、「保証やあらゆる種類の条件を含んでおらず」、
 * 「あるがまま」の状態で提供されるものとします。
 * このライセンスが適用される特定の許諾と制限については、このライセンス
 * を参照してください。
 */

package jp.sf.orangesignal.csv;

import java.io.Serializable;
import java.util.regex.Pattern;

/**
 * 区切り文字形式情報を提供します。
 *
 * @author 杉澤 浩二
 */
public class CsvConfig implements Serializable/*, Cloneable*/ {

	private static final long serialVersionUID = -7386036362569866918L;

	/**
	 * デフォルトの区切り文字です。
	 */
	public static final char DEFAULT_SEPARATOR = ',';

	/**
	 * デフォルトの囲み文字です。
	 */
	public static final char DEFAULT_QUOTE = '"';

	/**
	 * デフォルトのエスケープ文字です。
	 */
	public static final char DEFAULT_ESCAPE = '\\';

	/**
	 * デフォルトの先頭から読飛ばす論理行数です。
	 */
	public static final int DEFAULT_SKIP_LINES = 0;

	// ------------------------------------------------------------------------

	/**
	 * 区切り文字を保持します。
	 */
	private char separator;

	/**
	 * 囲み文字を保持します。
	 */
	private char quote;

	/**
	 * エスケープ文字を保持します。
	 */
	private char escape;

	/**
	 * 囲み文字を無効にするかどうかを保持します。
	 */
	private boolean quoteDisabled;

	/**
	 * エスケープ文字を無効にするかどうかを保持します。
	 */
	private boolean escapeDisabled;

	/**
	 * 値の改行文字列を置換える文字列を保持します。
	 */
	private String breakString;

	/**
	 * 値がないことを表す文字列を保持します。
	 */
	private String nullString;

	/**
	 * 値がないことを表す文字列の大文字と小文字を区別するかどうかを保持します。
	 */
	private boolean ignoreCaseNullString;

	/**
	 * 値より前のホワイトスペースを除去するかどうかを保持します。
	 */
	private boolean ignoreLeadingWhitespaces;

	/**
	 * 値より後ろのホワイトスペースを除去するかどうかを保持します。
	 */
	private boolean ignoreTrailingWhitespaces;

	/**
	 * 空行を無視するかどうかを保持します。
	 */
	private boolean ignoreEmptyLines;

	/**
	 * 無視する行の正規表現パターン群を保持します。
	 */
	private Pattern[] ignoreLinePatterns;

	/**
	 * 先頭から読飛ばす論理行数を保持します。
	 */
	private int skipLines = DEFAULT_SKIP_LINES;

	/**
	 * データ出力時の改行文字列を保持します。
	 */
	private String lineSeparator = System.getProperty("line.separator");

	// ------------------------------------------------------------------------
	// コンストラクタ

	/**
	 * デフォルトコンストラクタです。
	 */
	public CsvConfig() {
		this(DEFAULT_SEPARATOR, DEFAULT_QUOTE, DEFAULT_ESCAPE, true, true);
	}

	/**
	 * コンストラクタです。
	 *
	 * @param separator 区切り文字
	 */
	public CsvConfig(final char separator) {
		this(separator, DEFAULT_QUOTE, DEFAULT_ESCAPE, true, true);
	}

	/**
	 * コンストラクタです。
	 *
	 * @param separator 区切り文字
	 * @param quote 囲み文字
	 * @param escape エスケープ文字
	 * @throws IllegalArgumentException <code>quote</code> または <code>escape</code> が <code>separator</code> と同一文字の場合
	 */
	public CsvConfig(final char separator, final char quote, final char escape) {
		this(separator, quote, escape, false, false);
	}

	/**
	 * コンストラクタです。
	 *
	 * @param separator 区切り文字
	 * @param quote 囲み文字
	 * @param escape エスケープ文字
	 * @param quoteDisabled 囲み文字を無効にするかどうか
	 * @param escapeDisabled エスケープ文字を無効にするかどうか
	 * @throws IllegalArgumentException <code>quote</code> または <code>escape</code> が <code>separator</code> と同一文字の場合
	 */
	public CsvConfig(final char separator, final char quote, final char escape, final boolean quoteDisabled, final boolean escapeDisabled) {
		this.separator = separator;
		this.quote = quote;
		this.escape = escape;
		this.quoteDisabled = quoteDisabled;
		this.escapeDisabled = escapeDisabled;
	}

	// ------------------------------------------------------------------------

	/**
	 * 区切り文字および囲み文字、エスケープ文字の組合せを検証します。
	 *
	 * @throws IllegalArgumentException 区切り文字および囲み文字、エスケープ文字の組合せが不正な場合
	 */
	public void validate() throws IllegalArgumentException {
		if (separator == '\r' || separator == '\n') {
			throw new IllegalArgumentException("Invalid separator character");
		}
		if (!quoteDisabled) {
			if (quote == separator || quote == '\r' || quote == '\n') {
				throw new IllegalArgumentException("Invalid quote character");
			}
		}
		if (!escapeDisabled) {
			if (escape == separator || escape == '\r' || escape == '\n') {
				throw new IllegalArgumentException("Invalid escape character");
			}
		}
	}

	// ------------------------------------------------------------------------
	// セッター/ゲッター

	/**
	 * 区切り文字を返します。
	 *
	 * @return 区切り文字
	 */
	public char getSeparator() { return separator; }

	/**
	 * 区切り文字を設定します。
	 *
	 * @param separator 区切り文字
	 */
	public void setSeparator(final char separator) { this.separator = separator; }

	/**
	 * 囲み文字を返します。
	 *
	 * @return 囲み文字
	 */
	public char getQuote() { return quote; }

	/**
	 * 囲み文字を設定します。
	 *
	 * @param quote 囲み文字
	 */
	public void setQuote(final char quote) { this.quote = quote; }

	/**
	 * エスケープ文字を返します。
	 *
	 * @return エスケープ文字
	 */
	public char getEscape() { return escape; }

	/**
	 * エスケープ文字を設定します。
	 *
	 * @param escape エスケープ文字
	 */
	public void setEscape(final char escape) { this.escape = escape; }

	/**
	 * 囲み文字を無効にするかどうかを返します。
	 *
	 * @return 囲み文字を無効にするかどうか
	 */
	public boolean isQuoteDisabled() { return quoteDisabled; }

	/**
	 * 囲み文字を無効にするかどうかを設定します。
	 *
	 * @param disabled 囲み文字を無効にするかどうか
	 */
	public void setQuoteDisabled(final boolean disabled) { this.quoteDisabled = disabled; }

	/**
	 * エスケープ文字を無効にするかどうかを返します。
	 *
	 * @return エスケープ文字を無効にするかどうか
	 */
	public boolean isEscapeDisabled() { return escapeDisabled; }

	/**
	 * エスケープ文字を無効にするかどうかを設定します。
	 *
	 * @param disabled エスケープ文字を無効にするかどうか
	 */
	public void setEscapeDisabled(final boolean disabled) { this.escapeDisabled = disabled; }

	/**
	 * 値の改行文字列を置換える文字列を返します。
	 * 
	 * @return 値の改行文字列を置換える文字列
	 */
	public String getBreakString() { return breakString; }

	/**
	 * 値の改行文字列を置換える文字列を設定します。
	 * 
	 * @param breakString 値の改行文字列を置換える文字列
	 */
	public void setBreakString(final String breakString) { this.breakString = breakString; }

	/**
	 * 値がないことを表す文字列を返します。
	 *
	 * @return 値がないことを表す文字列
	 */
	public String getNullString() { return nullString; }

	/**
	 * 値がないことを表す文字列を設定します。
	 *
	 * @param nullString 値がないことを表す文字列
	 */
	public void setNullString(final String nullString) {
		setNullString(nullString, false);
	}

	/**
	 * 値がないことを表す文字列を設定します。
	 *
	 * @param nullString 値がないことを表す文字列
	 * @param ignoreCase 大文字と小文字を区別するかどうか
	 */
	public void setNullString(final String nullString, final boolean ignoreCase) {
		this.nullString = nullString;
		this.ignoreCaseNullString = ignoreCase;
	}

	/**
	 * 値がないことを表す文字列の大文字と小文字を区別するかどうかを返します。
	 * 
	 * @return 値がないことを表す文字列の大文字と小文字を区別するかどうか
	 */
	public boolean isIgnoreCaseNullString() { return ignoreCaseNullString; }

	/**
	 * 値より前のホワイトスペースを除去するかどうかを返します。
	 *
	 * @return 値より前のホワイトスペースを除去するかどうか
	 */
	public boolean isIgnoreLeadingWhitespaces() { return ignoreLeadingWhitespaces; }

	/**
	 * 値より前のホワイトスペースを除去するかどうかを設定します。
	 *
	 * @param ignore 値より前のホワイトスペースを除去するかどうか
	 * @see Character#isWhitespace(char)
	 */
	public void setIgnoreLeadingWhitespaces(final boolean ignore) { this.ignoreLeadingWhitespaces = ignore; }

	/**
	 * 値より後ろのホワイトスペースを除去するかどうかを返します。
	 *
	 * @return 値より後ろのホワイトスペースを除去するかどうか
	 */
	public boolean isIgnoreTrailingWhitespaces() { return ignoreTrailingWhitespaces; }

	/**
	 * 値より後ろのホワイトスペースを除去するかどうかを設定します。
	 *
	 * @param ignore 値より後ろのホワイトスペースを除去するかどうか
	 * @see Character#isWhitespace(char)
	 */
	public void setIgnoreTrailingWhitespaces(final boolean ignore) { this.ignoreTrailingWhitespaces = ignore; }

	/**
	 * 空行を無視するかどうかを返します。
	 *
	 * @return 空行を無視するかどうか
	 */
	public boolean isIgnoreEmptyLines() { return ignoreEmptyLines; }

	/**
	 * 空行を無視するかどうかを設定します。
	 *
	 * @param ignore 空行を無視するかどうか
	 */
	public void setIgnoreEmptyLines(final boolean ignore) { this.ignoreEmptyLines = ignore; }

	/**
	 * 無視する行の正規表現パターン群を返します。
	 *
	 * @return 無視する行の正規表現パターン群
	 */
	public Pattern[] getIgnoreLinePatterns() { return ignoreLinePatterns; }

	/**
	 * 無視する行の正規表現パターン群を設定します。
	 *
	 * @param ignoreLinePatterns 無視する行の正規表現パターン群
	 */
	public void setIgnoreLinePatterns(Pattern...ignoreLinePatterns) { this.ignoreLinePatterns = ignoreLinePatterns; }

	/**
	 * ファイルの先頭から読飛ばす行数を返します。
	 *
	 * @return ファイルの先頭から読飛ばす行数
	 */
	public int getSkipLines() { return skipLines; }

	/**
	 * ファイルの先頭から読飛ばす行数を設定します。
	 *
	 * @param skipLines ファイルの先頭から読飛ばす行数
	 */
	public void setSkipLines(final int skipLines) { this.skipLines = skipLines; }

	/**
	 * データ出力時の改行文字列を返します。
	 *
	 * @return データ出力時の改行文字列
	 */
	public String getLineSeparator() { return lineSeparator; }

	/**
	 * データ出力時の改行文字列を設定します。
	 *
	 * @param lineSeparator データ出力時の改行文字列
	 */
	public void setLineSeparator(final String lineSeparator) { this.lineSeparator = lineSeparator; }

}
