/*
 * 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 java.io.PrintWriter;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;

import org.opengion.hayabusa.common.HybsSystem;
import org.opengion.hayabusa.common.HybsSystemException;
import org.opengion.hayabusa.db.DBTableModel;
import org.opengion.hayabusa.db.DBColumn;
import org.opengion.fukurou.util.StringUtil;
import org.opengion.fukurou.util.HybsEntry;

/**
 * 区切り文字指定(初期値:タブ)ゼロカンマファイルの書き出しクラスです。
 *
 * ラベル，名前，データの出力部のみオーバーライドすれば，各種出力フォーマットに合わせた
 * サブクラスを実現する事が可能です。
 * ゼロカンマファイルとは、EXCELのゼロサプレス対策として、頭ゼロの文字型データを出力する
 * 時に、先頭にカンマ(')を付けて、ゼロが削除(見えなくなる)現象を抑止しているファイルです。
 *
 * このクラスは,可変長タブ区切り文字ファイルの出力機能を実現しています。
 *
 * @og.group ファイル出力
 *
 * @version  4.0
 * @author	 Kazuhiko Hasegawa
 * @since    JDK5.0,
 */
public abstract class AbstractTableWriter implements TableWriter {
	/** このプログラムのVERSION文字列を設定します。	{@value} */
	private static final String VERSION = "5.7.9.0 (2014/08/08)" ;

	// 5.1.6.0 (2010/05/01) dbType の簡易的な設定
	/** dbType の簡易的な設定	{@value} */
	public static final int STRING  = 0;
	/** dbType の簡易的な設定	{@value} */
	public static final int NVAR    = 1;
	/** dbType の簡易的な設定	{@value} */
	public static final int NUMBER  = 2;

	protected DBTableModel	  table 	= null;
	protected DBColumn[]	  dbColumn	= null;			// table に対するカラムオブジェクト(キャッシュ)
	protected int 	numberOfColumns		= -1;			// 4.0.0 (2005/01/31) 出力対象のカラム数
	protected int[]			  clmNo		= null;			// 出力対象のカラム番号配列
	protected int[]			  dbType	= null;

	private String	separator		= TAB_SEPARATOR;	// 項目区切り文字
	private String	headerSequence	= HybsSystem.sys( "WRITER_HEADER_SEQUENCE" ) ;
	private boolean append			= false;			// 3.5.4.2 (2003/12/15)
	private String	lang			= null;
	private String	columns			= null;				// 4.0.0 (2005/11/30) 外部指定のカラム名
	private String	encode			= null;				// 3.5.4.5 (2004/01/23) エンコード文字列指定

	private boolean	useNumber		= true;				// 3.7.0.2 (2005/02/14) 行番号情報を、出力する(true)/しない(false)を指定
	private boolean	useRenderer		= false;			// 5.2.1.0 (2010/10/01)

	/**
	 * DBTableModel から 各形式のデータを作成して,PrintWriter に書き出します。
	 * このメソッドは、EXCEL 書き出し時に使用します。
	 *
	 * @see #isExcel()
	 */
	abstract public void writeDBTable();

	/**
	 * DBTableModel から データを作成して,PrintWriter に書き出します。
	 *
	 * @param	writer PrintWriterオブジェクト
	 */
	abstract public void writeDBTable( final PrintWriter writer );

