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

import org.opengion.fukurou.util.Cleanable;
import org.opengion.hayabusa.common.HybsSystemException ;
import org.opengion.hayabusa.common.SystemManager ;
import org.opengion.hayabusa.resource.CodeData;
import static org.opengion.fukurou.system.HybsConst.CR ;				// 6.1.0.0 (2014/12/26)
import static org.opengion.fukurou.system.HybsConst.BUFFER_MIDDLE;		// 6.1.0.0 (2014/12/26) refactoring

import java.util.Map;
import java.util.WeakHashMap ;
import java.util.Collections ;											// 6.4.3.1 (2016/02/12) ConcurrentHashMap が使えないため。

/**
 * Selectionオブジェクトを取得する為に使用するファクトリクラスです。
 *
 * Selectionオブジェクト のキー(codeName)を元に、オブジェクトをキャッシュ管理
 * することが、主な機能です。
 *
 * @og.rev 3.5.5.7 (2004/05/10) 新規作成
 * @og.group 選択データ制御
 *
 * @version  4.0
 * @author   Kazuhiko Hasegawa
 * @since    JDK5.0,
 */
public final class SelectionFactory {
	// private static final Map<String,Selection>  codeMap = new WeakHashMap<>();
//	private static final Map<String,Selection>	DB_MAP			= new WeakHashMap<>( BUFFER_MIDDLE );		// 6.4.1.1 (2016/01/16) dbMap      → DB_MAP refactoring
//	private static final Map<String,Selection>	DB_RADIO_MAP	= new WeakHashMap<>( BUFFER_MIDDLE );		// 6.4.1.1 (2016/01/16) dbRadioMap → DB_RADIO_MAP refactoring
	/** 6.4.3.1 (2016/02/12) PMD refactoring. Collections.synchronizedMap で対応。  */
	private static final Map<String,Selection>	DB_MAP			= Collections.synchronizedMap( new WeakHashMap<>( BUFFER_MIDDLE ) );	// 6.4.3.1 (2016/02/12) Collections.synchronizedMap
	/** 6.4.3.1 (2016/02/12) PMD refactoring. Collections.synchronizedMap で対応。  */
	private static final Map<String,Selection>	DB_RADIO_MAP	= Collections.synchronizedMap( new WeakHashMap<>( BUFFER_MIDDLE ) );	// 6.4.3.1 (2016/02/12) Collections.synchronizedMap

	// 4.0.0 (2005/01/31) Cleanable インターフェースによる初期化処理
	static {
		final Cleanable clr = new Cleanable() {
			/**
			 * 初期化(クリア)します。
			 * 主に、キャッシュクリアで利用します。
			 */
			public void clear() {
				SelectionFactory.clear();
			}
		};

		SystemManager.addCleanable( clr );
	}

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

	/**
	 * DB検索(SQL)文字列より、データベースSelectionオブジェクトを構築します。
	 * Selection_DB では、検索行毎のクエリーがあるため、name + query でキャッシュします。
	 *
	 * @og.rev 4.0.0.0 (2006/11/15) lang 属性を追加します。
	 * @og.rev 6.2.0.0 (2015/02/27) キー:ラベル形式で表示するかどうかを、指定できるようにします。
	 * @og.rev 6.4.3.1 (2016/02/12) PMD refactoring. Collections.synchronizedMap で対応。
	 *
	 * @param   query DB検索(SQL)文字列
	 * @param	dbid  データベース接続先ID
	 * @param	lang  リソースを使用する場合の言語
	 * @param	addKeyLabel キー:ラベル形式で表示するかどうか[true/false/null]
	 *
	 * @return  Selectionオブジェクト
	 */
	public static Selection newDBSelection( final String query,final String dbid,final String lang,final String addKeyLabel ) {
		final String key = query+dbid+lang+addKeyLabel;						// 6.2.0.0 (2015/02/27) キー:ラベル形式

		// 6.4.3.1 (2016/02/12) v は、Selection オブジェクト
		// Selection select = DB_MAP.get( key );
		return DB_MAP.compute( key ,(k, v) ->
						v == null || v.isTimeOver() ? new Selection_DB( query,dbid,lang,addKeyLabel ) : v );

//		Selection select = DB_MAP.get( key );
//
//		if( select == null || select.isTimeOver() ) {
//			synchronized( DB_MAP ) {
//				select = new Selection_DB( query,dbid,lang,addKeyLabel );	// 6.2.0.0 (2015/02/27) キー:ラベル形式
//				DB_MAP.put( key,select );
//			}
//		}
//		return select;
	}

