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

import org.opengion.fukurou.system.OgRuntimeException;		// 6.4.2.0 (2016/01/29)
import java.lang.reflect.InvocationTargetException;			// 7.0.0.0
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.StringJoiner;								// 6.4.4.2 (2016/04/01)
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.Iterator;
import java.util.StringTokenizer;
import java.util.Locale;									// 5.7.2.3 (2014/01/31)
import java.text.DecimalFormat;								// 6.2.0.0 (2015/02/27)
import java.util.function.UnaryOperator;					// 6.9.2.1 (2018/03/12)
import java.util.function.Consumer;							// 8.0.0.2 (2021/10/15)

import org.opengion.fukurou.system.ThrowUtil;						// 6.4.2.0 (2016/01/29) package変更 fukurou.util → fukurou.system
// import org.opengion.fukurou.system.OgRuntimeException ;			// 8.0.0.0 (2021/09/30)

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.4.2.0 (2016/01/29) ローカル定義をやめて、HybsConst を使用する様に変更。

/**
 * StringUtil.java は、共通的に使用される String関連メソッドを集約した、クラスです。
 *
 * @og.group ユーティリティ
 *
 * @version  4.0
 * @author	 Kazuhiko Hasegawa
 * @since    JDK5.0,
 */
public final class StringUtil {

	/**
	 * code39 のチェックデジット計算に使用する モジュラス４３ の変換表です。
	 *
	 */
	private static final String MODULUS_43 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%" ;

	/**
	 * getUnicodeEscape で使用する桁合わせ用文字列配列です。
	 * Unicodeの HexString 変換後の桁に応じて、埋め合わせします。
	 *
	 */
	private static final String[] UTF_STR = { "&#x0000", "&#x000", "&#x00", "&#x0", "&#x" };

	// 4.0.3.0 (2007/12/26) 色コードにPURPLE を追加
	// 5.7.8.0 (2014/07/04) 透明追加
	// 6.0.2.1 (2014/09/26) ColorMap クラスに移動

	// 6.2.0.0 (2015/02/27) #numberFormat( String , int ) で使用するフォーマット変換オブジェクト
	private static final DecimalFormat[] FMT1 = new DecimalFormat[] {
									new DecimalFormat( "#,##0" ) ,
									new DecimalFormat( "#,##0.0" ) ,
									new DecimalFormat( "#,##0.00" ) ,
									new DecimalFormat( "#,##0.000" ) ,
									new DecimalFormat( "#,##0.0000" ) } ;

	private static final String ZERO = "00000000000000000000" ;		// ゼロ埋めの種

	private static final String[][] ESC_ARY = new String[][] {		// 6.9.8.1 (2018/06/11)
									 { "&lt;", "<" }
									,{ "&LT;", "<" }
									,{ "&gt;", ">" }
									,{ "&GT;", ">" }	};

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

	/**
	 * UTF-8 で、URLエンコードを行います。
	 * このメソッドは、JDK1.4 以上でないと使用できません。
	 *
	 * @param	value エンコードする文字列
	 *
	 * @return	 指定の文字コードでＵＲＬエンコードされた文字列
	 * @see		#urlEncode2( String )
	 * @og.rtnNotNull
	 */
	public static String urlEncode( final String value ) {
		if( value == null ) { return ""; }

		try {
			return URLEncoder.encode( value,"UTF-8" );
		}
		catch( final UnsupportedEncodingException ex ) {
			final String errMsg = "UnsupportedEncodingException [UTF-8]" + CR
						+ ex.getMessage() ;
			throw new OgRuntimeException( errMsg,ex );
		}
		catch( final RuntimeException ex2 ) {		// 3.6.0.0 (2004/09/17)
			final String errMsg = "予期せぬエラー value=[" + value + "] , encode=[UTF-8]" + CR
						+ ex2.getMessage();
			throw new OgRuntimeException( errMsg,ex2 );
		}
	}

	private static final String UN_CHANGE = ":/?=&._~" ;

	/**
	 * UTF-8 で、ASCII以外の文字の、URLエンコードします。
	 *
	 * 00 ～ 7F までのコードは、変換しません。
	 *
	 * これは、日本語ファイル名の直リンクなど、URLエンコードが必要ですが、
	 * http:// などのURL の場合は、':' , '/' は、エンコードしたくありません。
	 * また、openGion では、[カラム] などの特殊な変数を渡して、処理させているので
	 * それらのキーワードも変換してほしくありません。
	 * ただし、"%" と、";" は、変換します。
	 *
	 * @og.rev 6.2.0.1 (2015/03/06) ASCII以外の文字の、URLエンコードを行う。
	 * @og.rev 6.9.0.0 (2018/01/31) 半角の中でも ':' , '/' , '?' , '=' , '&amp;' , '.' , '_' , '~' 以外の文字は置き換えます。
	 *
	 * @param	value エンコードする文字列
	 *
	 * @return	 指定の文字コードでＵＲＬエンコードされた文字列(ASCII は省く)
	 * @see		#urlEncode( String )
	 * @og.rtnNotNull
	 */
	public static String urlEncode2( final String value ) {
		if( value == null ) { return ""; }

		final StringBuilder rtn = new StringBuilder( BUFFER_MIDDLE );
		final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE );

		for( int i=0; i<value.length(); i++ ) {
			final char ch = value.charAt(i);
//			if( ch > 0x7f ) { buf.append( ch ); }				// ASCII以外は、とりあえず貯めておく
			if( ch > 0x7f || UN_CHANGE.indexOf( ch ) < 0 ) { buf.append( ch ); }	// ASCII以外は、とりあえず貯めておく
			else {
				if( buf.length() > 0 ) {						// 前回のデータが残っている
					rtn.append( urlEncode( buf.toString() ) );	// ASCII以外のurlEncode処理と追加
					buf.setLength(0);							// 初期化
				}
				rtn.append( ch );
	//			// ファイル名に、";" や "%" が存在すると、認識できないため、半角文字でも変換しておきます。
	//			if(      ch == ';' ) {	rtn.append( "%3B" ); }	// 特殊処理
	//			else if( ch == '%' ) {	rtn.append( "%25" ); }
	//			else {					rtn.append( ch );    }	// ASCII文字の追加
			}
		}

		if( buf.length() > 0 ) {								// 残っている分
			rtn.append( urlEncode( buf.toString() ) );			// ASCII以外のurlEncode処理と追加
		}