	/**
	 * numberOfColumns と DBColumn を初期化します。
	 * 内部的に、DBTableModel、lang , columns を使用して、
	 * numberOfColumns 、dbColumn、clmNo、dbType の値を初期化します。
	 * カラムが１項目もない場合、言語(lnag)が未指定、DBTableModelが未指定(null)
	 * の場合は、false を返します。その場合は、以下の処理は正常に行えません。
	 * データが０件の場合は、処理を行います。通常、ヘッダーのみのファイルを
	 * 作成することになります。(これを雛形として、取込データを登録する事が可能)
	 *
	 * @og.rev 4.0.0.0 (2005/12/31) 外部指定のカラム名を使用
	 * @og.rev 5.1.6.0 (2010/05/01) DbType の初期値を設定する。
	 *
	 * @return	初期化成功：true / 失敗：false
	 */
	protected boolean createDBColumn() {

		boolean rtnFlag = false;
		if( lang != null && table != null ) {
			if( columns != null ) {
				String[] clms = StringUtil.csv2Array( columns );
				numberOfColumns = clms.length;
				clmNo = new int[numberOfColumns];
				for( int i=0; i<numberOfColumns; i++ ) {
					int no = table.getColumnNo( clms[i] );
					clmNo[i] = no;
				}
			}
			else {
				numberOfColumns = table.getColumnCount();
				clmNo = new int[numberOfColumns];
				for( int i=0; i<numberOfColumns; i++ ) {
					clmNo[i] = i;
				}
			}

			dbColumn = table.getDBColumns();
			rtnFlag = (numberOfColumns > 0);	// カラムが１項目以上あれば成功

			// 5.1.6.0 (2010/05/01) DbType の初期値を設定する。
			dbType = new int[numberOfColumns];
			for( int i=0; i<numberOfColumns; i++ ) {
				String type = dbColumn[clmNo[i]].getDbType();

				if( "NVAR".equals( type ) ) {
					dbType[i] = NVAR;
				}
				else if( "S9".equals( type ) || "R".equals( type )  ) {
					dbType[i] = NUMBER;
				}
				else {
					dbType[i] = STRING;
				}
			}
		}
		return rtnFlag;
	}

	/**
	 * PrintWriter に DBTableModelのヘッダー情報を書き込みます。
	 *
	 * @og.rev 3.1.1.0 (2003/03/28) 同期メソッド(synchronized付き)を非同期に変更する。
	 *
	 * @param	writer PrintWriterオブジェクト
	 */
	protected void writeHeader( final PrintWriter writer ) {
		// ここでは処理を行いません。
	}

	/**
	 * PrintWriter に DBTableModelのラベル情報を書き込みます。
	 * 第一カラム目は、ラベル情報を示す "#Label" を書き込みます。
	 * この行は、出力形式に無関係に、TableWriter.TAB_SEPARATOR で区切られます。
	 *
	 * @og.rev 3.1.1.0 (2003/03/28) 同期メソッド(synchronized付き)を非同期に変更する。
	 * @og.rev 3.7.0.2 (2005/02/14) 行番号情報を、出力する(true)/しない(false)を指定
	 * @og.rev 4.0.0.0 (2005/12/31) 外部指定のカラム名を使用
	 *
	 * @param	table DBTableModelオブジェクト
	 * @param	writer PrintWriterオブジェクト
	 */
	protected void writeLabel( final DBTableModel table,final PrintWriter writer ) {
		if( useNumber ) {
			writer.print( "#Label" );
			writer.print( separator );
		}
		else {
			writer.print( "#" );
		}

		for( int i=0; i<numberOfColumns; i++ ) {
			if( i != 0 ) { writer.print( separator ); }
			int clm = clmNo[i];
			writer.print( dbColumn[clm].getLabel() );
		}
		writer.println();
	}

	/**
	 * PrintWriter に DBTableModelの項目名情報を書き込みます。
	 * 第一カラム目は、項目名情報を示す "#Name" を書き込みます。
	 * この行は、出力形式に無関係に、TableWriter.TAB_SEPARATOR で区切られます。
	 *
	 * @og.rev 3.1.1.0 (2003/03/28) 同期メソッド(synchronized付き)を非同期に変更する。
	 * @og.rev 3.7.0.2 (2005/02/14) 行番号情報を、出力する(true)/しない(false)を指定
	 *
	 * @param	table DBTableModelオブジェクト
	 * @param	writer PrintWriterオブジェクト
	 */
	protected void writeName( final DBTableModel table,final PrintWriter writer ) {
		if( useNumber ) {
			writer.print( "#Name" );
			writer.print( separator );
		}
		else {
			writer.print( "#" );
		}

		for( int i=0; i<numberOfColumns; i++ ) {
			if( i != 0 ) { writer.print( separator ); }
			int clm = clmNo[i];
			writer.print( table.getColumnName(clm) );
		}
		writer.println();
	}

