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

import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.util.Locale;
import java.util.Properties;
import static org.opengion.fukurou.system.HybsConst.CR;				// 6.1.0.0 (2014/12/26) refactoring
import static org.opengion.fukurou.system.HybsConst.BUFFER_MIDDLE;	// 6.1.0.0 (2014/12/26) refactoring
import org.opengion.fukurou.system.OgRuntimeException ;		// 6.4.2.0 (2016/01/29)

/**
 * JAXBで自動生成されたDBIDクラスを拡張したクラスです。
 * (継承しているわけではない)
 * <BR>
 * 以下の属性が追加されています。<BR>
 * dbProductName<BR>
 * dbProductVersion<BR>
 * driverName<BR>
 * driverVersion<BR>
 *
 * @og.rev 4.0.0.0 (2007/10/25) 新規作成
 * @og.rev 5.1.7.0 (2010/06/01) メソッドの修正、Cloneable の追加(浅いコピー)
 * @og.rev 5.6.6.0 (2013/07/05) 表題(title)属性を追加
 *
 * @version  4.0
 * @author	 高橋正和
 * @since	 JDK5.0,
 */
public class EDbid implements Cloneable {

	private 	String	dbidKey			= "DEFAULT";
	private 	String	title			;						// 5.6.6.0 (2013/07/05) 表題(title)属性を追加
	private 	String	url				;
	private 	String	user			;
	private 	String	password		= "";					// 5.7.2.0 (2014/01/10) パスワードは空文字を初期値とする。
	private 	boolean	readonly		;
	private 	int		mincount		= 3;
	private 	int		maxcount		= 30;
	private 	int		pooltime		= 3000;		// (秒数)
	private 	boolean	isUseAppInfo	= true;
	private 	boolean isParamMetaData	;						// 5.3.8.0 (2011/08/01)

	private 	String	dbProductName	;
	private 	String	dbProductVersion;
	private 	String	driverName		;
	private 	String	driverVersion	;

	/* 5.5.2.0 (2012/05/01) DBConfigでpropertiesの指定を可能にします */
	private final Properties props = new Properties();

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

	/**
	 * dbidKeyの取得
	 *
	 * 内部的には、大文字のみで管理します。
	 *
	 * @return	dbidキー
	 */
	public String getDbidKey() {
		return dbidKey;
	}

	/**
	 * dbidキーの設定
	 *
	 * 引数が、null でなく、ゼロ文字列でもない場合のみ、セットします。
	 * 内部的には、大文字のみで管理します。
	 *
	 * @og.rev 4.1.0.1 (2008/01/21) 登録時に、大文字に変換する。
	 *
	 * @param value 接続先ID
	 */
	protected void setDbidKey( final String value ) {
		if( isNotNull( value ) ) { dbidKey = value.toUpperCase( Locale.JAPAN ); }
	}

	/**
	 * 表題(title)属性の取得
	 *
	 * この、dbidKey を表す表題を取得します。ラベル（名前）のようなものです。
	 *
	 * @og.rev 5.6.6.0 (2013/07/05) 新規追加
	 *
	 * @return	表題(title)
	 */
	public String getTitle() {
		return title;
	}

	/**
	 * 表題(title)の設定
	 *
	 * 引数が、null でなく、ゼロ文字列でもない場合のみ、セットします。
	 *
	 * @og.rev 5.6.6.0 (2013/07/05) 新規追加
	 * @og.rev 5.6.8.0 (2013/09/06) title が未設定の時は、dbidKey をセットしておきます。
	 *
	 * @param value 表題(title)
	 */
	protected void setTitle( final String value ) {
		if( isNotNull( value ) ) { title = value; }
		else					 { title = dbidKey; }		// 5.6.8.0 (2013/09/06)
	}

	/**
	 * ＵＲＬの取得。
	 *
	 * @return	ＵＲＬ
	 */
	public String getUrl() {
		return url;
	}

	/**
	 * ＵＲＬの設定
	 * 引数が、null でなく、ゼロ文字列でもない場合のみ、セットします。
	 *
	 * @param value ＵＲＬ文字列
	 */
	protected void setUrl( final String value ) {
		if( isNotNull( value ) ) { url = value; }
	}

