/*
 * Copyright (c) 2009 The openGion Project.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 */
package org.opengion.hayabusa.io;

import org.opengion.fukurou.system.OgRuntimeException ;		// 6.4.2.0 (2016/01/29)

import org.opengion.hayabusa.db.ColumnActionListener;		// 6.2.2.0 (2015/03/27)

/**
 * 指定の区切り記号(初期値:タブ区切り)ファイルの読み取りクラスです。
 *
 * 名前，データの入力部のみオーバーライドすれば，各種入力フォーマットに合わせた
 * サブクラスを実現する事が可能です。
 *
 * @og.group ファイル入力
 *
 * @version  4.0
 * @author   Kazuhiko Hasegawa
 * @since    JDK5.0,
 */
public abstract class AbstractTableReader implements TableReader {
	/** このプログラムのVERSION文字列を設定します。	{@value} */
	private static final String VERSION = "7.3.1.3 (2021/03/09)" ;

	/** システムの改行コードを設定します。*/

	/** 区切り文字列 */
	protected String	separator	= TAB_SEPARATOR;	// 6.2.0.0 (2015/02/27) 項目区切り文字。protected化

	private	  ColumnActionListener	listener	;		// 6.2.2.0 (2015/03/27)

	// 3.5.4.5 (2004/01/23) カラム名の外部指定を出来る様にする。
	/** カラム名 */
	protected String	columns		;			// 外部指定のカラム名 ( 4.3.4.7 (2009/01/22) protectedに変更 )
	// 6.1.0.0 (2014/12/26) 読み取り対象外のカラム列を、外部(タグ)より指定する。

	private boolean	  useNumber		= true;		// 3.7.0.5 (2005/04/11)

	private int		  skipRowCount	;			// 5.1.6.0 (2010/05/01) データの読み飛ばし設定

	// 5.2.1.0 (2010/10/01) コードリソース毎のラベル逆引きマップ

	// 6.2.0.0 (2015/02/27) TableReader クラスの呼び出し元メソッドの共通化(EXCEL,TEXT)
	/** シート名 */
	protected String  sheetName		;		// 3.5.4.2 (2003/12/15)
	/** シート番号 */
	protected String  sheetNos		;		// 5.5.7.2 (2012/10/09)
	/** カラム名(CSV形式) */
	protected String  constKeys		;		// 5.5.8.2 (2012/11/09) 固定値となるカラム名(CSV形式)
	/** 固定値となるアドレス */
	protected String  constAdrs		;		// 5.5.8.2 (2012/11/09) 固定値となるアドレス(行-列,行-列,・・・)
	/** BREAK条件 */
	protected String  nullBreakClm	;		// 5.5.8.2 (2012/11/09) 取込み条件/Sheet BREAK条件
	/** 行読み飛ばし */
	protected String  nullSkipClm	;		// 6.2.3.0 (2015/05/01) 行読み飛ばし

	private boolean	  useDebug		;		// 5.5.7.2 (2012/10/09) デバッグ情報の出力するかどうか

	/**
	 * デフォルトコンストラクター
	 *
	 * @og.rev 6.4.2.0 (2016/01/29) PMD refactoring. Each class should declare at least one constructor.
	 */
	protected AbstractTableReader() { super(); }		// これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。

