/*
 * 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.taglib;

import org.opengion.hayabusa.common.HybsSystem;
import org.opengion.hayabusa.common.HybsSystemException;
import org.opengion.fukurou.util.StringUtil;
import org.opengion.fukurou.util.ToString;
import org.opengion.fukurou.util.FileString;
import org.opengion.fukurou.model.POIUtil;
import org.opengion.fukurou.model.JacobUtil;
import org.opengion.fukurou.model.ExcelModel;
// import org.opengion.fukurou.model.TextConverter;					// 6.3.1.0 (2015/06/28)

import static org.opengion.fukurou.util.StringUtil.nval ;
import static org.opengion.fukurou.util.HybsConst.BUFFER_LARGE;		// 6.1.0.0 (2014/12/26) refactoring

import java.util.Map;
import java.util.LinkedHashMap;
import java.util.Arrays;
import java.io.File;

/**
 * ExcelFileTag は、各種パラメータを指定して、Excelファイルの操作をおこなうタグです。
 *
 * 主に、ExcelModel、POIUtil 、JacobUtil の各機能をJSP上で使用できるようにしました。
 * 入力の file1 を加工して、file2 を出力するというのが、基本パターンになります。
 * file1 と、file2 を同じ値に指定した場合は、上書き保存されます。
 *
 * ※ (saveAs,toPrint)属性は、JACOB(Java COM Bridge)の使用が前提です。
 *    これは、JavaからのCOMオートメーションコンポーネント呼び出しを可能とする 
 *    JAVA-COMブリッジです。COMライブラリのネイティブな呼び出しにJNIを使います。
 *    JACOBは、32bitおよび64bitのJVMをサポートするX86およびx64環境で動作します。 
 *    http://sourceforge.jp/projects/sfnet_jacob-project/  (日本語プロジェクト)
 *    http://sourceforge.net/projects/jacob-project/       (本家)
 *
 *    設定：
 *       jacob-1.18-M2.zip をダウンロードし、
 *         ①jacob-1.18-M2-x64.dll または、jacob-1.18-M2-x86.dll を、
 *           Windowsのシステムディレクトリにコピーします。 （例：C:\Windows\System32）
 *         ②jacob.jar を、クラスパスに登録します。
 *           ここでは、名称を、jacob-1.18-M2.jar に変更し、jre\lib\ext にコピーしています。
 *
 * @og.formSample
 * ●形式：&lt;og:excelFile action="…" fileURL="…" &gt;･･･&lt;/og:file&gt;
 * ●body：なし
 *
 * ●Tag定義：
 *   &lt;og:excelFile
 *       fileURL            【TAG】操作するファイルのディレクトリを指定します (初期値:FILE_URL[=filetemp/])
 *       file1            ○【TAG】入力ファイル名を指定します(必須)
 *       file2              【TAG】出力ファイル名を指定します
 *       sheetName          【TAG】EXCELファイルを読み込むときのシート名を設定します(初期値:指定なし)
 *       sheetNos           【TAG】EXCELファイルを読み込むときのシート番号を複数設定できます(初期値:0)
 *       sheetConstKeys     【TAG】EXCELファイルを読み込むときの固定値となるカラム名(CSV形式)
 *       sheetConstAdrs     【TAG】EXCELファイルを読み込むときの固定値となるアドレス(行-列,行-列,・・・)
 *       useActiveWorkbook  【TAG】EXCEL出力時に、セルの有効範囲を設定するかどうかを指定します(初期値:false)
 *       addTitleSheet      【TAG】EXCEL出力時に、存在するSheet名一覧を作成する場合に、そのSheet名を指定します。
 *       addImageFile       【TAG】画像ファイルを挿入します(画像ファイル名 シート番号 行 列)
 *       valueType          【TAG】sheetConstXXX,readXXXX のパラメータに登録する方法を指定します(CSV,LIST,MAP)
 *       readText           【TAG】ファイルを読み込んで、内容を 引数の変数にセットします。
 *       readSheet          【TAG】ファイルを読み込んで、シート一覧を 引数の変数にセットします。
 *       readName           【TAG】ファイルを読み込んで、名前一覧を 引数の変数にセットします。
 *       readStyle          【TAG】ファイルを読み込んで、スタイル名を 引数の変数にセットします。
 *       useConverter       【TAG】file1 のシート、セル、オブジェクトのテキストを変換します。
 *       convFile           【TAG】useConverter=true 時に、変換対応表をファイルから読み取ります。
 *       convMap            【TAG】useConverter=true 時に、変換対応表をMapから読み取ります。
 *   ※  saveAs             【TAG】file1 を指定のファイルの拡張子に合わせた変換を行って保存します(xls,xlsx,pdf)。
 *   ※  toPrint            【TAG】file1 を指定のプリンタに印刷します。
 *       scope              【TAG】キャッシュする場合のスコープ(request,session)を指定します(初期値:request)
 *       caseKey            【TAG】このタグ自体を利用するかどうかの条件キーを指定します(初期値:null) 5.7.7.2 (2014/06/20)
 *       caseVal            【TAG】このタグ自体を利用するかどうかの条件値を指定します(初期値:null) 5.7.7.2 (2014/06/20)
 *       caseNN             【TAG】指定の値が、null/ゼロ文字列 でない場合(Not Null=NN)は、このタグは使用されます(初期値:判定しない) 5.7.7.2 (2014/06/20)
 *       caseNull           【TAG】指定の値が、null/ゼロ文字列 の場合は、このタグは使用されます(初期値:判定しない) 5.7.7.2 (2014/06/20)
 *       caseIf             【TAG】指定の値が、true/TRUE文字列の場合は、このタグは使用されます(初期値:判定しない)
 *       debug              【TAG】デバッグ情報を出力するかどうか[true/false]を指定します(初期値:false)
 *   /&gt;
 *
 * ●使用例
 *    ・EXCEL を読み取って、シート名の一覧の目次を、先頭シートに追加します。
 *        &lt;og:excelFile  file1="input.xls" file2="input.xls" addTitleSheet="Title" /&gt;
 *
 * @og.group その他部品
 * @og.rev 6.2.6.0 (2015/06/19) 新規作成
 *
 * @version  6.2
 * @author	 Kazuhiko Hasegawa
 * @since    JDK8.0,
 */
