package org.phosphoresce.commons.util;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 文字列に対する操作を行うメソッドを提供するクラス<br>
 *
 * @author Kitagawa<br>
 *
 *<!--
 * 更新日		更新者			更新内容
 * 2005/07/02	Kitagawa		新規作成
 * 2006/05/10	Kitagawa		isAscii、バイト操作関係メソッドインタフェースにエンコード方式パラメータ追加
 * 2006/07/10	Kitagawa		無駄なStringインスタンス生成を除去
 * 2007/01/19	Kitagawa		半角記号文字配列定義を追加
 * 2007/02/01	Kitagawa		renderHTML追加
 * 2007/02/24	Kitagawa		escapeFilename追加
 * 2008/05/17	Kitagawa		equals追加
 * 2008/10/08	Kitagawa		native2ascii、ascii2native追加
 * 2008/10/08	Kitagawa		render*メソッドをto*メソッドにリファクタリング
 * 2008/11/13	Kitagawa		equalsIgnoreCase追加
 * 2008/11/14	Kitagawa		contains、containsIgnoreCase追加
 * 2008/11/21	Kitagawa		nullsafe追加
 * 2008/12/10	Kitagawa		native2ascii、ascii2nativeメソッド名変更(nativeToAscii、asciiToNative)
 * 2008/12/10	Kitagawa		nativeToBase64、base64ToNativeメソッド追加
 * 2009/11/20	Kitagawa		toLowerCamelCase、toUpperCamelCase、toLowerSnakeCase、toUpperSnakeCaseメソッド追加
 * 2009/12/15	Kitagawa		encode(String, String, String)、convert(String, String)、space(int)追加
 * 2009/12/16	Kitagawa		supplementByte**メソッドをsupplyByte**に名称変更
 * 2009/12/16	Kitagawa		各種メソッドにおいてNull不許可なパラメータは予め例外をスローするように修正
 * 2009/12/16	Kitagawa		冗長的なコードを汎用メソッドに委譲するように修正
 * 2009/12/16	Kitagawa		isValidCharacers追加、isInLenght***→is***LengthIn名称変更
 * 2009/12/17	Kitagawa		nativeToAscii、asciiToNativeをNative2AsciiUtilに委譲するように修正
 * 2009/12/18	Kitagawa		paddingChar、paddingCharHead、paddingCharFoot追加
 * 2010/07/14	Kitagawa		splitメソッド追加
 * 2010/10/04	Kitagawa		isValidityDateFormatメソッド追加
 * 2011/04/22	Kitagawa		charAtbメソッド追加
 *-->
 */
public final class StringUtil {

	/** 空文字列 */
	public static final String EMPTY = "";