	/**
	 * ユーザーの取得。
	 *
	 * @return	ユーザー
	 */
	public String getUser() {
		return user;
	}

	/**
	 * userの設定
	 * 引数が、null でなく、ゼロ文字列でもない場合のみ、セットします。
	 *
	 * @param value user文字列
	 */
	protected void setUser( final String value ) {
		if( isNotNull( value ) ) { user = value; }
	}

	/**
	 * パスワードの取得。
	 *
	 * @return	パスワード
	 */
	public String getPassword() {
		return password;
	}

	/**
	 * パスワードの設定
	 * 引数が、null でなく、ゼロ文字列でもない場合のみ、セットします。
	 *
	 * @param value パスワード文字列
	 */
	protected void setPassword( final String value ) {
		if( isNotNull( value ) ) { password = value; }
	}

	/**
	 * readonlyの取得。
	 *
	 * @return	[true:読み取り専用/false:通常]
	 */
	public boolean isReadonly() {
		return readonly ;
	}

	/**
	 * readonlyの設定
	 * 引数が、null でなく、ゼロ文字列でもない場合のみ、セットします。
	 *
	 * @param value  readonly文字列 [true/false]
	 */
	protected void setReadonly( final String value ) {
		if( isNotNull( value ) ) { readonly = Boolean.parseBoolean( value ); }		// 6.1.0.0 (2014/12/26) refactoring
	}

	/**
	 * 最小数の取得。
	 *
	 * @return	最小数
	 */
	public int getMincount() {
		return mincount;
	}

	/**
	 * 最小数の設定
	 * 引数が、null でなく、ゼロ文字列でもない場合のみ、セットします。
	 *
	 * @param value 最小数(数字タイプ文字列)
	 */
	protected void setMincount( final String value ) {
		if( isNotNull( value ) ) { mincount = Integer.parseInt( value ); }		// 6.0.2.4 (2014/10/17) メソッド間違い
	}

	/**
	 * 最大数の取得。
	 *
	 * @return	最大数
	 */
	public int getMaxcount() {
		return maxcount;
	}

	/**
	 * 最大数の設定
	 * 引数が、null でなく、ゼロ文字列でもない場合のみ、セットします。
	 *
	 * @param value 最大数(数字タイプ文字列)
	 */
	protected void setMaxcount( final String value ) {
		if( isNotNull( value ) ) { maxcount = Integer.parseInt( value ); }		// 6.0.2.4 (2014/10/17) メソッド間違い
	}

	/**
	 * プールに保管して置く時間(秒数)の取得。
	 *
	 * @return	プールに保管する時間(秒数)
	 */
	public int getPooltime() {
		return pooltime;
	}

	/**
	 * プールに保管して置く時間(秒数))の設定
	 * 引数が、null でなく、ゼロ文字列でもない場合のみ、セットします。
	 *
	 * @param value プールに保管して置く時間(秒数)(数字タイプ文字列)
	 */
	protected void setPooltime( final String value ) {
		if( isNotNull( value ) ) { pooltime = Integer.parseInt( value ); }		// 6.0.2.4 (2014/10/17) メソッド間違い
	}

	/**
	 * ApplicationInfoオブジェクトを使用するかどうか[true/false]の情報を取得。
	 *
	 * @return	使用するかどうか
	 */
	public boolean isApplicationInfo() {
		return isUseAppInfo ;
	}

	/**
	 * ApplicationInfoオブジェクトを使用するかどうか[true/false]の情報を設定
	 * 引数が、null でなく、ゼロ文字列でもない場合のみ、セットします。
	 *
	 * @param value ApplicationInfoオブジェクトを使用するかどうか [true/false]
	 */
	protected void setApplicationInfo( final String value ) {
		if( isNotNull( value ) ) { isUseAppInfo = Boolean.parseBoolean( value ); }	// 6.1.0.0 (2014/12/26) refactoring
	}