		return rtn.toString();
	}

	/**
	 * UTF-8 でURLエンコードされた文字列をデコードします。
	 * このメソッドは、JDK1.4 以上でないと使用できません。
	 *
	 * @og.rev 5.4.5.0 追加
	 * @param	value デコードする文字列
	 *
	 * @return	 デコードされた文字列
	 */
	public static String urlDecode( final String value ) {
		try {
			return URLDecoder.decode( value,"UTF-8" );
		}
		catch( final UnsupportedEncodingException ex ) {
			final String errMsg = "UnsupportedEncodingException [UTF-8]" + CR
						+ ex.getMessage() ;
			throw new OgRuntimeException( errMsg,ex );
		}
		catch( final RuntimeException ex2 ) {		// 3.6.0.0 (2004/09/17)
			final String errMsg = "予期せぬエラー value=[" + value + "] , encode=[UTF-8]" + CR
						+ ex2.getMessage();
			throw new OgRuntimeException( errMsg,ex2 );
		}
	}

	/**
	 * 文字列の後ろのスペースを削除します。
	 * String クラスの trim()メソッドは、文字列の両方のスペースを削除しますが、
	 * この rTrim( String ) は、後ろの半角スペースのみ、詰めます。
	 * 注意：'\u0020' (スペース文字) より小さい文字を切り取ります。
	 *
	 * @param	str 元の文字列
	 *
	 * @return	後ろの半角スペースを詰めた、新しい文字列
	 */
	public static String rTrim( final String str ) {
		if( str == null )  { return null; }
		final int count = str.length();

		int len = count;

		while( 0 < len && str.charAt(len-1) <= ' ' ) {
			len--;
		}
		return len < count ? str.substring(0, len) : str;	// len==0 の場合は、空文字列
	}

	/**
	 * 文字列の後ろから、指定の文字を削除します。
	 * 右側の文字が、指定の文字の場合、除去します。
	 *
	 * @og.rev 8.0.2.0 (2021/11/30) 新規作成
	 *
	 * @param str 対象文字列
	 * @param chr 指定文字
	 * @return 右側から指定文字を除去後の文字列
	 */
	public static String rTrim( final String str, final char chr ) {
		if( str == null )  { return null; }
		final int count = str.length();

		int len = count;

		while( 0 < len && str.charAt(len-1) == chr ) {
			len--;
		}
		return len < count ? str.substring(0, len) : str;	// len==0 の場合は、空文字列
	}

	/**
	 * 文字列の後ろから、" .0" の文字を削除した数字型文字列を返します。
	 * 数字型文字列は、入力文字列の後ろの スペース、小数点、ゼロを削除します。
	 * また、先頭が、"." で始まる場合は、"0" を追加します。
	 * 例: "123.00" ⇒ "123" , ".123" ⇒ "0.123"
	 *
	 * @og.rev 3.8.8.1 (2007/01/10) 新規作成
	 *
	 * @param	str 元の文字列
	 *
	 * @return	数字文字列化された、新しい文字列
	 */
	public static String toNumber( final String str ) {
		if( str == null )  { return null; }

		String rtn = str.trim() ;

		final int adrs = rtn.indexOf( '.' );
		final int count = rtn.length();
		int len = count;

		if( adrs >= 0 ) {
			while( adrs < len && ".0".indexOf( rtn.charAt(len-1) ) >= 0 ) {
				len--;
			}
		}

		if( len < count ) { rtn = rtn.substring(0, len); }
		if( adrs == 0 ) { rtn = "0" + rtn; }

		return rtn ;
	}

	/**
	 * 文字列の前方のゼロ(０)を削除します。
	 * 先頭の０を削除するまえに、trim して、スペースを削除しておきます。
	 * すべてがゼロ(０)の場合は、"0" を返します。
	 * 小数点( 0.01 など )の場合は、先頭の 0 がすべて消えるとまずいので、
	 * "0." 部分は、残します。
	 *
	 * @og.rev 3.5.4.5 (2004/01/23) 新規追加
	 *
	 * @param	inStr 元の文字列
	 *
	 * @return	前方のゼロ(０)を削除した、新しい文字列
	 */
	public static String lTrim0( final String inStr ) {
		if( inStr == null )  { return null; }
		final String str = inStr.trim();
		final int count = str.length();

		int len = 0;
		while( count > len && str.charAt(len) == '0' ) {
			len++;
		}

		if( len == 0 ) { return str; }				// 先頭がゼロでない。
		else if( len == count ) { return "0"; }		// すべてがゼロ
		else if( str.charAt(len) == '.' ) { return "0" + str.substring(len); }
		else { return str.substring(len); }
	}

	/**
	 * 文字列配列の各要素の後ろのスペースを削除します。
	 * 個々の配列要素に対して、rTrim( String str ) を適用します。
	 * 元の文字列配列に直接作用するのではなく、新しい文字列配列に
	 * 結果をコピーして返します。
	 * ただし、元の文字列配列が、null か、length == 0 の場合は、
	 * 元の文字列配列(アドレス)を返します。
	 * 注意：'\u0020' (スペース文字) より小さい文字を切り取ります。
	 *
	 * @param	str 元の文字列配列(可変長引数)
	 *
	 * @return	後ろの半角スペースを詰めた、新しい文字列配列
	 */
	public static String[] rTrims( final String... str ) {
		// 6.1.1.0 (2015/01/17) 可変長引数でもnullは来る。
		if( str == null || str.length == 0 ) { return str; }

		String[] rtn = new String[str.length];	// str.length == 0 の場合、長さゼロの新しい配列を返す。
		for( int i=0; i<str.length; i++ ) {
			rtn[i] = rTrim( str[i] );
		}
		return rtn ;
	}

	/**
	 * 文字列の前後のダブルクオートを取り外します。
	 * 前後にダブルクオートが入っていなければ、そのままの文字列を返します。
	 * 前後に入っていない(片方のみなど)場合も、そのままの文字列を返します。
	 * ※ 先頭に、'0 が含まれる場合は、カンマを削除します。
	 *    従来は、ダブルクオートしてから、rTrim してましたが、trim してから、
	 *    ダブルクオート外しを行います。
	 *
	 * @og.rev 6.2.1.0 (2015/03/13) 先頭に、'0 が含まれる場合は、カンマを削除
	 *
	 * @param	str 元の文字列
	 *
	 * @return	ダブルクオートを取り外した新しい文字列
	 */
	public static String csvOutQuote( final String str ) {
		if( str == null )  { return null; }

		// 6.2.1.0 (2015/03/13) 先頭に、'0 が含まれる場合は、カンマを削除
		String rtn = str.trim();									// ①前後のスペース削除
		if( rtn.startsWith( "'0" ) ) { rtn = rtn.substring(1); }	// ②先頭の'0 のカンマ外し
		else {
			final int end = rtn.length();							// ③前後のダブルクオート外し
			if( end >= 2 && str.charAt(0) == '"' && str.charAt( end-1 ) == '"' ) {
				rtn = rtn.substring( 1,end-1 );
			}
		}
		return rtn;
	}

	/**
	 * 内部で使われる byte[] から String 生成 メソッド。
	 *
	 * @param	byteValue	 変換するバイト列
	 * @param	start		 変換開始アドレス
	 * @param	length		 変換バイト数
	 * @param	encode		 変換する文字エンコード
	 *
	 * @return	変換後文字列
	 */
	public static String makeString( final byte[] byteValue, final int start, final int length,final String encode ) {

		if( encode.startsWith( "Unicode" ) ) {
			final String errMsg = "Unicode文字列は、変換できません。[" + encode + "]"  + CR;
			throw new OgRuntimeException( errMsg );
		}

		String rtn = null;
		if( byteValue != null ) {
			try {
				// encode コードで変換されている byte[] を、String に変換。
				rtn = new String( byteValue,start,length,encode );
			} catch( final UnsupportedEncodingException ex ) {	  // 変換コードが存在しないエラー
				final String errMsg = "文字変換コードが存在しません。[" + encode + "]" + CR
							+ ex.getMessage() ;
				throw new OgRuntimeException( errMsg,ex );
			}
		}
		return rtn;
	}

	/**
	 * 指定の文字列をバイトコードに変換します。
	 * 引数の文字列が null の場合は、return は、byte[0] を返します。
	 *
	 * @param	value	 変換するストリング値
	 * @param	encode	 変換する文字エンコード
	 *
	 * @return	変換後文字列
	 */
	public static byte[] makeByte( final String value,final String encode ) {
		byte[] rtnByte = new byte[0];
		if( value != null ) {
			try {
				rtnByte = value.getBytes( encode );		// byte[] に encode コードで変換。
			} catch( final UnsupportedEncodingException ex ) {	  // 変換コードが存在しないエラー
				final String errMsg = "文字変換コードが存在しません。[" + encode + "]" + CR
							+ ex.getMessage();
				throw new OgRuntimeException( errMsg,ex );
			}
		}
		return rtnByte;
	}

	/**
	 * 半角スペースで固定長(半角換算の数)に変換した文字列を返します。
	 * 半角スペース埋めは、文字が半角、全角混在でもかまいません。
	 * 内部にセットした文字列は、変化しません。
	 *
	 * @param	str 	 Fill埋めする文字列
	 * @param	su_fill  Fill埋めする文字列の長さ。(半角換算の数)
	 *
	 * @return	Fill埋めした新しいStringを返す。
	 * @og.rtnNotNull
	 */
	public static String stringXFill( final String str,final int su_fill ) {
		char[] charValue ;

		if( str == null ) { charValue = new char[0]; }
		else              { charValue = str.toCharArray(); }
		final int len = charValue.length;

		if( su_fill < len ) {
			final String errMsg = "元の文字数がフォームより長いです。(数字が壊れます。)"
					+ "su_fill[" + su_fill + "], len[" + len + "]" + CR
					+ "input=[" + str + "]" + CR;
			throw new OgRuntimeException( errMsg );
		}

		final char[] charbuf = new char[ su_fill ];			// 移す char 配列を新規作成
		Arrays.fill( charbuf,' ' );
		System.arraycopy( charValue,0,charbuf,0,len );

		return new String( charbuf );		 // コピーした配列全てを文字列に変換
	}

	/**
	 * 半角スペースで固定長(半角換算の数)に変換した文字列を返します。
	 * 半角スペース埋めは、文字が半角、全角混在でもかまいません。
	 * 内部にセットした文字列は、変化しません。
	 *
	 * @og.rev 6.3.6.0 (2015/08/16) System.arraycopy が使える箇所は、置き換えます。
	 *
	 * @param	str 	 Fill埋めする文字列
	 * @param	su_fill  Fill埋めする文字列の長さ。(半角換算の数)
	 * @param	encode   Fill埋めする文字列の文字エンコード
	 *
	 * @return	Fill埋めした新しいStringを返す。
	 */
	public static String stringFill( final String str,final int su_fill,final String encode ) {
		if( su_fill < 0 ) {
			final String errMsg = "指定文字数が負です。[" + su_fill + "]";
			throw new OgRuntimeException( errMsg );
		}

		final byte[] byteValue = makeByte( str,encode );
		final int len = byteValue.length;

		// 内部文字列が指定長より長い場合
		if( len >= su_fill ) {
			return makeString( byteValue,0,su_fill,encode );
		}
		else {
			byte[] space = makeByte( " ",encode );
			int spaceLen = space.length ;
			if( spaceLen == 4 ) {					// encode が、UnicodeLittle の場合の特殊処理
				space[0] = space[2];
				space[1] = space[3];
				spaceLen = 2;
			}
			byte[] bytebuf = new byte[su_fill];
			// 6.3.6.0 (2015/08/16) System.arraycopy が使える箇所は、置き換えます。
			System.arraycopy( byteValue,0,bytebuf,0,len );		// 6.3.6.0 (2015/08/16)

			int k = 0;
			for( int j=len; j<su_fill; j++ ) {		// 余った部分は、スペース埋め
				if( k >= spaceLen ) { k = 0; }
				bytebuf[j] = space[k++];
			}
			return makeString( bytebuf,0,su_fill,encode ); 	// 新たに、すべての長さの部分文字列を作成する。
		}
	}

	/**
	 * 整数のフォーム( 12 で、整数部 １２桁を表す)に合った新しい文字列を作り、それを返します。
	 * 実行できるのは、整数の String に対してのみです。
	 * 内部にセットした文字列は、変化しません。
	 * 桁数がオーバーする場合は、RuntimeException を throw します。
	 *
	 *   String str = StringUtil.intFill( "123",10 );
	 *
	 *   実行結果："0000000123"
	 *
	 * @param	str	整数の String
	 * @param	su_fill	フォームを表す正の数字 ( 12 で、整数部 １２桁を表す)
	 *
	 * @return	整数のフォームに合った文字列
	 * @og.rtnNotNull
	 * @see		#intFill( int ,int )
	 * @throws	RuntimeException su_fill が、負の数か、元の文字数がフォームより長い場合、エラー
	 */
	public static String intFill( final String str,final int su_fill ) {
		if( su_fill < 0 ) {
			final String errMsg = "指定文字数が負です。[" + su_fill + "]";
			throw new OgRuntimeException( errMsg );
		}

		final char[] charbuf = new char[ su_fill ];			// 移す char 配列を新規作成
		Arrays.fill( charbuf,'0' );

		if( str == null ) { return new String( charbuf ); }

		final char[] charValue = str.toCharArray();
		final int len = charValue.length;

		if( su_fill < len ) {
			final String errMsg = "元の文字数がフォームより長いです。(数字が壊れます。) su_fill[" + su_fill + "], len[" + len + "]";
			throw new OgRuntimeException( errMsg );
		}

		System.arraycopy( charValue,0,charbuf,su_fill-len,len );

		return new String( charbuf );		 // コピーした配列全てを文字列に変換
	}

	/**
	 * 整数のフォーム( 12 で、整数部 １２桁を表す)に合った新しい文字列を作り、それを返します。
	 * 実行できるのは、正の整数に対してのみです。
	 * 桁数がオーバーする場合は、オーバーしたまま返します。
	 *
	 *   String str = StringUtil.intFill( 123,10 );
	 *
	 *   実行結果："0000000123"
	 *
	 * @og.rev 6.0.2.4 (2014/10/17) 新規追加
	 *
	 * @param	num	正の整数
	 * @param	su_fill	フォームを表す数字 ( 12 で、整数部 １２桁を表す)
	 *
	 * @return	整数のフォームに合った文字列
	 * @see		#intFill( String ,int )
	 * @throws	RuntimeException su_fill または、num が、負の数の場合、エラー
	 */
	public static String intFill( final int num,final int su_fill ) {
		if( num < 0 || su_fill < 0 ) {
			final String errMsg = "指定文字数が負です。num=[" + num + "] , su_fill=[" + su_fill + "]";
			throw new OgRuntimeException( errMsg );
		}

		String rtn = String.valueOf( num );

		final int len = su_fill - rtn.length();						// 桁の不足分を算出
		if( len > 0 ) {
			rtn = "00000000000000000000".substring( 0,len ) + rtn ;
		}

		return rtn;
	}

	/**
	 * 全角スペースで固定長(半角換算の数)に変換した文字列を返します。
	 *
	 * @param	str      Fill埋めする文字列
	 * @param	su_fill  Fill埋めする文字列の長さ。(半角換算の数)
	 * @param	encode   Fill埋めする文字列の文字エンコード
	 *
	 * @return	全角スペースでFill埋めした新しいStringを返す。
	 */
	public static String stringKFill( final String str,final int su_fill,final String encode ) {
		if( su_fill < 0 ) {
			final String errMsg = "指定文字数が負です。[" + su_fill + "]";
			throw new OgRuntimeException( errMsg );
		}

		final byte[] byteValue = makeByte( str,encode );
		final int len = byteValue.length;

		// 内部文字列が指定長より長い場合
		if( len >= su_fill ) {
			return makeString( byteValue,0,su_fill,encode );
		}
		else {
			final byte[] bytebuf = new byte[ su_fill ];
			System.arraycopy( byteValue, 0, bytebuf, 0, len );				// 6.3.9.0 (2015/11/06) System.arraycopy is more efficient(PMD)

			final byte[] space = makeByte( "　",encode );
			final int spaceLen = space.length ;
			int k = 0;
			for( int j=len; j<su_fill; j++ ) {		// 余った部分は、スペース埋め
				if( k >= spaceLen ) { k = 0; }
				bytebuf[j] = space[k++];
			}
			return makeString( bytebuf,0,su_fill,encode ); 	// 新たに、すべての長さの部分文字列を作成する。
		}
	}

	/**
	 * 小数点のフォームに合った新しい文字列を作り、文字列を返します。
	 * 現在は、小数点が頭に付いたり、最後に付く場合の対応はしていません。
	 * フォームは、12.4 で、 000000000010.1000 という形で、ピリオドを含みます。
	 *
	 *  // 半角 整数部 10 桁 小数部 ５桁で固定長の文字を得る。
	 *  String str = StringUtil.realFill( "123.45" ,10.5 ) ;
	 *
	 *  実行結果：0000000123.45000
	 *
	 * @param	str		整数の String
	 * @param	su_fill	フォームを表す実数	( 12.4 で、整数部 １２桁、小数部 ４桁 計１７桁 )
	 *
	 * @return	value	小数点のフォーム文字列
	 * @og.rtnNotNull
	 */
	public static String realFill( final String str,final double su_fill ) {
		if( su_fill < 0 ) {
			final String errMsg = "指定文字数が負です。[" + su_fill + "]";
			throw new OgRuntimeException( errMsg );
		}

		final int su_seisu = (int)(su_fill); 						   // 指定のフォームの整数部を取り出す。
		final int su_shosu = (int)(su_fill*10 - su_seisu*10);		   // 小数部を取り出しす。
		char[] charbuf = new char[ su_seisu + su_shosu + 1 ];  // 移す char 配列
		Arrays.fill( charbuf,'0' );

		if( str == null ) {
			charbuf[su_seisu] = '.' ;
			return new String( charbuf );
		}

		// 検査する文字列の加工(検査文字列は、インデックスの値とバイト数で文字数を求める。)
		// 小数点の位置を求める。 本当は、String クラスの indexOf で求めず、byte[] で検索すべきである。
		final int valueindex = str.indexOf( '.' );
		if( valueindex < 0 ) {									// valueform 自体が、合っていない。
			final String errMsg = "元の文字列に小数点が、含まれません。";
			throw new OgRuntimeException( errMsg );
		}
		// 6.4.1.1 (2016/01/16) PMD refactoring. Avoid declaring a variable if it is unreferenced before a possible exit point.

		// フォームの整数文字数 ー 加工文字の整数文字部 ＝ 転送先配列位置
		int toIndex = su_seisu - valueindex;							// 6.4.1.1 (2016/01/16)
		if( toIndex < 0 ) {
			final String errMsg = "元の数字が、フォームより長いです。(数字が壊れます。) form[" + su_fill + "]";
			throw new OgRuntimeException( errMsg );
		}
		int endIndex;
		// 転送先配列終了位置は、お互いの小数部の文字数により、短い方を選ぶ。
		final char[] charValue  = str.toCharArray();
		final int su_valueshosu = charValue.length - valueindex - 1 ;	// 小数部の文字数は、全文字数－整数文字数－１
		if( su_shosu < su_valueshosu ) { endIndex = su_seisu + su_shosu + 1; }
		else						   { endIndex = su_seisu + su_valueshosu + 1; }

		int fromIndex = 0;
		while( toIndex < endIndex ) {
			charbuf[toIndex++] = charValue[fromIndex++];	   // 転送(移し替え)
		}
		return new String( charbuf );		 // コピーした配列全てを文字列に変換
	}

	/**
	 * ストリングの部分文字列を,別の文字列に置換えたストリングを返します。
	 * 例えば,リターンコードを&lt; br /&gt;に置換えて,画面上に改行表示させるが可能です。
	 *
	 * @og.rev 5.0.0.1 (2009/08/15) 不要なオブジェクトの生成を抑制する。
	 *
	 * @param	target 元の文字列
	 * @param	from   置換元部分文字列
	 * @param	to	   置換先部分文字列
	 *
	 * @return	置換えた文字列
	 */
	public static String replace( final String target,final String from,final String to ) {
		if( target == null || from == null || to == null || target.indexOf( from ) < 0 ) { return target; }

		final StringBuilder strBuf = new StringBuilder( target.length() );

		int start = 0;
		int end   = target.indexOf( from,start );
		while( end >= 0 ) {
			strBuf.append( target.substring( start,end ) );
			strBuf.append( to );
			start = end + from.length();
			end   = target.indexOf( from,start );
		}

		if( start > 0 ) {
			strBuf.append( target.substring( start ) );
			return strBuf.toString();
		}
		else {
			return target;			// 3.4.0.2 (2003/09/05)
		}
	}

	/**
	 * 変数の置き換え処理を行います。
	 *
	 * 変換元の文字列から、prefix と、suffix で囲まれた文字列をピックアップして、
	 * func で指定の関数を、適用します。
	 * 変換元の文字列に、複数含まれていてもかまいません。
	 *
	 * これは、単純な変数ではなく、${env.XXX}または、{&#064;ENV..XXX} の XXX を環境変数に置き換えたり、
	 * {&#064;DATE.XXXX} を、日付文字列に置き換えたりする場合に、使用できます。
	 * 例えば、環境変数 の置き換えは、
	 * replaceText( orgText , "${env." , "}" , System::getenv ); または、
	 * replaceText( orgText , "{&#064;ENV." , "}" , System::getenv );
	 * とします。
	 * 日付関数の置き換えは、
	 * replaceText( orgText , "{&#064;DATE." , "}" , HybsDateUtil::getDateFormat );
	 * とします。
	 * orgTxt , prefix , suffix , func は必須で、null,ゼロ文字列、空白文字等の判定で、
	 * true の場合は、変換元の文字列 をそのまま返します。
	 *
	 * @og.rev 6.9.2.1 (2018/03/12) 新規追加
	 *
	 * @param  orgTxt 変換元の文字列
	 * @param  prefix 変換処理を行うキーワードの先頭文字列
	 * @param  suffix 変換処理を行うキーワードの終了文字列
	 * @param  func 変換処理を行う、関数型インタフェース
	 * @return  置換処理したテキスト
	 */
	public static String replaceText( final String orgTxt,final String prefix,final String suffix,final UnaryOperator<String> func ) {
		if( isEmpty( orgTxt,prefix,suffix ) || func == null ) { return orgTxt; }

		final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE );

		// 環境変数の処理
		int st0 = 0;
		int st1 = orgTxt.indexOf( prefix );
		final int preLen = prefix.length() ;
		final int sufLen = suffix.length() ;
		while( st1 >= 0 ) {
			final int ed = orgTxt.indexOf( suffix , st1 );
			if( ed >= 0 ) {
				buf.append( orgTxt.substring( st0,st1 ) );
				final String key = orgTxt.substring( st1 + preLen , ed );
				buf.append( func.apply( key ) );

				st0 = ed + sufLen ;								// suffix の長さ分
				st1 = orgTxt.indexOf( prefix,st0 );
			}
			else {
				final String errMsg = orgTxt + "の、prefix[" + prefix + "] と、suffix[" + suffix + "]の整合性が取れていません。" ;
				throw new OgRuntimeException( errMsg );
			}
		}

		return buf.append( orgTxt.substring( st0 ) ).toString();
	}

	/**
	 * 引数の AA:01 BB:02 CC:03 … 形式の、元値:新値のｽﾍﾟｰｽ区切り文字列を元に、
	 * 元値を新値に置き換えます。
	 * これは、部分置換ではなく、完全一致で処理します。
	 * caseStr が null や、マッチしなかった場合は、元の値を返します。
	 * その場合、ignoreCase=true としている場合は、元の文字列 も大文字に変換されて返されます。
	 *
	 * ゼロ文字列を元値や新値で使用することは可能ですが、スペースを使用することはできません。
	 *
	 * @og.rev 5.7.2.3 (2014/01/31) 新規追加
	 *
	 * @param	target		元の文字列
	 * @param	caseStr		置換リスト(AA:01 BB:02 CC:03 … 形式)。null の場合は、比較しない。
	 * @param	ignoreCase	true:大文字として比較 / false:そのまま比較
	 *
	 * @return	元の文字列を置き換えた結果。置換リストに存在しなければ、元の文字列を返す。
	 */
	public static String caseReplace( final String target,final String caseStr,final boolean ignoreCase ) {
		if( target == null ) { return target; }

		String rtn = ignoreCase ? target.toUpperCase(Locale.JAPAN) : target ;

		if( caseStr != null ) {
			final String caseTmp = " " + caseStr.trim() + " " ;		// CASE文字列の形式をそろえる。

			final int adrs = caseTmp.indexOf( " " + rtn + ":" );		// 前スペースと後ろコロンで、単語を確定する。
			if( adrs >= 0 ) {
				final int st = caseTmp.indexOf( ':' , adrs+1 );		// 最初のコロンの位置。元値:新値 の 新値 の取出
				final int ed = caseTmp.indexOf( ' ' , st+1 );			// コロンの次から、最初のスペースの位置
				if( st >= 0 && ed >= 0 ) {
					rtn = caseTmp.substring( st+1,ed );			// コロンの次から、スペースの前までを切り出す。
				}
			}
		}

		return rtn ;
	}

	/**
	 * String型の配列から、カンマ(,)で連結されたString を作成します。
	 * これは，配列を表示用に変換する為のものです。
	 * array2line( array, ",", 0 ); と同等です。
	 *
	 * @param	array		元の文字列配列(可変長引数)
	 *
	 * @return	一列に変換した文字列(引数がnullの場合は、長さ０の文字列を返す)
	 * @og.rtnNotNull
	 */
	public static String array2csv( final String... array ) {
		return array2line( array, ",", 0 );
	}

	/**
	 * String型の配列から、セパレーターで連結されたString を作成します。
	 * これは，配列を表示用に変換する為のものです。
	 *
	 * @param	array		元の文字列配列
	 * @param	separator	区切り記号
	 *
	 * @return	一列に変換した文字列(引数がnullの場合は、長さ０の文字列を返す)
	 * @og.rtnNotNull
	 */
	public static String array2line( final String[] array,final String separator ) {
		return array2line( array, separator,0 );
	}

	/**
	 * String型の配列から、セパレーターで連結されたString を作成します。
	 * これは，配列を表示用に変換する為のものです。
	 *
	 * @param	array		元の文字列配列
	 * @param	separator	区切り記号
	 * @param	start		配列の連結開始アドレス
	 *
	 * @return	一列に変換した文字列(引数がnullの場合は、長さ０の文字列を返す)
	 * @og.rtnNotNull
	 */
	public static String array2line( final String[] array,final String separator,final int start ) {
		if( array == null || array.length <= start ) { return ""; }

		final StringBuilder rtn = new StringBuilder( BUFFER_MIDDLE );

		rtn.append( valueOf( array[start] ) );
		for( int i=start+1; i<array.length; i++ ) {
			rtn.append( separator );
			rtn.append( valueOf( array[i] ) );
		}
		return rtn.toString();
	}

	/**
	 * Enumerationから、オブジェクト配列データを返します。
	 * これは，Enumerationを表示用に変換する為のものです。
	 *
	 * @param	enume   元のEnumeration
	 *
	 * @return	オブジェクト配列
	 * @og.rtnNotNull
	 */
	public static Object[] enume2Array( final Enumeration<?> enume ) {		// 4.3.3.6 (2008/11/15) Generics警告対応
		if( enume == null || ! enume.hasMoreElements() ) { return new Object[0]; }

		final ArrayList<Object> obj = new ArrayList<>();

		while( enume.hasMoreElements() ) {
			obj.add( enume.nextElement() );
		}
		return obj.toArray();
	}

	/**
	 * Enumerationから、オブジェクト配列データを返します。
	 * これは，Enumerationを表示用に変換する為のものです。
	 *
	 * @param	enume   元のEnumeration
	 * @param	objs - 配列が十分な大きさを持つ場合は、Vector の要素が格納される配列。
	 *			そうでない場合は、要素を格納するために同じ実行時の型の新しい配列が割り当てられる
	 * @return	オブジェクト配列
	 */
	public static Object[] enume2Array( final Enumeration<?> enume,final Object[] objs ) {	// 4.3.3.6 (2008/11/15) Generics警告対応
		if( enume == null || ! enume.hasMoreElements() ) { return objs ; }

		final ArrayList<Object> list = new ArrayList<>();

		while( enume.hasMoreElements() ) {
			list.add( enume.nextElement() );
		}
		return list.toArray( objs );
	}

	/**
	 * Iteratorから、セパレーターで連結されたString を作成します。
	 * これは，Enumerationを表示用に変換する為のものです。
	 *
	 * @param	ite 		元のIterator
	 * @param	separator	区切り記号
	 *
	 * @return	一列に変換した文字列
	 * @og.rtnNotNull
	 */
	public static String iterator2line( final Iterator<?> ite,final String separator ) {
		if( ite == null || ! ite.hasNext() ) { return ""; }

		final StringBuilder rtn = new StringBuilder( BUFFER_MIDDLE );

		rtn.append( valueOf( ite.next() ) );
		while( ite.hasNext() ) {
			rtn.append( separator );
			rtn.append( valueOf( ite.next() ) );
		}
		return rtn.toString();
	}

	/**
	 * カンマ(,)で連結された String を、配列に分解して、その値を返します。
	 * これは，たとえば、AAA,BBB,CCC などのリソースデータを受けてから配列に入れ直して、
	 * メニューなりリストを作成するのに便利です。
	 * 要素が空の場合は、必ずカンマの間にスペースを入れて記述してください。
	 * 分割後の文字列の前後のスペースは、削除されます。
	 *
	 * @param	csvData 	元のデータ
	 *
	 * @return	文字列配列(引数がnull、ゼロ文字列の場合は、サイズ0の配列を返す)
	 * @og.rtnNotNull
	 */
	public static String[] csv2Array( final String csvData ) {
		return csv2Array( csvData, ',', 0 );
	}

	/**
	 * 区切り文字で連結された String を、配列に分解して、その値を返します。
	 * これは，たとえば、AAA,BBB,CCC などのリソースデータを受けてから配列に入れ直して、
	 * メニューなりリストを作成するのに便利です。
	 * 連続した区切り文字は、１文字に分割します。
	 * 分割後の文字列の前後のスペースは、削除されます。
	 *
	 * @param	csvData 	元のデータ
	 * @param	separator	区切り文字
	 *
	 * @return	文字列配列(引数がnull、ゼロ文字列の場合は、サイズ0の配列を返す)
	 * @og.rtnNotNull
	 */
	public static String[] csv2Array( final String csvData,final char separator ) {
		return csv2Array( csvData,separator,0 );
	}

	/**
	 * 区切り文字で連結された String を、配列に分解して、その値を返します。
	 * これは，たとえば、AAA,BBB,CCC などのリソースデータを受けてから配列に入れ直して、
	 * メニューなりリストを作成するのに便利です。
	 * 連続した区切り文字は、１文字に分割します。
	 * 分割後の文字列の前後のスペースは、削除されます。(区切り文字がTABの場合を除く：7.0.4.0 (2019/05/31) )
	 * 第３の引数は、リターンする配列の個数を指定します。
	 * len=0 だけは特別で、分解したデータの個数分の配列を作成します。指定の長さが短い場合は、
	 * そこまで分のみ取り込みます。指定の長さが長い場合は、余分に配列を作成します。
	 * データがNULLや、ゼロ文字列の場合は、長さゼロの配列を返します。
	 * セットされる値は、"" です。
	 *
	 * @og.rev 3.8.5.1 (2006/05/08) 設定配列の数を指定できるように変更
	 * @og.rev 3.8.8.2 (2007/01/26) 分割後の値の前後のスペースは削除します。
	 * @og.rev 6.4.5.1 (2016/04/28) CSVTokenizer のインターフェースを、Iterator に変更。
	 * @og.rev 6.8.5.0 (2018/01/09) 引数lenを最大配列長として処理します。
	 * @og.rev 7.0.4.0 (2019/05/31) separatorがタブの場合は、trim() しないように変更
	 *
	 * @param	csvData 	元のデータ
	 * @param	separator	区切り文字
	 * @param	len			指定の最大長さの配列で返します(0の場合は、オリジナルの長さの配列か、長さゼロの配列)。
	 *
	 * @return	文字列配列(引数がnull、ゼロ文字列の場合は、サイズ(len)の配列を返す)
	 * @og.rtnNotNull
	 */
	public static String[] csv2Array( final String csvData,final char separator, final int len ) {
//		if( csvData == null || csvData.isEmpty() ) {
		if( isEmpty( csvData ) ) {						// 6.9.2.1 (2018/03/12) isEmpty 置き換え
			final String[] rtn = new String[len] ;
			Arrays.fill( rtn,"" );
			return rtn;
		}

		// 7.0.4.0 (2019/05/31) separatorがタブの場合は、trim() しないように変更
		final boolean useTrim = separator != '\t' ;

		final CSVTokenizer token = new CSVTokenizer( csvData,separator );
		final int count = len > 0 ? len : token.countTokens() ;
		final String[] rtn = new String[count];
		int i = 0;
		for( ; i<count && token.hasNext() ; i++ ) {
//			rtn[i] = token.next().trim();				// 3.8.8.2 (2007/01/26)
			rtn[i] = token.next();						// 3.8.8.2 (2007/01/26)
			if( useTrim ) { rtn[i] = rtn[i].trim(); }	// 7.0.4.0 (2019/05/31)
		}
		for( ; i<count; i++ ) {
			rtn[i] = "" ;
		}

		return rtn;
	}

	/**
	 * 区切り文字で連結された String を、配列に分解して、その値を返します。
	 * これは，#csv2Array( String,char,int ) メソッドで、分割時のデータが
	 * ゼロ文字列の場合に、セットする初期値です。
	 * 元のデータがnull、ゼロ文字列の場合は、defVal がセットされた サイズlenの配列を返します。
	 *
	 * データ数が、指定の len より少ない場合、先のメソッドでは、ゼロ文字列を追加していましたが、
	 * ここでは、初期値の defVal をセットします。
	 * また、分解後、trim() されたデータが、ゼロ文字列の場合も、defVal をセットします。
	 *
	 * @og.rev 6.8.5.0 (2018/01/09) CSVTokenizer のインターフェースを、Iterator に変更。
	 *
	 * @param	csvData 	元のデータ
	 * @param	separator	区切り文字
	 * @param	len			指定の長さの配列で返します。
	 * @param	defVal		分割したデータが、ゼロ文字列の場合の初期値
	 *
	 * @return	文字列配列(引数がnull、ゼロ文字列の場合は、サイズ0の配列を返す)
	 * @og.rtnNotNull
	 */
	public static String[] csv2Array( final String csvData,final char separator, final int len , final String defVal ) {
		// 処理の中で対応しても良いが、オリジナルを尊重しておきます。
		final String[] rtn = csv2Array( csvData,separator,len );

		for( int i=0; i<rtn.length; i++ ) {
			if( rtn[i].isEmpty() ) { rtn[i] = defVal ; }
		}

		return rtn;
	}

	/**
	 * 区切り文字で連結された String を、配列に分解して、その値を返します。
	 * これは，たとえば、AAA,BBB,CCC などのリソースデータを受けてから配列に入れ直して、
	 * メニューなりリストを作成するのに便利です。
	 * csv2Array と異なり、連続した区切り文字は、分割せずにトークンのみ切り出します。
	 * トークンは、カンマ(,)のみで区切り、その後 trim() により
	 * 前後のスペースを削除します。
	 *
	 * @param	csvData 	元のデータ
	 *
	 * @return	文字列配列
	 * @og.rtnNotNull
	 */
	public static String[] csv2ArrayOnly( final String csvData ) {
//		if( csvData == null || csvData.isEmpty() ) { return new String[0] ; }
		if( isEmpty( csvData ) ) { return new String[0]; }						// 6.9.2.1 (2018/03/12) isEmpty 置き換え

		final StringTokenizer token = new StringTokenizer( csvData,"," );

		final ArrayList<String> list = new ArrayList<>();
		while( token.hasMoreTokens() ) {
			final String temp = token.nextToken().trim();
			if( temp.length() > 0 ) { list.add( temp ); }
		}

		return list.toArray( new String[list.size()] );
	}

	/**
	 * カンマ(,)、ハイフン(-)で連結された String を、配列に分解して、その値を返す処理のスペシャル版です。
	 * 0,1,3,5-8,10-* などの数字文字列から、必要な数字をピックアップした数字配列を返します。
	 * 引数の maxNo は、"*" が指定された場合の、最大の数値です。
	 * よって、"*" は、単独(１文字)では、0-maxNo を表し、N-* では、N-maxNo を意味します。
	 * CSV形式で指定される値は、基本的に数字で、重複(1,1,2,2)、逆転(3,2,1)で指定できます。
	 * 5-3 と指定した場合は、5,4,3 に分解されます。逆順に登録されます。
	 * 重複削除、昇順並べ替え等が、必要な場合は、取得後の配列を操作してください。
	 *
	 * @og.rev 5.5.7.2 (2012/10/09) 新規追加
	 * @og.rev 6.2.6.0 (2015/06/19) アルファベットの対応を廃止し、数字配列のみサポートします。
	 *
	 * @param	csvData 0,1,3,5-8,10-* などのCSV-ハイフン文字列
	 * @param	maxNo "*" が指定された場合の、最大数
	 * @return	数字配列(引数がnull、ゼロ文字列の場合は、サイズ0の配列を返す)
	 * @og.rtnNotNull
	 */
	public static Integer[] csv2ArrayExt( final String csvData , final int maxNo )  {
//		if( csvData == null || csvData.isEmpty() ) { return new Integer[0] ; }
		if( isEmpty( csvData ) ) { return new Integer[0]; }				// 6.9.2.1 (2018/03/12) isEmpty 置き換え

		String strData = csvData.replace( "-*" , "-"  + maxNo );	// まず、N-* 形式を、N-maxNo に変換します。
		strData        = strData.replace( "*"  , "0-" + maxNo );	// その後、"*" 単独(１文字)を、0-maxNo に変換します。

		final ArrayList<Integer> noList = new ArrayList<>();

//		final String[] nos = strData.split( "," );		// カンマで分解。N , N-M , N-* のどれか
//		for( int i=0; i<nos.length; i++ ) {
//			final String sno = nos[i] ;
		for( final String sno : strData.split( "," ) ) {						// カンマで分解。N , N-M , N-* のどれか
			final int hai = sno.indexOf( '-' );
			// ハイフンが含まれているときは前後に分解して、間を埋める
			if( hai > 0 ) {
				int       ch1 = Integer.parseInt( sno.substring( 0,hai ) );		// 先頭からハイフンまで
				final int ch2 = Integer.parseInt( sno.substring( hai+1 ) );		// ハイフンから最後まで
				if( ch1 < ch2 ) { while( ch1 <= ch2 ) { noList.add( ch1++ ); } }
				else			{ while( ch1 >= ch2 ) { noList.add( ch1-- ); } }

			// また、一文字だけの場合は、アルファベット(a-z,A-Zなど)も指定する事が可能です。
			// アルファベットの場合は、"*" は指定できません。
			//	final String st1 = sno.substring( 0,hai );	// 先頭からハイフンまで
			//	final String st2 = sno.substring( hai+1 );	// ハイフンから最後まで
			//	if( st1.length() == 1 &&  st2.length() == 1 ) {		// ともに１文字の場合は、char化して処理。(英数字処理)
			//		char ch1 = st1.charAt(0);
			//		final char ch2 = st2.charAt(0);
			//		if( ch1 < ch2 ) { while( ch1 <= ch2 ) { noList.add( String.valueOf(ch1++ ) ); } }
			//		else			{ while( ch1 >= ch2 ) { noList.add( String.valueOf(ch1--) ); } }
			//	}
			//	else {
			//		int ch1 = Integer.parseInt( st1 );
			//		final int ch2 = Integer.parseInt( st2 );
			//		if( ch1 < ch2 ) { while( ch1 <= ch2 ) { noList.add( String.valueOf(ch1++ ) ); } }
			//		else			{ while( ch1 >= ch2 ) { noList.add( String.valueOf(ch1--) ); } }
			//	}
			}
			else {
				noList.add( Integer.valueOf( sno ) );
			}
		}
		return noList.toArray( new Integer[noList.size()] ) ;
	}

	/**
	 * Object 引数の文字列表現を返します。
	 * これは，String.valueOf とほぼ同じ動作をしますが、引数が null の場合に、
	 * "null" という文字列を返すのではなく、なにもない文字列 "" を返します。
	 *
	 * @param	obj    文字列表現すべき元のオブジェクト
	 *
	 * @return	引数が null の場合は、"" に等しい文字列。そうでない場合は、obj.toString() の値
	 * @og.rtnNotNull
	 */
	public static String valueOf( final Object obj ) {
		// 6.4.1.1 (2016/01/16) PMD refactoring. A method should have only one exit point, and that should be the last statement in the method
		return obj == null ? "" : obj.toString();
	}

	/**
	 * HTML上のエスケープ文字を変換します。
	 *
	 * HTMLで表示する場合にきちんとエスケープ文字に変換しておかないと
	 * Script を実行されたり、不要なHTMLコマンドを潜り込まされたりするため、
	 * セキュリティーホールになる可能性があるので、注意してください。
	 *
	 * @og.rev 5.8.2.2 (2014/12/19) アポストロフィの対応
	 * @og.rev 6.2.2.3 (2015/04/10) htmlフィルターに、BR→改行処理機能を追加。互換性の為のメソッド。
	 *
	 * @param	input HTMLエスケープ前の文字列
	 *
	 * @return	エスケープ文字に変換後の文字列
	 * @og.rtnNotNull
	 */
	public static String htmlFilter( final String input ) {
		return htmlFilter( input , false );
	}

	/**
	 * HTML上のエスケープ文字を変換します。
	 *
	 * HTMLで表示する場合にきちんとエスケープ文字に変換しておかないと
	 * Script を実行されたり、不要なHTMLコマンドを潜り込まされたりするため、
	 * セキュリティーホールになる可能性があるので、注意してください。
	 *
	 * 引数のフラグは、BR→改行コード の変換処理を行うかどうかを指定します。
	 * true が、変換処理を行うです。
	 * titleなどのTips表示する場合、改行は、「\n(改行コード)」で行います。
	 * (HTMLで取り扱うので、&amp;#13;&amp;#10; の方が良いかもしれない。
	 *  その場合は、エスケープ処理と順番を入れ替えないと、そのまま表示されてしまう。)
	 * 一方、タグ等で改行を行うには、&lt;BR/&gt; で改行を指定します。
	 * 改行については、「\n」文字列を指定する事で統一します。
	 *
	 * @og.rev 5.8.2.2 (2014/12/19) アポストロフィの対応
	 * @og.rev 6.2.2.3 (2015/04/10) htmlフィルターに、BR→改行処理機能を追加。
	 * @og.rev 6.2.5.0 (2015/06/05) htmlフィルターで、BR→改行処理が、引数間違いの為うまくできていなかった。
	 *
	 * @param	input HTMLエスケープ前の文字列
	 * @param	flag  [true:BR変換する/false:BR変換しない]
	 *
	 * @return	エスケープ文字に変換後の文字列
	 * @og.rtnNotNull
	 */
	public static String htmlFilter( final String input , final boolean flag ) {
//		if( input == null || input.isEmpty() ) { return ""; }
		if( isEmpty( input ) ) { return ""; }							// 6.9.2.1 (2018/03/12) isEmpty 置き換え

		String temp = input ;
		if( flag ) {
			temp = temp.replaceAll( "<[bB][rR][\\s/]*>" , "\n" );		// <br> を置き換える。
			temp = temp.replaceAll( "\\\\n"             , "\n" );		//「\n」という文字列を置き換える。
		}

		final StringBuilder rtn = new StringBuilder( BUFFER_MIDDLE );
		char ch;
		for( int i=0; i<temp.length(); i++ ) {
			ch = temp.charAt(i);		// 6.2.5.0 (2015/06/05) バグ
			switch( ch ) {
				case '<'  : rtn.append( "&lt;"   );	break;
				case '>'  : rtn.append( "&gt;"   );	break;
				case '"'  : rtn.append( "&quot;" );	break;
				case '\'' : rtn.append( "&apos;" );	break;		// 5.8.2.2 (2014/12/19) アポストロフィの対応
				case '&'  : rtn.append( "&amp;"  );	break;
				default   : rtn.append( ch );		break;		// 6.0.2.5 (2014/10/31) break追記
			}
		}
		return rtn.toString() ;
	}

	/**
	 * 「\n」という文字列を、BRタグに変換します。
	 *
	 * titleなどのTips表示する場合、改行は、「\n」で行います。
	 * 一方、タグ等で改行を行うには、&lt;BR/&gt; で改行を指定します。
	 * BRタグは、リソーステーブル等に書き込みにくい為、また、本当の改行コードも
	 * 書き込みにくい為、改行については、「\n」文字列を指定する事で対応できるように
	 * 統一します。
	 *
	 * @og.rev 6.2.2.3 (2015/04/10) 「\n」という文字列を、BRタグに変換する処理を追加
	 * @og.rev 7.0.1.0 (2018/10/15) XHTML → HTML5 対応(空要素の、"／＞" 止めを、"＞" に変更します)。
	 *
	 * @param	input BR,\n変換前の文字列
	 *
	 * @return	変換後の文字列
	 * @og.rtnNotNull
	 */
	public static String yenN2br( final String input ) {
		// 6.4.1.1 (2016/01/16) PMD refactoring. A method should have only one exit point, and that should be the last statement in the method
//		return input == null || input.isEmpty() ? "" : input.replaceAll( "\\\\n" , "<br/>" );	// \n ではなく、「\n」という文字列と変換
//		return  isEmpty( input ) ? "" : input.replaceAll( "\\\\n" , "<br/>" );					// \n ではなく、「\n」という文字列と変換	// 6.9.2.1 (2018/03/12) isEmpty 置き換え
		return  isEmpty( input ) ? "" : input.replaceAll( "\\\\n" , "<br>" );					// \n ではなく、「\n」という文字列と変換	// 6.9.2.1 (2018/03/12) isEmpty 置き換え
	}

	/**
	 * JavaScript 等の引数でのクオート文字をASCII変換します。
	 *
	 * JavaScript の引数の値に、ダブルクオート(")、シングルクオート(')が
	 * 含まれると、文字列を表す為に前後に指定しているクオートと混乱し、
	 * データを表現できないケースがあります。その場合には、クオート文字を
	 * ASCII文字に置き換える事で、指定の文字を渡すことが可能になります。
	 * ここでは、引数文字列に、ダブルクオート(")、シングルクオート(')が、
	 * 含まれると、それぞれ、ASCII コード(￥ｘ２２、￥ｘ２７)に置き換えます。
	 * なお、null は、ゼロ文字列に変換して返します。
	 *
	 * @param	input 入力文字列
	 *
	 * @return	クオート文字をASCII文字に置き換えた文字列
	 * @og.rtnNotNull
	 */
	public static String quoteFilter( final String input ) {
//		if( input == null || input.isEmpty() ) { return ""; }
		if( isEmpty( input ) ) { return ""; }							// 6.9.2.1 (2018/03/12) isEmpty 置き換え
		if( input.indexOf( '\'' ) < 0 && input.indexOf( '"' ) < 0 ) { return input; }

		final StringBuilder rtn = new StringBuilder( BUFFER_MIDDLE );
		char ch;
		for( int i=0; i<input.length(); i++ ) {
			ch = input.charAt(i);
			switch( ch ) {
				case '"'  : rtn.append( "\\x22" );	break;
				case '\'' : rtn.append( "\\x27" );	break;
				default   : rtn.append( ch );		break;		// 6.0.2.5 (2014/10/31) break追記
			}
		}
		return rtn.toString() ;
	}

	/**
	 * JSON形式で出力する場合のためのエスケープ処理です。
	 *
	 * @og.rev 5.9.6.4(2016/03/25) 新規作成
	 *
	 * @param	input XMLエスケープ前の文字列
	 *
	 * @return	エスケープ文字に変換後の文字列
	 */
	public static String jsonFilter( final String input ) {
//		if( input == null || input.length() == 0 ) { return ""; }
		if( isEmpty( input ) ) { return ""; }							// 6.9.2.1 (2018/03/12) isEmpty 置き換え

		final StringBuilder rtn = new StringBuilder( BUFFER_MIDDLE );
		for(int i=0; i<input.length(); i++) {
			final char ch = input.charAt(i);
			switch( ch ) {
				case '"'	: rtn.append( "\\\"" );	break;
				case '\\'	: rtn.append( "\\\\" );	break;
				case '/'	: rtn.append( "\\/"  );	break;
				case '\b'	: rtn.append( "\\b"  );	break;
				case '\f'	: rtn.append( "\\f"  );	break;
				case '\n'	: rtn.append( "\\n"  );	break;
				case '\r'	: rtn.append( "\\r"  );	break;
				case '\t'	: rtn.append( "\\t"  );	break;
				default		: rtn.append( ch );		break;
			}
		}
		return rtn.toString() ;
	}

	/**
	 * 特殊文字のエスケープを元に戻す処理です。
	 * 元に戻すことで、htmlとして、使用します。
	 * scriptタグは動作しないようにしています。
	 * またignoreで指定したタグを除いて&lt;にします。
	 *
	 * @og.rev 5.9.33.0 (2018/06/01) 新規作成
	 *
	 * @param input 特殊文字がエスケープされた文字列
	 * @param ignore 指定したタグを除いて&lt;にします
	 *
	 * @return エスケープ前の文字列
	 */