	/** ヘキサ文字列 */
	private static final String[] HEX_STRINGS = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };

	/** 半角記号文字列 */
	public static final char[] SIGNS = { '!', '"', '#', '$', '%', '&', '\'', '(', ')', '=', '~', '|', '`', '{', '+', '*', '}', '<', '>', '?', '_', '-', '^', '\\', '@', '[', ';', ':', ']', ',', '.', '/' };

	/** BASE64変換文字列 */
	public static final String BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";

	/** 改行コード(CR+LF) */
	public static final String NEWLINE_CODE_CRLF = new String(new byte[] { 0x0D, 0x0A });

	/** 改行コード(CR) */
	public static final String NEWLINE_CODE_CR = new String(new byte[] { 0x0D });

	/** 改行コード(LF) */
	public static final String NEWLINE_CODE_LF = new String(new byte[] { 0x0A });

	/** ディフォルトJavaキャラクタセット */
	public static final String DEFAULT_JAVA_CHARSET = "MS932";

	/** ディフォルト出力ファイルキャラクタセット */
	public static final String DEFAULT_OUTPUTFILE_CHARSET = "8859_1";

	/**
	 * コンストラクタ<br>
	 */
	private StringUtil() {
		//
	}

	/**
	 * 指定された文字列がnullの場合に空文字として返却します。<br>
	 * @param value 文字列
	 * @return 文字列がnullの場合に空文字を返却
	 */
	public static String nullsafe(String value) {
		return value == null ? EMPTY : value;
	}

	/**
	 * 文字列が空であるか判定します。<br>
	 * @param source 判定対象文字列
	 * @return 文字列が空文字列またはnullの場合はtrueを返却
	 */
	public static boolean isEmpty(String source) {
		return source == null || source.length() == 0;
	}

	/**
	 * 指定された文字が半角文字か判定します。<br>
	 * @param c 判定対象文字
	 * @return 半角文字の場合はtrueを返却
	 */
	public static boolean isAscii(char c) {
		return isByteLengthIn(String.valueOf(c), 1);
	}

	/**
	 * 指定された文字列が全て半角文字で構成されているか判定します。<br>
	 * nullが指定された場合は無条件に半角文字であると判断します。<br>
	 * @param value 判定対象文字列
	 * @return 全て半角文字で構成されている場合、trueを返却
	 */
	public static boolean isAscii(String value) {
		if (isEmpty(value)) {
			return true;
		}
		//return value.length() == lenb(value);
		char[] cs = value.toCharArray();
		for (int i = 0; i <= cs.length - 1; i++) {
			if (!isAscii(cs[i])) {
				return false;
			}
		}
		return true;
	}

	/**
	 * 指定された文字列が数値として扱えるか判定します。<br>
	 * このメソッドは空文字列は不正な数値として判定します。<br>
	 * @param value 判定対象文字列
	 * @return 数字として扱える場合はtrueを返却
	 */
	public static boolean isDigit(String value) {
		try {
			//Double.valueOf(value);
			new BigDecimal(value);
			return true;
		} catch (Exception e) {
			return false;
		}
	}

	/**
	 * 指定された文字列が許可された数字のみで構成された文字列か判定します。<br>
	 * 当メソッドは単純に文字列が数字のみであるかの判定を行い、数値として扱えるかは判断しません。<br>
	 * 即ち"0.1"のような数字文字以外を含む数値はfalseを返却します。<br>
	 * 数値として扱えるかの反映を行う場合は{@link #isDigit(String)}を使用して下さい。<br>
	 * @param value 判定対象文字列
	 * @return 文字列が数字のみで構成された文字列の場合はtrueを返却
	 */
	public static boolean isNumeric(String value) {
		if (isEmpty(value)) {
			return true;
		} else {
			for (int i = 0; i <= value.length() - 1; i++) {
				char c = value.charAt(i);
				if (!(c >= '0' && c <= '9')) {
					return false;
				}
			}
		}
		return true;
	}

	/**
	 * 指定された文字列が許可された数字のみで構成された文字列か判定します。<br>
	 * 当メソッドは単純に文字列が数字のみであるかの判定を行い、数値として扱えるかは判断しません。<br>
	 * 即ち"0.1"のような数字文字以外を含む数値はfalseを返却します。<br>
	 * 数値として扱えるかの反映を行う場合は{@link #isDigit(String)}を使用して下さい。<br>
	 * @param value 判定対象文字列
	 * @param exclude 許可文字配列
	 * @return 文字列が数字のみで構成された文字列の場合はtrueを返却
	 */
	public static boolean isNumeric(String value, char[] exclude) {
		if (isEmpty(value)) {
			return true;
		} else {
			for (int i = 0; i <= value.length() - 1; i++) {
				char c = value.charAt(i);
				if (!(c >= '0' && c <= '9')) {
					boolean skip = false;
					for (int j = 0; j <= exclude.length - 1; j++) {
						if (c == exclude[j]) {
							skip = true;
						}
					}
					if (!skip) {
						return false;
					}
				}
			}
		}
		return true;
	}

	/**
	 * 指定された文字列が許可された英字のみで構成された文字列か判定します。<br>
	 * @param value 判定対象文字列
	 * @return 文字列が英字のみで構成された文字列の場合はtrueを返却
	 */
	public static boolean isAlphabet(String value) {
		if (isEmpty(value)) {
			return true;
		} else {
			for (int i = 0; i <= value.length() - 1; i++) {
				char c = value.charAt(i);
				if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))) {
					return false;
				}
			}
		}
		return true;
	}

	/**
	 * 指定された文字列が許可された英字のみで構成された文字列か判定します。<br>
	 * @param value 判定対象文字列
	 * @param exclude 許可文字配列
	 * @return 文字列が英字のみで構成された文字列の場合はtrueを返却
	 */
	public static boolean isAlphabet(String value, char[] exclude) {
		if (isEmpty(value)) {
			return true;
		} else {
			for (int i = 0; i <= value.length() - 1; i++) {
				char c = value.charAt(i);
				if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))) {
					boolean skip = false;
					for (int j = 0; j <= exclude.length - 1; j++) {
						if (c == exclude[j]) {
							skip = true;
						}
					}
					if (!skip) {
						return false;
					}
				}
			}
		}
		return true;
	}

	/**
	 * 指定された文字列が許可された英数字のみで構成された文字列か判定します。<br>
	 * @param value 判定対象文字列
	 * @return 文字列が英数字のみで構成された文字列の場合はtrueを返却
	 */
	public static boolean isAlphaNumeric(String value) {
		if (isEmpty(value)) {
			return true;
		} else {
			for (int i = 0; i <= value.length() - 1; i++) {
				char c = value.charAt(i);
				if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))) {
					return false;
				}
			}
		}
		return true;
	}

	/**
	 * 指定された文字列が許可された英数字のみで構成された文字列か判定します。<br>
	 * @param value 判定対象文字列
	 * @param exclude 許可文字配列
	 * @return 文字列が英数字のみで構成された文字列の場合はtrueを返却
	 */
	public static boolean isAlphaNumeric(String value, char[] exclude) {
		if (isEmpty(value)) {
			return true;
		} else {
			for (int i = 0; i <= value.length() - 1; i++) {
				char c = value.charAt(i);
				if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))) {
					boolean skip = false;
					for (int j = 0; j <= exclude.length - 1; j++) {
						if (c == exclude[j]) {
							skip = true;
						}
					}
					if (!skip) {
						return false;
					}
				}
			}
		}
		return true;
	}

	/**
	 * 指定された文字列が期待する文字のみで構成された文字列であるか判定します。<br>
	 * @param value 判定対象文字列
	 * @param chars 期待文字配列
	 * @return 文字列が期待する文字のみで構成された文字列である場合にtrueを返却
	 */
	public static boolean isValidCharacers(String value, char[] chars) {
		if (isEmpty(value)) {
			return true;
		} else {
			for (int i = 0; i <= value.length() - 1; i++) {
				char c = value.charAt(i);
				boolean valid = false;
				for (int j = 0; j <= chars.length - 1; j++) {
					if (c == chars[j]) {
						valid = true;
					}
				}
				if (!valid) {
					return false;
				}
			}
		}
		return true;
	}

	/**
	 * 指定された文字列が指定されたバイト長範囲内にあるか判定します。<br>
	 * @param value 判定対象文字列
	 * @param length チェック範囲長(byte長)
	 * @param encode エンコード方式
	 * @return 文字列が指定されたバイト長範囲内に収まっている場合はtrueを返却
	 */
	public static boolean isByteLengthIn(String value, int length, String encode) {
		if (isEmpty(value)) {
			return true;
		} else {
			return lenb(value, encode) > length ? false : true;
		}
	}

	/**
	 * 指定された文字列が指定されたバイト長範囲内にあるか判定します。<br>
	 * @param value 判定対象文字列
	 * @param length チェック範囲長(byte長)
	 * @return 文字列が指定されたバイト長範囲内に収まっている場合はtrueを返却
	 */
	public static boolean isByteLengthIn(String value, int length) {
		return isByteLengthIn(value, length, null);
	}

	/**
	 * 指定された文字列が指定された文字長(文字数)内にあるか判定します。<br>
	 * @param value 判定対象文字列
	 * @param length チェック範囲長(文字数)
	 * @return 文字列が指定された文字長(文字数)内に収まっている場合はtrueを返却
	 */
	public static boolean isLengthIn(String value, int length) {
		if (isEmpty(value)) {
			return true;
		} else {
			return len(value) > length ? false : true;
		}
	}

	/**
	 * 指定されたキャラクタセットが有効なキャラクタセット文字列か判定します。<br>
	 * @param encode キャラクタセット定義文字列
	 * @return 有効なキャラクタセットの場合trueを返却
	 */
	public static boolean isValidityEncode(String encode) {
		final String value = "foo";
		try {
			value.getBytes(encode);
		} catch (UnsupportedEncodingException e) {
			return false;
		}
		return true;
	}

	/**
	 * 指定された値が指定された日付書式に合致する文字列であるか判定します。<br>
	 * @param value 検査対象文字列
	 * @param format 日付書式
	 * @return 正常な日付文字列である場合にtrueを返却
	 */
	public static boolean isValidityDateFormat(String value, String format) {
		if (isEmpty(value)) {
			return true;
		} else {
			try {
				new SimpleDateFormat(format).parse(value);
			} catch (Throwable e) {
				return false;
			}
			return true;
		}
	}

	/**
	 * 指定された文字列同士が同一であるか判定します。<br>
	 * 文字列が両者ともnullの場合も同一として判定します。<br>
	 * 当メソッドでは大文字小文字の判断を行います。<br>
	 * @param value1 比較対象文字列
	 * @param value2 比較対象文字列
	 * @return 同一の文字列の場合にtrueを返却
	 */
	public static boolean equals(String value1, String value2) {
		if (value1 == null && value2 == null) {
			return true;
		} else if (value1 != null) {
			return value1.equals(value2);
		} else {
			return value2.equals(value1);
		}
	}

	/**
	 * 指定された文字列同士が同一であるか判定します。<br>
	 * 文字列が両者ともnullの場合も同一として判定します。<br>
	 * また、当メソッドでは大文字小文字の区別は行いません。<br>
	 * @param value1 比較対象文字列
	 * @param value2 比較対象文字列
	 * @return 同一の文字列の場合にtrueを返却
	 */
	public static boolean equalsIgnoreCase(String value1, String value2) {
		if (value1 == null && value2 == null) {
			return true;
		} else if (value1 != null) {
			return value1.equalsIgnoreCase(value2);
		} else {
			return value2.equalsIgnoreCase(value1);
		}
	}

	/**
	 * 指定された比較対象文字列配列内に比較対象と同一である文字があるか判定します。<br>
	 * @param compares 比較文字列配列
	 * @param value 比較対象文字列
	 * @return 同一である文字列がある場合にtrueを返却
	 */
	public static boolean contains(String[] compares, String value) {
		if (compares == null) {
			return false;
		}
		for (int i = 0; i <= compares.length - 1; i++) {
			String compare = compares[i];
			boolean result = equals(compare, value);
			if (result) {
				return true;
			}
		}
		return false;
	}

	/**
	 * 指定された比較対象文字列配列内に比較対象と同一である文字があるか判定します。<br>
	 * @param compares 比較文字列配列
	 * @param value 比較対象文字列
	 * @return 同一である文字列がある場合にtrueを返却
	 */
	public static boolean containsIgnoreCase(String[] compares, String value) {
		if (compares == null) {
			return false;
		}
		for (int i = 0; i <= compares.length - 1; i++) {
			String compare = compares[i];
			boolean result = equalsIgnoreCase(compare, value);
			if (result) {
				return true;
			}
		}
		return false;
	}

	/**
	 * 指定された文字列を区切り文字で分割したインデックス位置のトークンを提供します。<br>
	 * 指定されたインデックスがトークン数の範囲外の場合は空文字列が返却されます。<br>
	 * @param value 文字列
	 * @param delim 区切り文字
	 * @param index 取得トークンインデックス
	 * @return トークン文字列
	 */
	public static String split(String value, String delim, int index) {
		if (isEmpty(value)) {
			return "";
		}
		String[] tokens = value.split(delim);
		if (tokens.length - 1 < index) {
			return "";
		}
		return tokens[index];
	}

	/**
	 * 指定された文字列を区切り文字で分割したトークンを提供します。<br>
	 * @param value 文字列
	 * @param delim 区切り文字
	 * @param excludeEmpty 空のトークンを除外する場合にtrueを指定
	 * @return トークン文字列
	 */
	public static String[] splits(String value, String delim, boolean excludeEmpty) {
		if (isEmpty(value)) {
			return new String[0];
		}
		List result = new LinkedList();
		String[] tokens = value.split(delim);
		for (int i = 0; i <= tokens.length - 1; i++) {
			if (excludeEmpty && StringUtil.isEmpty(tokens[i])) {
				continue;
			}
			result.add(tokens[i]);
		}
		return (String[]) result.toArray(new String[0]);
	}

	/**
	 * 指定された数の半角スペース文字列を生成します。<br>
	 * @param count 生成するスペース数
	 * @return 半角スペース文字列
	 */
	public static String space(int count) {
		StringBuffer buffer = new StringBuffer();
		for (int i = 0; i <= count - 1; i++) {
			buffer.append(' ');
		}
		return buffer.toString();
	}

	/**
	 * 指定された文字列の文字長を取得します。<br>
	 * String#length()と同様の処理となるがNullPointerExceptionの考慮を加える。<br>
	 * @param value 判定文字列
	 * @return 文字長
	 */
	public static int len(String value) {
		return isEmpty(value) ? 0 : value.length();
	}

	/**
	 * 指定された文字列のバイト長を取得します。<br>
	 * @param value 判定文字列
	 * @param encode エンコード方式(nullが指定された場合はディフォルトエンコードが行われます)
	 * @return 文字列バイト長
	 */
	public static int lenb(String value, String encode) {
		try {
			if (encode == null) {
				return isEmpty(value) ? 0 : value.getBytes().length;
			} else {
				return isEmpty(value) ? 0 : value.getBytes(encode).length;
			}
		} catch (UnsupportedEncodingException e) {
			throw new RuntimeException("不正なエンコード方式が指定されました", e);
		}
	}

	/**
	 * 指定された文字列のバイト長を取得します。<br>
	 * @param value 判定文字列
	 * @return 文字列バイト長
	 */
	public static int lenb(String value) {
		return lenb(value, null);
	}

	/**
	 * 指定した文字数範囲で文字列をカットして提供します。<br>
	 * 基本的にはjava.lang.String#substringと変わりませんが、nullオブジェクトの場合に例外がスローされません。<br>
	 * また、null文字列が指定された場合は例外は発生せずnull文字列がそのまま返却されます。<br>
	 * @param value カット対象文字列
	 * @param position 開始位置
	 * @param length 取得文字数
	 * @return 編集を行った文字列
	 */
	public static String substr(String value, int position, int length) {
		if (value == null) {
			return null;
		}
		if (length == 0) {
			return EMPTY;
		}
		if (position > len(value) - 1) {
			return EMPTY;
		}
		if (position < 0) {
			position = 0;
		}
		if (length < 0) {
			length = 0;
		}
		if (len(value) < position + length) {
			length = len(value) - position;
		}
		return value.substring(position, position + length);
	}

	/**
	 * 指定したバイト長で文字列をカットして提供します。<br>
	 * 指定されたエンコード方式が不正だった場合は編集せずに返却します。<br>
	 * また、null文字列が指定された場合は例外は発生せずnull文字列がそのまま返却されます。<br>
	 * @param value カット対象文字列
	 * @param position 開始位置
	 * @param length 取得バイト数
	 * @param encode キャラクタセット
	 * @return 編集を行った文字列
	 */
	public static String substrb(String value, int position, int length, String encode) {
		if (value == null) {
			return null;
		}
		if (length == 0) {
			return EMPTY;
		}
		if (position > lenb(value, encode) - 1) {
			return EMPTY;
		}
		if (position < 0) {
			position = 0;
		}
		if (length < 0) {
			length = 0;
		}
		if (lenb(value, encode) < position + length) {
			length = lenb(value, encode) - position;
		}

		StringBuffer headBuffer = new StringBuffer();
		for (int i = 0; true; i++) {
			if (lenb(headBuffer.toString(), encode) >= position) {
				break;
			}
			headBuffer.append(value.substring(i, i + 1));
		}
		String headSpace = space(lenb(headBuffer.toString(), encode) - position);

		String buffer = headSpace + value.substring(headBuffer.toString().length());
		while (lenb(buffer, encode) > length) {
			buffer = buffer.substring(0, buffer.length() - 1);
		}
		buffer = buffer + space(length - lenb(buffer, encode));

		return buffer;
	}

	/**
	 * 指定したバイト長で文字列をカットして提供します。<br>
	 * 指定されたエンコード方式が不正だった場合は編集せずに返却します。<br>
	 * また、null文字列が指定された場合は例外は発生せずnull文字列がそのまま返却されます。<br>
	 * @param value カット対象文字列
	 * @param position 開始位置
	 * @param length 取得バイト数
	 * @return 編集を行った文字列
	 */
	public static String substrb(String value, int position, int length) {
		return substrb(value, position, length, null);
	}

	/**
	 * 指定された文字列を左から指定された長さで抽出して取得します。<br>
	 * @param value 抽出対象文字列
	 * @param length 抽出文字列長
	 * @return 指定された文字列を左から指定された長さで抽出した文字列
	 */
	public static String left(String value, int length) {
		return substr(value, 0, length);
	}

	/**
	 * 指定された文字列を左から指定されたバイト長で抽出して取得します。<br>
	 * @param value 抽出対象文字列
	 * @param length 抽出文字列バイト長
	 * @param encode エンコード方式
	 * @return 指定された文字列を左から指定された長さで抽出した文字列
	 */
	public static String leftb(String value, int length, String encode) {
		return substrb(value, 0, length, encode);
	}

	/**
	 * 指定された文字列を左から指定されたバイト長で抽出して取得します。<br>
	 * @param value 抽出対象文字列
	 * @param length 抽出文字列バイト長
	 * @return 指定された文字列を左から指定された長さで抽出した文字列
	 */
	public static String leftb(String value, int length) {
		return leftb(value, length, null);
	}

	/**
	 * 指定された文字列を右から指定された長さで抽出して取得します。<br>
	 * @param value 抽出対象文字列
	 * @param length 抽出文字列長
	 * @return 指定された文字列を右から指定された長さで抽出した文字列
	 */
	public static String right(String value, int length) {
		return substr(value, len(value) - length, length);
	}

	/**
	 * 指定された文字列を右から指定されたバイト長で抽出して取得します。<br>
	 * @param value 抽出対象文字列
	 * @param length 抽出文字列バイト長
	 * @param encode エンコード方式
	 * @return 指定された文字列を右から指定された長さで抽出した文字列
	 */
	public static String rightb(String value, int length, String encode) {
		return substrb(value, lenb(value) - length, length, encode);
	}

	/**
	 * 指定された文字列を右から指定されたバイト長で抽出して取得します。<br>
	 * @param value 抽出対象文字列
	 * @param length 抽出文字列バイト長
	 * @return 指定された文字列を右から指定された長さで抽出した文字列
	 */
	public static String rightb(String value, int length) {
		return rightb(value, length, null);
	}

	/**
	 * 指定された文字列を指定されたインデックスの開始、終了の範囲で抽出して取得します。<br>
	 * @param value 抽出対象文字列
	 * @param start 抽出開始インデックス
	 * @param end 抽出終了インデックス
	 * @return 指定された文字列を指定されたインデックスの開始、終了の範囲で抽出した文字列
	 */
	public static String mid(String value, int start, int end) {
		String buffer = right(value, len(value) - start);
		return left(buffer, end - start + 1);
	}

	/**
	 * 指定された文字列を指定されたバイトインデックスの開始、終了の範囲で抽出して取得します。<br>
	 * @param value 抽出対象文字列
	 * @param start 抽出開始バイトインデックス
	 * @param end 抽出終了バイトインデックス
	 * @param encode エンコード方式
	 * @return 指定された文字列を指定されたインデックスの開始、終了の範囲で抽出した文字列
	 */
	public static String midb(String value, int start, int end, String encode) {
		String buffer = rightb(value, lenb(value, encode) - start, encode);
		return leftb(buffer, end - start + 1, encode);
	}

	/**
	 * 指定された文字列を指定されたバイトインデックスの開始、終了の範囲で抽出して取得します。<br>
	 * @param value 抽出対象文字列
	 * @param start 抽出開始バイトインデックス
	 * @param end 抽出終了バイトインデックス
	 * @return 指定された文字列を指定されたインデックスの開始、終了の範囲で抽出した文字列
	 */
	public static String midb(String value, int start, int end) {
		return midb(value, start, end, null);
	}

	/**
	 * 指定された文字列の置換を行い、その結果を取得します。<br>
	 * 当メソッドは正規表現は使用しません。<br>
	 * @param source 処理の対象の文字列
	 * @param before 置換前の文字列
	 * @param after 置換後の文字列
	 * @return 置換処理後の文字列
	 */
	public static String replace(String source, String before, String after) {
		if (isEmpty(source) || source.indexOf(before) == -1) {
			return source;
		}
		StringBuffer buffer = new StringBuffer();
		int index = source.indexOf(before);
		buffer.append(source.substring(0, index) + after);
		if (index + before.length() < source.length()) {
			String rest = source.substring(index + before.length(), source.length());
			buffer.append(replace(rest, before, after));
		}
		return buffer.toString();
	}

	/**
	 * 指定された文字列を全て逆順に並べなおして提供します。<br>
	 * @param source 並べ替える対象の文字列
	 * @return 指定された文字列を全て逆順に並べなおした文字列
	 */
	public static String reverse(String source) {
		return new StringBuffer(source).reverse().toString();
	}

	/**
	 * 指定された文字列内に含まれる指定文字の数を取得します。<br>
	 * @param source 検索対象文字列
	 * @param ch 検索文字
	 * @return 文字列内に含まれる指定文字の数
	 */
	public static int countChar(String source, char ch) {
		if (isEmpty(source)) {
			return 0;
		}
		int counter = 0;
		for (int i = 0; i <= source.length() - 1; i++) {
			if (source.charAt(i) == ch) {
				counter++;
			}
		}
		return counter;
	}

	/**
	 * 見た目の文字長を指定された長さまで補正します。<br>
	 * 当メソッドは正確なバイト長での補正ではなく全角2byte、半角1byteとして見た目の補正を行います。<br>
	 * その為、UTF-8等におけるバイト長の正確な補正は行えません。<br>
	 * バイト長の正確な補正を行う場合は{@link #supplyByte(String, int, String, char, boolean)}を使用してください。<br>
	 * @param value 元文字列
	 * @param lenb 調整バイト長
	 * @param c 補正文字
	 * @param headSupply 先頭に補完する場合はtrueを指定
	 * @return 補完された文字列
	 */
	public static String paddingChar(String value, int lenb, char c, boolean headSupply) {
		final String RENDER_CHARSET = "Shift_JIS";
		if (value == null) {
			throw new NullPointerException("value");
		}
		if (lenb <= convert(value, RENDER_CHARSET).length) {
			return value;
		}

		StringBuffer buffer = new StringBuffer(value);
		while (convert(buffer.toString(), RENDER_CHARSET).length < lenb) {
			if (headSupply) {
				buffer.insert(0, c);
			} else {
				buffer.append(c);
			}
		}

		return buffer.toString();
	}

	/**
	 * 見た目の文字長を指定された長さまで先頭に文字を追加して補正します。<br>
	 * 当メソッドは正確なバイト長での補正ではなく全角2byte、半角1byteとして見た目の補正を行います。<br>
	 * その為、UTF-8等におけるバイト長の正確な補正は行えません。<br>
	 * バイト長の正確な補正を行う場合は{@link #supplyByte(String, int, String, char, boolean)}を使用してください。<br>
	 * @param value 元文字列
	 * @param lenb 調整バイト長
	 * @param c 補正文字
	 * @return 補完された文字列
	 */
	public static String paddingCharHead(String value, int lenb, char c) {
		return paddingChar(value, lenb, c, true);
	}

	/**
	 * 見た目の文字長を指定された長さまで末尾に文字を追加して補正します。<br>
	 * 当メソッドは正確なバイト長での補正ではなく全角2byte、半角1byteとして見た目の補正を行います。<br>
	 * その為、UTF-8等におけるバイト長の正確な補正は行えません。<br>
	 * バイト長の正確な補正を行う場合は{@link #supplyByte(String, int, String, char, boolean)}を使用してください。<br>
	 * @param value 元文字列
	 * @param lenb 調整バイト長
	 * @param c 補正文字
	 * @return 補完された文字列
	 */
	public static String paddingCharFoot(String value, int lenb, char c) {
		return paddingChar(value, lenb, c, false);
	}

	/**
	 * 指定された文字列を指定バイト長となるまで文字を追加して提供します。<br>
	 * @param value 元文字列
	 * @param lenb 調整バイト長
	 * @param encode キャラクタセット
	 * @param c 補正文字
	 * @param headSupply 先頭に補完する場合はtrueを指定
	 * @return 補完された文字列
	 */
	public static String supplyByte(String value, int lenb, String encode, char c, boolean headSupply) {
		if (value == null) {
			throw new NullPointerException("value");
		}
		if (lenb <= lenb(value, encode)) {
			return value;
		}

		StringBuffer buffer = new StringBuffer(value);
		while (lenb(buffer.toString(), encode) < lenb) {
			if (headSupply) {
				buffer.insert(0, c);
			} else {
				buffer.append(c);
			}
		}

		return leftb(buffer.toString(), lenb, encode);
	}

	/**
	 * 指定された文字列を指定バイト長となるまで文字を追加して提供します。<br>
	 * @param value 元文字列
	 * @param lenb 調整バイト長
	 * @param c 補正文字
	 * @param headSupply 先頭に補完する場合はtrueを指定
	 * @return 補完された文字列
	 */
	public static String supplyByte(String value, int lenb, char c, boolean headSupply) {
		return supplyByte(value, lenb, DEFAULT_JAVA_CHARSET, c, headSupply);
	}

	/**
	 * 指定された文字列を指定バイト長となるまで先頭にスペースを追加して提供します。<br>
	 * @param value 元文字列
	 * @param lenb 調整バイト長
	 * @param encode キャラクタセット
	 * @return 補完された文字列
	 */
	public static String supplyByteSpaceHead(String value, int lenb, String encode) {
		return supplyByte(value, lenb, encode, ' ', true);
	}

	/**
	 * 指定された文字列を指定バイト長となるまで先頭にスペースを追加して提供します。<br>
	 * @param value 元文字列
	 * @param lenb 調整バイト長
	 * @return 補完された文字列
	 */
	public static String supplyByteSpaceHead(String value, int lenb) {
		return supplyByte(value, lenb, DEFAULT_JAVA_CHARSET, ' ', true);
	}

	/**
	 * 指定された文字列を指定バイト長となるまで末尾にスペースを追加して提供します。<br>
	 * @param value 元文字列
	 * @param lenb 調整バイト長
	 * @param encode キャラクタセット
	 * @return 補完された文字列
	 */
	public static String supplyByteSpaceFoot(String value, int lenb, String encode) {
		return supplyByte(value, lenb, encode, ' ', false);
	}

	/**
	 * 指定された文字列を指定バイト長となるまで末尾にスペースを追加して提供します。<br>
	 * @param value 元文字列
	 * @param lenb 調整バイト長
	 * @return 補完された文字列
	 */
	public static String supplyByteSpaceFoot(String value, int lenb) {
		return supplyByte(value, lenb, DEFAULT_JAVA_CHARSET, ' ', false);
	}

	/**
	 * 指定された文字列を指定バイト長となるまで先頭に"0"を追加して提供します。<br>
	 * @param value 元文字列
	 * @param lenb 調整バイト長
	 * @param encode キャラクタセット
	 * @return 補完された文字列
	 */
	public static String supplyByteZeroHead(String value, int lenb, String encode) {
		return supplyByte(value, lenb, encode, '0', true);
	}

	/**
	 * 指定された文字列を指定バイト長となるまで先頭に"0"を追加して提供します。<br>
	 * @param value 元文字列
	 * @param lenb 調整バイト長
	 * @return 補完された文字列
	 */
	public static String supplyByteZeroHead(String value, int lenb) {
		return supplyByte(value, lenb, DEFAULT_JAVA_CHARSET, '0', true);
	}

	/**
	 * 指定された文字列を指定バイト長となるまで末尾に"0"を追加して提供します。<br>
	 * @param value 元文字列
	 * @param lenb 調整バイト長
	 * @param encode キャラクタセット
	 * @return 補完された文字列
	 */
	public static String supplyByteZeroFoot(String value, int lenb, String encode) {
		return supplyByte(value, lenb, encode, '0', false);
	}

	/**
	 * 指定された文字列を指定バイト長となるまで末尾に"0"を追加して提供します。<br>
	 * @param value 元文字列
	 * @param lenb 調整バイト長
	 * @return 補完された文字列
	 */
	public static String supplyByteZeroFoot(String value, int lenb) {
		return supplyByte(value, lenb, DEFAULT_JAVA_CHARSET, '0', false);
	}

	/**
	 * 指定された文字列(MS932を想定)を指定されたキャラクタセットにエンコードします。<br>
	 * @param value 変換対象文字列
	 * @param charset キャラクタセット
	 * @return キャラクタエンコードされた文字列
	 */
	public static String encode(String value, String charset) {
		return encode(value, DEFAULT_JAVA_CHARSET, charset);
	}

	/**
	 * 指定された文字列を指定されたキャラクタセットにエンコードします。<br>
	 * @param value 変換対象文字列
	 * @param charsetFrom 変換前キャラクタセット
	 * @param charsetTo 変換後キャラクタセット
	 * @return キャラクタエンコードされた文字列
	 */
	public static String encode(String value, String charsetFrom, String charsetTo) {
		if (value == null) {
			return null;
		} else {
			try {
				return new String(value.getBytes(charsetFrom), charsetTo);
			} catch (UnsupportedEncodingException e) {
				throw new RuntimeException("不正なエンコード方式が指定されました", e);
			}
		}
	}

	/**
	 * 指定された文字列を指定されたキャラクタセットに変換してそのバイト情報を提供します。<br>
	 * @param value 変換対象文字列
	 * @param charset 変換対象キャラクタセット
	 * @return 変換された文字列のバイト情報
	 */
	public static byte[] convert(String value, String charset) {
		try {
			CharsetEncoder encoder = Charset.forName(charset).newEncoder();

			value = value.replaceAll("\uff5e", "\u301c"); //～
			value = value.replaceAll("\uff0d", "\u2212"); //－

			ByteBuffer encoded = encoder.encode(CharBuffer.wrap(value.toCharArray()));
			byte[] encodedArray = encoded.array();

			List buffer = new LinkedList();
			for (int i = 0; i < encoded.limit(); i++) {
				buffer.add(new Byte(encodedArray[i]));
			}

			byte[] result = result = new byte[buffer.size()];
			for (int i = 0; i < result.length; i++) {
				result[i] = ((Byte) buffer.get(i)).byteValue();
			}

			return result;
		} catch (CharacterCodingException e) {
			throw new RuntimeException("キャラクタセットの変換に失敗しました", e);
		}
	}

	/**
	 * 指定されたオブジェクトをStringオブジェクトとして取得します。<br>
	 * オブジェクトがnullの場合は空文字列として取得します。<br>
	 * @param value オブジェクト
	 * @return Stringオブジェクト
	 */
	public static String valueOf(Object value) {
		if (value == null) {
			return EMPTY;
		} else {
			return String.valueOf(value);
		}
	}

	/**
	 * 指定されたプリミティブ変数をStringオブジェクトとして取得します。<br>
	 * 冗長的な実装ではあるがStringUtils#valueOf(Object value)と統一性
	 * を取る為に当クラスに設ける。<br>
	 * @param value プリミティブ変数
	 * @return Stringオブジェクト
	 */
	public static String valueOf(boolean value) {
		return String.valueOf(value);
	}

	/**
	 * 指定されたプリミティブ変数をStringオブジェクトとして取得します。<br>
	 * 冗長的な実装ではあるがStringUtils#valueOf(Object value)と統一性
	 * を取る為に当クラスに設ける。<br>
	 * @param value プリミティブ変数
	 * @return Stringオブジェクト
	 */
	public static String valueOf(char value) {
		return String.valueOf(value);
	}

	/**
	 * 指定されたプリミティブ変数をStringオブジェクトとして取得します。<br>
	 * 冗長的な実装ではあるがStringUtils#valueOf(Object value)と統一性
	 * を取る為に当クラスに設ける。<br>
	 * @param value プリミティブ変数
	 * @return Stringオブジェクト
	 */
	public static String valueOf(char[] value) {
		return String.valueOf(value);
	}

	/**
	 * 指定されたプリミティブ変数をStringオブジェクトとして取得します。<br>
	 * 冗長的な実装ではあるがStringUtils#valueOf(Object value)と統一性
	 * を取る為に当クラスに設ける。<br>
	 * @param value プリミティブ変数
	 * @return Stringオブジェクト
	 */
	public static String valueOf(char[] value, int offset, int count) {
		return String.valueOf(value, offset, count);
	}

	/**
	 * 指定されたプリミティブ変数をStringオブジェクトとして取得します。<br>
	 * 冗長的な実装ではあるがStringUtils#valueOf(Object value)と統一性
	 * を取る為に当クラスに設ける。<br>
	 * @param value プリミティブ変数
	 * @return Stringオブジェクト
	 */
	public static String valueOf(double value) {
		return String.valueOf(value);
	}

	/**
	 * 指定されたプリミティブ変数をStringオブジェクトとして取得します。<br>
	 * 冗長的な実装ではあるがStringUtils#valueOf(Object value)と統一性
	 * を取る為に当クラスに設ける。<br>
	 * @param value プリミティブ変数
	 * @return Stringオブジェクト
	 */
	public static String valueOf(float value) {
		return String.valueOf(value);
	}

	/**
	 * 指定されたプリミティブ変数をStringオブジェクトとして取得します。<br>
	 * 冗長的な実装ではあるがStringUtils#valueOf(Object value)と統一性
	 * を取る為に当クラスに設ける。<br>
	 * @param value プリミティブ変数
	 * @return Stringオブジェクト
	 */
	public static String valueOf(int value) {
		return String.valueOf(value);
	}

	/**
	 * 指定されたプリミティブ変数をStringオブジェクトとして取得します。<br>
	 * 冗長的な実装ではあるがStringUtils#valueOf(Object value)と統一性
	 * を取る為に当クラスに設ける。<br>
	 * @param value プリミティブ変数
	 * @return Stringオブジェクト
	 */
	public static String valueOf(long value) {
		return String.valueOf(value);
	}

	/**
	 * 指定された数値文字列をLong型に変換します。<br>
	 * 値が空の場合はnullオブジェクトを返却します。<br>
	 * @param value 数値文字列
	 * @return Longオブジェクト
	 */
	public static Long parseLong(String value) {
		return isEmpty(value) ? null : Long.valueOf(value);
	}

	/**
	 * 指定された数値文字列をInteger型に変換します。<br>
	 * 値が空の場合はnullオブジェクトを返却します。<br>
	 * @param value 数値文字列
	 * @return Integerオブジェクト
	 */
	public static Integer parseInteger(String value) {
		return isEmpty(value) ? null : Integer.valueOf(value);
	}

	/**
	 * 指定された数値文字列をBigDecimal型に変換します。<br>
	 * 値が空の場合はnullオブジェクトを返却します。<br>
	 * @param value 数値文字列
	 * @return BigDecimalオブジェクト
	 */
	public static BigDecimal parseBigDecimal(String value) {
		//return isEmpty(value) ? null : BigDecimal.valueOf(Long.parseLong(value));
		return isEmpty(value) ? null : new BigDecimal(value);
	}

	/**
	 * 指定された数値文字列をintに変換します。<br>
	 * 値が空の場合は0を返却します。<br>
	 * @param value 数値文字列
	 * @return int数値
	 */
	public static int parsePrimitiveInt(String value) {
		return isEmpty(value) ? 0 : Integer.parseInt(value);
	}

	/**
	 * 指定された数値文字列をlongに変換します。<br>
	 * 値が空の場合は0を返却します。<br>
	 * @param value 数値文字列
	 * @return long数値
	 */
	public static long parsePrimitiveLong(String value) {
		return isEmpty(value) ? 0 : Long.parseLong(value);
	}

	/**
	 * \###,###,###形式文字列をBigDecimalオブジェクトとして取得します。<br>
	 * 空文字列の場合は、nullを返却します。また、不正な文字列の場合は例外が発生します。<br>
	 * @param value \###,###,###形式文字列
	 * @return BigDecimalオブジェクト
	 */
	public static BigDecimal parseCurrency(String value) {
		if (isEmpty(value)) {
			return null;
		} else {
			String buffer = value;
			buffer = replace(buffer, ",", EMPTY);
			buffer = replace(buffer, "\\", EMPTY);
			return parseBigDecimal(value);
		}
	}

	/**
	 * 指定された数値を指定桁数まで先頭に0を補完した文字列として返却します。<br>
	 * 尚、当メソッドは負数は許可しません。<br>
	 * @param value 数値
	 * @param length 補完桁数
	 * @return 補完した文字列
	 */
	public static String formatZeroFix(int value, int length) {
		if ((value < 0) || (length < 0)) {
			throw new IllegalArgumentException("数値または補正桁数に負数が指定されました");
		}
		return supplyByteZeroHead(valueOf(value), length);
	}

	/**
	 * 指定された数値を指定桁数まで先頭に0を補完した文字列として返却します。<br>
	 * @param value 数値
	 * @param length 補完桁数
	 * @return 補完した文字列
	 */
	public static String formatZeroFix(long value, int length) {
		if ((value < 0) || (length < 0)) {
			throw new IllegalArgumentException("数値または補正桁数に負数が指定されました");
		}
		return supplyByteZeroHead(valueOf(value), length);
	}

	/**
	 * 指定された数値文字列をカンマ編集して提供します。<br>
	 * 当メソッドでは文字種チェック等は行いませんが、任意の文字列が指定された場合でも例外は発生しません。<br>
	 * @param value 数値文字列
	 * @return カンマ編集後文字列
	 */
	public static String formatComma(String value) {
		if (StringUtil.isEmpty(value) || StringUtil.isEmpty(value.trim())) {
			return EMPTY;
		}
		value = value.trim();

		if (!StringUtil.isDigit(value)) {
			return value;
		}

		// 少数位置分解
		String[] token1 = value.split("[.]", 2);
		String integer = token1.length > 0 ? token1[0] : EMPTY;
		String fraction = token1.length > 1 ? token1[1] : EMPTY;

		// 符号分解
		String mark = integer.replaceAll("[0-9]", EMPTY);
		String number = integer.replaceAll("[+-]", EMPTY);

		// 整数部カンマ編集
		number = number.replaceAll("(\\d{1,3})(?=(?:\\d\\d\\d)+(?!\\d))", "$1,");

		// 文字列結合
		StringBuffer buffer = new StringBuffer();
		buffer.append(mark);
		buffer.append(StringUtil.isEmpty(number) && !StringUtil.isEmpty(fraction) ? "0" : number);
		buffer.append(StringUtil.isEmpty(fraction) ? EMPTY : ".");
		buffer.append(fraction);

		return buffer.toString();
	}

	/**
	 * 指定されたlong値を数値カンマ形式に書式変換します。<br>
	 * @param value long値
	 * @return ###,###,###形式
	 */
	public static String formatComma(long value) {
		return formatComma(EMPTY + value);
	}

	/**
	 * 指定されたint値を数値カンマ形式に書式変換します。<br>
	 * @param value int値
	 * @return ###,###,###形式
	 */
	public static String formatComma(int value) {
		return formatComma(EMPTY + value);
	}

	/**
	 * 指定されたBigDecimalオブジェクトを数値カンマ形式に書式変換します。<br>
	 * @param value BigDecimalオブジェクト
	 * @return ###,###,###形式
	 */
	public static String formatComma(BigDecimal value) {
		if (value == null) {
			return EMPTY;
		} else {
			return formatComma(value.toString());
		}
	}

	/**
	 * 指定されたBigDecimalオブジェクトを金額形式に書式変換します。<br>
	 * @param value BigDecimalオブジェクト
	 * @return \###,###,###形式
	 */
	public static String formatCurrency(BigDecimal value) {
		if (value == null) {
			return EMPTY;
		} else {
			String buffer = formatComma(value);
			if (lenb(buffer) == 0) {
				return EMPTY;
			} else {
				if ("+".equals(leftb(buffer, 1)) || "-".equals(leftb(buffer, 1))) {
					return leftb(buffer, 1) + "\\" + rightb(buffer, lenb(buffer) - 1);
				} else {
					return "\\" + buffer;
				}
			}
		}
	}

	/**
	 * 指定されたDoubleオブジェクトを指定された小数点以下桁数でパーセント表記文字列として取得します。<br>
	 * @param value 変換対象数値
	 * @param point 小数点以下桁数
	 * @return 00.00%表記文字列
	 */
	public static String formatPercent(BigDecimal value, int point) {
		if (value == null) {
			return EMPTY;
		} else {
			String buffer = valueOf(value.doubleValue() * 100);
			buffer = buffer.indexOf("0") > 0 ? buffer + "00" : buffer + ".00";
			return point == 0 ? buffer.substring(0, buffer.indexOf(".")) : buffer.substring(0, buffer.indexOf(".") + point + 1) + "%";
		}
	}

	/**
	 * 指定されたDoubleオブジェクトを指定された小数点以下桁数でパーセント表記文字列として取得します。<br>
	 * @param value 変換対象数値
	 * @param point 小数点以下桁数
	 * @return 00.00%表記文字列
	 */
	public static String formatPercent(Double value, int point) {
		if (value == null) {
			return EMPTY;
		} else {
			return formatPercent(new BigDecimal(value.toString()), point);
		}
	}

	/**
	 * 指定されたDoubleオブジェクトを指定された小数点以下桁数でパーセント表記文字列として取得します。<br>
	 * @param value 変換対象数値
	 * @param point 小数点以下桁数
	 * @return 00.00%表記文字列
	 */
	public static String formatPercent(double value, int point) {
		return formatPercent(new Double(value), point);
	}

	/**
	 * 指定された文字長分の*文字列を返却します。<br>
	 * @param value 変換対象文字列
	 * @return 指定された文字長分の*文字列
	 */
	public static String formatPassword(String value) {
		if (isEmpty(value)) {
			return EMPTY;
		} else {
			StringBuffer result = new StringBuffer();
			for (int i = 0; i <= lenb(value) - 1; i++) {
				result.append("*");
			}
			return result.toString();
		}
	}

	/**
	 * 指定されたカンマ編集文字列をカンマを除去して提供します。<br>
	 * @param value カンマ編集文字列
	 * @return カンマを除去した文字列
	 */
	public static String removeComma(String value) {
		if (StringUtil.isEmpty(value) || StringUtil.isEmpty(value.trim())) {
			return EMPTY;
		}
		return value.trim().replaceAll("[,]", EMPTY);
	}

	/**
	 * 指定された文字列をファイル名として使用できる文字列に修正します。<br>
	 * ファイル名として使用不可能な文字は指定された文字で置き換えられます。<br>
	 * @param value 修正対象文字列
	 * @param replace 使用不可文字を置き換える文字
	 * @return ファイル名として使用可能な文字列
	 */
	public static String escapeFilename(String value, String replace) {
		if (isEmpty(value)) {
			return EMPTY;
		} else {
			value = replace(value, "\\", replace);
			value = replace(value, "/", replace);
			value = replace(value, ":", replace);
			value = replace(value, "*", replace);
			value = replace(value, "?", replace);
			value = replace(value, "\"", replace);
			value = replace(value, "<", replace);
			value = replace(value, ">", replace);
			value = replace(value, "|", replace);
			return value;
		}
	}

	/**
	 * 指定された文字列に対して指定された装飾文字列を前後に付加して提供します。<br>
	 * @param source 装飾対象文字列
	 * @param deco 装飾文字列
	 * @return 装飾文字列を前後に付加した文字列
	 */
	public static String bundleDecorate(String source, String deco) {
		return new StringBuffer(source).append(deco).insert(0, deco).toString();
	}

	/**
	 * 指定された文字列内の{#}に対してパラメータStringオブジェクト配列順に文字列を挿入して提供します。<br>
	 * パラメータStringオブジェクトがnullの場合は空文字を挿入します。<br>
	 * 例：StringUtils.binder("Test Binding is {0}", new String[]{"success"}) → Test Binding is success<br>
	 * @param source バインド対象ベース文字列
	 * @param params バインドパラメータStringオブジェクト配列
	 * @return バインド編集後文字列
	 */
	public static String bind(String source, String[] params) {
		if (params == null || params.length == 0 || source == null || source.length() == 0) {
			return source;
		}
		String render = source;
		for (int i = 0; i <= params.length - 1; i++) {
			render = replace(render, "{" + i + "}", params[i] == null ? EMPTY : params[i]);
		}
		return render;
	}

	/**
	 * 指定された文字列をnative2asciiエンコード後の文字列として提供します。<br>
	 * @param value エンコード対象文字列
	 * @return native2asciiエンコード後の文字列
	 */
	public static String nativeToAscii(String value) {
		//		final char[] HEX_DIGIT = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
		//		final String SPECIAL_SAVE_CHARS = "=: \t\r\n\f#!";
		//		int length = value.length();
		//		StringBuffer outBuffer = new StringBuffer(length * 2);
		//
		//		for (int x = 0; x < length; x++) {
		//			char c = value.charAt(x);
		//			switch (c) {
		//				case ' ':
		//					//if (x == 0 || escapeSpace) {
		//					outBuffer.append('\\');
		//					//}
		//					outBuffer.append(' ');
		//					break;
		//				case '\\':
		//					outBuffer.append('\\');
		//					outBuffer.append('\\');
		//					break;
		//				case '\t':
		//					outBuffer.append('\\');
		//					outBuffer.append('t');
		//					break;
		//				case '\n':
		//					outBuffer.append('\\');
		//					outBuffer.append('n');
		//					break;
		//				case '\r':
		//					outBuffer.append('\\');
		//					outBuffer.append('r');
		//					break;
		//				case '\f':
		//					outBuffer.append('\\');
		//					outBuffer.append('f');
		//					break;
		//				default:
		//					if ((c < 0x0020) || (c > 0x007e)) {
		//						outBuffer.append('\\');
		//						outBuffer.append('u');
		//						outBuffer.append(HEX_DIGIT[(((c >> 12) & 0xF) & 0xF)]);
		//						outBuffer.append(HEX_DIGIT[(((c >> 8) & 0xF) & 0xF)]);
		//						outBuffer.append(HEX_DIGIT[(((c >> 4) & 0xF) & 0xF)]);
		//						outBuffer.append(HEX_DIGIT[((c & 0xF) & 0xF)]);
		//					} else {
		//						if (SPECIAL_SAVE_CHARS.indexOf(c) != -1) {
		//							outBuffer.append('\\');
		//						}
		//						outBuffer.append(c);
		//					}
		//			}
		//		}
		//		return outBuffer.toString();
		return Native2AsciiUtil.native2ascii(value);
	}

	/**
	 * 指定されたnative2asciiエンコード文字列を通常の文字列に変換します。<br>
	 * @param value native2asciiエンコード文字列
	 * @return 通常文字列
	 */
	public static String asciiToNative(String value) {
		//		char c;
		//		int length = value.length();
		//		StringBuffer buffer = new StringBuffer(length);
		//		for (int x = 0; x < length;) {
		//			c = value.charAt(x++);
		//			if (c == '\\') {
		//				c = value.charAt(x++);
		//				if (c == 'u') {
		//					int ci = 0;
		//					for (int i = 0; i < 4; i++) {
		//						c = value.charAt(x++);
		//						switch (c) {
		//							case '0':
		//							case '1':
		//							case '2':
		//							case '3':
		//							case '4':
		//							case '5':
		//							case '6':
		//							case '7':
		//							case '8':
		//							case '9':
		//								ci = (ci << 4) + c - '0';
		//								break;
		//							case 'a':
		//							case 'b':
		//							case 'c':
		//							case 'd':
		//							case 'e':
		//							case 'f':
		//								ci = (ci << 4) + 10 + c - 'a';
		//								break;
		//							case 'A':
		//							case 'B':
		//							case 'C':
		//							case 'D':
		//							case 'E':
		//							case 'F':
		//								ci = (ci << 4) + 10 + c - 'A';
		//								break;
		//							default:
		//								throw new IllegalArgumentException("Malformed \\uxxxx encoding.");
//						}
		//					}
		//					buffer.append((char) ci);
		//				} else {
		//					if (c == 't')
		//						c = '\t';
		//					else if (c == 'r')
		//						c = '\r';
		//					else if (c == 'n')
		//						c = '\n';
		//					else if (c == 'f')
		//						c = '\f';
		//					buffer.append(c);
		//				}
		//			} else
		//				buffer.append(c);
		//		}
		//		return buffer.toString();
		try {
			return Native2AsciiUtil.ascii2native(value);
		} catch (IOException e) {
			throw new IllegalArgumentException("value");
		}
	}

	/**
	 * 指定されたbyteを16進数で提供します。<br>
	 * @param b 16進数編集対象byte
	 * @return 16進数編集をしたbyte
	 */
	public static String toHex(byte b) {
		int lbs = b & 0xf;
		int sbs = (b & 0xf0) >> 4;
		return HEX_STRINGS[sbs] + HEX_STRINGS[lbs];
	}

	/**
	 * 指定されたbyte配列を16進数で提供します。<br>
	 * @param b 16進数編集対象byte配列
	 * @return 16進数編集をしたbyte配列
	 */
	public static String toHex(byte[] b) {
		String result = EMPTY;
		for (int i = 0; i < b.length; i++) {
			result += toHex(b[i]);
		}
		return result;
	}

	/**
	 * 指定された文字列のMD5ハッシュコードを提供します。<br>
	 * @param value MD5ハッシュコード算出対象文字列
	 * @return 指定された文字列のハッシュコード
	 */
	public static String toMD5Hash(String value) {
		MessageDigest md = null;

		try {
			md = MessageDigest.getInstance("MD5");
		} catch (NoSuchAlgorithmException e) {
			// アルゴリズムが存在しないことは有り得ないので例外はスローさせない
			e.printStackTrace();
		}

		md.update(value == null ? EMPTY.getBytes() : value.getBytes());

		return toHex(md.digest());
	}

	/**
	 * 指定されたBASE64エンコードバイトデータの元のバイトデータ値を提供します。<br>
	 * @param data BASE64エンコードバイトデータ
	 * @return 元のバイトデータ値
	 */
	private static byte base64ToNativeValue(byte data) {
		final byte[] BASE64_CHARS_BYTES = BASE64_CHARS.getBytes();
		if (data == '=') {
			return 0;
		}
		for (int j = 0; j < BASE64_CHARS_BYTES.length; j++) {
			if (data == BASE64_CHARS_BYTES[j]) {
				return (byte) j;
			}
		}
		return 0;
	}

	/**
	 * 指定されたBASE64エンコード文字列を元のバイトデータに変換して提供します。<br>
	 * @param value デコード対象BASE64文字列
	 * @param start デコード対象とする開始バイトインデックス
	 * @param length デコード対象のバイト長
	 * @return デコード後のバイトデータ
	 */
	public static byte[] base64ToNative(String value, int start, int length) {
		byte[] bytes = value.getBytes();
		byte[] buffer = new byte[length];
		int j = 0;
		for (int i = start; i < start + length; i += 4) {
			buffer[j] = (byte) ((base64ToNativeValue(bytes[i]) << 2) | ((base64ToNativeValue(bytes[i + 1]) & 0x30) >>> 4));
			if (bytes[i + 2] == (byte) '=') {
				j++;
				break;
			}
			buffer[j + 1] = (byte) (((base64ToNativeValue(bytes[i + 1]) & 0x0f) << 4) | ((base64ToNativeValue(bytes[i + 2]) & 0x3c) >>> 2));
			if (bytes[i + 3] == (byte) '=') {
				j += 2;
				break;
			}
			buffer[j + 2] = (byte) (((base64ToNativeValue(bytes[i + 2]) & 0x03) << 6) | (base64ToNativeValue(bytes[i + 3]) & 0x3f));
			j += 3;
		}

		byte[] result = new byte[j];
		System.arraycopy(buffer, 0, result, 0, j);

		return result;
	}

	/**
	 * 指定されたBASE64エンコード文字列を元のバイトデータに変換して提供します。<br>
	 * @param value デコード対象BASE64文字列
	 * @return デコード後のバイトデータ
	 */
	public static byte[] base64ToNative(String value) {
		return base64ToNative(value, 0, value.getBytes().length);
	}

	/**
	 * 指定されたバイトデータをBASE64エンコード文字列に変換して提供します。<br>
	 * @param bytes バイトデータ
	 * @param start 変換対象のバイトインデックス
	 * @param length 変換を行うバイト長
	 * @return BASE64エンコード文字列
	 */
	public static String nativeToBase64(byte[] bytes, int start, int length) {
		final byte[] BASE64_CHARS_BYTES = BASE64_CHARS.getBytes();
		byte[] buffer = new byte[length * 2];
		int i, j, k;

		int index = (length / 3) * 3 + start;
		i = 0;
		for (j = start; j < index; j += 3) {
			k = (bytes[j] >>> 2) & 0x3f;
			buffer[i++] = BASE64_CHARS_BYTES[k];
			k = (bytes[j] & 0x03) << 4 | (bytes[j + 1] >>> 4) & 0x0f;
			buffer[i++] = BASE64_CHARS_BYTES[k];
			k = (bytes[j + 1] & 0x0f) << 2 | (bytes[j + 2] >>> 6) & 0x03;
			buffer[i++] = BASE64_CHARS_BYTES[k];
			k = bytes[j + 2] & 0x3f;
			buffer[i++] = BASE64_CHARS_BYTES[k];
		}

		index = (start + length) - index;
		if (index == 1) {
			k = (bytes[j] >>> 2) & 0x3f;
			buffer[i++] = BASE64_CHARS_BYTES[k];
			k = ((bytes[j] & 0x03) << 4) & 0x3f;
			buffer[i++] = BASE64_CHARS_BYTES[k];
			buffer[i++] = (byte) '=';
			buffer[i++] = (byte) '=';
		} else if (index == 2) {
			k = (bytes[j] >>> 2) & 0x3f;
			buffer[i++] = BASE64_CHARS_BYTES[k];
			k = (bytes[j] & 0x03) << 4 | (bytes[j + 1] >>> 4) & 0x0f;
			buffer[i++] = BASE64_CHARS_BYTES[k];
			k = ((bytes[j + 1] & 0x0f) << 2) & 0x3f;
			buffer[i++] = BASE64_CHARS_BYTES[k];
			buffer[i++] = (byte) '=';
		}

		byte[] result = new byte[i];
		System.arraycopy(buffer, 0, result, 0, i);

		return new String(result);
	}

	/**
	 * 指定されたバイトデータをBASE64エンコード文字列に変換して提供します。<br>
	 * @param value エンコード対象文字列
	 * @return BASE64エンコード文字列
	 */
	public static String nativeToBase64(String value) {
		byte[] bytes = value.getBytes();
		return nativeToBase64(bytes, 0, bytes.length);
	}

	/**
	 * 指定された通常文字列をHTML形式に合わせた文字列として提供します。<br>
	 * @param value 変換対象文字列
	 * @return HTML形式に合わせた文字列
	 */
	public static String toHTML(String value) {
		String buffer = value;
		buffer = StringUtil.replace(buffer, "&", "&amp;");
		buffer = StringUtil.replace(buffer, "<", "&lt;");
		buffer = StringUtil.replace(buffer, ">", "&gt;");
		buffer = StringUtil.replace(buffer, " ", "&nbsp;");
		buffer = StringUtil.replace(buffer, "\t", "&nbsp;&nbsp;&nbsp;&nbsp;");
		buffer = StringUtil.replace(buffer, "\n", "<br>");
		return buffer;
	}

	/**
	 * スネークケース(snake_case)から小文字開始のキャメルケース(camelCase)に変換して提供します。<br>
	 * @param value 変換対象文字列
	 * @return 小文字開始のキャメルケース(camelCase)文字列
	 */
	public static String toLowerCamelCase(String value) {
		if (value == null) {
			return null;
		}
		Matcher matcher = Pattern.compile("(_)([a-z0-9])").matcher(value.toLowerCase());
		StringBuffer buffer = new StringBuffer();
		while (matcher.find()) {
			matcher.appendReplacement(buffer, matcher.group(2).toUpperCase());
		}
		matcher.appendTail(buffer);
		return buffer.toString();
	}

	/**
	 * スネークケース(snake_case)から大文字開始のキャメルケース(CamelCase)に変換して提供します。<br>
	 * @param value 変換対象文字列
	 * @return 大文字開始のキャメルケース(CamelCase)文字列
	 */
	public static String toUpperCamelCase(String value) {
		if (value == null) {
			return null;
		}
		String buffer = toLowerCamelCase(value);
		if (buffer.length() == 0) {
			return EMPTY;
		} else if (buffer.length() == 1) {
			return value.toUpperCase();
		}
		return buffer.substring(0, 1).toUpperCase() + buffer.substring(1);
	}

	/**
	 * キャメルケース(camelCase)から小文字のスネークケース(snake_case)に変換します。<br>
	 * @param value 変換対象文字列
	 * @return 小文字のスネークケース(snake_case)文字列
	 */
	public static String toLowerSnakeCase(String value) {
		if (value == null) {
			return null;
		}
		return value.replaceAll("([A-Z0-9]+)([A-Z0-9][a-z0-9])", "$1_$2").replaceAll("([a-z])([A-Z])", "$1_$2").toLowerCase();
	}

	/**
	 * キャメルケース(camelCase)から大文字のスネークケース(SNAKE_CASE)に変換します。<br>
	 * @param value 変換対象文字列
	 * @return 大文字のスネークケース(SNAKE_CASE)文字列
	 */
	public static String toUpperSnakeCase(String value) {
		if (value == null) {
			return null;
		}
		return toLowerSnakeCase(value).toUpperCase();
	}

	/**
	 * 指定されたバイト位置にある文字を取得します。<br>
	 * @param value 対象文字列
	 * @param index バイトインデックス
	 * @return 指定されたバイト位置にある文字
	 */
	public static char charAtb(String value, int index) {
		return substrb(value, index, 1).toCharArray()[0];
	}
}
