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

import org.opengion.fukurou.util.StringUtil;		// 6.9.8.0 (2018/05/28)

/**
 * JavaDB(derby) や、hsqldb に対する、Javaの拡張組込み関数です。
 *
 * staticメソッドとして、関数を定義します。引数や返り値は、各ﾃﾞｰﾀﾍﾞｰｽの
 * 定義に準拠します。
 *
 * <pre>
 * ① JavaDB の場合
 * 【概要】
 *     実行するﾃﾞｰﾀﾍﾞｰｽから見えるところに、ファイルを配置する必要があります。
 *     java8 までなら、Javaのエクステンション(JAVA_HOME\)jre\lib\ext などですが、
 *     java9以降は、CLASSPATH に設定します。
 *     openGionでは、bin/const.bat で、OG_CLASSPATH 環境変数にパスを通して、
 *     使用しています。
 *     標準の Java staticメソッドを FUNCTION 定義することも出来ます。
 * 【設定】
 *     JavaDBに FUNCTION を定義します。(サンプル)
 *      DROP FUNCTION TO_CHAR;
 *
 *      CREATE FUNCTION TO_CHAR ( VAL DOUBLE )
 *      RETURNS VARCHAR(20)
 *      DETERMINISTIC           -- 引数が同じなら常に同じ値を返すことを示す.(省略時はnot deterministic)
 *      PARAMETER STYLE JAVA    -- 戻り値のタイプ
 *      NO SQL LANGUAGE JAVA    -- 関数の中でSQLは実行しないことを示す
 *      EXTERNAL NAME 'org.opengion.fukurou.db.Functions.toChar' ;
 *
 * ② HSQLDB の場合
 * 【概要】
 *
 * </pre>
 *
 * @og.rev 6.8.5.1 (2018/01/15) org.opengion.javadb → org.opengion.fukurou.db にパッケージ変更
 * @og.group 拡張組込み関数
 *
 * @version  1.1.0
 * @author	 Kazuhiko Hasegawa
 * @since    JDK8.0,
 */
public final class Functions {
    private static final String ENCODE = "UTF-8";

	/**
	 *	デフォルトコンストラクターをprivateにして、
	 *	オブジェクトの生成をさせないようにする。
	 *
	 * @og.rev 6.9.7.0 (2018/05/14) 新規作成
	 */
	private Functions() {}

	/**
	 * 数値を文字列に変換します。
	 *
	 * この関数は、引数の double が、小数点を含まない場合は、
	 * 小数点以下を出しません。
	 * JavaDBの場合、数字と文字列の連結が出来ないため、文字列変換関数を用意します。
	 *
	 *      DROP FUNCTION TO_CHAR;
	 *
	 *      CREATE FUNCTION TO_CHAR ( VAL DOUBLE )
	 *      RETURNS VARCHAR(20)
	 *      DETERMINISTIC           -- 引数が同じなら常に同じ値を返すことを示す.(省略時はnot deterministic)
	 *      PARAMETER STYLE JAVA    -- 戻り値のタイプ
	 *      NO SQL LANGUAGE JAVA    -- 関数の中でSQLは実行しないことを示す
	 *      EXTERNAL NAME 'org.opengion.fukurou.db.Functions.toChar' ;
	 *
	 * @og.rev 6.7.3.0 (2017/01/27) 新規作成
	 * @og.rev 6.8.5.1 (2018/01/15) org.opengion.javadb → org.opengion.fukurou.db にパッケージ変更
	 * @og.rev 6.9.8.0 (2018/05/28) FindBugs:浮動小数点の等価性のためのテスト
	 *
	 * @param	val	文字列に変換すべき数値
	 * @return	変換した文字列
	 */
	public static String toChar( final double val ) {
		//  6.9.8.0 (2018/05/28) FindBugs の警告を避ける方法が、見つかりませんでした。
		return val == (int)val ? String.valueOf( (int)val ) : String.valueOf( val );

//		final int intVal = (int)val;
//		return ((double)intVal) == val ? String.valueOf( intVal ) : String.valueOf( val );
	}

	/**
	 * 特殊な文字列の連結を行います。
	 *
	 * これは、第１引数の数字と、第２、第３、第４の文字列をスペースで連結した文字列を返します。
	 * 引数の個数が、可変に出来ないため、完全に決め打ちです。
	 *
	 *      DROP FUNCTION JOIN2;
	 *
	 *      CREATE FUNCTION JOIN2 ( INTEGER , VARCHAR(2000) , VARCHAR(2000) , VARCHAR(2000) )
	 *      RETURNS VARCHAR(4000)
	 *      DETERMINISTIC			-- 引数が同じなら常に同じ値を返すことを示す.(省略時はnot deterministic)
	 *      PARAMETER STYLE JAVA	-- 戻り値のタイプ
	 *      NO SQL LANGUAGE JAVA	-- 関数の中でSQLは実行しないことを示す
	 *      EXTERNAL NAME 'org.opengion.fukurou.db.Functions.join2' ;
	 *
	 * @og.rev 6.7.3.0 (2017/01/27) 新規作成
	 * @og.rev 6.8.5.1 (2018/01/15) org.opengion.javadb → org.opengion.fukurou.db にパッケージ変更
	 *
	 * @param	no		第１引数の数字
	 * @param	arg2	 第２引数
	 * @param	arg3	第３引数
	 * @param	arg4	第４引数
	 * @return	連結したした文字列
	 */
	public static String join2( final int no , final String arg2 , final String arg3 , final String arg4 ) {
		return new StringBuilder()
				.append( no ).append( ' ' )
				.append( arg2 == null ? "" : arg2 ).append( ' ' )
				.append( arg3 == null ? "" : arg3 ).append( ' ' )
				.append( arg4 == null ? "" : arg4 ).append( ' ' )
				.toString() ;
	}