//	public static String escapeFilter( String input, String ignore ) {
	public static String escapeFilter( final String input, final String ignore ) {
//		if( input == null || input.length() == 0 ) { return ""; }
		if( isEmpty( input ) ) { return ""; }			// 6.9.8.1 (2018/06/11) isEmpty 置き換え

		// 6.9.8.1 (2018/06/11) static 変数 ESC_ARY として、定義
//		final String[][] list = new String[][] {
//			 { "&lt;", "<" }
//			,{ "&LT;", "<" }
//			,{ "&gt;", ">" }
//			,{ "&GT;", ">" }
//		};

//		for( final String[] trg : list ) {
//			input = replace( input, trg[0], trg[1] );
//		}

		String output = input;
		for( final String[] trg : ESC_ARY ) {
			output = replace( input, trg[0], trg[1] );
		}

		// XSS対策
		// jquery.cleditor.jsと同様の対応。
		// スクリプトは実行させない
//		input = input.replaceAll( "<(?=/?(?i)script)", "&lt;" );
		output = output.replaceAll( "<(?=/?(?i)script)", "&lt;" );

		// <と>の表示対応
		// jquery.cleditor.custom.jsのupdateFrame(TextRich用)に同様処理を実装。(エスケープ文字の\有無が異なります)
		//strong|font|a|br|p|span|div
		// 指定のタグ前方の<以外の<は、&lt;に変換する。
//		input = input.replaceAll( "<(?!/?(?i)("+ignore+")( |>|/))", "&lt;" );
		// 6.9.8.1 (2018/06/11) ignore の isEmpty 判定を追加
		if( !isEmpty( ignore ) ) {
			output = output.replaceAll( "<(?!/?(?i)("+ignore+")( |>|/))", "&lt;" );
		}

//		return input;
		return output;
	}

	/**
	 * 所定のキャラクタコードを取り除いた文字列を作成します。
	 *
	 * 実現したい機能は、String#replace( 'x','' ) 的な表現です。
	 * つまり、指定のキャラクタを取り除きたいのですが、上記コマンドでは、
	 * コンパイル時にエラーが発生します。
	 * 取り除きたいキャラクタコードが存在しない場合は、指定の文字列を
	 * そのまま返します。
	 *
	 * @param	value 処理対象の文字列
	 * @param	ch 取り除きたいキャラクタ
	 *
	 * @return	処理後の文字列
	 */
	public static String deleteChar( final String value,final char ch ) {
		if( value == null || value.indexOf( ch ) < 0 ) { return value; }
		char[] chs = value.toCharArray() ;
		int j=0;
		for( int i=0;i<chs.length; i++ ) {
			// 6.3.9.0 (2015/11/06) true/false を変更します。
			if( chs[i] != ch ) { chs[j++] = chs[i]; }
		}
		return String.valueOf( chs,0,j );
	}

	/**
	 * 文字列に含まれる、特定の文字の個数をカウントして返します。
	 *
	 * @og.rev 5.2.0.0 (2010/09/01)
	 *
	 * @param	value 処理対象の文字列
	 * @param	ch カウントする文字
	 *
	 * @return	カウント数
	 */
	public static int countChar( final String value,final char ch ) {
		if( value == null || value.indexOf( ch ) < 0 ) { return 0; }
//		final char[] chs = value.toCharArray() ;
		int cnt=0;
//		for( int i=0;i<chs.length; i++ ) {
//			if( chs[i] == ch ) { cnt++; }
//		}
		for( final char tmp : value.toCharArray() ) {
			if( tmp == ch ) { cnt++; }
		}
		return cnt;
	}

	/**
	 * CODE39 の 文字列を作成します。
	 *
	 * CODE39 は、『0～9, A～Z,-,・,　,$,/,+,%』のコードが使用できる
	 * バーコードの体系です。通常 * で始まり * で終了します。
	 * また、チェックデジット に、モジュラス43 が使われます。
	 * ここでは、指定の文字列の前後に、* を付与し、必要であれば
	 * チェックデジットも付与します。
	 * 指定の入力文字列には、* を付けないでください。
	 *
	 * @param	value 処理対象の文字列
	 * @param	checkDigit チェックデジットの付与(true:付ける/false:付けない)
	 *
	 * @return	処理後の文字列
	 * @og.rtnNotNull
	 */
	public static String code39( final String value,final boolean checkDigit ) {
		final String rtn = ( value == null ) ? "" : value ;
		if( ! checkDigit ) { return "*" + rtn + "*"; }

		int kei = 0;
		int cd;
		for( int i=0; i<rtn.length(); i++ ) {
			cd = MODULUS_43.indexOf( rtn.charAt(i) );
			if( cd < 0 ) {
				final String errMsg = "指定の文字中に、CODE39 規定外文字が使用されています。[" + rtn.charAt(i) + "]" ;
				throw new OgRuntimeException( errMsg );
			}
			kei += cd ;
		}
		final char digit = MODULUS_43.charAt( kei % 43 );

		return "*" + rtn + digit + "*" ;
	}

	/**
	 * 引数 inStr が、null または、ゼロ文字列の場合は､デフォルト値 def を返します。
	 * もちろん、inStr も def も null の場合は､null を返します。
	 *
	 * ※ 影響範囲が大きいので、空白文字の判定は入れません。
	 *
	 * @param    inStr 基準となる文字列
	 * @param    def デフォルト文字列
	 *
	 * @return   引数 inStr が、null または、ゼロ文字列の場合は､デフォルト値を返す。
	 */
	public static String nval( final String inStr,final String def ) {
//		return inStr == null || inStr.isEmpty() ? def : inStr ;
		return isEmpty( inStr ) ? def : inStr ;							// 6.9.2.1 (2018/03/12) isEmpty 置き換え
	}

	/**
	 * 引数 inStr が、null または、ゼロ文字列、空白文字列の場合は､デフォルト値 def を返します。
	 *
	 * 数値変換なので、空白文字列の場合も、デフォルト値を使用します。
	 *
	 * @param    inStr 基準となる文字列
	 * @param    def デフォルト数字
	 *
	 * @return   引数 inStr を変換した数字(int)。変換できない場合は デフォルト値 def
	 */
	public static int nval( final String inStr,final int def ) {
//		return inStr == null || inStr.isEmpty() ? def : Integer.parseInt( inStr ) ;
		return isNull( inStr ) ? def : Integer.parseInt( inStr ) ;		// 6.9.2.1 (2018/03/12) isNull 置き換え
	}

	/**
	 * 引数 inStr が、null または、ゼロ文字列、空白文字列の場合は､デフォルト値 def を返します。
	 *
	 * @param    inStr 基準となる文字列
	 * @param    def デフォルト数字
	 *
	 * @return   引数 inStr を変換した数字(long)。変換できない場合は デフォルト値 def
	 */
	public static long nval( final String inStr,final long def ) {
//		return inStr == null || inStr.isEmpty() ? def : Long.parseLong( inStr ) ;
		return isNull( inStr ) ? def : Long.parseLong( inStr ) ;			// 6.9.2.1 (2018/03/12) isNull 置き換え
	}

	/**
	 * 引数 inStr が、null または、ゼロ文字列、空白文字列の場合は､デフォルト値 def を返します。
	 *
	 * @og.rev 6.9.2.1 (2018/03/12) 新規追加
	 *
	 * @param    inStr 基準となる文字列
	 * @param    def デフォルト数字
	 *
	 * @return   引数 inStr を変換した数字(double)。変換できない場合は デフォルト値 def
	 */
	public static double nval( final String inStr,final double def ) {
		return isNull( inStr ) ? def : Double.parseDouble( inStr ) ;		// 6.9.2.1 (2018/03/12) isNull 置き換え
	}

	/**
	 * 引数 inStr が、null または、ゼロ文字列、空白文字列の場合は､デフォルト値 def を返します。
	 * 通常は、"true" または、 "TRUE" 文字列を、論理値の true に変換します。
	 * ただし、文字列長が 1文字の場合のみ、"0" 以外を true に変換します。
	 *
	 * @og.rev 6.8.0.1 (2017/06/30) つづり間違いに対応するため、厳密にチェックします。
	 *
	 * @param    inStr 基準となる文字列
	 * @param    def デフォルト論理値
	 *
	 * @return   引数 inStr を変換した論理値。変換できない場合は デフォルト値 def
	 */
	public static boolean nval( final String inStr,final boolean def ) {
		// 6.8.0.1 (2017/06/30) つづり間違いに対応するため、厳密にチェックします。
		if( inStr != null && inStr.length() > 1 && !"true".equalsIgnoreCase( inStr ) && !"false".equalsIgnoreCase( inStr ) ) {
			final String errMsg = "指定の文字列には、true か、false を指定してください。[" + inStr + "]" ;
			throw new OgRuntimeException( errMsg );
		}

		// 6.4.1.1 (2016/01/16) PMD refactoring.
//		return inStr == null || inStr.isEmpty()
		return isNull( inStr )											// 6.9.2.1 (2018/03/12) isNull 置き換え
					? def
					: inStr.length() == 1
						? ! "0".equals( inStr )
						: "true".equalsIgnoreCase( inStr ) ;
	}

	/**
	 * 引数 inStr が、null、"_"、ゼロ文字列、空白文字列の場合は､デフォルト値 def を返します。
	 *
	 * さらに、メモリ領域を節約する為、intern() の結果を返します。
	 * ※ #nval(String) との整合性を取るため、空白文字の判定は入れません。
	 *
	 * @og.rev 5.2.2.0 (2010/11/01) "_" の取り扱い変更
	 *
	 * @param    inStr 基準となる文字列
	 * @param    def デフォルト文字列
	 *
	 * @return  null、ゼロ文字列、"_"の場合は、デフォルト文字列を、そうでなければ、入力文字を返す。
	 */
	public static String nval2( final String inStr,final String def ) {
//		return inStr == null || inStr.isEmpty() || "_".equals( inStr ) ? def : inStr.intern() ;
		return isEmpty( inStr ) || "_".equals( inStr ) ? def : inStr.intern() ;		// 6.9.2.1 (2018/03/12) isNull 置き換え
	}

	/**
	 * 引数 inStr が、null または、ゼロ文字列、空白文字列の場合は､デフォルト値 def を返します。
	 * ただし、NULL代替文字(_)は デフォルト値 def2 に置き換えます。
	 *
	 * さらに、メモリ領域を節約する為、intern() の結果を返します。
	 * ※ #nval(String) との整合性を取るため、空白文字の判定は入れません。
	 *
	 * @og.rev 5.2.2.0 (2010/11/01) "_" の取り扱い変更
	 *
	 * @param    inStr 基準となる文字列
	 * @param    def デフォルト文字列
	 * @param    def2 NULL代替文字(_)の場合のデフォルト文字列
	 *
	 * @return  null、ゼロ文字列の場合は、def1文字列を、"_"の場合は、def2文字列を、そうでなければ、入力文字を返す。
	 */
	public static String nval2( final String inStr,final String def,final String def2 ) {
//		return inStr == null || inStr.isEmpty() ? def : "_".equals( inStr ) ? def2 : inStr.intern() ;
		return isEmpty( inStr ) ? def : "_".equals( inStr ) ? def2 : inStr.intern() ;		// 6.9.2.1 (2018/03/12) isNull 置き換え
	}

	/**
	 * 引数の CSV形式文字列 が、null または、ゼロ文字列の場合は､デフォルト値 def を返します。
	 * それ以外の場合は、CSV形式の文字列を正規化します。
	 *
	 * 正規化とは、カンマで区切った後、trim() して、ゼロ文字列でない場合のみカンマで再結合します。
	 *
	 * @og.rev 7.0.5.0 (2019/09/09) 新規追加
	 *
	 * @param    strCsv 基準となるCSV形式文字列
	 * @param    def デフォルト文字列
	 *
	 * @return   引数の CSV形式文字列 が、null または、ゼロ文字列の場合は､デフォルト値を返す。
	 */
	public static String nvalCsv( final String strCsv,final String def ) {
		return isNull( strCsv ) ? def : join( "," , csv2Array( strCsv ) );
	}

	/**
	 * 指定のCharSequence同士を連結させます。
	 * CharSequenceが、 null の場合は、連結しません。
	 * すべてが null の場合は、ゼロ文字列が返されます。
	 *
	 * ここでは、空白文字やタブ、改行コードも、指定されていれば、連結されます。
	 *
	 * @og.rev 6.0.2.4 (2014/10/17) 新規追加
	 * @og.rev 6.4.5.0 (2016/04/08) 引数を可変長CharSequenceに変更
	 *
	 * @param	strs... 可変長CharSequence
	 *
	 * @return	null以外の文字列が連結された状態
	 * @see		#join( String,CharSequence... )
	 * @og.rtnNotNull
	 */
	public static String nvalAdd( final CharSequence... strs ) {
		final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE );

		for( final CharSequence str : strs ) {
			if( str != null ) { buf.append( str ); }
		}

		return buf.toString();
	}

	/**
	 * 最初の null(または、ゼロ文字列、空白文字、タブや改行コード) 以外の値を返します。
	 * nval の 変数が、無制限版です。
	 * すべてが null(または、ゼロ文字列) の場合は、null が返されます。
//	 * 空白文字、タブや改行コードが来ても、返されます。
	 *
	 * @og.rev 6.0.2.4 (2014/10/17) 新規追加
	 * @og.rev 6.4.5.0 (2016/04/08) 引数を可変長CharSequenceに変更
	 * @og.rev 6.9.7.0 (2018/05/14) タブや空白文字も null系と判断。
	 *
	 * @param	strs... 可変長CharSequence
	 *
	 * @return	最初に現れた、null以外のCharSequenceを、Stringに変換したもの
	 */