public class ExcelFileTag extends CommonTagSupport {
	/** このプログラムのVERSION文字列を設定します。	{@value} */
	private static final String VERSION = "6.3.9.0 (2015/11/06)" ;
	private static final long serialVersionUID = 639020151106L ;

	/** sheetConstXXX,readXXXX のパラメータに登録する方法を指定します(CSV/LIST/MAP) */
	private enum TypeEnum { CSV , LIST , MAP ; }

	private TypeEnum valueType			= TypeEnum.CSV ;	// sheetConstXXX,readXXXX のパラメータに登録する方法を指定します(CSV/LIST/MAP)

	private String	fileURL 			= HybsSystem.sys( "FILE_URL" );
	private String	file1				;
	private String	file2				;

	private String	sheetName			;		// EXCELファイルを読み込むときのシート名を設定します(初期値:指定なし)
	private String	sheetNos			;		// EXCELファイルを読み込むときのシート番号を複数設定できます(初期値:0)
	private String	sheetConstKeys		;		// EXCELファイルを読み込むときの固定値となるカラム名(CSV形式)
	private String	sheetConstAdrs		;		// EXCELファイルを読み込むときの固定値となるアドレス(行-列,行-列,・・・)
	private boolean	useActiveWorkbook	;		// EXCEL出力時に、セルの有効範囲を設定するかどうかを指定します(初期値:false)
	private String	addTitleSheet		;		// EXCEL出力時に、存在するSheet名一覧を作成する場合に、そのSheet名を指定します。
	private String	addImageFile		;		// 画像ファイルを挿入します(画像ファイル名 シート番号 行 列)
	private String	readText			;		// ファイルを読み込んで、内容を 引数の変数にセットします。
	private String	readSheet			;		// ファイルを読み込んで、シート一覧を 引数の変数にセットします。
	private String	readName			;		// ファイルを読み込んで、名前一覧を 引数の変数にセットします。
	private String	readStyle			;		// ファイルを読み込んで、スタイル名を 引数の変数にセットします。

	private boolean	useConverter		;		// file1 のシート、セル、オブジェクトのテキストを変換します。
	private String	convFile			;		// useConverter=true 時に、変換対応表をファイルから読み取ります。
	private String	convMap				;		// useConverter=true 時に、変換対応表をMapから読み取ります。

	private String	saveAs				;		// file1 を指定のファイルの拡張子に合わせた変換を行って保存します(xls,xlsx,pdf)。
	private String	toPrint				;		// file1 を指定のプリンタに印刷します。

	private String	scope				= "request";	// "request","session"

	/**
	 * Taglibの終了タグが見つかったときに処理する doEndTag() を オーバーライドします。
	 *
	 * @og.rev 6.2.6.0 (2015/06/19) 新規作成
	 * @og.rev 6.3.1.0 (2015/06/28) readText の取得方法を変更する。
	 *
	 * @return	後続処理の指示
	 */
	@Override
	public int doEndTag() {
		debugPrint();		// 4.0.0 (2005/02/28)
		if( useTag() ) {
			super.setScope( scope );		// デフォルトscope が "request" なので、再設定している。

			final String directory = HybsSystem.url2dir( fileURL );
			final File inFile = new File( StringUtil.urlAppend( directory,file1 ) );

			if( toPrint != null ) {
				JacobUtil.toPrint( inFile,toPrint );
			}
			else if( saveAs != null ) {
				if( file2 != null ) {
					final String errMsg = "file2とsaveAsは、同時に指定する事はできません。" + CR
									+ "  file2=[" + file2 + "] , saveAs=[" + saveAs + "]";
					throw new HybsSystemException( errMsg );
				}

				final File outFile = new File( StringUtil.urlAppend( directory,saveAs ) );
				JacobUtil.saveAs( inFile,outFile );
			}
			else {
				final ExcelModel excel = new ExcelModel( inFile );
				if( addTitleSheet != null ) { excel.setAddTitleSheet( addTitleSheet );	}
				if( addImageFile  != null ) { putImageFile( excel );					}

				// 6.3.1.0 (2015/06/28) readText の取得方法を変更する。
//				if( readText    != null ) { setObject(      readText    , POIUtil.extractor( inFile )	); }
				if( readText    != null ) { setObject(      readText    , getExcelText( excel )			); }
				if( readSheet   != null ) { setAttriObject( readSheet   , excel.getSheetNames()			); }
				if( readName    != null ) { setAttriObject( readName    , excel.getNames()				); }
				if( readStyle   != null ) { setAttriObject( readStyle   , excel.getStyleNames()			); }

				if( sheetConstKeys != null && sheetConstAdrs != null ) { sheetConstSet( excel ); }

				if( useConverter		) { textConverter( excel );			}
				if( useActiveWorkbook	) { excel.activeWorkbook( true );	}	// 引数のtrueは、「空セルを削除する」の指定

				if( file2 != null ) {
					final File outFile = new File( StringUtil.urlAppend( directory,file2 ) );
					excel.saveFile( outFile );
				}
			}
		}
		return EVAL_PAGE ;
	}