	/**
	 * PrintWriter に DBTableModelのサイズ情報を書き込みます。
	 * 第一カラム目は、サイズ情報を示す "#Size" を書き込みます。
	 * この行は、出力形式に無関係に、TableWriter.TAB_SEPARATOR で区切られます。
	 *
	 * @og.rev 3.1.1.0 (2003/03/28) 同期メソッド(synchronized付き)を非同期に変更する。
	 * @og.rev 3.5.5.5 (2004/04/23) DBColumn の size と maxlength の 意味を変更
	 * @og.rev 3.7.0.2 (2005/02/14) 行番号情報を、出力する(true)/しない(false)を指定
	 *
	 * @param	table DBTableModelオブジェクト
	 * @param	writer PrintWriterオブジェクト
	 */
	protected void writeSize( final DBTableModel table,final PrintWriter writer ) {
		if( useNumber ) {
			writer.print( "#Size" );
			writer.print( separator );
		}
		else {
			writer.print( "#" );
		}

		for( int i=0; i<numberOfColumns; i++ ) {
			if( i != 0 ) { writer.print( separator ); }
			int clm = clmNo[i];
			writer.print( dbColumn[clm].getTotalSize() );	// 4.0.0 (2005/01/31) メソッド名変更
		}
		writer.println();
	}

	/**
	 * PrintWriter に DBTableModelのクラス名情報を書き込みます。
	 * 第一カラム目は、サイズ情報を示す "#Class" を書き込みます。
	 * この行は、出力形式に無関係に、TableWriter.TAB_SEPARATOR で区切られます。
	 *
	 * @og.rev 3.1.1.0 (2003/03/28) 同期メソッド(synchronized付き)を非同期に変更する。
	 *
	 * @param	table DBTableModelオブジェクト
	 * @param	writer PrintWriterオブジェクト
	 */
	protected void writeClass( final DBTableModel table,final PrintWriter writer ) {
		if( useNumber ) {
			writer.print( "#Class" );
			writer.print( separator );
		}
		else {
			writer.print( "#" );
		}

		for( int i=0; i<numberOfColumns; i++ ) {
			if( i != 0 ) { writer.print( separator ); }
			int clm = clmNo[i];
			writer.print( dbColumn[clm].getClassName() );
		}
		writer.println();
	}

	/**
	 * PrintWriter に セパレーターを書き込みます。
	 * 第一カラム目は、サイズ情報を示す "#----" を書き込みます。
	 * この行は、出力形式に無関係に、TableWriter.TAB_SEPARATOR で区切られます。
	 *
	 * @og.rev 3.1.1.0 (2003/03/28) 同期メソッド(synchronized付き)を非同期に変更する。
	 * @og.rev 3.7.0.2 (2005/02/14) 行番号情報を、出力する(true)/しない(false)を指定
	 *
	 * @param	table DBTableModelオブジェクト
	 * @param	writer PrintWriterオブジェクト
	 */
	protected void writeSeparator( final DBTableModel table,final PrintWriter writer ) {
		String sep = "----" ;
		if( useNumber ) {
			writer.print( "#----" );
			writer.print( separator );
		}
		else {
			writer.print( "#" );
		}

		for( int i=0; i<numberOfColumns; i++ ) {
			if( i != 0 ) { writer.print( separator ); }
			writer.print( sep );
		}
		writer.println();
	}