//	public static CharSequence coalesce( final CharSequence... strs ) {
	public static String coalesce( final CharSequence... strs ) {
		for( final CharSequence str : strs ) {
//			if( str != null && str.length() > 0 ) { return str.toString(); }
//			if( ! isEmpty( str ) ) { return str.toString(); }				// 6.9.2.1 (2018/03/12) isEmpty 置き換え
			if( ! isNull( str ) ) { return str.toString(); }				// 6.9.7.0 (2018/05/14) タブや空白文字も null系と判断。
		}

		return null;
	}

	/**
	 * キーワードに対して、可変長引数の文字列が、含まれているかどうかを判定します。
	 * キーワードが、null でなく、比較先の文字列が、ひとつでも含まれると、true が返ります。
	 * 大文字小文字は、厳密に判定されます。
	 *
	 * key != null &amp;&amp; ( key.contains( val1 ) || key.contains( val2 ) ･･･ )
	 * の結果と同じです。
	 *
	 * @og.rev 6.4.4.2 (2016/04/01) contains 判定を行う新しいメソッドを新規追加します。
	 *
	 * @param	key     キーワード
	 * @param	vals... 比較先の可変長文字列(OR判定)
	 *
	 * @return	キーワード文字列の中に、比較先文字列がひとつでも含まれると、true
	 */
	public static boolean contains( final String key , final String... vals ) {
		if( key != null && vals != null ) {
			for( final String val : vals ) {
				if( val != null && key.contains( val ) ) { return true; }	// ひとつでも、contains があれば、true
			}
		}
		return false;
	}

	/**
	 * 連結文字列を使用して、可変長引数のCharSequenceを連結して返します。
	 * 連結文字列(delimiter)が、null の場合は、CharSequenceをそのまま連結(nvalAdd)していきます。
	 * 連結する文字列が null の場合は、連結しません。
	 * 連結文字列は、一番最後は出力されません。
	 * 処理できない場合は、長さゼロの文字列を返します。
	 *
	 * @og.rev 6.4.4.2 (2016/04/01) join 処理を行う新しいメソッドを新規追加します。
	 * @og.rev 6.4.5.0 (2016/04/08) 引数を可変長CharSequenceに変更
	 *
	 * @param	delimiter	連結文字列
	 * @param	vals... 連結するCharSequence
	 *
	 * @return	連結された結果の文字列
	 * @see		#nvalAdd( CharSequence... )
	 * @og.rtnNotNull
	 */
	public static String join( final String delimiter , final CharSequence... vals ) {
		if( delimiter == null ) { return nvalAdd( vals ); }

		final StringJoiner sjo = new StringJoiner( delimiter );
		for( final CharSequence val : vals ) {
//			if( val != null && val.length() > 0 ) { sjo.add( val ); }
			if( ! isEmpty( val ) ) { sjo.add( val ); }						// 6.9.2.1 (2018/03/12) isEmpty 置き換え
		}

		return sjo.toString();
	}

	/**
	 * 引数 vals が、一つでも、null または、ゼロ文字列の場合は､true を返します。
	 * それ以外は false を返します。
	 *
	 * isNull との違いは、スペースやタブ、改行だけの文字列は、null と判定しません。
	 * 文字列置換などで、スペースやタブなどと置換する場合、null やゼロ文字列では困る場合などの
	 * 判定で使用します。
	 *
	 * @og.rev 6.9.2.1 (2018/03/12) 新規追加
	 *
	 * @param    vals 判定するCharSequence(可変長引数)
	 *
	 * @return  NULL文字列関係の場合は、true を、そうでなければ、false を返す。
	 * @see		#isNull( CharSequence... )
	 */
	public static boolean isEmpty( final CharSequence... vals ) {
		if( vals != null && vals.length > 0 ) {
			for( final CharSequence val : vals ) {
				if( val == null || val.length()==0 ) { return true; }	// val の nullﾁｪｯｸ 必要？
			}
			return false;
		}
		return true;
	}

	/**
	 * 引数 vals が、一つでも、null または、ゼロ文字列、またはすべて空白文字(スペース、タブ、改行)の場合は､true を返します。
	 * それ以外は false を返します。
	 *
	 * isEmpty との違いは、判定前に、trim() 処理を行っているため、スペースやタブ、改行だけの文字列も、null と判定します。
	 * キーワードとして使用できないケースで、この判定を利用します。
	 * また、引数は可変長になっており、指定の｢どれか｣が、成立すれば、true と判定します。
	 * 処理的には、 val1 == null || val1.trim().length()==0 || val2 == null || val2.trim().length()==0 ･･･
	 *
	 * CharSequence 系の配列自体の null チェックも兼ねています。
	 *
	 * 注意は、オールスペースやタブ文字、改行文字も true になります。
	 *
	 * @og.rev 6.4.5.0 (2016/04/08) 引数をCharSequenceに変更
	 * @og.rev 6.9.0.0 (2018/01/31) 引数を可変長引数に変更
	 * @og.rev 6.9.2.1 (2018/03/12) 新規追加
	 *
	 * @param    vals 判定するCharSequence(可変長引数)
	 *
	 * @return  NULL文字列関係の場合は、true を、そうでなければ、false を返す。
	 * @see		#isNotNull( CharSequence... )
	 */
	public static boolean isNull( final CharSequence... vals ) {
		// 6.9.0.0 (2018/01/31) 引数を可変長引数に変更
		if( vals != null && vals.length > 0 ) {
			for( final CharSequence val : vals ) {
				if( val == null || val.length()==0 ) { return true; }	// val の nullﾁｪｯｸ 必要？

				boolean flag = true;
				// String.trim().isEmpty() の高速版
				for( int i=0; i<val.length(); i++ ) {
					if( !Character.isWhitespace( val.charAt(i) ) ) {	// 空白文字でなければ
						flag = false;									// 小ループを抜ける。
						break;
					}
				}
				if( flag ) { return true; }								// すべてが空白文字なら、true
			}
			return false;
		}
		return true;
	}

	/**
	 * 引数 vals が、すべて、null または、ゼロ文字列、またはすべて空白文字(スペース、タブ、改行)でない場合は､true を返します。
	 *
	 * #isNull( CharSequence... ) の反転です。
	 * そのため、｢すべて｣の文字列が、null か、ゼロ文字、空白文字でない場合のみ、true になります。
	 * isNull の表示上、'!' マークのあるなしは、判別しにくいため、メソッドを用意しています。
	 *
	 * @og.rev 6.9.2.1 (2018/03/12) 新規追加
	 *
	 * @param    vals 判定するCharSequence(可変長引数)
	 *
	 * @return  NULL文字列関係でない場合は、true を、｢どれか｣が、NULL文字列関係の場合は、false を返す。
	 * @see		#isNull( CharSequence... )
	 */
	public static boolean isNotNull( final CharSequence... vals ) {
		return !isNull( vals );
	}

	/**
	 * 浮動小数点数について、カンマ編集を行います。
	 *
	 * このメソッドでは、1.23 E12 などの数字は扱いません。通常の
	 * 数字とピリオドで構成された文字列のみ、変換対象になります。
	 * (ただし、不正な文字列を与えてもエラーチェックはしていません。)
	 * minFraction には、小数点部に与える固定値を指定します。入力文字列が
	 * その桁数より少ない場合は、０埋めします。
	 * 多い場合は、四捨五入します。
	 * minFraction が 0 の場合は、小数点は付きません。
	 * ".12" などの小数点は、必ず先頭に 0 が付きます。
	 * 入力文字列が null か、ゼロ文字列時は、そのまま入力データを返します。
	 *
	 * <pre>
	 *	DecimalFormat format = new DecimalFormat( "#,##0.00########" );
	 *	double dd = Double.parseDouble( val );
	 *	return format.format( dd );
	 * </pre>
	 * に対して、minFraction分の小数以下のゼロの指定と、inに ',' が
	 * 含まれた処理を追加した感じになります。
	 *
	 * @og.rev 4.0.0.0 (2007/10/26) 空白のトリム処理を追加
	 * @og.rev 6.0.4.0 (2014/11/28) 小数点指定が、0 の場合、小数点以下は表示しない
	 * @og.rev 6.2.0.0 (2015/02/27) 小数点指定の精度に合わせるのと、内部ロジック完全置換
	 * @og.rev 6.2.0.1 (2015/03/06) 互換性の関係で、nullかゼロ文字列の時は、そのまま、in を返す。
	 * @og.rev 6.3.6.1 (2015/08/28) throw new OgRuntimeException するのではなく、System.err.println する。
	 * @og.rev 6.3.8.5 (2015/10/16) ogErrMsgPrint 使用。
	 * @og.rev 6.4.2.0 (2016/01/29) ogErrMsgPrint メソッドを、ThrowUtil クラスに移動のため、修正
	 *
	 * @param	inStr			変換元の文字列
	 * @param	minFraction 	変換時の小数点以下の固定桁数
	 *
	 * @return	カンマ編集後の数字型文字列
	 */
	public static String numberFormat( final String inStr, final int minFraction ) {
//		if( inStr == null || inStr.isEmpty() ) { return inStr ; }		// 6.2.0.1 (2015/03/06) 互換性の関係
		if( isNull( inStr ) ) { return inStr ; }						// 6.9.2.1 (2018/03/12) isNull 置き換え

		String rtn = inStr;

		try {
			final double dd = StringUtil.parseDouble( rtn );

			if( FMT1.length > minFraction ) {
				synchronized( FMT1[minFraction] ) {
					rtn = FMT1[minFraction].format( dd );
				}
			}
			else {
				final String fmt = "#,##0." + ZERO.substring( 0,minFraction );
				rtn = new DecimalFormat( fmt ).format( dd );
			}
		}
		catch( final Throwable th ) {
			final String errMsg = "ERROR:" + th.getLocalizedMessage() + CR
							+ " in=[" + inStr + "] , minFraction=[" + minFraction + "]" ;
			// 6.3.8.5 (2015/10/16) ogErrMsgPrint 使用。
			System.err.println( ThrowUtil.ogThrowMsg( errMsg,th ) );				// 6.4.2.0 (2016/01/29)
		}

		return rtn;
	}