	/**
	 * タグリブオブジェクトをリリースします。
	 * キャッシュされて再利用されるので、フィールドの初期設定を行います。
	 *
	 * @og.rev 6.2.6.0 (2015/06/19) 新規作成
	 *
	 */
	@Override
	protected void release2() {
		super.release2();
		valueType			= TypeEnum.CSV;	// sheetConstXXX,readXXXX のパラメータに登録する方法を指定します(CSV/LIST/MAP)
		fileURL 			= HybsSystem.sys( "FILE_URL" );
		file1				= null;
		file2				= null;
		sheetName			= null;			// EXCELファイルを読み込むときのシート名を設定します(初期値:指定なし)
		sheetNos			= null;			// EXCELファイルを読み込むときのシート番号を複数設定できます(初期値:0)
		sheetConstKeys		= null;			// EXCELファイルを読み込むときの固定値となるカラム名(CSV形式)
		sheetConstAdrs		= null;			// EXCELファイルを読み込むときの固定値となるアドレス(行-列,行-列,・・・)
		useActiveWorkbook	= false;		// EXCEL出力時に、セルの有効範囲を設定するかどうかを指定します(初期値:false)
		addTitleSheet		= null;			// EXCEL出力時に、存在するSheet名一覧を作成する場合に、そのSheet名を指定します。
		addImageFile		= null;			// 画像ファイルを挿入します(画像ファイル名 シート番号 行 列)
		readText			= null;			// ファイルを読み込んで、内容を 引数の変数にセットします。
		readSheet			= null;			// ファイルを読み込んで、シート一覧を 引数の変数にセットします。
		readName			= null;			// ファイルを読み込んで、名前一覧を 引数の変数にセットします。
		readStyle			= null;			// ファイルを読み込んで、スタイル名を 引数の変数にセットします。
		useConverter		= false;		// file1 のシート、セル、オブジェクトのテキストを変換します。
		convFile			= null;			// useConverter=true 時に、変換対応表をファイルから読み取ります。
		convMap				= null;			// useConverter=true 時に、変換対応表をMapから読み取ります。
		saveAs				= null;			// file1 を指定のファイルの拡張子に合わせた変換を行って保存します(xls,xlsx,pdf)。
		toPrint				= null;			// file1 を指定のプリンタに印刷します。
		scope				= "request";	// "request","session"
	}

	/**
	 * addImageFile属性の実際の処理を行います。
	 *
	 * @og.rev 6.2.6.0 (2015/06/19) 新規作成
	 *
	 * @param	excel ExcelModelオブジェクト
	 */
	private void putImageFile( final ExcelModel excel ) {
		final String[] org = addImageFile.split( " " );		// 連続するスペースは個別に "" に分割される。

		final String[] vals = Arrays.copyOf( org , 10 );	// １０個の配列を用意します。

		final String imgFile = nval( vals[0] , null );					// 挿入するイメージファイル名
		final int    shtNo	 = nval( vals[1] , 0 );						// シート番号
		final int    row1	 = nval( vals[2] , 0 );						// 挿入する行(開始)
		final int    col1	 = nval( vals[3] , 0 );						// 挿入する列(開始)
		final int    row2	 = nval( vals[4] , row1 );					// 挿入する行(終了-含まず)
		final int    col2	 = nval( vals[5] , col1 );					// 挿入する列(終了-含まず)
		final int    dx1	 = nval( vals[6] , 0 );						// 開始セルのX軸座標(マージン)
		final int    dy1	 = nval( vals[7] , 0 );						// 開始セルのY軸座標(マージン)
		final int    dx2	 = nval( vals[8] , 0 );						// 終了セルのX軸座標(マージン)
		final int    dy2	 = nval( vals[9] , 0 );						// 終了セルのY軸座標(マージン)

		excel.addImageFile( imgFile,shtNo,row1,col1,row2,col2,dx1,dy1,dx2,dy2 );
	}

	/**
	 * テキスト情報を取得します。
	 *
	 * EXCELファイルのテキスト情報として、シート名、セル値、オブジェクト文字列を取得します。
	 * POIUtil.extractor( File ) でもテキストを取得できますが、細かい点で、調整できません。
//	 * ここでは、TextConverter を用いたデータ取得方法で、テキストを取得します。
	 *
	 * @og.rev 6.3.1.0 (2015/06/28) 新規作成
	 * @og.rev 6.3.9.0 (2015/11/06) Java 8 ラムダ式に変更
	 *
	 * @param	excel ExcelModelオブジェクト
	 * @return	テキスト情報
	 */
	private String getExcelText( final ExcelModel excel ) {
		final StringBuilder buf = new StringBuilder( BUFFER_LARGE );

		// 6.3.9.0 (2015/11/06) Java 8 ラムダ式に変更
		excel.textConverter(
			(val,cmnt) -> {
				if( val != null ) { buf.append( val ).append( CR ); }
				return null;
			}
		);

//		// 6.3.9.0 (2015/11/06) 名前付き static 内部クラスにリファクタリングできる(findbugs)。
//		excel.textConverter(
//			new TextConverter<String,String>() {
//				/** 
//				 * 入力文字列を、変換します。
//				 *
//				 * @param	val  入力文字列
//				 * @param	cmnt コメント
//				 * @return	変換文字列(変換されない場合は、null)
//				 */
//				@Override
//				public String change( final String val,final String cmnt ) {
//					if( val != null ) { buf.append( val ).append( CR ); }
//					return null;		// 変換しない。
//				}
//			}
//		);

		return buf.toString();
	}