	/**
	 * PrintWriter に DBTableModelのテーブル情報を書き込みます。
	 * このクラスでは，データを ダブルコーテーション(")で囲みます。
	 * PrintWriter に DBTableModelのテーブル情報を書き込みます。
	 *
	 * @og.rev 2.0.0.5 (2002/09/30) 先頭が0 でかつ数字タイプ(S9 or R)でない場合に ' を出力するように修正。
	 * @og.rev 2.3.1.2 (2003/01/28) データ出力時に、改行が余分に出される箇所を修正。
	 * @og.rev 3.1.0.0 (2003/03/20) DBColumn から、getDbType() キーを直接取り出す
	 * @og.rev 3.1.1.0 (2003/03/28) 同期メソッド(synchronized付き)を非同期に変更する。
	 * @og.rev 3.3.3.1 (2003/07/18) ファイルリード/ライト時に後ろスペースの除去を行います。
	 * @og.rev 3.7.0.2 (2005/02/14) 行番号情報を、出力する(true)/しない(false)を指定
	 * @og.rev 3.8.0.1 (2005/06/17) DBTypeが NVAR の場合は、元のUnicodeに戻します。
	 * @og.rev 5.1.6.0 (2010/05/01) DbType の初期値(dbType)を利用する。
	 * @og.rev 5.2.1.0 (2010/10/01) このメソッドは、abstract 化します。
	 *
	 * @param	table DBTableModelオブジェクト
	 * @param	writer PrintWriterオブジェクト
	 */
	abstract protected void writeData( final DBTableModel table,final PrintWriter writer ) ;
//	protected void writeData( final DBTableModel table,final PrintWriter writer ) {
//		int numberOfRows =	table.getRowCount();
//
//		for( int row=0; row<numberOfRows; row++ ) {
//			if( useNumber ) {
//				writer.print( row+1 );
//				writer.print( separator );
//			}
//
//			for( int i=0; i<numberOfColumns; i++ ) {
//				if( i != 0 ) { writer.print( separator ); }
//
//				int clm = clmNo[i];
//				String val = table.getValue(row,clm);
////				if( "NVAR".equals( dbColumn[clm].getDbType()) ) {
//				if( dbType[i] == NVAR ) {
//					val = StringUtil.getReplaceEscape( val );
//				}
//
//				if( val != null && val.length() > 0 && val.charAt(0) == '0' &&
////					NUMBER_TYPE_LIST.indexOf( dbColumn[clm].getDbType() ) < 0 ) {
//					dbType[i] == NUMBER ) {
//						writer.print( "'" );		// 開始日などの 00000000 を文字列タイプで渡す
//				}
//				writer.print( StringUtil.rTrim( val ) );
//			}
//			writer.println();
//		}
//	}

	/**
	 * DBTableModel をセットします。
	 *
	 * @og.rev 3.1.1.0 (2003/03/28) 同期メソッド(synchronized付き)を非同期に変更する。
	 * @og.rev 3.5.4.2 (2003/12/15) lang 引数も同時に設定します。
	 *
	 * @param	table DBTableModelオブジェクト
	 * @param	lang 言語コード
	 */
	public void setDBTableModel( final DBTableModel table, final String lang ) {
		this.table = table;
		this.lang = lang;
	}

	/**
	 * 内部の DBTableModel を返します。
	 *
	 * @return	DBTableModelオブジェクト
	 */
	public DBTableModel getDBTableModel() {
		return table;
	}

	/**
	 * DBTableModelの出力順をセットします。
	 * Label,Name,Size,Class,Data の各フィールドの頭文字のアルファベットで
	 * 出力順を設定します。
	 *
	 * なお，出力順に指定しない項目は出力されません
	 *
	 * @og.rev 3.1.1.0 (2003/03/28) 同期メソッド(synchronized付き)を非同期に変更する。
	 *
	 * @param	hs 出力順 (LNSCD など)
	 */
	public void setHeaderSequence( final String hs ) {
		if( hs != null ) { headerSequence = hs ; }
	}

	/**
	 * DBTableModelの出力順を返します。
	 * Label,Name,Size,Class,Data の各フィールドの頭文字のアルファベットで
	 * 出力順を設定します。
	 *
	 * なお，出力順に指定しない項目は出力されません
	 *
	 * @return	出力順 (LNSCD など)
	 */
	public String getHeaderSequence() {
		return headerSequence ;
	}

	/**
	 * データを書き込む場合の,区切り文字をセットします。
	 *
	 * @og.rev 3.1.1.0 (2003/03/28) 同期メソッド(synchronized付き)を非同期に変更する。
	 *
	 * @param	sep 区切り文字
	 */
	public void setSeparator( final String sep ) {
		if( sep != null ) { this.separator = sep; }
	}

	/**
	 * データを書き込む場合の,区切り文字を返します。
	 *
	 * @return	区切り文字
	 */
	public String getSeparator() {
		return separator;
	}

	/**
	 * DBTableModelのデータとして書き込むときに、追加モードで書き込むかどうかを設定します。
	 * 初期値は、false(新規モード)です。
	 *
	 * @og.rev 3.5.4.2 (2003/12/15) 新規追加
	 *
	 * @param	flag	[true:追加モード/false:新規モード]
	 */
	public void setAppend( final boolean flag ) {
		append = flag;
	}