	/**
	 * DBColumn オブジェクトをDBTable に設定します。
	 *
	 * ここでは、omitNames 属性を加味した、カラム名を作成します。
	 * その際、ゼロ文字列のカラム名は、omit します。
	 * 戻り値は、新しいカラム数(omitNames を加味、ゼロ文字列のカラムも除去)です。
	 *
	 * ※ 6.1.0.0 (2014/12/26) で、omitNames 属性を追加します。
	 *    これに伴い、従来は、EXCELのみ、#NAME で、ゼロ文字列のカラム名や
	 *    columns 指定で、a,b,,,,,,x のようなカラム名指定で、カラム飛ばしを
	 *    実装していましたが、その他の Reader でも、対応します。
	 *    これは、互換性に影響しますので、ご注意ください。
	 *    (読み込んでも、カラム名が無いので、使えなかったと思いますけど…)
	 *
	 * @og.rev 3.5.4.2 (2003/12/15) private を protected に変更。
	 * @og.rev 3.5.4.5 (2004/01/23) DBColumn 配列に値をセットします。
	 * @og.rev 5.2.1.0 (2010/10/01) useRenderer対応(コードリソース毎のラベル逆引き)
	 * @og.rev 6.1.0.0 (2014/12/26) omitNames 属性を追加
	 * @og.rev 6.2.1.0 (2015/03/13) ロングラベルから、コード値を見つける機能を、廃止します。
	 * @og.rev 6.2.2.0 (2015/03/27) ColumnActionListener 対応。
	 * @og.rev 6.3.9.0 (2015/11/06) コンストラクタで初期化されていないフィールドを null チェックなしで利用している(findbugs)
	 *
	 * @param names カラム名配列
	 * @return 新しいカラム数(omitNames を加味、ゼロ文字列のカラムも除去)
	 */
	protected int setTableDBColumn( final String[] names ) {
		// 6.3.9.0 (2015/11/06) コンストラクタで初期化されていないフィールドを null チェックなしで利用している(findbugs)
		if( listener == null ) {
			final String errMsg = "#setColumnActionListener(ColumnActionListener)を先に実行しておいてください。" ;
			throw new OgRuntimeException( errMsg );
		}

		listener.columnNames( names );			// 6.2.2.0 (2015/03/27)

		return names == null ? 0 : names.length;
	}

	/**
	 * #NAME のオリジナルカラム名配列がそろった段階で、イベントが発生します。
	 *
	 * @og.rev 7.3.1.3 (2021/03/09) #NAMEのオリジナルを取得できるようにします。
	 *
	 * @param   names  カラム名配列
	 */
	protected void setOriginalNames( final String[] names ) {
		listener.originalNames( names );
	}

	/**
	 * DBTableModelオブジェクトに、１行分のデータを追加します。
	 * これ自体は、メソッドの共通化による 拡張をしやすくするために用意しました。
	 *
	 * @og.rev 5.2.1.0 (2010/10/01) 新規作成
	 * @og.rev 6.2.0.0 (2015/02/27) コードリソースの逆引き処理の前に、Key:Val 分解を実行する。
	 * @og.rev 6.2.1.0 (2015/03/13) ロングラベルから、コード値を見つける機能を、廃止します。
	 * @og.rev 6.2.2.0 (2015/03/27) ColumnActionListener 対応。
	 * @og.rev 6.3.9.0 (2015/11/06) コンストラクタで初期化されていないフィールドを null チェックなしで利用している(findbugs)
	 *
	 * @param values １行分のデータ配列
	 * @param rowNo  行番号
	 */
	protected void setTableColumnValues( final String[] values, final int rowNo ) {
		// 6.3.9.0 (2015/11/06) コンストラクタで初期化されていないフィールドを null チェックなしで利用している(findbugs)
		if( listener == null ) {
			final String errMsg = "#setColumnActionListener(ColumnActionListener)を先に実行しておいてください。" ;
			throw new OgRuntimeException( errMsg );
		}

		listener.values( values,rowNo );					// 6.2.2.0 (2015/03/27)
	}

	/**
	 * 新しくEXCELのシートを処理する際に、シート名をセットするときに呼び出されます。
	 * EXCEL関係の処理の場合のみ呼び出されます。
	 *
	 * @og.rev 7.3.1.1 (2021/02/25) 現在実行中のシート名をセットする
	 *
	 * @param   sheetName   現在実行中のシート名
	 */
	protected void shtName( final String sheetName ) {
		// 6.3.9.0 (2015/11/06) コンストラクタで初期化されていないフィールドを null チェックなしで利用している(findbugs)
		if( listener == null ) {
			final String errMsg = "#setColumnActionListener(ColumnActionListener)を先に実行しておいてください。" ;
			throw new OgRuntimeException( errMsg );
		}

		listener.shtName( sheetName );
	}

	/**
	 * データを読み込む場合の,区切り文字をセットします。
	 *
	 * なお，このメソッドは,サブクラスによっては,使用しない場合があります。
	 * もし，使用しないサブクラスを作成する場合は, UnsupportedOperationException
	 * を throw するように,サブクラスで実装して下さい。
	 *
	 * @og.rev 3.1.1.0 (2003/03/28) 同期メソッド(synchronized付き)を非同期に変更する。
	 *
	 * @param   sep 区切り文字
	 */
	public void setSeparator( final String sep ) {
		if( sep != null ) { this.separator = sep; }
	}

