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

import org.opengion.fukurou.system.OgRuntimeException ;				// 6.4.2.0 (2016/01/29)
import org.opengion.fukurou.system.LogWriter;
import org.opengion.fukurou.system.DateSet;							// 6.4.2.0 (2016/01/29)
import org.opengion.fukurou.system.MsgUtil;							// 6.4.3.2 (2016/02/19)
import org.opengion.fukurou.util.StringUtil;
import org.opengion.fukurou.util.HybsEntry;
import static org.opengion.fukurou.system.HybsConst.CR;				// 6.1.0.0 (2014/12/26) refactoring
import static org.opengion.fukurou.system.HybsConst.FS;				// 6.1.0.0 (2014/12/26) refactoring
import static org.opengion.fukurou.system.HybsConst.BUFFER_MIDDLE;	// 6.2.6.0 (2015/06/19)

import java.util.Set;
import java.util.TreeSet;											// 6.4.3.3 (2016/03/04)

import java.util.Map;
import java.util.concurrent.ConcurrentMap;							// 6.4.3.3 (2016/03/04)
import java.util.concurrent.ConcurrentHashMap;						// 6.4.3.1 (2016/02/12) refactoring
import java.util.concurrent.ConcurrentSkipListMap;					// 6.4.3.3 (2016/03/04)
import java.util.Locale;											// 6.4.3.2 (2016/02/19)
import java.io.File;												// 6.2.4.1 (2015/05/22) REAL_PATH 対応
import java.net.InetAddress;
import java.net.UnknownHostException;

import javax.print.PrintServiceLookup;								// 6.2.6.0 (2015/06/19)
import javax.print.PrintService;									// 6.2.6.0 (2015/06/19)

/**
 * 共通的に、使用されるシステム関連メソッドを集約したクラスです。
 *
 * 全変数は、public static final 宣言されています。
 * メソッドは、日付取得，エラーメッセージ処理 を行っています。
 *
 * @og.group 初期化
 *
 * @version  4.0
 * @author	 Kazuhiko Hasegawa
 * @since    JDK5.0,
 */
public final class HybsSystem {

	/** システム設定ＤＢのキーと値を、Map に読み込みます。  */
	// 4.0.0 (2005/01/31) システムパラメータの管理オブジェクト
	/** 6.4.3.1 (2016/02/12) ConcurrentHashMap で同期処理を行います。  */
	private static final ConcurrentMap<String,String> SYS_MAP = new ConcurrentHashMap<>();					// 6.4.3.3 (2016/03/04)

	// 4.3.1.0 (2008/09/03) リクエストでのみ生成できる設定値のキャッシュ
	/** 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。  */
	private static final ConcurrentMap<String, String> REQ_CACHE = new ConcurrentHashMap<>();		// 6.4.9.1 (2016/08/05) reqCache → REQ_CACHE 変更

	/** URL の /(ルート)に対応する物理ディレクトリ  */
	private static String  realPath	;			// 4.0.0 (2005/01/31)

	/** リクエスト関係パラメータの初期設定フラグ */
	private static boolean paramSetFlag	;		// 4.0.0 (2005/11/30)

	/**
	 * テーブル表示時のチェックボックスのname属性  {@value}
	 * テーブル表示時にチェックボックスを表示しておき、どの行が選ばれたかを
	 * 指定する場合に、その name 属性に指定するキーの値です。
	 * 受け取る側は、このキーで、request 情報より選ばれた行番号を知ることができます。
	 */
	public static final String ROW_SEL_KEY = "h_rowSel";

	/**
	 * テーブル表示のチェックボックスを特定する id の 名称( id は、この名称＋行番号)   {@value}
	 */
	public static final String ROW_ID_KEY = "cb";	// 3.6.0.0 (2004/09/17)

	/** session オブジェクトに関連付ける DBTableModel インスタンスのデフォルトキー {@value} */
	public static final String TBL_MDL_KEY = "h_tblmdl" ;

	// 3.8.1.1 (2005/11/21) ExcelOut の整合性を取る為の仕掛け
	/** session オブジェクトに関連付ける ExcelOut の整合性チェック用のキー */
	public static final String TBL_MDL_CONKEY = "h_tblmdlCon" ;

	/** session オブジェクトに関連付ける Viewform インスタンスのデフォルトキー {@value} */
	public static final String VIEWFORM_KEY = "h_vform" ;

	/** session オブジェクトに関連付ける ViewLink インスタンスのデフォルトキー {@value} */
	public static final String VIEWLINK_KEY = "h_vlink" ;

	/** session オブジェクトに関連付ける ViewMark インスタンスのデフォルトキー {@value} */
	public static final String VIEW_MARK_KEY = "h_vmark" ;

	// 3.8.6.1 (2006/10/20) 編集時にカラムを置き換える 機能に対応
	/** session オブジェクトに関連付ける ViewMark:editor インスタンスのデフォルトキー */
	public static final String EDIT_MARK_KEY = "h_emark" ;