	/**
	 * DBTableModelのデータとして書き込むときに、追加モードで書き込むかどうかを取得します。
	 * 初期値は、false(新規モード)です。
	 *
	 * @og.rev 3.5.4.2 (2003/12/15) 新規追加
	 *
	 * @return	true(追加モード)/false(新規モード)
	 */
	public boolean isAppend() {
		return append ;
	}

	/**
	 * DBTableModelのデータとして読み込むときのシート名を設定します。
	 * 初期値は、"Sheet1" です。
	 * これは、EXCEL追加機能として実装されています。
	 * ※ このクラスでは実装されていません。
	 *
	 * @og.rev 3.5.4.2 (2003/12/15) 新規追加
	 * @og.rev 3.5.4.3 (2004/01/05) 実装廃止(TableWriter_Excel へ移動)
	 *
	 * @param   sheetName シート名
	 */
	public void setSheetName( final String sheetName ) {
		String errMsg = "このメソッドは、EXCEL追加機能ですので、使用できません。";
		throw new UnsupportedOperationException( errMsg );
	}

	/**
	 * EXCEL雛型参考ファイルのシート名を設定します。
	 * これは、EXCEL追加機能として実装されています。
	 *
	 * EXCELファイルを書き出す時に、雛型として参照するシート名を指定します。
	 * これにより、複数の形式の異なるデータを順次書き出したり(appendモードを併用)する
	 * ことや、シートを指定して新規にEXCELを作成する場合にフォームを設定する事が可能になります。
	 * 初期値は、null(第一シート) です。
	 * ※ このクラスでは実装されていません。
	 *
	 * @og.rev 3.5.4.3 (2004/01/05) 新規追加
	 *
	 * @param   sheetName シート名
	 */
	public void setRefSheetName( final String sheetName )  {
		String errMsg = "このメソッドは、EXCEL追加機能ですので、使用できません。";
		throw new UnsupportedOperationException( errMsg );
	}

	/**
	 * このクラスが、EXCEL対応機能を持っているかどうかを返します。
	 *
	 * EXCEL対応機能とは、シート名のセット、雛型参照ファイル名のセット、
	 * 書き込み元ファイルのFileオブジェクト取得などの、特殊機能です。
	 * 本来は、インターフェースを分けるべきと考えますが、taglib クラス等の
	 * 関係があり、問い合わせによる条件分岐で対応します。
	 *
	 * @og.rev 3.5.4.3 (2004/01/05) 新規追加
	 *
	 * @return	EXCEL対応機能を持っているかどうか(ここでは、false固定です)
	 */
	public boolean isExcel() {
		return false;
	}

	/**
	 * 出力先ファイル名をセットします。(DIR + Filename)
	 * これは、EXCEL追加機能として実装されています。
	 * ※ このクラスでは実装されていません。
	 * このメソッドでは、必ず、UnsupportedOperationException が、throw されます。
	 *
	 * @og.rev 3.5.4.3 (2004/01/05) 新規作成
	 *
	 * @param   filename EXCEL雛型参考ファイル名
	 */
	public void setFilename( final String filename ) {
		String errMsg = "このメソッドは、EXCEL追加機能ですので、使用できません。";
		throw new UnsupportedOperationException( errMsg );
	}

	/**
	 * EXCEL雛型参考ファイル名をセットします。(DIR + Filename)
	 * これは、EXCEL追加機能として実装されています。
	 * ※ このクラスでは実装されていません。
	 * このメソッドでは、必ず、UnsupportedOperationException が、throw されます。
	 *
	 * @og.rev 3.5.4.3 (2004/01/05) 新規作成
	 *
	 * @param   filename EXCEL雛型参考ファイル名
	 */
	public void setRefFilename( final String filename ) {
		String errMsg = "このメソッドは、EXCEL追加機能ですので、使用できません。";
		throw new UnsupportedOperationException( errMsg );
	}
	
	/**
	 * Excelで指定したシートが存在した場合に上書きするかどうか。
	 *
	 * @og.rev 5.9.12.1 (2016/09/09) 新規追加
	 *
	 * @param   flag シートを上書きするかどうか[true:上書き/false:別名]
	 */
	public void setSheetOverwrite( final boolean flag ) {
		String errMsg = "このメソッドは、EXCEL追加機能ですので、使用できません。";
		throw new UnsupportedOperationException( errMsg );
	}
	