	/**
	 * DBTableModelのデータとしてEXCELファイルを読み込むときのシート名を設定します。
	 * これにより、複数の形式の異なるデータを順次読み込むことや、シートを指定して
	 * 読み取ることが可能になります。
	 * sheetNos と sheetName が同時に指定された場合は、sheetNos が優先されます。エラーにはならないのでご注意ください。
	 * のでご注意ください。
	 * このメソッドは、isExcel() == true の場合のみ利用されます。
	 *
	 * ※ このクラスでは実装されていません。
	 *
	 * @og.rev 3.5.4.2 (2003/12/15) 新規追加
	 * @og.rev 6.2.0.0 (2015/02/27) TableReader クラスの呼び出し元メソッドの共通化(EXCEL,TEXT)
	 *
	 * @param   sheetName シート名
	 */
	public void setSheetName( final String sheetName ) {
		this.sheetName = sheetName;
	}

	/**
	 * EXCELファイルを読み込むときのシート番号を指定します(初期値:0)。
	 *
	 * EXCEL読み込み時に複数シートをマージして取り込みます。
	 * シート番号は、0 から始まる数字で表します。
	 * ヘッダーは、最初のシートのカラム位置に合わせます。（ヘッダータイトルの自動認識はありません。）
	 * よって、指定するシートは、すべて同一レイアウトでないと取り込み時にカラムのずれが発生します。
	 *
	 * シート番号の指定は、CSV形式で、複数指定できます。また、N-M の様にハイフンで繋げることで、
	 * N 番から、M 番のシート範囲を一括指定可能です。また、"*" による、全シート指定が可能です。
	 * これらの組み合わせも可能です。（ 0,1,3,5-8,10-* ）
	 * ただし、"*" に関しては例外的に、一文字だけで、すべてのシートを表すか、N-* を最後に指定するかの
	 * どちらかです。途中には、"*" は、現れません。
	 * シート番号は、重複(1,1,2,2)、逆転(3,2,1) での指定が可能です。これは、その指定順で、読み込まれます。
	 * sheetNos と sheetName が同時に指定された場合は、sheetNos が優先されます。エラーにはならないのでご注意ください。
	 * このメソッドは、isExcel() == true の場合のみ利用されます。
	 *
	 * 初期値は、0（第一シート） です。
	 *
	 * ※ このクラスでは実装されていません。
	 *
	 * @og.rev 5.5.7.2 (2012/10/09) 新規追加
	 * @og.rev 6.2.0.0 (2015/02/27) TableReader クラスの呼び出し元メソッドの共通化(EXCEL,TEXT)
	 *
	 * @param   sheetNos EXCELファイルのシート番号（0から始まる）
	 * @see		#setSheetName( String )
	 */
	public void setSheetNos( final String sheetNos ) {
		this.sheetNos = sheetNos;
	}

	/**
	 * 固定値となるカラム名(CSV形式)と、constAdrs 固定値となるアドレス(行-列,行-列,・・・)を設定します。
	 *
	 * アドレスは、EXCEL上の行-列をCSV形式で指定します。
	 * 行列は、EXCELオブジェクトに準拠するため、０から始まる整数です。
	 * 0-0 ⇒ A1 , 1-0 ⇒ A2 , 0-1 ⇒ B1 になります。
	 * これにより、シートの一か所に書かれている情報を、DBTableModel のカラムに固定値として
	 * 設定することができます。
	 * 例として、DB定義書で、テーブル名をシートの全レコードに設定したい場合などに使います。
	 * このメソッドは、isExcel() == true の場合のみ利用されます。
	 *
	 * 5.7.6.3 (2014/05/23) より、
	 *   ①EXCEL表記に準拠した、A1,A2,B1 の記述も処理できるように対応します。
	 *     なお、A1,A2,B1 の記述は、必ず、英字1文字＋数字 にしてください。(A～Zまで)
	 *   ②処理中のEXCELシート名をカラムに割り当てるために、"SHEET" という記号に対応します。
	 * 例えば、sheetConstKeys="CLM,LANG,NAME" とし、sheetConstAdrs="0-0,A2,SHEET" とすると、
	 * NAMEカラムには、シート名を読み込むことができます。
	 * これは、内部処理の簡素化のためです。
	 *
	 * 例として、DB定義書で、テーブル名をシートの全レコードに設定したい場合などに使います。
	 * このメソッドは、isExcel() == true の場合のみ利用されます。
	 *
	 * @og.rev 5.5.8.2 (2012/11/09) 新規追加
	 *
	 * @param   constKeys 固定値となるカラム名(CSV形式)
	 * @param   constAdrs 固定値となるアドレス(行-列,行-列,・・・)
	 */
	public void setSheetConstData( final String constKeys,final String constAdrs ) {
		this.constKeys = constKeys;
		this.constAdrs = constAdrs;
	}