	/** session オブジェクトに関連付ける UserInfo インスタンスのキー {@value} */
	public static final String USERINFO_KEY = "h_uInfo" ;

	/** session オブジェクトに関連付ける GUIInfo インスタンスのキー {@value} */
	public static final String GUIINFO_KEY = "h_gInfo" ;

	/** session オブジェクトに関連付ける リクエスト情報キャッシュのキー {@value} */
	public static final String REQ_CACHE_KEY = "h_req" ;

	/** session オブジェクトに関連付ける エラーメッセージ情報キャッシュのキー {@value} */
	public static final String ERR_MSG_KEY = "h_errMsg" ;

	/** 6.4.4.1 (2016/03/18) application オブジェクトに関連付ける 共通メッセージ情報のキー {@value} */
	public static final String COMMON_MSG_KEY = "h_cmnMsg" ;

	// 2.3.1.0 (2002/12/20) 管理者が全てのユーザーにメッセージを同報できるタグを追加
	/** session 管理者がステータスバーに設定するオブジェクトのキー {@value} */
	public static final String ST_BAR_OBJ_KEY = "h_stBar" ;

	// 3.0.0.0 (2002/12/25) SceduleTag 追加の伴う、Timerオブジェクトの管理用キー追加
	/** Timerオブジェクトの管理用キー {@value} */
	public static final String SCEDULE_KEY = "h_timer" ;

	// 3.0.0.0 (2002/12/25) SceduleTag 追加の伴う、Timerオブジェクトの管理用キー追加
	/** TimerTaskオブジェクトの管理用キー {@value} */
	public static final String SCEDULE_TASK_KEY = "h_timer_task" ;

	// 3.1.2.0 (2003/04/07) ソースコード中の固定値を、定義ファイルを使用するように変更する。
	/** Forward時の転送先ページ名をセッションに登録するときのキー {@value} */
	public static final String FORWARD_PAGE_KEY = "h_fpg" ;

	// 3.1.2.0 (2003/04/07) ソースコード中の固定値を、定義ファイルを使用するように変更する。
	/** Forward時の転送元ページ名をセッションに登録するときのキー {@value} */
	public static final String REVIEW_PAGE_KEY = "h_rpg" ;

	// 3.1.8.0 (2003/05/16) BACK_GAMENID のキャッシュ方法を、通常のリクエスト情報と分離して、管理する。
	/** BACK_GAMENID情報をセッションに登録するときのキー {@value} */
	public static final String BACK_GAMENID_KEY = "h_bgid" ;

	// 4.0.1.0 (2007/12/17) BACK_ADDRESS のキャッシュ方法を、通常のリクエスト情報と分離して、管理する。
	/** BACK_GAMENID情報をセッションに登録するときのキー {@value} */
	public static final String BACK_ADDRESS_KEY = "h_bgadrs" ;

	// 3.7.0.3 (2005/03/01) 戻るリンクで戻る行番号を指定します。(BACK_GAMENIDとセット)
	/** BACK_ROW情報をセッションに登録するときのキー {@value} */
	public static final String BACK_ROW_KEY = "h_brow" ;

	// 3.1.9.0 (2003/05/16) ShellTag で、Shell オブジェクトをキープする為のキーを作成。
	/** Shell オブジェクトをセッションに登録するときのキー {@value} */
	public static final String SHELL_KEEP_KEY = "h_shKeepKey" ;

	// 3.5.0.0 (2003/09/17) ユーザー変更時の、旧ユーザーIDのをキープする為のキーを作成。
	/** oldUserId 文字列をセッションに登録するときのキー {@value} */
	public static final String OLD_USERID_KEY = "h_oldID" ;

	// 3.5.4.7 (2004/02/06) DBTableModelのソート時のカラム名の受け渡しキー。
	/** sortColumns 文字列をリクエスト情報より取得するときのキー {@value} */
	public static final String SORT_COLUMNS = "h_sortClmns" ;

	// 3.5.4.7 (2004/02/06) DBTableModelのソート時のソート方向の受け渡しキー。
	/** sortAscending をリクエスト情報より取得するときのキー {@value} */
	public static final String SORT_ASCENDING = "h_sortAsc" ;

	// 3.5.5.0 (2004/03/12) キー＋行番号の連結文字列(例：__)を設定します。
	/** カラムID(連結文字列)行番号の連結文字列を定義 {@value} */
	public static final String JOINT_STRING = "__" ;

	// 3.5.5.5 (2004/04/23) DBTableModel の整合性パラメータを hidden で出力します。
	/** DBTableModel の整合性パラメータをリクエスト情報より取得するときのキー {@value} */
	public static final String CONSISTENCY_KEY = "h_consisKey" ;

	// 3.5.5.5 (2004/04/23) 転送不要パラメータの先頭に付けるキーワードです。
	/** SubmitTag で出力されるボタンパラメータは、次ページへ転送しません。 {@value} */
	public static final String NO_XFER_KEY = "hX_" ;