	/**
	 * メタデータを受け取って dbProductName,dbProductVersion,driverName,driverVersion を一括で設定します。
	 *
	 * @og.rev 4.0.0.0 (2007/10/30) 保持情報オブジェクト化に伴う変更
	 * @og.rev 5.3.8.0 (2011/08/01) postgreSQL時のカラムタイプ判定の必要有無(ApplicationInfo.useParameterMetaData)
	 * @og.rev 6.4.9.5 (2016/09/09) DERBY も、isParamMetaData=true を返します。
	 *
	 * @param meta メタデータオブジェクト
	 * @throws SQLException データベースアクセスエラー
	 */
	public void setMetaDataInfo( final DatabaseMetaData meta ) throws SQLException {
		dbProductName	= meta.getDatabaseProductName() ;
		dbProductVersion= meta.getDatabaseProductVersion() ;
		driverName		= meta.getDriverName() ;
		driverVersion	= meta.getDriverVersion() ;

		// dbProductName は、大文字にしておいても問題は無いが、今は影響範囲を小さくしておきます。
		final String dbName = dbProductName.toUpperCase( Locale.JAPAN );		// 6.4.9.5 (2016/09/09) 大文字化します。

		isUseAppInfo	= isUseAppInfo && "ORACLE".equalsIgnoreCase( dbProductName ) ;
		isParamMetaData	= dbName.contains( "POSTGRES" )
					   || dbName.contains( "DERBY" );					// 6.4.9.5 (2016/09/09) 
	}

	/**
	 * DBプロダクト名の取得。
	 *
	 * @return	DBプロダクト名
	 */
	public String getDbProductName() {
		return dbProductName;
	}

	/**
	 * DBプロダクト・バージョンの取得。
	 *
	 * @return	DBプロダクト・バージョン
	 */
	public String getDbProductVersion() {
		return dbProductVersion;
	}

	/**
	 * ドライバー名の取得。
	 *
	 * @return	ドライバー名
	 */
	public String getDriverName() {
		return driverName;
	}

	/**
	 * ドライバーのバージョンの取得。
	 *
	 * @return	ドライバーのバージョン
	 */
	public String getDriverVersion() {
		return driverVersion;
	}

	/**
	 * ParamMetaData を利用するかどうか[true/false]を返します。
	 *
	 * これは、PostgreSQL の時は、trueになります。
	 *
	 * @og.rev 5.3.8.0 (2011/08/01) 新規追加
	 *
	 * @return	利用するかどうか[true/false]
	 */
	public boolean useParamMetaData() {
		return isParamMetaData ;
	}

	/**
	 * propMapへの追加。
	 *
	 * @og.rev 5.5.2.0 (2012/05/01) 新規追加
	 * @param key プロパティのキー
	 * @param val ポロパティの値
	 */
	protected void addProp( final String key, final String val ) {
		props.put ( key, val );
	}

	/**
	 * propMapへの追加。
	 * 最初の=でkeyとvalueを分割する
	 *
	 * @og.rev 5.5.2.0 (2012/05/01) 新規追加
	 * @og.rev 5.5.2.1 (2012/05/07) propsへの追加漏れ,カンマを=に変更
	 * @param prop プロパティのキーと値の組み合わせ
	 */
	protected void addProp( final String prop ) {
		if( prop!=null && prop.indexOf( '=' ) > 0 ){
			final String key = prop.substring( 0 , prop.indexOf('=') );
			final String val = prop.substring( prop.indexOf('=') + 1 );
			addProp( key, val ); // 5.5.2.1 (2012/05/07)
		}
	}

	/**
	 * propMapの取得。
	 *
	 * @og.rev 5.5.2.0 (2012/05/01) 新規追加
	 * @og.rev 6.3.9.0 (2015/11/06) 内部Propertiesオブジェクトではなく、複製して返します。
	 *
	 * @return 複製Propertiesオブジェクト
	 */
	public Properties getProps(){
		return new Properties( props );			// 6.3.9.0 (2015/11/06)
	}

	/**
	 * 文字列がnullでも空文字列でもないかどうかを評価します。
	 *
	 * スペース文字は、trim() してから、!isEmpty() で判断しますので、false になります。
	 *
	 * @og.rev 5.1.7.0 (2010/06/01) 新規作成
	 *
	 * @param value 評価する文字列
	 *
	 * @return 結果(true:nullでも空文字列でもない)
	 */
	private boolean isNotNull( final String value ) {
		return value != null && !value.trim().isEmpty() ;			// 6.0.2.5 (2014/10/31) refactoring
	}