	/**
	 * EXCELで、出力処理の最後にセルの計算式の再計算をさせるシート名をカンマ区切りで指定します。
	 *
	 * @og.rev 5.9.12.1 (2016/09/09) 新規追加
	 *
	 * @param  sheet 対象シート名
	 */
	public void setRecalcSheetName( final String sheet ){
		String errMsg = "このメソッドは、EXCEL追加機能ですので、使用できません。";
		throw new UnsupportedOperationException( errMsg );
	}

	/**
	 * EXCEL出力時のデフォルトフォント名を設定します。
	 * これは、EXCEL追加機能として実装されています。
	 *
	 * EXCELファイルを書き出す時に、デフォルトフォント名を指定します。
	 * フォント名は、EXCELのフォント名をそのまま使用してください。
	 * 内部的に、POI の org.apache.poi.hssf.usermodel.HSSFFont#setFontName( String )
	 * に設定されます。
	 * 初期値は、システムリソース の TABLE_WRITER_DEFAULT_FONT_NAME です。
	 *
	 * ※ このクラスでは実装されていません。
	 * このメソッドでは、必ず、UnsupportedOperationException が、throw されます。
	 *
	 * @og.rev 3.8.5.3 (2006/08/07) 新規追加
	 *
	 * @param   fontName フォント名
	 */
	public void setFontName( final String fontName ) {
		String errMsg = "このメソッドは、EXCEL追加機能ですので、使用できません。";
		throw new UnsupportedOperationException( errMsg );
	}

	/**
	 * EXCEL出力時のデフォルトフォントポイント数を設定します。
	 * これは、EXCEL追加機能として実装されています。
	 *
	 * EXCELファイルを書き出す時に、デフォルトポイント数を指定します。
	 * 内部的に、POI の org.apache.poi.hssf.usermodel.HSSFFont#setFontHeightInPoints( short )
	 * に設定されます。
	 * 初期値は、システムリソース の TABLE_WRITER_DEFAULT_FONT_POINTS です。
	 *
	 * ※ このクラスでは実装されていません。
	 * このメソッドでは、必ず、UnsupportedOperationException が、throw されます。
	 *
	 * @og.rev 3.8.5.3 (2006/08/07) 新規追加
	 *
	 * @param	point	フォントポイント数
	 */
	public void setFontPoint( final short point ) {
		String errMsg = "このメソッドは、EXCEL追加機能ですので、使用できません。";
		throw new UnsupportedOperationException( errMsg );
	}

	/**
	 * 読み取り元ファイルのエンコード文字列を指定します。
	 * ファイルは、BufferedReader で受け取る為、本来は、エンコードは不要ですが、
	 * 固定長ファイルの読み取り時のバイトコード分割時に、指定のエンコードで
	 * 分割する必要があります。(例えば、半角文字は、Shift_JIS では、１バイト)
	 *
	 * @og.rev 3.5.4.5 (2004/01/23) 新規作成
	 *
	 * @param   enc ファイルのエンコード文字列
	 */
	public void setEncode( final String enc ) {
		encode = enc;
	}

	/**
	 * 読み取り元ファイルのエンコード文字列を取得します。
	 * ファイルは、BufferedReader で受け取る為、本来は、エンコードは不要ですが、
	 * 固定長ファイルの読み取り時のバイトコード分割時に、指定のエンコードで
	 * 分割する必要があります。(例えば、半角文字は、Shift_JIS では、１バイト)
	 *
	 * @og.rev 3.5.4.5 (2004/01/23) 新規作成
	 *
	 * @return	ファイルのエンコード文字列
	 */
	protected String getEncode() {
		return encode;
	}