	// 3.8.0.0 (2005/06/07) 同一セッションでのマルチ起動対策用キーワードです。
	/** 同一セッションでのマルチ起動された場合に、前に開かれた画面からのアクセスを禁止します。 */
	public static final String MULTI_SESSION_CHECK = "MSC" ;

	/** 3.8.9.1 (2005/07/29) JFreeChart オブジェクトのキャッシュ {@value}  */
	public static final String JF_CHART_CACHE_KEY = "h_chartKey" ;

	// 3.8.0.8 (2005/10/03) GET時のURL に使用可能な文字数制限
	/** URL に使用可能な文字数は最大 2,083 文字
	 * http://support.microsoft.com/default.aspx?scid=kb;ja;208427
	 */
	public static final int MAX_GET_LENGTH = 2083 ;

	/** 4.0.0 (2005/01/31) GUIのロールを区切る、区切り文字		{@value}  */
	public static final char GUI_DELIMITER = '|' ;

	/** 4.0.0 (2005/01/31) ローカルリソースのオブジェクト保存時のキー	{@value}  */
	public static final String LOCAL_RES_KEY = "h_lclRes" ;

	/** 4.0.0 (2005/02/28) Tomcat起動時エラーが発生した場合のエラーキー	{@value}  */
	public static final String LOCAL_CONTX_ERR_KEY = "h_lclConErr" ;

	/** 4.0.0 (2005/08/31) グループメニューのキャッシュキー	{@value} */
	public static final String GUI_GR_MENU_CACHE_KEY = "h_grpCache" ;

	/** 4.0.0 (2005/09/30) 個人別お気に入りメニューの GE16 登録キーワード	{@value} */
	public static final String GUI_FAV_MENU_KEY = "GUI_FAV_MENU_KEY" ;

	/** 4.0.0 (2005/06/04) DBTableWriter の columns 属性のセッション登録キーワード	{@value} */
	public static final String TBL_WRITER_CLMS = "TBL_WRITER_CLMS" ;

	/** 4.1.2.1 (2008/03/13) must 属性をrequest変数にセットする場合の キー {@value} */
	public static final String MUST_KEY = "h_must_" ;

	/** 6.2.0.0 (2015/02/27) aimai 属性をrequest変数にセットする場合の キー {@value} */
	public static final String AIMAI_KEY = "h_aimai_" ;

	/** 4.3.0.0 (2008/07/04) 最後に Queryを実行した情報の保存時のキー {@value} */
	public static final String DB_LAST_SQL_KEY = "h_lastSql" ;

	/** 4.3.3.0 (2008/10/01) ページ内で画面遷移なしモードを指定しようする場合のキー	{@value}  */
	public static final String NO_TRANSITION_MODE_KEY = "h_noTrnsKey" ;

	/** 4.3.6.0 (2009/04/01) イベントカラムを利用したかどうかのチェックキー	{@value}  */
	public static final String USE_EVENT_COLUMN_KEY = "h_eventColumn" ;

	/** 5.1.7.0 (2010/06/01) イベントカラムをラップするspanタグのクラス	{@value}  */
	public static final String EVENT_COLUMN_CLASS = "h_evElm";

	/** 5.1.7.0 (2010/06/01) イベントカラムをラップするspanタグで、子カラムの書き込み可/不可を設定する属性名	{@value}  */
	public static final String EVENT_COLUMN_WRITABLE = "h_evWrtbl";

	/** 5.1.7.0 (2010/06/01) イベントカラムをラップするspanタグで、子カラムのIDを設定する属性名	{@value}  */
	public static final String EVENT_COLUMN_ID = "h_evCid";

	/** 5.1.7.0 (2010/06/01) イベントカラムをラップするspanタグで、子カラムの初期値を設定する属性名	{@value}  */
	public static final String EVENT_COLUMN_INITVAL = "h_evVal";

	/** 4.3.7.0 (2009/06/01) linkタグのurlCheckキーを出力する際のパラメーターキー	{@value}  */
	public static final String URL_CHECK_KEY = "h_urlCheck" ;

	/** 4.3.7.5 (2009/07/08) ページ内でajaxSubmitを利用する場合のキー	{@value}  */
	public static final String USE_AJAX_SUBMIT_KEY = "h_ajaxSubmit" ;

	/** 5.1.6.0 (2010/05/01) Viewでソート済みかどうかを判定する際のキー	{@value} */
	public static final String VIEW_SORT_KEY = "h_viewSrt";

	/** 5.1.9.0 (2010/08/01) トランザクションを実現する際のキー	{@value} */
	public static final String TRANSACTION_KEY = "h_tran";

	/** 5.2.2.0 (2010/11/01) URLの引数部をハッシュする際のリクエストキー	{@value} */
	public static final String URL_HASH_REQ_KEY = "h_r";

	/** 5.3.2.0 (2011/02/01) リクエストパラメーターのキー一覧を保存する際のキー	{@value} */
	public static final String PARAM_NAMES_KEY = "h_prmNms";