	/**
	 * 対象の文字列の部分文字列を置換します。
	 *
	 * ただし、source、target、replacement のどれかが、null(ゼロ文字列)の場合は、
	 * 処理を実行せず、source をそのまま返します。
	 *
	 *      DROP FUNCTION REPLACE;
	 *
	 *      CREATE FUNCTION REPLACE ( VARCHAR(2000) , VARCHAR(2000) , VARCHAR(2000) )
	 *      RETURNS VARCHAR(4000)
	 *      DETERMINISTIC			-- 引数が同じなら常に同じ値を返すことを示す.(省略時はnot deterministic)
	 *      PARAMETER STYLE JAVA	-- 戻り値のタイプ
	 *      NO SQL LANGUAGE JAVA	-- 関数の中でSQLは実行しないことを示す
	 *      EXTERNAL NAME 'org.opengion.fukurou.db.Functions.replace' ;
	 *
	 * @og.rev 6.7.3.0 (2017/01/27) 新規作成
	 * @og.rev 6.8.5.1 (2018/01/15) org.opengion.javadb → org.opengion.fukurou.db にパッケージ変更
	 * @og.rev 6.9.8.0 (2018/05/28) source、target、replacement のどれかが、null(ゼロ文字列)の場合は、source を返す。
	 *
	 * @param	source 対象の文字列
	 * @param	target 置換したい文字列
	 * @param	replacement 置換する文字列
	 * @return	置換した文字列。
	 */
	public static String replace( final String source , final String target , final String replacement ) {
		// 6.9.8.0 (2018/05/28) source、target、replacement のどれかが、null(ゼロ文字列)の場合は、source を返す。
		if( StringUtil.isEmpty( source , target , replacement ) ) {		// 一つでも、null の場合、true
			return source;
		}
		else {
			return source.replace( target,replacement );
		}

//		if( source != null && target != null || !target.isEmpty() && replacement != null && !replacement.isEmpty() ) {
//			return source.replace( target,replacement );
//		}
//		else {
//			return source;
//		}
	}

//	/**
//	 * この文字列内にあるすべてのoldCharをnewCharに置換した結果生成される文字列を返します。
//	 *
//	 * String#replace( char , char )を、繰り返します。
//	 *
//	 * @og.rev 6.7.9.0 (2017/04/28) 新規作成
//	 *
//	 * @param	source 対象の文字列
//	 * @param	target 以前の文字の集合
//	 * @param	replacement 置換する文字の集合
//	 * @return	置換した文字列。
//	 */
//	public static String translate( final String source , final String target , final String replacement ) {
//		String rtn = source ;
//
//		if( source != null && target != null && replacement != null && target.length() == replacement.length() ) {
//			for( int i=0; i<target.length(); i++ ) {
//				rtn = rtn.replace( target.charAt(i) , replacement.charAt(i) );
//			}
//		}
//
//		return rtn;
//	}

	/**
	 * substr関数のバイト数版
	 * 過去に、hsqldb 用に作成したJava関数です。
	 *
	 * @og.rev 6.8.5.1 (2018/01/15) org.opengion.hsqldb → org.opengion.fukurou.db にパッケージ変更
	 *
	 * @param	value		 変換する文字列
	 * @param	start		 変換開始アドレス
	 * @param	length		 変換バイト数
	 * @return	変換後文字列
	 * @throws	UnsupportedEncodingException	文字のエンコーディングがサポートされていません。
	 */
	public static String substrb( final String value, final int start, final int length ) throws UnsupportedEncodingException {
		String rtn = null;
		final byte[] byteValue = makeByte( value );
		if( byteValue != null ) {
			rtn = new String( byteValue,start-1,length,ENCODE );
		}
		return rtn;
	}

	/**
	 * length関数のバイト数版
	 * 過去に、hsqldb 用に作成したJava関数です。
	 *
	 * @og.rev 6.8.5.1 (2018/01/15) org.opengion.hsqldb → org.opengion.fukurou.db にパッケージ変更
	 *
	 * @param	value		バイト数をカウントする文字列
	 * @return	バイト数
	 * @throws	UnsupportedEncodingException	文字のエンコーディングがサポートされていません。
	 */
	public static int lengthb( final String value ) throws UnsupportedEncodingException {
		return makeByte( value ).length;
	}

	/**
	 * 指定の文字列をバイトコードに変換します。
	 * 引数の文字列が null の場合は、return は、byte[0] を返します。
	 *
	 * @og.rev 6.8.5.1 (2018/01/15) org.opengion.hsqldb → org.opengion.fukurou.db にパッケージ変更
	 *
	 * @param	value	 変換するストリング値
	 * @param	encode	 変換する文字エンコード
	 * @return	変換後文字列
	 */
	private static byte[] makeByte( final String value ) throws UnsupportedEncodingException {
		byte[] rtnByte = new byte[0];
		if( value != null ) {
			rtnByte = value.getBytes( ENCODE );
		}
		return rtnByte;
	}
}