	/**
	 * sheetConstKeys,sheetConstAdrs属性の実際の処理を行います。
	 *
	 * EXCELファイルを読み込むときの固定値となるカラム名(CSV形式)と
	 * アドレス(行-列,行-列,・・・) から、値を取得します。
	 * 取得した結果は、valueType 属性で指定した方法でセーブします。
	 * アドレスは、１シートに１箇所ですが、シートが複数存在する場合は、
	 * 複数の値が求められます。その値の設定方法は、valueType属性で指定します。
	 *
	 * @og.rev 6.2.6.0 (2015/06/19) 新規作成
	 *
	 * @param	excel ExcelModelオブジェクト
	 */
	private void sheetConstSet( final ExcelModel excel ) {
		final Map<String,String> kvMap = getCSVParameter( sheetConstKeys , sheetConstAdrs );

		final int size = kvMap.size();
		final String[] keys = new String[size];
		final int[] rows = new int[size];
		final int[] cols = new int[size];

		// 行-列 文字列を、分解します。
		int no = 0;
		for( final Map.Entry<String,String> entry : kvMap.entrySet() ) {
			keys[no] = entry.getKey();
			final String val = entry.getValue();
			final int[] rci = POIUtil.kigo2rowCol( val );		// A1,B5 などの形式
			rows[no] = rci[0];
			cols[no] = rci[1];

			no++ ;
		}
		// size と、no は、同じ値のはず。

		// EXCELからデータを抜き出します。
		final int shtSize = excel.getNumberOfSheets();
		final String[][] kvAry = new String[size][shtSize];
		for( int shtNo=0; shtNo<shtSize; shtNo++ ) {
			final String shtNm = excel.getSheetName( shtNo );			// シート名の取得と同時に、内部状態の Sheetオブジェクトを設定する。
			for( int i=0; i<size; i++ ) {
				if( rows[i] < 0 ) {
					kvAry[i][shtNo] = shtNm;					// rowNo が -1 の場合は、シート名を設定
				}
				else {
					kvAry[i][shtNo] = excel.getValue( rows[i],cols[i] );	// 配列の配置がループと会わないが、後でセットするため。
				}
			}
		}

		// キーと、各シートから集めた値を設定します。
		for( int i=0; i<size; i++ ) {
			setAttriObject( keys[i] , kvAry[i] ) ;
		}
	}

	/**
	 * シート、セル、オブジェクトのテキストを変換します。
	 *
	 * convFile : 変換対応表をファイルから読み取ります。
	 * convMap  : 変換対応表をMapから読み取ります。
	 *
	 * このメソッドは、useConverter=true の時のみ、呼ばれます。
	 * また、convFileとconvMapの両方とも、定義されていない場合は、
	 * 何もしません。
	 * 両方とも定義されていた場合は、if文の関係で、convFileが優先されます。
	 *
	 * @og.rev 6.2.6.0 (2015/06/19) 新規作成
	 *
	 * @param	excel ExcelModelオブジェクト
	 */
	@SuppressWarnings(value={"unchecked"})
	private void textConverter( final ExcelModel excel ) {
		Map<String,String> changeMap = null;

		if( convFile != null ) {
			final String directory = HybsSystem.url2dir( fileURL );
			final String inFile = StringUtil.urlAppend( directory,convFile );

			final FileString fs = new FileString();
			fs.setFilename( inFile );
			fs.setEncode( "UTF-8" );
			final String[] lines = fs.getValue( CR );			// \n でなく、CR とします。
			final int len = lines.length;

			changeMap = new LinkedHashMap<>();
			for( int i=0; i<len; i++ ) {
				final String line = lines[i];
				final int indx = line.indexOf( '\t' );
				if( indx <= 0 ) { continue ; }					// TAB が先頭や、存在しない行は読み飛ばす。
				final String key = line.substring( 0,indx );
				final String cng = line.substring( indx+1 );
				changeMap.put( key,cng );
			}
		}
		else if( convMap != null ) {
			changeMap = (Map<String,String>)getObject( convMap );	// 警告: [unchecked] 無検査キャスト
		}

		// convFile も、convMap も定義されていない場合、changeMap は、null のまま。
		if( changeMap != null ) {
			excel.textConverter( changeMap );
		}
	}

	/**
	 * 【TAG】操作するファイルのディレクトリを指定します
	 *		(初期値:FILE_URL[={@og.value SystemData#FILE_URL}])。
	 *
	 * @og.tag
	 * この属性で指定されるディレクトリのファイルを操作します。
	 * 指定方法は、通常の fileURL 属性と同様に、先頭が、'/' (UNIX) または、２文字目が、
	 * ":" (Windows)の場合は、指定のURLそのままのディレクトリに、そうでない場合は、
	 * (初期値:システム定数のFILE_URL[={@og.value SystemData#FILE_URL}])。
	 *
	 * @og.rev 6.2.6.0 (2015/06/19) 新規作成
	 *
	 * @param	url ファイルURL
	 * @see		org.opengion.hayabusa.common.SystemData#FILE_URL
	 */
	public void setFileURL( final String url ) {
		String furl = nval( getRequestParameter( url ),null );
		if( furl != null ) {
			final char ch = furl.charAt( furl.length()-1 );
			if( ch != '/' && ch != '\\' ) { furl = furl + "/"; }
			fileURL = StringUtil.urlAppend( fileURL,furl );
		}
	}