//	/**
//	 * 識別id に応じた オブジェクトを作成します。
//	 * 作成するには、デフォルトコンストラクターが必要です。
//	 *
//	 * @og.rev 7.2.5.0 (2020/06/01) ClassLoaderを引数にするnewInstanceメソッドを廃止します。
//	 *
//	 * @param	cls 作成するクラスのフルネーム
//	 *
//	 * @return	オブジェクト
//	 * @og.rtnNotNull
//	 * @throws RuntimeException 何らかのエラーが発生した場合
//	 */
//	public static Object newInstance( final String cls ) {
//		return newInstance( cls,Thread.currentThread().getContextClassLoader() );
//	}

	/**
	 * 指定されたクラスローダを使って、識別id に応じた オブジェクトを作成します。
	 * 作成するには、デフォルトコンストラクターが必要です。
	 * initialize パラメータは true 相当(それまでに初期化されていない場合だけ初期化)です。
	 *
	 * @og.rev 6.4.3.3 (2016/03/04) リフレクション系の例外の共通クラスに置き換えます。
	 * @og.rev 6.8.2.3 (2017/11/10) java9対応(cls.newInstance() → cls.getDeclaredConstructor().newInstance())
	 * @og.rev 7.2.5.0 (2020/06/01) ClassLoaderを引数にするnewInstanceメソッドを廃止します。
	 *
	 * @param	cls		作成するクラスのフルネーム
	 *
	 * @return	オブジェクト
	 * @og.rtnNotNull
	 * @throws RuntimeException 何らかのエラーが発生した場合
	 */