	/**
	 * 自分自身の clone を返します。
	 *
	 * Cloneable の実装で、内部的には、Object クラスの clone メソッドを読んでいます。
	 *
	 * @og.rev 5.1.7.0 (2010/06/01) 新規作成
	 *
	 * @return	自分自身の clone を返します。
	 * @throws	RuntimeException clone が失敗しました場合
	 * @og.rtnNotNull
	 */
	@Override
	public EDbid clone() {
		try {
			return (EDbid)super.clone() ;
		}
		catch( final CloneNotSupportedException ex ) {
			final String errMsg = "clone が失敗しました。"
								+ ex.getMessage() ;
			throw new OgRuntimeException( errMsg,ex );
		}
	}

	/**
	 * 自分自身の clone を返します。
	 *
	 * ここでは、dbidKey を指定した、clone() を作成します。
	 * Titleも、指定した、dbidKey をセットしておきます。
	 *
	 * @og.rev 6.4.3.4 (2016/03/11) 新規作成
	 *
	 * @param id 接続先ID
	 * @return	dbidKeyを指定して、自分自身のcloneを返します。
	 * @og.rtnNotNull
	 */
	public EDbid clone( final String id ) {
		final EDbid obj = this.clone();
		obj.setDbidKey( id );
		obj.setTitle( id );

		return obj;
	}

	/**
	 * 簡易な内部情報の文字列化します。
	 *
	 * @og.rev 5.3.4.0 (2011/04/01) toString() の簡易版
	 * @og.rev 5.5.2.1 (2012/05/07) properties情報を出力するようにする
	 * @og.rev 5.6.6.0 (2013/07/05) 表題(title)属性を追加
	 *
	 * @return 内部情報の文字列
	 * @og.rtnNotNull
	 */
	public String info() {
		final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE )
			.append( "DBID=["  ).append( dbidKey ).append( "] , " )
			.append( "TITLE=[" ).append( title   ).append( "] , " )			// 5.6.6.0 (2013/07/05) 表題(title)属性を追加
			.append( "URL=["   ).append( url     ).append( "] , " )
			.append( "USER=["  ).append( user    ).append( ']'  )
			.append( CR )
			.append( "DB Product=[" ).append( dbProductName )
			.append( '(' ).append( dbProductVersion ).append( ')' ).append( ']' )
			.append( CR )
			.append( "DB Driver =[" ).append( driverName )
			.append( '(' ).append( driverVersion ).append( ')' ).append( ']' )
			.append( CR )
			.append( "PROPERTIES=[" ).append( props.toString() ).append( ']' ) // 5.5.2.1 (2012/05/07)
			.append( CR );
		return buf.toString();
	}

	/**
	 * 内部情報を文字列化します。
	 *
	 * @og.rev 5.3.4.0 (2011/04/01) info() メソッドを利用するように変更
	 *
	 * @return	内部情報の文字列
	 * @og.rtnNotNull
	 */
	@Override
	public String toString() {
		final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE )
			.append( info() )
		//	.append( "DBID=[" ).append( dbidKey ).append( "]," )
		//	.append( "URL =[" ).append( url     ).append( "]," )
		//	.append( "USER=[" ).append( user    ).append( "]," )
		//	.append( CR )
		//	.append( "DB Product=[" ).append( dbProductName )
		//	.append( "(" ).append( dbProductVersion ).append( ")" ).append( "]" )
		//	.append( CR )
		//	.append( "DB Driver =[" ).append( driverName )
		//	.append( "(" ).append( driverVersion ).append( ")" ).append( "]" )
		//	.append( CR )
			.append( "MINCOUNT=[" ).append( mincount ).append( "]," )
			.append( "MAXCOUNT=[" ).append( maxcount ).append( "]," )
			.append( "POOLTIME=[" ).append( pooltime ).append( "]," )
			.append( "READONLY=[" ).append( readonly ).append( "]," )
			.append( "APPINFO =[" ).append( isUseAppInfo ).append( ']' )		// 6.0.2.5 (2014/10/31) char を append する。
			.append( CR );
		return buf.toString();
	}
}