	/**
	 * ここに指定されたカラム列に NULL が現れた時点で読み取りを中止します。
	 *
	 * これは、指定のカラムは必須という事を条件に、そのレコードだけを読み取る処理を行います。
	 * 複数Sheetの場合は、次のSheetを読みます。
	 * 現時点では、Excel の場合のみ有効です。
	 *
	 * @og.rev 5.5.8.2 (2012/11/09) 新規追加
	 * @og.rev 6.2.0.0 (2015/02/27) TableReader クラスの呼び出し元メソッドの共通化(EXCEL,TEXT)
	 *
	 * @param   clm カラム列
	 */
	public void setNullBreakClm( final String clm ) {
		nullBreakClm = clm;
	}

	/**
	 * ここに指定されたカラム列に NULL が現れたレコードは読み飛ばします。
	 *
	 * 例えば、更新対象カラムで、null の場合は、何もしない、などのケースで使用できます。
	 * 複数カラムの場合は、AND条件やOR条件などが、考えられるため、
	 * カラムを一つにまとめて、指定してください。
	 *
	 * @og.rev 6.2.3.0 (2015/05/01) 行読み飛ばし nullSkipClm追加
	 *
	 * @param   clm カラム列
	 */
	public void setNullSkipClm( final String clm ) {
		nullSkipClm = clm;
	}

	/**
	 * 読み取り元ファイルのカラム列を、外部(タグ)より指定します。
	 * ファイルに記述された #NAME より優先して使用されます。
	 *
	 * @og.rev 3.5.4.5 (2004/01/23) 新規作成
	 *
	 * @param   clms 読み取り元ファイルのカラム列(CSV形式)
	 */
	public void setColumns( final String clms ) {
		columns = clms ;
	}

	/**
	 * 行番号情報を指定[true:使用している/false:していない]します(初期値:true)。
	 *
	 * 通常のフォーマットでは、各行の先頭に行番号が出力されています。
	 * 読み取り時に、#NAME 属性を使用する場合は、この行番号を無視しています。
	 * #NAME 属性を使用せず、columns 属性でカラム名を指定する場合(他システムの
	 * 出力ファイルを読み取るケース等)では、行番号も存在しないケースがあり、
	 * その様な場合に、useNumber="false" を指定すれば、データの最初から読み取り始めます。
	 * この場合、出力データのカラムの並び順が変更された場合、columns 属性も
	 * 指定しなおす必要がありますので、できるだけ、#NAME 属性を使用するように
	 * してください。
	 * なお、EXCEL 入力には、この設定は適用されません。(暫定対応)
	 * 初期値は、true(使用する) です。
	 *
	 * @og.rev 3.7.0.5 (2005/04/11) 新規追加
	 *
	 * @param	useNumber	行番号情報  [true:使用する/false:使用しない]
	 */
	public void setUseNumber( final boolean useNumber ) {
		this.useNumber = useNumber ;
	}

	/**
	 * データの読み始めの初期値を取得します。
	 *
	 * TAB区切りテキストやEXCEL等のデータの読み始めの初期値を指定します。
	 * ファイルの先頭行が、０行としてカウントしますので、設定値は、読み飛ばす
	 * 件数になります。(１と指定すると、１件読み飛ばし、２行目から読み込みます。)
	 * 読み飛ばしは、コメント行などは、無視しますので、実際の行数分読み飛ばします。
	 * ＃NAME属性や、columns 属性は、有効です。
	 *
	 * @og.rev 5.1.6.0 (2010/05/01) 新規作成
	 *
	 * @return	読み始めの初期値
	 */
	public int getSkipRowCount() {
		return skipRowCount ;
	}