	/**
	 * 行番号情報を、出力する(true)/しない(false)を指定します。
	 *
	 * 通常のフォーマットでは、各行の先頭に行番号を出力します。
	 * これは、#NAME 属性を使用する場合には、必ず出力する必要があります。
	 * (#NAME 属性は、読み取り時には、必須です。)
	 * この、先頭の行番号が不要な場合(つまり、他のシステムへのデータ出力、
	 * このシステムでは、#NAME 属性が出力されないため、読み込みできません。)
	 * この行番号を出力しないようにできます。
	 * 初期値は、true(出力する) です。
	 *
	 * @og.rev 3.7.0.2 (2005/02/14) 新規追加
	 *
	 * @param   useNumber 行番号情報を [true:出力する/false:しない]
	 */
	public void setUseNumber( final boolean useNumber ) {
		this.useNumber = useNumber;
	}

	/**
	 * 行番号情報を、出力する(true)/しない(false)を返します。
	 *
	 * 通常のフォーマットでは、各行の先頭に行番号を出力します。
	 * これは、#NAME 属性を使用する場合には、必ず出力する必要があります。
	 * (#NAME 属性は、読み取り時には、必須です。)
	 * この、先頭の行番号が不要な場合(つまり、他のシステムへのデータ出力、
	 * このシステムでは、#NAME 属性が出力されないため、読み込みできません。)
	 * この行番号を出力しないようにできます。
	 * 初期値は、true(出力する) です。
	 *
	 * @og.rev 3.7.0.2 (2005/02/14) 新規追加
	 *
	 * @return   行番号情報を、出力する(true)/しない(false)
	 */
	protected boolean isUseNumber() {
		return useNumber;
	}

	/**
	 * パラメーターリストをセットします。
	 * 内部は、HybsEntry クラスを持っています。
	 * 引数が、null の場合は、何もしません。
	 * ※ このクラスでは実装されていません。
	 *
	 * @og.rev 4.0.0.0 (2005/01/31) 新規追加
	 *
	 * @param   listParam	パラメーターリスト
	 */
	public void setParam( final List<HybsEntry> listParam ) {
		String errMsg = "このメソッドは、XML追加機能ですので、使用できません。";
		throw new UnsupportedOperationException( errMsg );
	}

	/**
	 * 出力先ファイルのカラム列を、外部(タグ)よりCSV形式の文字列で指定します。
	 * ただし、指定のカラム名は、DBTableModel上に存在している必要があります。
	 *
	 * @og.rev 4.0.0.0 (2005/11/30) 新規追加
	 *
	 * @param   clms 出力先ファイルのカラム列(カンマ区切り文字)
	 */
	public void setColumns( final String clms ) {
		columns = clms ;
	}

	/**
	 * データの書き込み開始行番号を設定します(初期値:0)。
	 *
	 * TAB区切りテキストやEXCEL等のデータの書き込みの開始行番号を指定します。
	 * 属性名は、行を飛ばす処理ということで、readTable タグと同じ名称です。
	 * ファイルの先頭行が、０行としてカウントしますので、設定値は、読み飛ばす
	 * 件数になります。(１と指定すると、１件読み飛ばし、２行目から読み込みます。)
	 * 行の読み飛ばしと、カラムの読み飛ばし(columns)、refFileURL、refFilename、
	 * refSheetName とともに使用すれば、ある程度のレイアウト設定が可能です。
	 * なお、この機能は、TableWriter_Excel のみに実装します。
	 *
	 * @og.rev 5.7.9.0 (2014/08/08) 新規作成
	 *
	 * @param	skipRowCount 書き込み開始行番号
	 */
	public void setSkipRowCount( final int skipRowCount ) {
		if( skipRowCount != 0 ) {
			String errMsg = "このメソッドは、TableWriter_Excel のみの機能ですので、使用できません。";
			throw new UnsupportedOperationException( errMsg );
		}
	}

	/**
	 * 書込処理でコードリソースのラベル変換を行うかどうか[true/false]を指定します。
	 *
	 * コードリソースをそのままの値で出力すると、数字や記号になり何が書かれているのか
	 * 不明になります。
	 * これは、コードリソースをラベルに変換して出力するかどうかを指定します。
	 * 当然、コードはユニークですが、ラベルはユニークになるかどうか保障はされていませんので
	 * TableReader 系で読み込む場合には、リスクが発生します。
	 * また、TableReader 系で読み込む場合にも、ラベルからコードを求める逆変換を行うように、
	 * setUseRenderer メソッドで指定する必要があります。
	 *
	 * 従来は、TableWriter 系に、TableWriter_Renderer 系のクラスを作って対応していましたが、
	 * このメソッドの属性値のフラグで、制御します。
	 *
	 * @og.rev 5.2.1.0 (2010/10/01) 新規作成
	 *
	 * @param	useRenderer	コードリソースのラベル変換を行うかどうか [true:行う/false:行わない]
	 */
	public void setUseRenderer( final boolean useRenderer ) {
		this.useRenderer = useRenderer;
	}