	/**
	 * 【TAG】基準となるファイル名を指定します(コマンドの左辺のファイル名です)。
	 *
	 * @og.tag
	 * コマンドの左辺のファイル名です。
	 *
	 * @og.rev 6.2.6.0 (2015/06/19) 新規作成
	 *
	 * @param	fname ファイル名１
	 */
	public void setFile1( final String fname ) {
		file1 = nval( getRequestParameter( fname ),file1 );
	}

	/**
	 * 【TAG】内部 Workbook オブジェクトをファイルに書き出します。
	 *
	 * @og.tag
	 * この属性を指定しない場合は、ファイルに出力されません。
	 * また、file1 と同じファイルを指定することは可能です。その場合は、
	 * 元のファイルが上書き保存されます。
	 * Excelの形式は、ここで指定する出力ファイルの拡張子ではなく、file1で
	 * 指定したファイルの拡張子で決まります。
	 * 異なる形式の拡張子を持つファイルを指定した場合、強制的に、オープンした 
	 * Workbook の形式の拡張子を追加します。
	 *
	 * 拡張子は、Excel 2007以降の形式(.xlsx)か、Excel 2003以前の形式(.xls) が指定できます。
	 * 拡張子が未設定の場合は、オープンした Workbook の形式に合わせた拡張子を付与します。
	 *
	 * @og.rev 6.2.6.0 (2015/06/19) 新規作成
	 *
	 * @param	fname ファイル名２
	 */
	public void setFile2( final String fname ) {
		file2 = nval( getRequestParameter( fname ),file2 );
	}

	/**
	 * 【TAG】EXCELファイルを読み込むときのシート名を設定します(初期値:指定なし)。
	 *
	 * @og.tag
	 * EXCELファイルを読み込む時に、シート名を指定します。
	 * sheetNos と sheetName が同時に指定された場合は、sheetNos が優先されます。
	 * エラーにはならないのでご注意ください。
	 * 初期値は、指定なしです。
	 *
	 * @og.rev 6.2.6.0 (2015/06/19) 新規作成
	 *
	 * @param   sheet EXCELファイルのシート名
	 * @see		#setSheetNos( String ) 
	 */
	public void setSheetName( final String sheet ) {
		sheetName = nval( getRequestParameter( sheet ),sheetName );
	}

	/**
	 * 【TAG】EXCELファイルを読み込むときのシート番号を指定します(初期値:0)。
	 *
	 * @og.tag
	 * EXCEL読み込み時に複数シートをマージして取り込みます。
	 * シート番号は、0 から始まる数字で表します。
	 * 
	 * シート番号の指定は、CSV形式で、複数指定できます。また、N-M の様にハイフンで繋げることで、
	 * N 番から、M 番のシート範囲を一括指定可能です。また、"*" による、全シート指定が可能です。
	 * これらの組み合わせも可能です。（ 0,1,3,5-8,10-* ）
	 * ただし、"*" に関しては例外的に、一文字だけで、すべてのシートを表すか、N-* を最後に指定するかの
	 * どちらかです。途中には、"*" は、現れません。
	 * シート番号は、重複(1,1,2,2)、逆転(3,2,1) での指定が可能です。これは、その指定順で、読み込まれます。
	 * sheetNos と sheetName が同時に指定された場合は、sheetNos が優先されます。
	 * エラーにはならないのでご注意ください。
	 * 
	 * 初期値は、0（第一シート） です。
	 *
	 * @og.rev 6.2.6.0 (2015/06/19) 新規作成
	 *
	 * @param   sheet EXCELファイルのシート番号（0から始まる）
	 * @see		#setSheetName( String ) 
	 */
	public void setSheetNos( final String sheet ) {
		sheetNos = nval( getRequestParameter( sheet ),sheetNos );
		if( sheetNos != null && sheetNos.length() > 0 ) {
			boolean errFlag = false;
			for( int i=0; i<sheetNos.length(); i++ ) {
				final char ch = sheetNos.charAt(i);
				if( ch == '-' || ch == ',' ) { continue; }
				if( ch == '*' && ( i==0 || i==sheetNos.length()-1 ) ) { continue; }
				if( ch < '0' || ch > '9' ) { errFlag = true; break; }
			}
			if( errFlag ) {
				final String errMsg = "sheetNos の指定を見直してください。sheetNos=[" + sheetNos + "]";
				throw new HybsSystemException( errMsg );
			}
		}
	}

	/**
	 * 【TAG】EXCELファイルを読み込むときのシート単位の固定値を設定するためのキーをCSV形式で指定します。
	 *
	 * @og.tag
	 * カラム名は、CSV形式で指定します。
	 * これにより、シートの一か所に書かれている情報を、固定値として取得することができます。
	 * sheetConstAdrs 属性で指定したセル位置から取得した値を、sheetConstKeys で指定したキーに
	 * 設定します。
	 * 値の設定方法は、valueType 属性で指定します。
	 *
	 * @og.rev 6.2.6.0 (2015/06/19) 新規作成
	 *
	 * @param   constKeys 固定値となるキー(CSV形式)
	 * @see		#setSheetConstAdrs( String ) 
	 */
	public void setSheetConstKeys( final String constKeys ) {
		sheetConstKeys = constKeys;
	}