	/**
	 * DB検索(SQL)文字列より、データベースSelectionオブジェクトを構築します。
	 * Selection_DB では、検索行毎のクエリーがあるため、name + query でキャッシュします。
	 *
	 * @og.rev 4.3.3.6 (2008/11/15) 新規作成
	 * @og.rev 6.4.3.1 (2016/02/12) PMD refactoring. Collections.synchronizedMap で対応。
	 *
	 * @param   query DB検索(SQL)文字列
	 * @param	dbid  データベース接続先ID
	 * @param	lang  リソースを使用する場合の言語
	 *
	 * @return  Selectionオブジェクト
	 */
	public static Selection newDBRadioSelection( final String query,final String dbid,final String lang ) {
		final String key = query+dbid+lang;

		// 6.4.3.1 (2016/02/12) v は、Selection オブジェクト
		// Selection select = DB_RADIO_MAP.get( key );
		return DB_RADIO_MAP.compute( key ,(k, v) ->
						v == null || v.isTimeOver() ? new Selection_DBRADIO( query,dbid,lang ) : v );

//		Selection select = DB_RADIO_MAP.get( key );
//
//		if( select == null || select.isTimeOver() ) {
//			synchronized( DB_RADIO_MAP ) {
//				select = new Selection_DBRADIO( query,dbid,lang );
//				DB_RADIO_MAP.put( key,select );
//			}
//		}
//		return select;
	}

	/**
	 * 各種Selectionオブジェクトを構築します。
	 * ここでは、Selectionオブジェクトのタイプが、(KEYVAL,HM,NUM,YMD)について作成されます。
	 * ここで作成されるオブジェクトは、この、SelectionFactoryではキャッシュしません。
	 * 各RendererやEditorが共有されているので、そちらでキャッシュされています。
	 * type が指定のキーワード以外の場合は、Exception が返されます。
	 * ※ type="NULL" も使用可能です。これは、どんな場合でも、引数の param を返す Selection
	 * オブジェクトを返します。内部的に、CodeDataが存在しない場合など、エラーメッセージを
	 * 引数に与えて修正を促すようなケースで使用します。
	 * ※ type="MENU" を指定した場合は、KBMENU(type=KEYVAL) を構築します。
	 * パラメータは、キー：値 の組み合わせの文字列です。
	 *
	 * ※ 指定のタイプが存在しない場合、HybsSystemException が throw されます。
	 *
	 * @og.rev 5.7.3.0 (2014/02/07) 新規作成
	 * @og.rev 6.0.4.0 (2014/11/28) type に、MENU を指定できるように変更
	 * @og.rev 6.2.6.0 (2015/06/19) type別Selectionの場合、ラベルリソースを使用する為、言語を引数で渡す。
	 * @og.rev 6.3.4.0 (2015/08/01) Selection_HM の引数から、lang 属性を削除します。
	 *
	 * @param   type  Selectionオブジェクトのタイプ(KEYVAL,MENU,HM,NUM,YMD,MENU)
	 * @param	editPrm	type別のパラメータ文字列
	 * @param	lang  言語
	 *
	 * @return  Selectionオブジェクト
	 */
	public static Selection newSelection( final String type,final String editPrm,final String lang ) {
		Selection select = null;
		if( "KEYVAL".equalsIgnoreCase( type ) || "MENU".equalsIgnoreCase( type ) ) {
			select = new Selection_KEYVAL( editPrm,lang );	// 6.2.6.0 (2015/06/19)
		}
		else if( "HM".equalsIgnoreCase( type ) ) {
//			select = new Selection_HM( editPrm,lang );		// 6.2.6.0 (2015/06/19)
			select = new Selection_HM( editPrm );			// 6.3.4.0 (2015/08/01)
		}
		else if( "NUM".equalsIgnoreCase( type ) ) {
//			select = new Selection_NUM( editPrm,lang );		// 6.2.6.0 (2015/06/19)
			select = new Selection_NUM( editPrm );			// 6.3.4.0 (2015/08/01)
		}
		else if( "YMD".equalsIgnoreCase( type ) ) {
//			select = new Selection_YMD( editPrm,lang );		// 6.2.6.0 (2015/06/19)
			select = new Selection_YMD( editPrm );			// 6.3.4.0 (2015/08/01)
		}
		else if( "NULL".equalsIgnoreCase( type ) ) {
//			select = new Selection_NULL( editPrm,lang );	// 6.2.6.0 (2015/06/19)
			select = new Selection_NULL( editPrm );			// 6.3.4.0 (2015/08/01)
		}
		else {
			final String errMsg = "指定のタイプ[" + type + "]が、存在しません。" + CR
									+ "  タイプ一覧=[KEYVAL,MENU,HM,NUM,YMD]" + CR
									+ "  param=[" + editPrm + "]" + CR;
			throw new HybsSystemException( errMsg );
		}

		return select;
	}