	/** 5.3.6.0 (2011/06/01) クエリを呼び出し時のフォーカスを制御するためのキー	{@value} */
	public static final String QUERY_FOCUS_KEY = "h_qryFcs";

	/** 5.5.9.1 (2012/12/07) セッション作成時に、セッションIDを保存しておく、規定のキー	{@value} */
	public static final String SESSION_KEY = "h_session_key";

	/** 5.7.6.2 (2014/05/16) セッション作成時に、セッションIDを保存しておく、規定のキー	{@value} */
	public static final String IE_HTML5_KEY = "h_html5_key";

	/** 6.4.5.2 (2016/05/06) UPLOADから呼ばれたかどうかを判定するための、規定のキー	{@value} */
	public static final String USE_UPLOAD_KEY = "h_UPLOAD";

	/** 6.8.4.2 (2017/12/25) エンコード変換対応対応のキー(fileDownloadサーブレットでエンコードをON/OFF指定に利用)	{@value} */
	public static final String USE_STR_CONV_KEY = "useStringConvert";

	/**
	 * 帳票エンコーディング	{@value}
	 * 設定されていない場合は、UTF-8 に設定されます。
	 */
	public static final String REPORT_ENCODE = "UTF-8" ;

	/** 実行しているサーバーの名称 */
	public static final String HOST_NAME ;
	/** 実行しているサーバーのIPアドレス */
	public static final String HOST_ADRS ;

	static {
		String dmnHost ;
		String dnmAdrs ;
		try {
			final InetAddress address = InetAddress.getLocalHost();
			dmnHost = address.getHostName() ;
			dnmAdrs = address.getHostAddress() ;
		}
		catch( final UnknownHostException ex ) {
			dmnHost = "Unknown";
			dnmAdrs = "Unknown";
		}
		HOST_NAME = dmnHost;
		HOST_ADRS = dnmAdrs;
	}

	/**
	 * デフォルトコンストラクターをprivateにして、
	 * オブジェクトの生成をさせないようにする。
	 *
	 */
	private HybsSystem() {}

	/**
	 * 内部システムリソースマップオブジェクトを設定します。
	 *
	 * @og.rev 3.5.3.1 (2003/10/31) 新規作成
	 * @og.rev 4.0.0.0 (2005/01/31) Ver4 のシステムパラメータ情報の取得処理を追加します。
	 * @og.rev 4.3.1.0 (2008/09/03) SERVER_NAME,SERVER_URL,CONTEXT_URLをキャッシュから復元
	 * @og.rev 4.3.1.0 (2008/09/03) SERVER_NAME,SERVER_URL,CONTEXT_URLをキャッシュから復元
	 * @og.rev 6.2.4.1 (2015/05/22) REAL_PATH 対応。realPath は、setInitialData ではなく、setRealPathメソッドで設定する。
	 * @og.rev 6.4.3.1 (2016/02/12) ConcurrentHashMap で同期処理を行います。
	 * @og.rev 6.4.3.3 (2016/03/04) SYS_MAP を、ConcurrentHashMap で同期するため、not null制限チェックを追加します。
	 * @og.rev 6.4.9.1 (2016/08/05) reqCache → REQ_CACHE 変更
	 *
	 * @param	param	システムリソースマップ
	 */
	public static void setInitialData( final Map<String,String> param ) {
		// key,val の not null 制限が解除できないので、とりあえず、ConcurrentHashMap で対応。
		SYS_MAP.clear();					// 初期化した上で、全部コピーします。
		if( param != null ) {
			// 6.4.3.3 (2016/03/04) SYS_MAP を、ConcurrentHashMap で同期するため、not null制限チェックを追加します。
			param.forEach( (k,v) -> { if( k != null && v != null ) { SYS_MAP.put( k,v ); } } );
		}
		// 4.3.1.0 (2008/09/03) SERVER_NAME,SERVER_URL,CONTEXT_URLをキャッシュから復元
		if( paramSetFlag ) {
			SYS_MAP.putAll( REQ_CACHE );	// 6.4.9.1 (2016/08/05)
		}

		System.out.println( "    [" + SYS_MAP.get( "CONTEXT_NAME" ) + "] のシステムパラメータが、[" + SYS_MAP.size() + "] 件設定されました。" );
	}

	/**
	 * 内部システムパラメータ に、realPath を設定します。
	 *
	 * その際、Tomcatのバージョン(過去の一部バージョン)で、最後のファイルセパレータが
	 * 付かないケースがありましたので、付ける方向で、統一します。
	 * このメソッドは、SystemParameter#loadInitialParameter( ServletContext , String )からのみ
	 * 呼ばれることを想定しています。
	 *
	 * @og.rev 6.2.4.1 (2015/05/22) REAL_PATH 対応。realPath は、setInitialData ではなく、setRealPathメソッドで設定する。
	 *
	 * @param	path	context.getRealPath( "" ) を引数で渡してきます。
	 */
	/* default */ static void setRealPath( final String path ) {
		if( path != null && path.endsWith( File.separator ) ) {
			realPath = path ;										// 後ろにセパレータが付いていれば、そのまま。
		}
		else {
			realPath = path + File.separator ;						// 付いていなければ、付ける。
		}
	}