	/**
	 * 【TAG】EXCELファイルを読み込むときのシート単位の固定値を設定するためのキーに対応するアドレスをCSV形式で指定します。
	 *
	 * @og.tag
	 * アドレスは、EXCEL上の行-列形式か、EXCEL表記に準拠した、A1,A2,B1形式が使用できます。
	 * また、特殊な文字として、"SHEET" という記号が使用できます。
	 *   ①行-列形式
	 *     行列は、EXCELオブジェクトに準拠するため、０から始まる整数です。
	 *     0-0 ⇒ A1 , 1-0 ⇒ A2 , 0-1 ⇒ B1 になります。
	 *   ②EXCEL表記
	 *     EXCEL表記に準拠した、A1,A2,B1 の記述も処理できるように対応します。
	 *     なお、A1,A2,B1 の記述は、必ず、英字1文字＋数字 にしてください。(A～Zまで)
	 *   ③EXCELシート名をキーに割り当てるために、"SHEET" という記号に対応します。
	 *     readSheet 属性で、同等のことが出来まが、統一的に処理できるようにします。
	 *
	 * 例えば、sheetConstKeys="CLM,LANG,NAME" とし、sheetConstAdrs="0-0,A2,SHEET" とすると、
	 * NAMEカラムには、シート名を読み込むことができます。
	 * これは、内部処理の簡素化のためです。
	 *
	 * ちなみに、EXCELのセルに、シート名を表示させる場合の関数は、下記の様になります。
	 * =RIGHT(CELL("filename",$A$1),LEN(CELL("filename",$A$1))-FIND("]",CELL("filename",$A$1)))
	 *
	 * これにより、シートの一か所に書かれている情報を、固定値として取得することができます。
	 * sheetConstAdrs 属性で指定したセル位置から取得した値を、sheetConstKeys で指定したキーに
	 * 設定します。
	 * 値の設定方法は、valueType 属性で指定します。
	 *
	 * @og.rev 6.2.6.0 (2015/06/19) 新規作成
	 *
	 * @param   constAdrs 固定値となるアドレス (行-列,行-列,・・・)
	 * @see		#setSheetConstKeys( String ) 
	 */
	public void setSheetConstAdrs( final String constAdrs ) {
		sheetConstAdrs = constAdrs;
	}

	/**
	 * 【TAG】EXCEL出力時に、セルの有効範囲を設定するかどうかを指定します(初期値:false)。
	 *
	 * @og.tag
	 * セルの有効範囲というのは、EXCELでの 空行、空列の存在しない範囲を指します(初期値:false)。
	 * 通常、空行でも、データとして残っている場合は、EXCELのセルオブジェクトは存在します。
	 * ここで、useActiveWorkbook="true" とすると、空行、空列を削除します。
	 * 初期値は、false:しない です。
	 *
	 * @og.rev 6.2.6.0 (2015/06/19) 新規作成
	 *
	 * @param	useActWB 有効範囲の設定 [true:する/false:しない]
	 */
	public void setUseActiveWorkbook( final String useActWB ) {
		useActiveWorkbook = nval( getRequestParameter( useActWB ),useActiveWorkbook );
	}

	/**
	 * 【TAG】EXCEL出力時に、Sheet一覧を作成する場合のSheet名を指定します。
	 *
	 * @og.tag
	 * これは、Workbook に含まれる Sheet 一覧を作成する場合に、ここに指定した
	 * シート名で、目次を作成します。
	 *
	 * @og.rev 6.2.6.0 (2015/06/19) 新規作成
	 *
	 * @param	sheetName タイトルシート名
	 */
	public void setAddTitleSheet( final String sheetName ) {
		addTitleSheet = nval( getRequestParameter( sheetName ),addTitleSheet );
	}

	/**
	 * 【TAG】指定のシートの行・列の箇所に、イメージファイルを挿入します。
	 *
	 * @og.tag
	 * ここでは、セル範囲ではなく、指定の行列の箇所に、アンカーを設定して、画像ファイルを
	 * 挿入します。一応、リサイズして、元の大きさ近くに戻しますが、縦横比が変わってしまいます。
	 * 正確に挿入する場合は、セル範囲の指定と、マージンを指定しなければなりませんが、
	 * 微調整が必要です。
	 * 引数は、スペース区切りで、下記の順番で指定します。
	 *
	 *    imgFile   挿入するイメージファイル名
	 *    shtNo     シート番号
	 *    row1      挿入する行(開始)
	 *    col1      挿入する列(開始)
	 *   [row2]     挿入する行(終了-含まず)      (未指定時は、row1)
	 *   [col2]     挿入する列(終了-含まず)      (未指定時は、col1)
	 *   [dx1 ]     開始セルのX軸座標(マージン)  (未指定時は、0)
	 *   [dy1 ]     開始セルのY軸座標(マージン)  (未指定時は、0)
	 *   [dx2 ]     終了セルのX軸座標(マージン)  (未指定時は、0)
	 *   [dy2 ]     終了セルのY軸座標(マージン)  (未指定時は、0)
	 *
	 * @og.rev 6.2.6.0 (2015/06/19) 新規作成
	 *
	 * @param	imgFile (画像ファイル名 シート番号 開始行 開始列 [終了行 終了列 開始セルのX軸ﾏｰｼﾞﾝ 開始セルのY軸ﾏｰｼﾞﾝ 終了セルのX軸ﾏｰｼﾞﾝ 終了セルのY軸ﾏｰｼﾞﾝ])
	 */
	public void setAddImageFile( final String imgFile ) {
		addImageFile = nval( getRequestParameter( imgFile ),addImageFile );
	}