	/**
	 * 各種Selectionオブジェクトを構築します。
	 * ここでは、Selectionオブジェクトのタイプが、(MENU,RADIO)について作成されます。
	 * ここで作成されるオブジェクトは、この、SelectionFactoryではキャッシュしません。
	 * 各RendererやEditorが共有されているので、そちらでキャッシュされています。
	 * type が指定のキーワード以外の場合は、Exception が返されます。
	 * codeData オブジェクトが null の場合は、Selectionオブジェクト は null が返されます。
	 *
	 * ※ 指定のタイプが存在しない場合、HybsSystemException が throw されます。
	 *
	 * @og.rev 5.7.3.0 (2014/02/07) 新規作成
	 * @og.rev 6.2.0.0 (2015/02/27) キー:ラベル形式で表示するかどうかを、指定できるようにします。
	 * @og.rev 6.2.2.4 (2015/04/24) BITBOX 新規追加
	 *
	 * @param   type  Selectionオブジェクトのタイプ(MENU,RADIO)
	 * @param	codeData CodeDataオブジェクト
	 * @param	addKeyLabel キー:ラベル形式で表示するかどうか[true/false/null]
	 *
	 * @return  Selectionオブジェクト(codeData オブジェクトが null の場合は、null)
	 */
	public static Selection newSelection( final String type,final CodeData codeData,final String addKeyLabel ) {
		Selection select = null;
		if( codeData != null ) {
			if( "MENU".equalsIgnoreCase( type ) ) {
				select = new Selection_CODE( codeData,addKeyLabel );
			}
			else if( "RADIO".equalsIgnoreCase( type ) ) {
				select = new Selection_RADIO( codeData );
			}
			// 6.2.2.4 (2015/04/24) BITBOX 新規追加
			else if( "BITBOX".equalsIgnoreCase( type ) ) {
				select = new Selection_BITBOX( codeData );
			}
			else {
				final String errMsg = "指定のタイプ[" + type + "]が、存在しません。タイプ一覧=[MENU,RADIO]" + CR ;
				throw new HybsSystemException( errMsg );
			}
		}

		return select;
	}

	/**
	 * Selectionオブジェクトをプールからすべて削除します。
	 * システム全体を初期化するときや、動作が不安定になったときに行います。
	 * プールの方法自体が,一種のキャッシュ的な使いかたしかしていない為,
	 * 実行中でも、いつでもプールを初期化できます。
	 *
	 * @og.rev 4.3.3.6 (2008/11/15) DBRadioMap追加
	 * @og.rev 6.4.3.1 (2016/02/12) PMD refactoring. Collections.synchronizedMap で対応。
	 */
	public static void clear() {
		// synchronized( codeMap ) { codeMap.clear(); }
//		synchronized( DB_MAP      ) { DB_MAP.clear(); }
//		synchronized( DB_RADIO_MAP ) { DB_RADIO_MAP.clear(); }	// 4.3.3.6 (2008/11/15)
		DB_MAP.clear();
		DB_RADIO_MAP.clear();	// 4.3.3.6 (2008/11/15)
	}
}