	/**
	 * 内部システムパラメータ に、realPath を取得します。
	 *
	 * その際、Tomcatのバージョン(過去の一部バージョン)で、最後のファイルセパレータが
	 * 付かないケースがありましたので、付ける方向で、統一します。
	 *
	 * @og.rev 6.2.4.1 (2015/05/22) REAL_PATH 対応。realPath は、setInitialData ではなく、setRealPathメソッドで設定する。
	 *
	 * @return	REAL_PATH コンテキストのトップパス
	 */
	public static String getRealPath() {
		return realPath ;
	}

	/**
	 * システムパラメータ情報を取得します。
	 * Ver3 では、プロパティーファイルでリソースを提供している為、
	 * 立ち上げタイミングとの関係で始めて使用する場合にデータを取得します。
	 *
	 * @og.rev 4.0.0.0 (2005/01/31) 新規作成
	 * @og.rev 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。
	 *
	 * @param	key	システムパラメータのキー(not null)
	 *
	 * @return	システムパラメータ の値
	 */
	private static String getParam( final String key ) {
			return SYS_MAP.get( key );
	}

	/**
	 * システムパラメータの追加情報セットが終了しているかどうかを取得します。
	 * 設定処理を行うかどうかの判定に使用します。
	 *
	 * @og.rev 4.0.0.0 (2005/11/30) 新規作成
	 *
	 * @return	true:設定完了 / false:未完了(設定可能)
	 * @see #setRequestParam( String,String,int,String )
	 */
	public static boolean isParamSet() {
		return paramSetFlag;
	}

	/**
	 * システムパラメータ情報を追加設定します。
	 * これは、SERVER_NAME、SERVER_URL、CONTEXT_URL などのリクエストオブジェクトから
	 * しか取得できない共有情報を、登録する場合に使用します。
	 * これらの値は、taglib/JspInitTag タグでセットされます。
	 * セットする前に、isParamSet() で設定済みかどうか判断してください。
	 * このメソッドは、isParamSet() の値に関係なく、セットします。
	 *
	 * @og.rev 4.0.0.0 (2005/01/31) 新規作成
	 * @og.rev 4.3.1.0 (2008/09/03) SERVER_NAME,SERVER_URL,CONTEXT_URLのキャッシュ処理追加
	 * @og.rev 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。
	 * @og.rev 6.4.9.1 (2016/08/05) reqCache → REQ_CACHE 変更
	 *
	 * @param	serverName	サーバー名
	 * @param	scheme	スキーマ
	 * @param	port	ポート番号
	 * @param	path	コンテキストパス
	 * @see #isParamSet()
	 */
	public static void setRequestParam( final String serverName,final String scheme,
										final int port,final String path ) {

			final String portStr = ( port == 80 ) ? "" : ":" + port ;
			final String serverURL = scheme + "://" + serverName + portStr ;
			final String contextURL = serverURL + path + "/" ;

			SYS_MAP.put( "SERVER_NAME"	, serverName );
			SYS_MAP.put( "SERVER_URL"	, serverURL + "/" );
			SYS_MAP.put( "CONTEXT_URL"	, contextURL );

			// 4.3.1.0 (2008/09/03) SERVER_NAME,SERVER_URL,CONTEXT_URLをキャッシュして置く
			// 6.4.9.1 (2016/08/05) reqCache → REQ_CACHE 変更
			REQ_CACHE.put( "SERVER_NAME"	, serverName );
			REQ_CACHE.put( "SERVER_URL"		, serverURL + "/" );
			REQ_CACHE.put( "CONTEXT_URL"	, contextURL );
			paramSetFlag = true;
	}

	/**
	 * 現在日付、時刻を所得します。
	 * 出力フォーマットは、システムパラメータ の SYS_TIME の
	 * 設定を適用します。( "yyyy/MM/dd HH:mm:ss" )
	 *
	 * @og.rev 4.0.0.0 (2005/01/31) getSystemResource() ⇒ sysParam に変更
	 * @og.rev 6.4.2.0 (2016/01/29) DateSet.getDate( String ) を利用するように修正します。
	 *
	 * @return	現在日付、時刻 ( 例 2001/04/17 15:48:22 )
	 */
	public static String getDate() {
		return DateSet.getDate( getParam( "SYS_TIME" ) );
	}

	/**
	 * 指定時刻をデフォルトのフォーマットで、所得します。
	 * フォーマットの指定方法は、java.text.SimpleDateFormat の指定方法と同一です。
	 * 出力フォーマットは、システムパラメータ の SYS_TIME の
	 * 設定を適用します。( "yyyy/MM/dd HH:mm:ss" )
	 *
	 * @og.rev 4.0.0.0 (2005/01/31) getSystemResource() ⇒ sysParam に変更
	 * @og.rev 5.5.7.2 (2012/10/09) HybsDateUtil を利用するように修正します。
	 *
	 * @param	time 指定のカレントタイム
	 *
	 * @return	現在日付、時刻( 例 2001/04/17 15:48:22 )
	 */
	public static String getDate( final long time ) {
		final String form = getParam( "SYS_TIME" );
		return DateSet.getDate( time , form );
	}