//	public static Object newInstance( final String cls,final ClassLoader loader ) {
	public static Object newInstance( final String cls ) {
		try {
//			return Class.forName( cls,true,loader ).getDeclaredConstructor().newInstance();			// 6.8.2.3 (2017/11/10)
			return Class.forName( cls ).getDeclaredConstructor().newInstance();						// 7.2.5.0 (2020/06/01)
		}
		catch( final NoSuchMethodException | InvocationTargetException ex ) {						// 6.8.2.3 (2017/11/10)
			final String errMsg = "指定のメソッド(コンストラクタ)が見つかりませんでした。class=[" + cls + "]" + CR
						+ ex.getMessage();
			throw new OgRuntimeException( errMsg,ex );
		}
		catch( final ReflectiveOperationException ex ) {
			final String errMsg = "Class.forName( String,boolean,ClassLoader ).newInstance() 処理に失敗しました class=[" + cls + "]" + CR
						+ ex.getMessage() ;
			throw new OgRuntimeException( errMsg,ex );
		}
	}

	/**
	 * 指定のURL文字列同士を連結させます。
	 * そのとき、後方URLが、絶対パスの場合は、連結せず 後方URLを返します。
	 * 第２引数以降は、絶対パス判定をせず直前のURLの末尾判定のみで連結します。
	 *
	 * 絶対パスかどうかは、通常のファイル属性と同様に、先頭が、'/' (UNIX)または、
	 * ２文字目が、":" (Windows)の場合、または、先頭が "\" (ネットワークパス)で
	 * 始まる場合で判断します。
	 * 連結時に、前方URLの末尾に "/" を付加します。
	 *
	 * 処理の互換性確保のため、第３引数の可変長引数を追加しています。
	 *
	 * @og.rev 5.0.0.1 (2009/08/15) 不要なオブジェクトの生成を抑制する。
	 * @og.rev 5.6.5.2 (2013/06/21) 第３引数を可変長引数に変更
	 * @og.rev 6.4.5.0 (2016/04/08) 引数をCharSequenceに変更
	 * @og.rev 6.4.7.2 (2016/06/20) 絶対パスの判定を、可変長URLにも適用する。
	 *
	 * @param	url1 先頭URLCharSequence
	 * @param	urls 後方URL可変長CharSequence(絶対パスの場合は、返り値)
	 *
	 * @return	URL文字列同士の連結結果 url1 + url2(url2が絶対パスの場合は、url2から連結開始)
	 * @og.rtnNotNull
	 */
	public static String urlAppend( final CharSequence url1,final CharSequence... urls ) {
		final StringBuilder rtnUrl = new StringBuilder( BUFFER_MIDDLE );

//		if( url1 != null && url1.length() > 0 ) { rtnUrl.append( url1 ) ; }
		if( isNotNull( url1 ) ) { rtnUrl.append( url1 ) ; }					// 6.9.2.1 (2018/03/12) isNotNull 置き換え

		// ここからが、追加分
		for( final CharSequence url : urls ) {
//			if( url != null && url.length() > 0 ) {
			if( isNotNull( url ) ) {										// 6.9.2.1 (2018/03/12) isNotNull 置き換え
				if( rtnUrl.length() == 0									// 戻り値が未設定の場合。
					||	 url.charAt(0) == '/'								// 実ディレクトリが UNIX
					||	 url.length() > 1 && url.charAt(1) == ':'			// 実ディレクトリが Windows
					||	 url.charAt(0) == '\\' ) {							// 実ディレクトリが ネットワークパス
						rtnUrl.setLength( 0 );				// クリア
						rtnUrl.append( url ) ;
				}
				else {
					final char ch = rtnUrl.charAt( rtnUrl.length()-1 ) ;	// 必ず、何らかのURLがappend済みのはず。
					if( ch == '/' || ch == '\\' ) {
						rtnUrl.append( url ) ;
					}
					else {
						rtnUrl.append( '/' ).append( url ) ;				// 6.0.2.5 (2014/10/31) char を append する。
					}
				}
			}
		}

		return rtnUrl.toString() ;
	}

	/**
	 * Unicode文字列の値を HTML のエスケープ記号(&amp;#xZZZZ;)に変換します。
	 *
	 * SJIS(JA16SJIS) で作成されたデータベースに、(NVARCHAR2)を使用して中国語等を登録するのは
	 * 非常に複雑でかつ、リスクが大きい処理になります。
	 * ORACLE殿でも、自信を持っては勧められない機能とのコメントを頂いています。
	 * そこで、HTMLでのエスケープ文字を使用して、Unicodeを文字列化して登録する為の
	 * DBType として、新規に作成します。
	 * ここでは、入力文字を、キャラクタ(char)型に分解し、(&amp;#xZZZZ;)に変換していきます。
	 * よって、通常に１文字(Shift-JISで２Byte,UTF-8で３Byte)が、８Byteになります。
	 * この変換された文字列を、HTML上でそのまま取り出すと、元のUnicode文字に戻る為、
	 * 通常のShift-JISでは、扱えない文字(中国語など)でも表示可能になります。
	 * ここでは、2バイト文字のみ、変換しています。
	 *
	 * @og.rev 6.4.5.0 (2016/04/08) 引数をCharSequenceに変更
	 *
	 * @param	value 変換前のCharSequence
	 *
	 * @return	HTMLのエスケープ記号(&amp;#xZZZZ;)
	 * @og.rtnNotNull
	 */
	public static String getUnicodeEscape( final CharSequence value ) {
//		if( value == null || value.length() == 0 ) { return ""; }
		if( isEmpty( value ) ) { return ""; }					// 6.9.2.1 (2018/03/12) isEmpty 置き換え

		final StringBuilder rtn = new StringBuilder( value.length() * 4 );

		for( int i=0; i<value.length(); i++ ) {
			final char ch = value.charAt(i);

			if( ch > 0xff ) {
				final String hex = Integer.toHexString( (int)ch ) ;
				rtn.append( UTF_STR[hex.length()] ).append( hex ).append( ';' );		// 6.0.2.5 (2014/10/31) char を append する。
			}
			else {
				rtn.append( ch );
			}
		}

		return rtn.toString();
	}

	/**
	 * HTML のエスケープ記号(&amp;#xZZZZ;)をUnicode文字列に戻します。
	 *
	 * HTMLでのエスケープ文字を使用して登録された文字を、Unicodeに戻します。
	 * (&amp;#xZZZZ;)の８Byteを、もとのキャラクタコードに戻し、合成します。
	 * ここでは、通常の文字列に混在したエスケープ文字も戻せるようにします。
	 *
	 * @og.rev 5.9.5.3 (2016/02/26) 無限ループ対応
	 *
	 * @param	value	HTMLのエスケープ記号(&amp;#xZZZZ;)を含む文字列
	 *
	 * @return	通常のUnicode文字列
	 * @og.rtnNotNull
	 */
	public static String getReplaceEscape( final String value ) {
//		if( value == null || value.isEmpty() ) { return ""; }
		if( isEmpty( value ) ) { return ""; }					// 6.9.2.1 (2018/03/12) isEmpty 置き換え

		final StringBuilder rtn = new StringBuilder( value );

		int st = rtn.indexOf( "&#" );
		while( st >= 0 ) {
			if( st+7 < rtn.length() && rtn.charAt( st+7 ) == ';' ) {
				final int ch = Integer.parseInt( rtn.substring( st+3,st+7 ),16 );
				rtn.replace( st,st+8, Character.toString( (char)ch ) );
			}
			st = rtn.indexOf( "&#",st + 1 ); // 5.9.5.3 (2016/02/26) 無限ループ対応
		}

		return rtn.toString();
	}

	/**
	 * 文字列をdoubleに変換します。
	 *
	 * これは、Double.parseDouble( value ) と、ほぼ同じ動作を行います。
	 * 内部的には、引数の カンマ(,) を削除した文字列を、Double.parseDouble( value )
	 * に渡します。
	 * また、引数が、null,ゼロ文字列,'_' の時には、0.0 を返します。
	 *
	 * @og.rev 6.3.9.0 (2015/11/06) もう少し判りやすくする。(処理速度は落ちてます。)
	 *
	 * @param	value	doubleに変換する元の文字列
	 *
	 * @return	変換後のdouble数値
	 */
	public static double parseDouble( final String value ) {
		double rtn ;

//		if( value == null || value.isEmpty() || value.equals( "_" ) ) {
		if( isNull( value ) || "_".equals( value ) ) {					// 6.9.2.1 (2018/03/12) isNull 置き換え
			rtn = 0.0d;
		}
		else if( value.indexOf( ',' ) < 0 ) {
			rtn = Double.parseDouble( value );
		}
		else {
			// 6.3.9.0 (2015/11/06) もう少し判りやすくする。(処理速度は落ちてます。)
			rtn = Double.parseDouble( value.replaceAll( ",","" ) );
		}

		return rtn ;
	}

	/**
	 * 引数からspanタグを取り除いて返します。
	 *
	 * 引数が、&lt;span ･･･&gt;YYYY&lt;/span&gt;形式の場合、YYYY のみ出力します。
	 * この処理では、先頭にspan が一つだけある場合、削除します。
	 * 複数の span や、div などを削除する場合は、#tagCut(String) メソッドで処理します。
	 *
	 * @og.rev 4.3.4.3 (2008/12/22) TableWriterで利用していたものを移動
	 *
	 * @param	 data 元のString文字列
	 *
	 * @return	spanタグが取り除かれた文字列(引数が null の場合は、そのまま null が返ります)
	 * @see		#tagCut(String)
	 */
	public static String spanCut( final String data ) {
		String rtn = data;
		if( data != null && data.startsWith( "<span" ) ) {
			final int st = data.indexOf( '>' );
			final int ed = data.indexOf( "</span>",st );
			rtn = data.substring( st+1,ed );
		}

		return rtn ;
	}

	/**
	 * 引数からタグを取り除いて返します。
	 *
	 * 引数が、&lt;xxxx ･･･&gt;YYYY&lt;/xxxx&gt;形式の場合、YYYY のみ出力します。
	 * この処理では、すべてのタグを削除し、BODY部をつなげます。
	 * &lt;xxxx/&gt; の様な、BODY要素を持たない場合は、ゼロ文字列になります。
	 *
	 * @og.rev 6.2.0.0 (2015/02/27) 引数からタグを削除し、BODY文字列を切り出します。
	 *
	 * @param	 data 元のString文字列
	 *
	 * @return	 タグが取り除かれた文字列(引数が null の場合は、そのまま null が返ります)
	 */
	public static String tagCut( final String data ) {
//		if( data == null || data.isEmpty() || data.indexOf( '<' ) < 0 ) { return data; }
		if( isEmpty( data ) || data.indexOf( '<' ) < 0 ) { return data; }			// 6.9.2.1 (2018/03/12) isEmpty 置き換え

		final StringBuilder rtn = new StringBuilder( BUFFER_MIDDLE );

		boolean tagOut = true;
		for( int i=0; i<data.length(); i++ ) {
			final char ch =data.charAt( i );
			if(      ch == '<' ) { tagOut = false; continue; }	// タグの開始
			else if( ch == '>' ) { tagOut = true;  continue; }	// タグの終了

			if( tagOut ) { rtn.append( ch ); }
		}

		return rtn.toString() ;
	}

	/**
	 * 簡易CSS形式のフォーマットを、Mapにセットします。
	 *
	 * 簡易CSS形式とは、セレクタのない、{ プロパティ1 : 値1 ; ・・・ } 形式とします。
	 * これを、プロパティ1 と 値1 のMap にセットする処理を行います。
	 * ブロックコメントは、削除されます。ラインコメントは使えません。
	 * また、同一プロパティが記述されている場合は、後処理を採用します。
	 *
	 * なお、入力テキストが、null か、{…} が存在しない場合は、空のMapを返します。
	 *
	 * @og.rev 5.6.5.2 (2013/06/21) 新規追加
	 * @og.rev 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。
	 * @og.rev 6.4.3.3 (2016/03/04) 戻すMapが、not null制限つきであることを示すため、ConcurrentMap に置き換えます。
	 * @og.rev 8.0.0.0 (2021/09/30) CSS形式の整合性チェックを行います。
	 *
	 * @param	 cssText 簡易CSS形式のフォーマット文字列
	 *
	 * @return	パース結果のMap(ConcurrentMap)
	 * @throws	OgRuntimeException 簡易CSS形式のフォーマットの整合性不良の時
	 * @og.rtnNotNull
	 */
	public static ConcurrentMap<String,String> cssParse( final String cssText ) {
		final ConcurrentMap<String,String> cssMap = new ConcurrentHashMap<>();

		if( cssText != null ) {
			// まずコメントを削除します。
			final StringBuilder buf = new StringBuilder( cssText );

			int ad1 = buf.indexOf( "/*" );
			while( ad1 >= 0 ) {
				final int ad2 = buf.indexOf( "*/" , ad1 );
				if( ad2 < 0 ) { buf.delete( ad1,buf.length() ); break; }		// 閉じてなければ以降を全削除
				buf.delete( ad1,ad2+2 );
				ad1 = buf.indexOf( "/*" );		// コメントは削除されたので、初めから検索する。
			}

			// 処理対象は、{ ～ } の間の文字列
			ad1 = buf.indexOf( "{" ) ;					// なければ、0:先頭から
			final int ad2 = buf.lastIndexOf( "}" );		// 後ろから検索(複数存在する場合の中間は無視する)

			if( ad1 >= 0 && ad2 > 0 ) {
				final String tempText = buf.substring( ad1+1,ad2 ).trim();		// これが処理対象の文字列
				if( tempText.isEmpty() ) { return cssMap ; }					// 空文字列の場合は抜ける

				if( tempText.contains( "//" ) ) {
					final String errMsg = "ラインコメント『//』は使えません。"+ CR
									+ " cssText=[" + cssText + "]" ;
					throw new OgRuntimeException( errMsg );
				}

				// 8.0.0.0 (2021/09/30) CSS形式の整合性チェックを行います。
				// KEY:VAL; なので、':' と ';' の個数は一致するはず
				int cnt1 = 0;
				int cnt2 = 0;
				boolean errFlag = false;
				for( int i=0; i<tempText.length(); i++ ) {
					final char ch = tempText.charAt(i);
					if( ch == ':' ) { cnt1++; }					// 必ず最初に見つかる
					else if( ch == ';' ) { cnt2++; }			// 次に見つかる

					if( cnt1 != cnt2 && cnt1 != cnt2+1 || ch == '{' || ch == '}' ) {	// ：と;の数と前後関係のﾁｪｯｸ
						errFlag = true;
						break;
					}
				}
				if( errFlag || cnt1 == 0 || cnt2 == 0 ) {								// ':' と ';' の個数が不一致か存在しない場合
					final String errMsg = "':' と ';' の個数が不一致か存在しないか、{} が不整合です。"+ CR
									+ " cssText=[" + cssText + "]" ;
					throw new OgRuntimeException( errMsg );
				}

				// 6.4.3.3 (2016/03/04) ちょっとした変更
				for( final String recode : tempText.split( ";" ) ) {	// KEY1 : VAL1; の ; で分割する。
					final int ad = recode.indexOf( ':' );
					if( ad > 0 ) {
						final String key = recode.substring( 0,ad ).trim();
						final String val = recode.substring( ad+1 ).trim();
						if( key.isEmpty() || val.isEmpty() ) { continue; }		// どちらかが空文字列の場合は、設定しない。

						cssMap.put( key,val );
					}
				}
			}
		}

		return cssMap ;
	}