	/**
	 * 書き出し処理でコードリソースのラベル変換を行うかどうかを返します。
	 * 初期値は、false(行わない) です。
	 *
	 * @og.rev 5.2.1.0 (2010/10/01) 新規作成
	 *
	 * @return	ラベル変換を行うかどうか(true:行う/false:行わない)
	 */
	protected boolean isUseRenderer() {
		return useRenderer ;
	}

	/**
	 * データを ダブルコーテーション(")で囲みます。
	 *
	 * この処理では、前後をダブルクオートで囲うため、改行の有無は判定する
	 * 必要はありません。
	 * ダブルクオートを含む場合は、その直前にダブルクオートを強制的に追加します。
	 *
	 * @param	 data 元のString文字列
	 *
	 * @return	 ダブルコーテーションで囲まれた文字列
	 */
	protected String quotation( final String data ) {
		return "\"" + StringUtil.replace( data,"\"","\"\"" ) + "\"" ;
	}

	/**
	 * データに対して 改行、ダブルクオート等の処理を行います。
	 *
	 * これは、データの前後をダブルクオートで括る quotation(String)と異なり
	 * 前後には、ダブルクオート を追加しません。
	 * ただし、以下の２つの条件で処理を行います。
	 * １．改行を含む場合は、ダブルクオートを強制的に前後に追加する。
	 * ２．ダブルクオートを含む場合は、その直前にダブルクオートを強制的に追加する。
	 *
	 * @og.rev 5.2.2.0 (2010/11/01) 新規追加
	 *
	 * @param	 data 元のString文字列
	 *
	 * @return	 改行、ダブルクオート等の処理
	 */
	protected String quotation2( final String data ) {
		String rtn = StringUtil.replace( data,"\"","\"\"" );
		if( rtn != null && rtn.indexOf( HybsSystem.CR ) >= 0 ) {
			rtn = "\"" + rtn + "\"" ;
		}

		return rtn ;
	}

	/**
	 * keys の整合性チェックを行います。
	 *
	 * キーかkeysMapのどちらかが null の場合は、何もしません。
	 * チェックするキーは、内部で大文字に変換しておきます。
	 * keysMap に登録するキーは、大文字で登録しておいてください。
	 * ここのチェックでは、キーマップに存在しなければ、エラーになります。
	 *
	 * @og.rev 5.6.6.1 (2013/07/12) keys の整合性チェックを行います。
	 *
	 * @param   key チェックするキー文字列(null の場合は、処理しない)
	 * @param   keysMap 利用可能なキーのマップ(大文字に統一)
	 */
	protected void checkParam( final String key,final Map<String,String> keysMap ) {
		// key か keysMap かどちらかが null の場合は、処理を行わない。
		if( key == null || keysMap == null ) { return; }

		String upKey = key.toUpperCase(Locale.JAPAN);

		if( ! keysMap.containsKey( upKey ) ) {
			String BR = "<br />" + HybsSystem.CR ;
			StringBuilder errMsg = new StringBuilder();
			errMsg.append( BR )
				  .append( "指定のキーは、この tableWriter では、使用できません。" ).append( BR )
				  .append( "  class=[" ).append( getClass().getName() ).append( "]" ).append( BR )
				  .append( "  key  =[" ).append( key				  ).append( "]" ).append( BR )
				  .append( "  ======== usage keys ======== " ).append( BR ) ;
			for( Map.Entry<String, String> entry : keysMap.entrySet() ) {
				errMsg.append( "  " ).append( entry.getKey() ).append( " : " )
									 .append( entry.getValue() ).append( BR ) ;
			}
			errMsg.append( "  ============================ " ).append( BR );

			throw new HybsSystemException( errMsg.toString() );
		}
	}
}