	/**
	 * システム設定ファイルのデータを所得します。
	 * キーが存在しない場合は、null を返します。
	 * キーがnullの場合は、HybsSystemException をスローします。
	 *
	 * @og.rev 6.7.4.1 (2017/02/17) キーが存在しなかった場合のログ出力を出すかどうか(true:出す)
	 *
	 * @param	key システム設定 キー
	 *
	 * @return	システム設定値
	 * @og.rtnNotNull
	 * @throws HybsSystemException キーが null の場合。
	 */
	public static String sys( final String key ) throws HybsSystemException {
		return sys( key , true );
	}

	/**
	 * システム設定ファイルのデータを所得します。
	 * キーが存在しない場合は、null を返します。
	 * キーがnullの場合は、HybsSystemException をスローします。
	 *
	 * @og.rev 3.5.5.0 (2004/03/12) {&#064;SYS.XXXX} で使用できる変数を増やします。
	 * @og.rev 3.5.5.3 (2004/04/09) {&#064;SYS.XXXX} に、DATE というキーを追加します。
	 * @og.rev 4.0.0.0 (2005/01/31) getSystemResource() ⇒ sysParam に変更
	 * @og.rev 6.7.4.1 (2017/02/17) キーが存在しなかった場合のログ出力を出すかどうか(true:出す)
	 *
	 * @param	key システム設定 キー
	 * @param	useNoKeyLog システムリソースキーが存在しない場合に、ログ出力するかどうか [true:出力する/false:出力しない]
	 *
	 * @return	システム設定値
	 * @og.rtnNotNull
	 * @throws HybsSystemException キーが null の場合。
	 */
	public static String sys( final String key , final boolean useNoKeyLog ) throws HybsSystemException {
		if( key == null ) {
			final String errMsg = "キーが null です。";
			throw new HybsSystemException( errMsg );		// 3.5.5.4 (2004/04/15) 引数の並び順変更
		}

		if( "DATE".equals( key ) ) { return getDate() ; }	// 3.5.5.3 (2004/04/09)

		final String rtn = getParam( key );
		if( rtn == null && ! key.startsWith( "h_" ) && useNoKeyLog ) {
			final String errMsg = CR + "システムリソースキーが存在しません。key = " + key;
			LogWriter.log( errMsg );
		}
		return rtn;
	}

	/**
	 * システム設定ファイルから、複数のデータを所得します。
	 * キーが,"ABC" とすれば、それで始まるデータ( ABCD, ABC_01 など)
	 * を配列で取得できます。
	 * リターン配列は、ソートされています。
	 *
	 * @og.rev 4.0.0.0 (2005/01/31) getSystemResource() ⇒ sysParam に変更
	 * @og.rev 6.3.9.0 (2015/11/06) Map.keySet() ではなく、Map.entrySet() を使う様に変更。
	 * @og.rev 6.4.3.3 (2016/03/04) HybsSystem.newInstance(String,String) への置き換え。
	 *
	 * @param	key システム設定 キー(の先頭の部分文字列)
	 *
	 * @return	システム設定配列 HybsEntry
	 */
	public static HybsEntry[] sysEntry( final String key ) {
		final Set<HybsEntry> entSet = new TreeSet<>();

		// 6.4.3.3 (2016/03/04) SYS_MAP を、ConcurrentHashMap で同期するため、not null制限チェックを追加します。
		SYS_MAP.forEach( (k,v) -> { if( k.startsWith( key ) ) { entSet.add( new HybsEntry( k,v ) ); } } );

		return entSet.toArray( new HybsEntry[entSet.size()] );
	}

	/**
	 * システム設定ファイルのデータを所得します。
	 * 特別に,int 型にデータを変換して,渡します。
	 * 変換できない場合は,NumberFormatException がスローされます。
	 *
	 * @og.rev 3.5.3.1 (2003/10/31) エラー時に、判るようにメッセージを出します。
	 * @og.rev 4.0.0.0 (2005/01/31) getSystemResource() ⇒ sysParam に変更
	 *
	 * @param	key システム設定 キー
	 *
	 * @return	システム設定値
	 */
	public static int sysInt( final String key ) {
		if( key == null ) {
			final String errMsg = "キーが null です。";
			throw new HybsSystemException( errMsg );		// 3.5.5.4 (2004/04/15) 引数の並び順変更
		}

		final String tmp = getParam( key );
		int rtn ;
		try {
			rtn = Integer.parseInt( tmp );
		}
		catch( final NumberFormatException ex2 ) {
			final String errMsg = "システムリソース の値が数字ではありません。" + CR
					+ "  Resource key=[" + key + "] val=[" + tmp + "]"  ;
			throw new HybsSystemException( errMsg,ex2 );		// 3.5.5.4 (2004/04/15) 引数の並び順変更
		}

		return rtn;
	}