	/**
	 * 【TAG】sheetConstXXX,readXXXX のパラメータに登録する方法を指定します(CSV/LIST/MAP)。
	 *
	 * @og.tag
	 * キーに対して、値は複数存在する場合があります。
	 * その値を、設定する場合の、３つの方法を指定できます。
	 *
	 *   CSV : キーに対して、値をCSV形式でセットします。valueタグや、{&#064;XXX} で、取り出せます。
	 *         キーが複数ある場合は、個別に指定する必要があります。
	 *   LIST: キーに対して、値をListオブジェクトにセーブします。
	 *         キーが複数ある場合は、個別に指定する必要があります。
	 *   MAP : キー自体を、Mapオブジェクトに設定します。値は、CSV形式の文字列です。
	 *
	 * 初期値は、CSV です。
	 *
	 * @og.rev 6.2.6.0 (2015/06/19) 新規作成
	 *
	 * @param	type 保管方法(CSV/LIST/MAP)
	 */
	public void setValueType( final String type ) {
		final String tp = nval( getRequestParameter( type ),null );
		if( tp != null ) {
			valueType = TypeEnum.valueOf( tp );
		}
	}

	/**
	 * 【TAG】ファイルを読み込んで、内容を引数の変数にセットします。
	 *
	 * @og.tag
	 * EXCELのセルと、テキストボックスオブジェクトの値を取得します。
	 * 引数に、キーとなるパラメータを指定します。
	 * テキストは、一つのキーに設定されます。
	 * valueType 属性の指定とは無関係にパラメータに登録されます。
	 *
	 * @og.rev 6.2.6.0 (2015/06/19) 新規作成
	 *
	 * @param	read 書き込む変数名
	 */
	public void setReadText( final String read ) {
		readText = nval( getRequestParameter( read ),readText );
	}

	/**
	 * 【TAG】ファイルを読み込んで、シート一覧を引数の変数にセットします。
	 *
	 * @og.tag
	 * EXCELのシート一覧を取得します。
	 * 引数に、キーとなるパラメータを指定します。
	 * 値の設定方法は、valueType 属性で指定します。
	 *
	 * @og.rev 6.2.6.0 (2015/06/19) 新規作成
	 *
	 * @param	read 書き込む変数名
	 */
	public void setReadSheet( final String read ) {
		readSheet = nval( getRequestParameter( read ),readSheet );
	}

	/**
	 * 【TAG】ファイルを読み込んで、名前一覧を 引数の変数にセットします。
	 *
	 * @og.tag
	 * EXCELの名前一覧を取得します。
	 * 名前一覧に、不正な値や、他のファイルのリンク等が設定されていると、
	 * EXCELを開くのに時間がかかる場合があります。EXCEL帳票などでは、問題になります。
	 * そこで、この名前が不正かどうか判別するのに、名前一覧を使用します。
	 * 引数に、キーとなるパラメータを指定します。
	 * 値の設定方法は、valueType 属性で指定します。
	 *
	 * @og.rev 6.2.6.0 (2015/06/19) 新規作成
	 *
	 * @param	read 書き込む変数名
	 */
	public void setReadName( final String read ) {
		readName = nval( getRequestParameter( read ),readName );
	}

	/**
	 * 【TAG】ファイルを読み込んで、スタイル名を 引数の変数にセットします。
	 *
	 * @og.tag
	 * EXCELのスタイル名を取得します。
	 * スタイル名に、不正な値が設定されていると、EXCELを開くのに
	 * 時間がかかる場合があります。EXCEL帳票などでは、問題になります。
	 * そこで、このスタイル名が不正かどうか判別するのに、使用します。
	 * 引数に、キーとなるパラメータを指定します。
	 * 値の設定方法は、valueType 属性で指定します。
	 *
	 * @og.rev 6.2.6.0 (2015/06/19) 新規作成
	 *
	 * @param	read 書き込む変数名
	 */
	public void setReadStyle( final String read ) {
		readStyle = nval( getRequestParameter( read ),readStyle );
	}

	/**
	 * 【TAG】シート名、セル、オブジェクトのテキストを変換するかどうか指定します(初期値:false)。
	 *
	 * @og.tag
	 * この属性では、テキスト変換を行うかどうかを指定します。例え、convFile属性や
	 * convMap属性が指定されていても、この属性を true にしないと、変換は行われません。
	 * また、convFile属性と、convMap属性がどちらも指定されていない場合も、実行されません。
	 * 初期値は、false:行わない です。
	 *
	 * @og.rev 6.2.6.0 (2015/06/19) 新規作成
	 *
	 * @param	useConv コンバータ処理を行うかどうか [true:行う/false:行わない]
	 */
	public void setUseConverter( final String useConv ) {
		useConverter = nval( getRequestParameter( useConv ),useConverter );
	}

	/**
	 * 【TAG】useConverter=true 時に、変換対応表をファイルから読み取ります。
	 *
	 * @og.tag
	 * この属性は、useConverter=true を指定しないと、無視されます。
	 * テキスト変換を行う変換対応表を、ファイルで指定します。
	 * このテキストファイルは、変換元と変換先の文字列をタブ区切りで定義された、UTF-8ファイルです。
	 *
	 * @og.rev 6.2.6.0 (2015/06/19) 新規作成
	 *
	 * @param	cfile コンバータ処理の変換対応表ファイル
	 */
	public void setConvFile( final String cfile ) {
		convFile = nval( getRequestParameter( cfile ),convFile );
	}