	/**
	 * データの読み飛ばし件数を設定します。
	 *
	 * TAB区切りテキストやEXCEL等のデータの読み始めの初期値を指定します。
	 * ファイルの先頭行が、０行としてカウントしますので、設定値は、読み飛ばす
	 * 件数になります。(１と指定すると、１件読み飛ばし、２行目から読み込みます。)
	 * 読み飛ばしは、コメント行などは、無視しますので、実際の行数分読み飛ばします。
	 * ＃NAME属性や、columns 属性は、有効です。
	 *
	 * @og.rev 5.1.6.0 (2010/05/01) 新規作成
	 *
	 * @param	count 読み始めの初期値
	 */
	public void setSkipRowCount( final int count ) {
		skipRowCount = count;
	}

	/**
	 * 行番号情報を、使用している(true)/していない(false)を返します。
	 *
	 * 通常のフォーマットでは、各行の先頭に行番号が出力されています。
	 * 読み取り時に、#NAME 属性を使用する場合は、この行番号を無視しています。
	 * #NAME 属性を使用せず、columns 属性でカラム名を指定する場合(他システムの
	 * 出力ファイルを読み取るケース等)では、行番号も存在しないケースがあり、
	 * その様な場合に、useNumber="false" を指定すれば、データの最初から読み取り始めます。
	 * この場合、出力データのカラムの並び順が変更された場合、columns 属性も
	 * 指定しなおす必要がありますので、できるだけ、#NAME 属性を使用するように
	 * してください。
	 * なお、EXCEL 入力には、この設定は適用されません。(暫定対応)
	 * 初期値は、true(使用する) です。
	 *
	 * @og.rev 3.7.0.5 (2005/04/11) 新規追加
	 * @og.rev 4.0.0.0 (2007/07/20) メソッド名変更(getUseNumber() ⇒  isUseNumber())
	 *
	 * @return	行番号情報を、使用している(true)/していない(false)を指定
	 */
	protected boolean isUseNumber() {
		return useNumber ;
	}

	/**
	 * ColumnActionListenerオブジェクトを設定します。
	 *
	 * ColumnActionListenerオブジェクトは、カラム名配列設定時と、それに対応する値配列設定時に
	 * 呼ばれるイベントリスナーです。
	 * 具体的なテーブル処理は、このイベントを使用して書き込みを行います。
	 *
	 * @og.rev 6.2.2.0 (2015/03/27) 新規作成
	 *
	 * @param	listener	ColumnActionListenerオブジェクト
	 */
	public void setColumnActionListener( final ColumnActionListener listener ) {
		this.listener = listener;
	}

	/**
	 * デバッグ情報を出力するかどうか[true:する/false:しない]を指定します。
	 *
	 * EXCELなどを読み取る場合、シートマージで読み取ると、エラー時の行番号が、連番になるため、
	 * どのシートなのか、判らなくなります。
	 * そこで、どうしてもわからなくなった場合に備えて、デバッグ情報を出力できるようにします。
	 * 通常は使用しませんので、設定を無視します。
	 * 初期値は、false:デバッグ情報を出力しない です。
	 *
	 * @og.rev 5.5.7.2 (2012/10/09) 新規作成
	 *
	 * @param	useDebug	デバッグ出力するか [true:する/false:しない]
	 */
	public void setDebug( final boolean useDebug ) {
		this.useDebug = useDebug;
	}

	/**
	 * デバッグ情報を出力するかどうか[true:する/false:しない]を取得します。
	 *
	 * EXCELなどを読み取る場合、シートマージで読み取ると、エラー時の行番号が、連番になるため、
	 * どのシートなのか、判らなくなります。
	 * そこで、どうしてもわからなくなった場合に備えて、デバッグ情報を出力できるようにします。
	 *
	 * @og.rev 5.5.7.2 (2012/10/09) 新規作成
	 *
	 * @return	デバッグ出力 [true:する/false:しない]
	 */
	protected boolean isDebug() {
		return useDebug ;
	}
}