	/**
	 * システム設定ファイルのデータを所得します。
	 * 特別に,boolean 型にデータを変換して,渡します。
	 * 変換できない場合は,NumberFormatException がスローされます。
	 *
	 * @og.rev 3.5.6.0 (2004/06/18) Booleanのコンストラクタの代わりに、Boolean.valueOf(...) を使用します。
	 *
	 * @param	key システム設定 キー
	 *
	 * @return	システム設定値
	 */
	public static boolean sysBool( final String key ) {
		return Boolean.parseBoolean( HybsSystem.sys( key ) );		// 6.1.0.0 (2014/12/26) refactoring
	}

	/**
	 * システム依存のURL文字列から，実際のディレクトリ名を所得します。
	 * UNIX と Windows を URL パス名から判断して、実ディレクトリを返します。
	 * 指定方法は、通常の fileURL 属性と同様に、先頭が、'/' (UNIX) または、２文字目が、
	 * ":" (Windows)の場合は、指定のURLそのままのディレクトリに、そうでない場合は、
	 * システムパラメータ の FILE_URL 属性で指定のフォルダの下に、作成されます。
	 * 相対パス、または、URL の場合は、頭に / を入れないでください。UNIX の
	 * 絶対パスと判断されます。
	 * また、絶対パスの最後には、/ を入れておいてください。
	 *
	 * @og.rev 4.0.0.0 (2005/01/31) StringUtil.urlAppend メソッドの利用
	 * @og.rev 6.4.2.1 (2016/02/05) 引数をすべて、StringUtil.urlAppend していきます。
	 *
	 * @param	urls URL文字列(可変長配列)
	 *
	 * @return	ディレクトリ文字列
	 */
	public static String url2dir( final String... urls ) {
		return StringUtil.urlAppend( realPath,urls ).replace( '/',FS );
	}

	/**
	 * システムリソース情報を取得します。
	 * 形式は、Map です。
	 * 実質的には、キーでソーティングされた、ConcurrentSkipListMap を返します。
	 *
	 * @og.rev 6.4.3.3 (2016/03/04) 配列返しではなく、Map を返します。
	 *
	 * @return	システムリソース情報:キー([0])、値([1])の２次元配列
	 */
	public static ConcurrentMap<String,String> getSystemResourceMap() {
		return new ConcurrentSkipListMap<String,String>( SYS_MAP );
	}

	/**
	 * システムのコンテキスト名を取得します。
	 * コンテキスト名は、Tomcat の servr.xml で指定する仮想フォルダ名です。
	 *
	 * @og.rev 3.5.4.3 (2004/01/05) 新規追加
	 *
	 * @return	コンテキスト名(仮想フォルダ名)
	 */
	public static String getContextName() {
		return sys( "CONTEXT_NAME" );	// 4.0.0 (2005/01/31)
	}