//	/**
//	 * 引数から空白文字を削除して返します。
//	 *
//	 * @og.rev 5.6.9.4 (2013/10/31) TableWriterで利用していたものを移動
//	 * @og.rev 6.9.2.1 (2018/03/12) 使用箇所が、１箇所だけなので、StringUtilから移動する。
//	 *
//	 * @param	 data 元のString文字列
//	 *
//	 * @return	 空白文字が取り除かれた文字列
//	 */
//	public static String deleteWhitespace( final String data ) {
//		// 6.4.1.1 (2016/01/16) PMD refactoring. A method should have only one exit point, and that should be the last statement in the method
//		return data == null || data.isEmpty() ? data : data.replaceAll( "\\s", "" ) ;		// isNull 判定は使えない。
//	}

	/**
	 * 引数の文字列が、引数の char で始まるかどうか判定します[始まる場合は、true]。
	 *
	 * これは、PMDで言う所の、String.startsWith can be rewritten using String.charAt(0)
	 * の書き換え処理に相当します。
	 * boolean flag = data != null &amp;&amp; data.startsWith( chStr ); 的な処理を、
	 * boolean flag = data != null &amp;&amp; data.length() &gt; 0 &amp;&amp; data.charAt(0) == ch;
	 * に書き換える代わりに、このメソッドを使用します。
	 *
	 * 内部ロジックは、上記の相当します。
	 *
	 * @og.rev 6.2.0.0 (2015/02/27) １文字 String.startsWith の String.charAt(0) 変換
	 * @og.rev 6.4.5.0 (2016/04/08) 引数をCharSequenceに変更
	 *
	 * @param	 data 引数のCharSequence
	 * @param	 ch   チェックするchar
	 *
	 * @return	引数文字列が、nullでなく、ゼロ文字列でなく、引数char で始まる場合は、true
	 * @see		java.lang.String#startsWith(String)
	 */
	public static boolean startsChar( final CharSequence data , final char ch ) {
		return data != null && data.length() > 0 && data.charAt(0) == ch;			// スペースも判定対象にするため、isNull は使わない。
	}

	/**
	 * 引数から指定文字の分のバイト数で切った文字列を返します。
	 * 文字列のバイト数は指定のエンコードでカウントします。
	 * （文字の途中で切れる事はありません）
	 *
	 * @og.rev 5.9.1.3 (2015/10/30) 新規作成
	 * @og.rev 6.4.2.0 (2016/01/29) StringUtil#ogStackTrace(Throwable) を、ThrowUtil##ogStackTrace(Throwable) に変更。
	 *
	 * @param	org			元のString文字列
	 * @param	cutBytes	切るバイト数
	 * @param	enc			文字列のエンコード
	 *
	 * @return	 バイト数で切った文字列
	 */
	public static String cut( final String org, final int cutBytes, final String enc ) {
		try {
//			if( org == null || org.length() == 0 || cutBytes <= 0 || org.getBytes(enc).length <= cutBytes ) {		// isNul 判定は使いません。
//				return org;
//			}
			if( isEmpty( org,enc ) || cutBytes <= 0 || org.getBytes(enc).length <= cutBytes ) { return org; }		// 6.9.2.1 (2018/03/12) isEmpty 置き換え

			final StringBuilder cutSb = new StringBuilder( BUFFER_MIDDLE );
			final StringBuilder tmpSb = new StringBuilder( BUFFER_MIDDLE );

			for( int i=0; i<org.length(); i++ ) {
				final String cut = org.substring(i, i + 1);
				if( cutBytes < tmpSb.toString().getBytes(enc).length + cut.getBytes(enc).length ) {
					cutSb.append( tmpSb.toString() );
					break;
				}
				tmpSb.append(cut);
			}
			return cutSb.toString();
		}
		catch( final UnsupportedEncodingException ex ) {
			// 6.4.1.1 (2016/01/16) PMD refactoring. 	Avoid printStackTrace(); use a logger call instead.
			// 6.4.2.0 (2016/01/29) StringUtil#ogStackTrace(Throwable) を、ThrowUtil##ogStackTrace(Throwable) に変更。
			final String errMsg = "エンコードが不正のため、バイトカットできません。"
										+ " org=[" + org + "] , byteSize=[" + cutBytes + "] , encode=[" + enc + "]" ;

			System.err.println( ThrowUtil.ogThrowMsg( errMsg,ex ) );
			return org;
		}
	}

	/**
	 * 引数から指定文字の分のバイト数で切った文字列を返します。
	 * バイト数のカウントはUTF-8として行います。
	 *
	 * @og.rev 5.9.1.3 (2015/10/30) 新規作成
	 *
	 * @param	org 元のString文字列
	 * @param	cutBytes 切るバイト数
	 *
	 * @return	 バイト数で切った文字列
	 */
	public static String cut( final String org, final int cutBytes ) {
		return cut( org, cutBytes, "UTF-8");
	}

	/**
	 * 元の引数から開始文字列と終了文字列の間の文字列と、残りを連結した文字列を生成します。
	 * 戻り値は、連結した文字列で、切り取った間の文字列は、関数型インタフェースの引数に設定されます。
	 * なお、文字列の切り出しは、1度だけです。最初に見つかった、開始文字列と、その次に見つかった
	 * 終了文字列の間を切り取って、前後を連結するだけです。
	 *
	 * 元の文字列が、nullの場合は、return null で、関数I/Fは call されません。
	 * 元の文字列に、stStr(開始文字列)が含まれない/またはnullの場合は、return 元の文字列で、関数I/Fは call されません。
	 * 元の文字列に、stStr(開始文字列)が含まれ、edStr(終了文字列)が含まれない場合は、return 開始文字列以前と、(v) -> 開始文字列以降 をcallします。
	 * 元の文字列に、stStrもedStrも含む場合は、それらの間の文字列を削除(stStr,edStr含む)と、その間の文字列を引数に、
	 *   return 連結した文字列と、(v) -> 切り取った間の文字列 をcallします。
	 * 上記の場合、間の文字列が存在しない場合は、空文字列になります。
	 *
	 * stStr(開始文字列)とed(終了文字列)に関しては、trim() して、前後の空欄は削除します。
	 *
	 * 例） splitStartEnd( "abc class= XYZ ; efg" , "class=" , ";" , v -> call(v) );
	 * return "abc  efg"
	 * call   "XYZ"
	 *
	 * 例） splitStartEnd( "abc class= XYZ efg" , "class=" , ";" , v -> call(v) );
	 * return "abc "
	 * call   "XYZ efg"
	 *
	 * 例） splitStartEnd( "abc style= XYZ ; efg" , "class=" , ";" , v -> call(v) );
	 * return "abc style= XYZ ; efg"
	 * call   実行されない(nullだから)
	 *
	 * @og.rev 8.0.0.2 (2021/10/15) 新規作成
	 *
	 * @param	org  元のString文字列(nullも可)
	 * @param	stStr 開始文字列
	 * @param	edStr 終了文字列
	 * @param	cons  関数型インタフェースで、切り取った文字列を引数に呼び出されます。
	 *
	 * @return 削除後連結文字列と切り出した文字列の配列(必ず 2個の配列を返す)
	 */
	public static String splitStartEnd( final String org, final String stStr, final String edStr , final Consumer<String> cons ) {
		String val1 = org ;		// 切り取られた、前後の文字列を連結

		if( org != null && stStr != null ) {
			final int st = org.indexOf( stStr );
			if( st >= 0 ) {
				final int cutSt = st + stStr.length();				// 見つけた開始文字列＋開始文字列の長さ
				// edStr の nullﾁｪｯｸと、indexOf 判定を兼ねる。
				final int ed = edStr == null ? -1 : org.indexOf( edStr , cutSt );
				// stStr と、edStr の間の文字列を切り取る
				final String val2 ;		// 間の文字列
				if( ed >= 0 ) {
					val1 = org.substring( 0,st ) + org.substring( ed+edStr.length() );	// edStr は含まない
					// "abc class= XYZ ; efg" の場合、"XYZ" を取得
					val2 = org.substring( cutSt , ed ).trim();		// 前後の空白は除去
				}
				else {
					val1 = org.substring( 0,st );
					// "abc class= XYZ efg" の場合、"XYZ efg" を取得
					val2 = org.substring( cutSt ).trim();			// 前後の空白は除去
				}
				cons.accept( val2 );	// 関数型インタフェースの呼び出し。
			}
		}
		return val1 ;
	}

	/**
	 * Unicode文字列から元の文字列に変換します。(例："￥u3042" → "あ")
	 *
	 * @og.rev 8.0.2.0 (2021/11/30) 新規作成
	 *
	 * @param	unicode	Unicode文字列("\u3042")
	 * @return	通常の文字列
	 */
	public static String convertToOiginal( final String unicode ) {
		final StringBuilder rtn = new StringBuilder( unicode );

		int st = rtn.indexOf( "\\u" );
		while( st >= 0 ) {
			final int ch = Integer.parseInt( rtn.substring( st+2,st+6 ),16 );
			rtn.replace( st,st+6, Character.toString( (char)ch ) );

			st = rtn.indexOf( "\\u",st + 1 );
		}
		return rtn.toString();
	}
}