	/**
	 * 【TAG】useConverter=true 時に、変換対応表をMapから読み取ります。
	 *
	 * @og.tag
	 * この属性は、useConverter=true を指定しないと、無視されます。
	 * テキスト変換を行う変換対応表を、Mapオブジェクトで指定します。
	 * メモリ上のMapオブジェクトの取得キーを、指定します。
	 * スコープは、scope 属性を使います。
	 *
	 * @og.rev 6.2.6.0 (2015/06/19) 新規作成
	 *
	 * @param	cmap コンバータ処理の変換対応表Mapの取得キー
	 */
	public void setConvMap( final String cmap ) {
		convMap = nval( getRequestParameter( cmap ),convMap );
	}

	/**
	 * 【TAG】file1 を指定のファイルの拡張子に合わせた変換を行って保存します(xls,xlsx,pdf)。
	 *
	 * @og.tag
	 * この属性は、JACOB(Java COM Bridge)の使用が前提です。
	 * file2と同時に指定する事はできません。
	 * file2 属性と異なり、拡張子が異なるケースで、使用します。
	 * また、xslx拡張子の場合でも、EXCELのバージョンや、設定によっては、POIでは
	 * 読み取ることが出来ない場合があり、JACOBで変換することで、読み取ることが
	 * できる形式に変換できます。
	 * また、拡張子に、pdf を指定すると、PDFファイルに変換できます。
	 * ※ この属性は単独でしか使用できません。(fileURL,file1 以外)
	 *
	 * @og.rev 6.2.6.0 (2015/06/19) 新規作成
	 *
	 * @param	name セーブするファイル名
	 */
	public void setSaveAs( final String name ) {
		saveAs = nval( getRequestParameter( name ),saveAs );
	}

	/**
	 * 【TAG】file1 を指定のプリンタに印刷します。
	 *
	 * @og.tag
	 * この属性は、JACOB(Java COM Bridge)の使用が前提です。
	 * サーバーから使用できるプリンタ名を指定してEXCELを印刷します。
	 * ※ この属性は単独でしか使用できません。(fileURL,file1 以外)
	 *
	 * @og.rev 6.2.6.0 (2015/06/19) 新規作成
	 *
	 * @param	printer プリンタ名
	 */
	public void setToPrint( final String printer ) {
		toPrint = nval( getRequestParameter( printer ),toPrint );
	}

	/**
	 * 【TAG】キャッシュする場合のスコープ(request,session)を指定します(初期値:request)。
	 *
	 * @og.tag
	 * "request","session" が指定できます。
	 * 初期値は、 "request" です。
	 *
	 * @param	scp	スコープ
	 */
	@Override
	public void setScope( final String scp ) {
		scope = nval( getRequestParameter( scp ),scope );
	}

	/**
	 * 指定のキーに、パラメータを登録します。
	 *
	 * キーに対して、値は複数存在する場合があります。
	 * その値を、設定する方法は、valueType属性 で指定します。
	 * 登録する場合の スコープも、scope属性 で指定します。
	 *
	 * @og.rev 6.2.6.0 (2015/06/19) 新規作成
	 * @og.rev 6.3.9.0 (2015/11/06) switch 文の2つの case のために同じコードを使用している(findbugs)
	 *
	 * @param	key  登録するキー
	 * @param	data 登録するデータ配列
	 */
	private void setAttriObject( final String key,final String[] data ) {
		// 6.3.9.0 (2015/11/06) switch 文の2つの case のために同じコードを使用している(findbugs)
		if( valueType == TypeEnum.LIST ) {
			setObject( key , Arrays.asList ( data ) );
		}
		else {
			setObject( key , StringUtil.array2csv( data ) );
		}

//		switch( valueType ) {
//			case CSV  : setObject( key , StringUtil.array2csv( data ) );	break;
//			case LIST : setObject( key , Arrays.asList ( data ) );			break;
//			default   :	// TypeEnum に、追加時に変更忘れがない限り、基本的にはありえない。
//						setObject( key , StringUtil.array2csv( data ) );	break;
//		}
	}

	/**
	 * このオブジェクトの文字列表現を返します。
	 * 基本的にデバッグ目的に使用します。
	 *
	 * @return このクラスの文字列表現
	 * @og.rtnNotNull
	 */
	@Override
	public String toString() {
		return ToString.title( this.getClass().getName() )
				.println( "VERSION"				,VERSION			)
				.println( "fileURL"				,fileURL			)
				.println( "file1"				,file1				)
				.println( "file2"				,file2				)
				.println( "sheetName"			,sheetName			)
				.println( "sheetNos"			,sheetNos			)
				.println( "sheetConstKeys"		,sheetConstKeys		)
				.println( "sheetConstAdrs"		,sheetConstAdrs		)
				.println( "useActiveWorkbook"	,useActiveWorkbook	)
				.println( "addTitleSheet"		,addTitleSheet		)
				.println( "addImageFile"		,addImageFile		)
				.println( "valueType"			,valueType			)
				.println( "readText"			,readText			)
				.println( "readSheet"			,readSheet			)
				.println( "readName"			,readName			)
				.println( "readStyle"			,readStyle			)
				.println( "useConverter"		,useConverter		)
				.println( "convFile"			,convFile			)
				.println( "convMap"				,convMap			)
				.println( "saveAs"				,saveAs				)
				.println( "toPrint"				,toPrint			)
				.println( "Other..."	,getAttributes().getAttribute() )
				.fixForm().toString() ;
	}
}