	/**
	 * サーバーのプリンター一覧のスペース区切り文字列を返します。
	 * プリンタ名にスペースが含まれる場合は、前後にダブルクオートを付けます。
	 * ここで求めた値は、"SYS.PRINTER" で、取得可能です。(最初の取得時に内部キャッシュされます。)
	 * SystemData#OMIT_PRINTER_NAMES に登録されている一覧名と先頭一致するプリンタは除外します。
	 * 例えば、Windows等で、初めから登録されている Fax,Microsoft XPS などは、不要なのでOMITします。
	 *
	 * @og.rev 6.2.6.0 (2015/06/19) サーバーのプリンター一覧のスペース区切り文字列対応。
	 * @og.rev 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。
	 *
	 * @return	サーバーのプリンター一覧のスペース区切り文字列
	 */
	public static String getPrinter() {
		String rtn = getParam( "PRINTER" );
		if( rtn == null ) {
			final String[] omitNames = HybsSystem.sys( "OMIT_PRINTER_NAMES" ).split( "," ) ;

			final PrintService[] service = PrintServiceLookup.lookupPrintServices( null,null );

			final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE ) ;
			for( int i=0; i<service.length; i++ ) {
				final String name = service[i].getName() ;
				boolean isAdd = true;	// true:追加する/false:追加しない
				for( int j=0; j<omitNames.length; j++ ) {
					if( name.startsWith( omitNames[j] ) ) { isAdd = false; break; }
				}
				if( isAdd ) {
					if( name.contains( " " ) ) {
						buf.append( '"' ).append( name ).append( "\" " );
					}
					else {
						buf.append( name ).append( ' ' );
					}
				}
			}
			buf.setLength( buf.length()-1 );		// 最後のスペースを削除

			rtn = buf.toString();
				SYS_MAP.put( "PRINTER",rtn );
		}
		return rtn ;
	}

	/**
	 * HybsSystem.sys から識別idとなるクラス名を取得して、オブジェクトを作成します。
	 * 簡易的なメソッドです。
	 *
	 * @og.rev 6.4.3.3 (2016/03/04) 新規作成。
	 *
	 * @param	<T>		キャストされて生成される新しいオブジェクト
	 * @param	sysKey	クラスのキーとなるID(共通キー)
	 * @param	cls		作成するクラスの個別キー
	 *
	 * @return	Tオブジェクト
	 * @throws RuntimeException 何らかのエラーが発生した場合
	 */
	@SuppressWarnings(value={"unchecked"})
	public static <T> T newInstance( final String sysKey,final String cls ) {
		final String clsStr = HybsSystem.sys( sysKey + cls );

		// NullPointerException が発生するので、事前にチェックします。
		if( clsStr == null ) {
			final String errMsg = sysKey + " クラスが見つかりません。class=" + sysKey + cls;
			throw new OgRuntimeException( errMsg );
		}

		return (T) newInstance( clsStr );
	}

	/**
	 * 標準のクラスローダを使って、識別id に応じた オブジェクトを作成します。
	 * 作成するには、デフォルトコンストラクターが必要です。
	 *
	 * @og.rev 6.4.3.3 (2016/03/04) リフレクション系の見直しと、例外の共通クラスに置き換えます。
	 * @og.rev 6.8.2.3 (2017/11/10) java9対応(cls.newInstance() → cls.getDeclaredConstructor().newInstance())
	 *
	 * @param	<T>		キャストされて生成される新しいオブジェクト
	 * @param	cls	作成するクラスのフルネーム(識別id)
	 *
	 * @return	Tオブジェクト
	 * @throws RuntimeException 何らかのエラーが発生した場合
	 */
	@SuppressWarnings(value={"unchecked"})
	public static <T> T newInstance( final String cls ) {
		try {
			return (T) Class.forName( cls ).getDeclaredConstructor().newInstance();			// 6.8.2.3 (2017/11/10)
		}
		catch( final ReflectiveOperationException ex ) {
			final String errMsg = "Class.forName( String ).newInstance() 処理に失敗しました class=[" + cls + "]" + CR
						+ ex.getMessage() ;
			throw new OgRuntimeException( errMsg,ex );
		}
	}

	/**
	 * "org.opengion.hayabusa.message" の、Locale.JAPANESE リソースから取得するメッセージを文字列で返します。
	 *
	 * id と引数を受け取り、ResourceBundle と、MessageFormat.format で加工した
	 * 文字列を返します。
	 * baseName は、F_BS_NM で、Locale に、Locale.JAPANESE を指定したメッセージを作成します。
	 * hayabusaパッケージと、pluginパッケージで主に使われる、メッセージの取得方法です。
	 *
	 * @og.rev 6.4.3.2 (2016/02/19) 新規追加
	 *
	 * @param id	リソースのキーとなるID。
	 * @param args	リソースを、MessageFormat.format で加工する場合の引数。
	 *
	 * @return MessageFormat.formatで加工された文字列
	 * @see		MsgUtil#H_BS_NM
	 */
	public static String getMsg( final String id , final Object... args ) {
		return MsgUtil.getMsg( MsgUtil.H_BS_NM , Locale.JAPANESE , id , args );
	}

	/**
	 * 入力文字列に、{&#064;SYS},{&#064;ENV} の文字列変換を行います。
	 *
	 *   {&#064;SYS.XXX}  ｼｽﾃﾑﾘｿｰｽMapを元に、文字列を変換します。
	 *   {&#064;ENV.XXX}  環境変数(Javaオプション)の文字列を変換します。(java -Dkey=value オプション)
	 *
	 * @og.rev 6.9.0.1 (2018/02/05) 新規追加
	 *
	 * @param  inMsg 入力文字列(nullの場合、そのまま返します)
	 *
	 * @return  変換後文字列
	 */
	public static String changeParam( final String inMsg ) {
		if( inMsg == null ) { return inMsg; }

		String message = inMsg;

		// {@SYS.XXXX} 変数の置換処理
		int adrs = message.indexOf( "{@SYS." ) ;
		while( adrs >= 0 ) {
			final int end = message.indexOf( '}',adrs ) ;
			final String key = message.substring( adrs+6,end );			// 6 = "{@SYS.".length のこと。
			final String oldData = "{@SYS." + key + "}" ;
			final String newData = SYS_MAP.getOrDefault( key , "" );	// SYS_MAP は、ConcurrentMap なので、null は、返らない。
			message = StringUtil.replace( message,oldData,newData );
			adrs = message.indexOf( "{@SYS.",adrs ) ;
		}
		// {@ENV.XXXX} 変数の置換処理
		adrs = message.indexOf( "{@ENV." ) ;
		while( adrs >= 0 ) {
			final int end = message.indexOf( '}',adrs ) ;
			final String key = message.substring( adrs+6,end );			// // 6 = "{@ENV.".length のこと。
			final String oldData = "{@ENV." + key + "}" ;
			final String newData = System.getProperty( key,"" );
			message = StringUtil.replace( message,oldData,newData );
			adrs = message.indexOf( "{@ENV.",adrs ) ;
		}

		return message;
	}
}
