package org.apache.commons.lang;

/* ====================================================================
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 2002 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution, if
 *    any, must include the following acknowlegement:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowlegement may appear in the software itself,
 *    if and wherever such third-party acknowlegements normally appear.
 *
 * 4. The names "The Jakarta Project", "Commons", and "Apache Software
 *    Foundation" must not be used to endorse or promote products derived
 *    from this software without prior written permission. For written
 *    permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache"
 *    nor may "Apache" appear in their names without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */

import java.util.StringTokenizer;

import java.util.Iterator;

/**
 * <p>共通的な <code>String</code> に対する処理ルーチンです。
 * {@primary Common <code>String</code> manipulation routines.}</p>
 *
 * <p>オリジナルは <a href="http://jakarta.apache.org/turbine/">Turbine</a>
 * と GenerationJavaCore ライブラリです。
 * {@primary Originally from 
 * <a href="http://jakarta.apache.org/turbine/">Turbine</a> and the
 * GenerationJavaCore library.}</p>
 *
 * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a>
 * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a>
 * @author <a href="mailto:gcoladonato@yahoo.com">Greg Coladonato</a>
 * @author <a href="mailto:bayard@generationjava.com">Henri Yandell</a>
 * @author <a href="mailto:ed@apache.org">Ed Korthof</a>
 * @author <a href="mailto:rand_mcneely@yahoo.com">Rand McNeely</a>
 * @author <a href="mailto:scolebourne@joda.org">Stephen Colebourne</a>
 * @author <a href="mailto:fredrik@westermarck.com">Fredrik Westermarck</a>
 * @translator 日置 聡
 * @status firstdraft
 * @update 2003/08/12
 * @version $Id: StringUtils.java,v 1.1.1.1 2004/02/13 10:02:04 hioki Exp $
 */
public class StringUtils {

    /**
     * StringUtils のインスタンスは一般的なプログラムからは生成すべきではありません。
     * 代わりに <code>StringUtils.trim(" foo ");</code> というように使用すべきです。
     * このコンストラクタは、処理に JavaBean インスタンスを必要とするツールのために
     * public になっています。
     * {@primary StringUtils instances should NOT be constructed in standard programming.
     * Instead, the class should be used as <code>StringUtils.trim(" foo ");</code>.
     * This constructor is public to permit tools that require a JavaBean instance
     * to operate.}
     */
    public StringUtils() {
    }

    // Empty
    //--------------------------------------------------------------------------

    /**
     * 指定された文字列の最初と最後の空白を削除します。
     * <code>null</code> が渡された場合には空の文字列を返します。
     * {@primary Removes control characters, including whitespace,  from both ends of this 
     * string, handling null by returning an empty string.}
     *
     * @see java.lang.String#trim()
     * @param str  チェックする文字列
     * {@primary the string to check}
     * @return トリムされた文字列(必ず<code>null</code>ではない)
     * {@primary the trimmed text (never <code>null</code>)}
     */
    public static String clean(String str) {
        return (str == null ? "" : str.trim());
    }

    /**
     * 指定された文字列の最初と最後の空白を削除します。
     * <code>null</code> が渡された場合には <code>null</code> を返します。
     * {@primary Removes control characters,  including whitespace, from both ends of this
     * string, handling null by returning null. }
     *
     * @see java.lang.String#trim()
     * @param str  チェックする文字列
     * {@primary the string to check}
     * @return トリムされた文字列(または <code>null</code>)
     * {@primary the trimmed text (or <code>null</code>)}
     */
    public static String trim(String str) {
        return (str == null ? null : str.trim());
    }

    /**
     * 文字列から全ての空白文字を削除します。
     * 推奨されないメソッド Character.isSpace で定義されている
     * {' ', '\t', '\r', '\n', '\b'} を空白文字として認識します。
     * {@primary Deletes all 'space' characters from a String.
     * Spaces are defined as {' ', '\t', '\r', '\n', '\b'&#125;
     * in line with the deprecated Character.isSpace}
     *
     * @param str  空白文字を削除する対象となる文字列
     * {@primary String target to delete spaces from}
     * @return 空白文字が削除された文字列
     * {@primary the text without spaces}
     * @throws NullPointerException
     */
    public static String deleteSpaces(String str) {
        return CharSetUtils.delete(str, " \t\r\n\b");
    }

    /**
     * 文字列から全ての空白(whitespace)を削除します。
     * 空白(whitespace) は Character.isWhitespace で定義されています。
     * {@primary Deletes all whitespace from a String.
     * Whitespace is defined by Character.isWhitespace}
     *
     * @param str  空白を削除する対象となる文字列
     * {@primary String target to delete whitespace from}
     * @return 空白が削除された文字列
     * {@primary the text without whitespace}
     * @throws NullPointerException
     */
    public static String deleteWhitespace(String str) {
        StringBuffer buffer = new StringBuffer();
        int sz = str.length();
        for (int i=0; i<sz; i++) {
            if(!Character.isWhitespace(str.charAt(i))) {
                buffer.append(str.charAt(i));
            }
        }
        return buffer.toString();
    }

    /**
     * 文字列が null でなく、空(長さが0)でないかチェックします。
     * {@primary Checks if a String is non null and is not empty (length > 0).}
     *
     * @param str  チェックする文字列
     * {@primary the string to check}
     * @return 文字列が null でなく、長さが0でない場合に true
     * {@primary true if the String is non-null, and not length zero}
     */
    public static boolean isNotEmpty(String str) {
        return (str != null && str.length() > 0);
    }

    /**
     * (トリムされた)文字列が null 、または空(長さが0)であるかチェックします。
     * {@primary Checks if a (trimmed) String is null or empty.}
     *
     * @param str  チェックする文字列
     * {@primary the string to check}
     * @return true 文字列が null 、またはトリムされて長さが0の場合に true
     * {@primary true if the String is null, or length zero once trimmed}
     */
    public static boolean isEmpty(String str) {
        return (str == null || str.trim().length() == 0);
    }

    // Equals and IndexOf
    //--------------------------------------------------------------------------

    /**
     * 2つの文字列を比較して、等しい場合に true を返します。
     * null は例外を発生せずに扱われます。2つの <code>null</code> の比較は等しいと判断されます。
     * 大文字と小文字は区別されます。
     * {@primary Compares two Strings, returning true if they are equal.
     * Nulls are handled without exceptions. Two <code>null</code>
     * references are considered equal. Comparison is case sensitive.}
     *
     * @see java.lang.String#equals(Object)
     * @param str1  最初の比較対照となる文字列
     * {@primary the first string}
     * @param str2  次の比較対照となる文字列
     * {@primary the second string}
     * @return true 大文字小文字を意識して文字列が等しい場合、または両方とも null の場合
     * {@primary true if the Strings are equal, case sensitive, or both null}
     */
    public static boolean equals(String str1, String str2) {
        return (str1 == null ? str2 == null : str1.equals(str2));
    }

    /**
     * 2つの文字列を比較して、大文字と小文字を区別せずに等しい場合に true を返します。
     * null は例外を発生せずに扱われます。2つの <code>null</code> の比較は等しいと判断されます。
     * 大文字と小文字は区別されません。
     * {@primary Compares two Strings, returning true if they are equal ignoring case.
     * Nulls are handled without exceptions. Two <code>null</code>
     * references are considered equal. Comparison is case insensitive.}
     *
     * @see java.lang.String#equalsIgnoreCase(String)
     * @param str1  最初の比較対照となる文字列
     * {@primary the first string}
     * @param str2  次の比較対照となる文字列
     * {@primary the second string}
     * @return true 大文字小文字を意識せず文字列が等しい場合、または両方とも null の場合
     * {@primary true if the Strings are equal, case insensitive, or both null}
     */
    public static boolean equalsIgnoreCase(String str1, String str2) {
        return (str1 == null ? str2 == null : str1.equalsIgnoreCase(str2));
    }

    /**
     * この文字列内で、指定された部分文字列のセットうちのどれかが最初に出現する位置のインデックスを返します。
     * 該当する文字がこの文字列内にない場合は、-1 が返されます。
     * {@primary Find the earliest index of any of a set of potential substrings.
     * Null string will return -1.}
     * 
     * @param str  チェックする文字列
     * {@primary the string to check}
     * @param searchStrs  検索する文字列の一覧
     * {@primary the strings to search for}
     * @return 検索する文字列のどれかが最初に出現する位置
     * {@primary the earliest index of any of the strings}
     * @throws NullPointerException searchStrs[i] のうちのどれかが null だった場合
     * {@primary if any of searchStrs[i] is null}
     */
    public static int indexOfAny(String str, String[] searchStrs) {
        if ((str == null) || (searchStrs == null)) {
            return -1;
        }
        int sz = searchStrs.length;

        // String's can't have a MAX_VALUEth index.
        int ret = Integer.MAX_VALUE;

        int tmp = 0;
        for (int i = 0; i < sz; i++) {
            tmp = str.indexOf(searchStrs[i]);
            if (tmp == -1) {
                continue;
            }

            if (tmp < ret) {
                ret = tmp;
            }
        }

        return (ret == Integer.MAX_VALUE) ? -1 : ret;
    }

    /**
     * この文字列内で、指定された部分文字列のセットうちのどれかが最後に出現する位置のインデックスを返します。
     * 該当する文字がこの文字列内にない場合は、-1 が返されます。
     * {@primary Find the latest index of any of a set of potential substrings.
     * Null string will return -1.}
     * 
     * @param str  チェックする文字列
     * {@primary the string to check}
     * @param searchStrs  検索する文字列の一覧
     * {@primary the strings to search for}
     * @return 検索する文字列のどれかが最後に出現する位置
     * {@primary the last index of any of the strings}
     * @throws NullPointerException searchStrs[i] のうちのどれかが null だった場合
     * {@primary if any of searchStrs[i] is null}
     */
    public static int lastIndexOfAny(String str, String[] searchStrs) {
        if ((str == null) || (searchStrs == null)) {
            return -1;
        }
        int sz = searchStrs.length;
        int ret = -1;
        int tmp = 0;
        for (int i = 0; i < sz; i++) {
            tmp = str.lastIndexOf(searchStrs[i]);
            if (tmp > ret) {
                ret = tmp;
            }
        }
        return ret;
    }

    // Substring
    //--------------------------------------------------------------------------
    
    /**
     * 指定された文字列の部分文字列を例外を発生させることなく取得します。
     * マイナスの開始位置の指定は文字列の最後から開始位置を遡って指定する場合に使用されます。
     * {@primary Gets a substring of the specified string avoiding exceptions.
     * A negative start position can be used to start n characters from
     * the end of the string.}
     * 
     * @param str  部分文字列の取得s対象となる文字列
     * {@primary the string to get the substring from}
     * @param start  部分文字列の開始位置。
     * マイナスの値は文字列の最後からその値だけ戻ることを意味します
     * {@primary the position to start from,  negative means 
     * count back from the end of the string by this many characters}
     * @return 開始位置からの部分文字列
     * {@primary substring from start position}
     */
    public static String substring(String str, int start) {
        if (str == null) {
            return null;
        }

        // handle negatives, which means last n characters
        if (start < 0) {
            start = str.length() + start; // remember start is negative
        }

        if (start < 0) {
            start = 0;
        }
        if (start > str.length()) {
            return "";
        }

        return str.substring(start);
    }
    
    /**
     * 指定された文字列の部分文字列を例外を発生させることなく取得します。
     * マイナスの開始/終了位置の指定は文字列の最後から開始/終了位置を遡って指定する場合に使用されます。
     * {@primary Gets a substring of the specified string avoiding exceptions.
     * A negative start position can be used to start/end n characters
     * from the end of the string.}
     * 
     * @param str  部分文字列の取得s対象となる文字列
     * {@primary the string to get the substring from}
     * @param start  部分文字列の開始位置。
     * マイナスの値は文字列の最後からその値だけ戻ることを意味します
     * {@primary the position to start from,  negative means 
     * count back from the end of the string by this many characters}
     * @param end  部分文字列の終了位置。
     * マイナスの値は文字列の最後からその値だけ戻ることを意味します
     * {@primary the position to end at (exclusive),  negative means 
     * count back from the end of the string by this many characters}
     * @return 開始位置から終了位置までの部分文字列
     * {@primary substring from start position to end positon}
     */
    public static String substring(String str, int start, int end) {
        if (str == null) {
            return null;
        }

        // handle negatives
        if (end < 0) {
            end = str.length() + end; // remember end is negative
        }
        if (start < 0) {
            start = str.length() + start; // remember start is negative
        }

        // check length next
        if (end > str.length()) {
            // check this works.
            end = str.length();
        }

        // if start is greater than end, return ""
        if (start > end) {
            return "";
        }

        if (start < 0) {
            start = 0;
        }
        if (end < 0) {
            end = 0;
        }

        return str.substring(start, end);
    }

    /**
     * 指定された文字列の左側から指定された長さの部分文字列を取得します。
     * 文字列の長さが指定された長さに満たなかった場合、文字列に null が指定された場合には、
     * 例外は発生せず、元の文字列を返します。
     * {@primary Gets the leftmost n characters of a string. If n characters are not 
     * available, or the string is null, the string will be returned 
     * without an exception.}
     *
     * @param str  左側の部分文字列を取得する対象となる文字列
     * {@primary the string to get the leftmost characters from}
     * @param len  部分文字列の長さ
     * {@primary the length of the required string}
     * @return 左側の部分文字列
     * {@primary leftmost characters}
     * @throws IllegalArgumentException len にマイナスの値が指定された場合
     * {@primary if len is less than zero}
     */
    public static String left(String str, int len) {
        if (len < 0) {
            throw new IllegalArgumentException("Requested String length " + len + " is less than zero");
        }
        if ((str == null) || (str.length() <= len)) {
            return str;
        } else {
            return str.substring(0, len);
        }
    }

    /**
     * 指定された文字列の右側から指定された長さの部分文字列を取得します。
     * 文字列の長さが指定された長さに満たなかった場合、文字列に null が指定された場合には、
     * 例外は発生せず、元の文字列を返します。
     * {@primary Gets the rightmost n characters of a string. If n characters are not 
     * available, or the string is null, the string will be returned 
     * without an exception.}
     *
     * @param str  右側の部分文字列を取得する対象となる文字列
     * {@primary the string to get the rightmost characters from}
     * @param len  部分文字列の長さ
     * {@primary the length of the required string}
     * @return 右側の部分文字列
     * {@primary rightmost characters}
     * @throws IllegalArgumentException len にマイナスの値が指定された場合
     * {@primary if len is less than zero}
     */
    public static String right(String str, int len) {
        if (len < 0) {
            throw new IllegalArgumentException("Requested String length " + len + " is less than zero");
        }
        if ((str == null) || (str.length() <= len)) {
            return str;
        } else {
            return str.substring(str.length() - len);
        }
    }

    /**
     * 文字列の指定された開始位置から指定された長さの文字列を取得します。
     * 指定された長さ分の文字列が取得できなかった場合には例外を発生せずに取得できた分だけの文字列を返します。
     * 指定された文字列が null だった場合には null を返します。
     * {@primary Gets n characters from the middle of a string. If n characters are 
     * not available, the remainder of the string will be returned 
     * without an exception. If the string is null, null will be returned.}
     *
     * @param str  部分文字列を取得する対象となる文字列
     * {@primary the string to get the characters from}
     * @param pos  開始位置
     * {@primary the position to start from}
     * @param len  部分文字列の長さ
     * {@primary the length of the required string}
     * @return 取得された部分文字列
     * {@primary the leftmost characters}
     * @throws IndexOutOfBoundsException pos が範囲外である場合
     * {@primary if pos is out of bounds}
     * @throws IllegalArgumentException len にマイナスの値が指定された場合
     * {@primary if len is less than zero}
     */
    public static String mid(String str, int pos, int len) {
        if ((pos < 0) ||
            (str != null && pos > str.length())) {
            throw new StringIndexOutOfBoundsException("String index " + pos + " is out of bounds");
        }
        if (len < 0) {
            throw new IllegalArgumentException("Requested String length " + len + " is less than zero");
        }
        if (str == null) {
            return null;
        }
        if (str.length() <= (pos + len)) {
            return str.substring(pos);
        } else {
            return str.substring(pos, pos + len);
        }
    }

    // Splitting
    //--------------------------------------------------------------------------
    
    /**
     * 文字列を空白を区切り文字として配列にします。
     * 区切り文字は配列内の文字列には含まれません。
     * {@primary Splits the provided text into a list, using whitespace as the separator.
     * The separator is not included in the returned String array.}
     *
     * @param str  パースされる文字列
     * {@primary str  the string to parse}
     * @return パースされた文字列の配列
     * {@primary an array of parsed Strings}
     */
    public static String[] split(String str) {
        return split(str, null, -1);
    }

    /**
     * @see #split(String, String, int)
     */
    public static String[] split(String text, String separator) {
        return split(text, separator, -1);
    }

    /**
     * 文字列を指定された区切り文字を使用してて配列にします。
     * 区切り文字は配列内の文字列には含まれません。
     * 文字列を区切る際の最大数を指定することができます。
     * 区切り文字に null 画指定された場合には空白が使用されます。
     * {@primary Splits the provided text into a list, based on a given separator.
     * The separator is not included in the returned String array.
     * The maximum number of splits to perfom can be controlled.
     * A null separator will cause parsing to be on whitespace.}
     *
     * <p>このメソッドは( <code>StringTokenizer</code> がするような)
     * トークンの一覧を作成する処理の代わりに、すばやく文字列を配列に変換するのに有用です。
     * {@primary This is useful for quickly splitting a string directly into
     * an array of tokens, instead of an enumeration of tokens (as
     * <code>StringTokenizer</code> does).}
     *
     * @param str  パースされる文字列
     * {@primary str  the string to parse}
     * @param separator 区切り文字として使用される文字。
     * <code>null</code> の場合には空白を使用
     * {@primary Characters used as the delimiters. If
     * <code>null</code>, splits on whitespace.}
     * @param max 配列の最大の大きさ。
     * 0またはマイナスの値は制限がないことを意味します。
     * {@primary The maximum number of elements to include in the
     * list.  A zero or negative value implies no limit.}
     * @return パースされた文字列の配列
     * {@primary an array of parsed Strings }
     */
    public static String[] split(String str, String separator, int max) {
        StringTokenizer tok = null;
        if (separator == null) {
            // Null separator means we're using StringTokenizer's default
            // delimiter, which comprises all whitespace characters.
            tok = new StringTokenizer(str);
        } else {
            tok = new StringTokenizer(str, separator);
        }

        int listSize = tok.countTokens();
        if (max > 0 && listSize > max) {
            listSize = max;
        }

        String[] list = new String[listSize];
        int i = 0;
       int lastTokenBegin = 0;
        int lastTokenEnd = 0;
        while (tok.hasMoreTokens()) {
            if (max > 0 && i == listSize - 1) {
                // In the situation where we hit the max yet have
                // tokens left over in our input, the last list
                // element gets all remaining text.
                String endToken = tok.nextToken();
                lastTokenBegin = str.indexOf(endToken, lastTokenEnd);
                list[i] = str.substring(lastTokenBegin);
                break;
            } else {
                list[i] = tok.nextToken();
                lastTokenBegin = str.indexOf(list[i], lastTokenEnd);
                lastTokenEnd = lastTokenBegin + list[i].length();
            }
            i++;
        }  
        return list;
    }

    // Joining
    //--------------------------------------------------------------------------
    /**
     * 配列内のの要素を一つの文字列に連結します。
     * join メソッドとの違いは、連結時の区切り文字をもたないことです。
     * {@primary Concatenates elements of an array into a single string.
     * The difference from join is that concatenate has no delimiter.}
     * 
     * @param array  連結の対象となる配列
     * {@primary the array of values to concatenate.}
     * @return 連結された文字列
     * {@primary the concatenated string.}
     */
    public static String concatenate(Object[] array) {
        return join(array, "");
    }
    
    /**
     * 配列内のの要素を一つの文字列に連結します。
     * 区切り文字に null が指定された場合には空の文字列として扱われ
     * いかなるデリミターも、リストの前後には加えられません。
     * {@primary Joins the elements of the provided array into a single string
     * containing the provided list of elements. 
     * No delimiter is added before or after the list.
     * A null separator is the same as a blank String.}
     *
     * @param array  連結の対象となる配列
     * {@primary the array of values to join together}
     * @param separator  区切り文字
     * {@primary the separator character to use}
     * @return 連結された文字列
     * {@primary the joined String}
     */
    public static String join(Object[] array, String separator) {
        if (separator == null) {
            separator = "";
        }
        int arraySize = array.length;
        int bufSize = (arraySize == 0 ? 0 : (array[0].toString().length() +
                                 separator.length()) * arraySize);
        StringBuffer buf = new StringBuffer(bufSize);

        for (int i = 0; i < arraySize; i++) {
            if (i > 0) {
                buf.append(separator);
            }
            buf.append(array[i]);
        }
        return buf.toString();
    }

    /**
     * イテレータ内のの要素を一つの文字列に連結します。
     * 区切り文字に null が指定された場合には空の文字列として扱われ
     * いかなるデリミターも、リストの前後には加えられません。
     * {@primary Joins the elements of the provided iterator into a single string
     * containing the provided elements.
     * No delimiter is added before or after the list.
     * A null separator is the same as a blank String.}
     *
     * @param iterator  連結の対象となるイテレータ
     * {@primary the iterator of values to join together}
     * @param separator  区切り文字
     * {@primary the separator character to use}
     * @return 連結された文字列
     * {@primary the joined String}
     */
    public static String join(Iterator iterator, String separator) {
        if (separator == null) {
            separator = "";
        }
        StringBuffer buf = new StringBuffer(256);  // Java default is 16, probably too small
        while (iterator.hasNext()) {
            buf.append(iterator.next());
            if (iterator.hasNext()) {
                buf.append(separator);
            }
        }
        return buf.toString();
    }



    // Replacing
    //--------------------------------------------------------------------------
    
    /**
     * 対象の文字列内で最初に見つかった指定された文字列(repl)を指定された文字列(eith)に変換します。
     * {@primary Replace a string with another string inside a larger string, once.}
     *
     * @see #replace(String text, String repl, String with, int max)
     * @param text  入れ替え対象の文字列を含むテキスト
     * {@primary text to search and replace in}
     * @param repl  入れ替え対象となる文字列
     * {@primary String to search for}
     * @param with  入れ替えられる文字列
     * {@primary String to replace with}
     * @return 文字列の入れ替えが行われたテキスト
     * {@primary the text with any replacements processed}
     */
    public static String replaceOnce(String text, String repl, String with) {
        return replace(text, repl, with, 1);
    }

    /**
     * 対象の文字列内の全ての指定された文字列(repl)を指定された文字列(eith)に変換します。
     * {@primary Replace all occurances of a string within another string.}
     *
     * @see #replace(String text, String repl, String with, int max)
     * @param text  入れ替え対象の文字列を含むテキスト
     * {@primary text to search and replace in}
     * @param repl  入れ替え対象となる文字列
     * {@primary String to search for}
     * @param with  入れ替えられる文字列
     * {@primary String to replace with}
     * @return 文字列の入れ替えが行われたテキスト
     * {@primary the text with any replacements processed}
     */
    public static String replace(String text, String repl, String with) {
        return replace(text, repl, with, -1);
    }

    /**
     * 対象の文字列内の指定された文字列(repl)を先頭から <code>max</code>
     * の回数分、指定された文字列(eith)に変換します。
     * <code>null</code> がこのメソッドに渡された場合には何も行いません。
     * {@primary Replace a string with another string inside a larger string,
     * for the first <code>max</code> values of the search string.  A
     * <code>null</code> reference is passed to this method is a
     * no-op.}
     *
     * @param text  入れ替え対象の文字列を含むテキスト
     * {@primary text to search and replace in}
     * @param repl  入れ替え対象となる文字列
     * {@primary String to search for}
     * @param with  入れ替えられる文字列
     * {@primary String to replace with}
     * @param max  入れ替えを行う最大回数。<code>-1</code> の場合には制限なし
     * <code>-1</code> if no maximum
     * {@primary maximum number of values to replace, or <code>-1</code> if no maximum}
     * @return 文字列の入れ替えが行われたテキスト
     * {@primary the text with any replacements processed}
     * @throws NullPointerException 入れ替え対象となる文字列(repl) が null だった場合
     * {@primary if repl is null}
     */
    public static String replace(String text, String repl, String with,
                                 int max) {
        if (text == null) {
            return null;
        }

        StringBuffer buf = new StringBuffer(text.length());
        int start = 0, end = 0;
        while ((end = text.indexOf(repl, start)) != -1) {
            buf.append(text.substring(start, end)).append(with);
            start = end + repl.length();

            if (--max == 0) {
                break;
            }
        }
        buf.append(text.substring(start));
        return buf.toString();
    }

    /**
     * 文字列内の指定されたインデックスの場所を指定された文字列で上書きします。
     * {@primary Overlay a part of a string with another string.}
     *
     * @param text 上書きを行う対象となる文字列
     * {@primary String to do overlaying in}
     * @param overlay 上書きされる文字列
     * {@primary String to overlay}
     * @param start 上書きを行う開始位置
     * {@primary int to start overlaying at}
     * @param end   上書きを行う終了位置
     * {@primary int to stop overlaying before}
     * @return 文字列のうわがきが行われたテキスト
     * {@primary String with overlayed text}
     * @throws NullPointerException テキストまたは上書きする文字列が null だった場合
     * {@primary if text or overlay is null}
     */
    public static String overlayString(String text, String overlay, int start, int end) {
        return new StringBuffer(start + overlay.length() + text.length() - end + 1)
            .append(text.substring(0, start))
            .append(overlay)
            .append(text.substring(end))
            .toString();
    }

    // Centering
    //--------------------------------------------------------------------------
    
    /**
     * 指定された文字列が中心に配置された、指定された長さの文字列を返します。
     * 空白が文字列を埋める際に使用されます。
     * これは <code>center(str, size, " ")</code> と同等です。
     * {@primary Center a string in a larger string of size n.
     * Uses spaces as the value to buffer the string with.
     * Equivalent to <code>center(str, size, "")</code>}
     *
     * @param str  中央に配置する文字列
     * {@primary str  String to center}
     * @param size  新たに生成される文字列の長さ
     * {@primary size  int size of new String}
     * @return センタリングされた文字列
     * {@primary String containing centered String}
     * @throws NullPointerException str が null だった場合
     * {@primary if str is null}
     */
    public static String center(String str, int size) {
        return center(str, size, " ");
    }

    /**
     * 指定された文字列が中心に配置された、指定された長さの文字列を返します。
     * 指定された文字列が文字列を埋める際に使用されます。
     * {@primary Center a string in a larger string of size n.
     * Uses a supplied String as the value to buffer the string with..}
     *
     * @param str  中央に配置する文字列
     * {@primary str  String to center}
     * @param size  新たに生成される文字列の長さ
     * {@primary size  int size of new String}
     * @param delim  新たな文字列の(左右を)埋めるために使用される文字列
     * {@primary String to buffer the new String with}
     * @return センタリングされた文字列
     * {@primary String containing centered String}
     * @throws NullPointerException str または delim が null だった場合
     * {@primary if str or delim is null}
     * @throws ArithmeticException delim が空の文字列だった場合
     * {@primary if delim is the empty string}
     */
    public static String center(String str, int size, String delim) {
        int sz = str.length();
        int p = size - sz;
        if (p < 1) {
            return str;
        }
        str = leftPad(str, sz + p / 2, delim);
        str = rightPad(str, size, delim);
        return str;
    }

    // Chomping
    //--------------------------------------------------------------------------
    
    /** 
     * (文字列の)最後の改行と、その後ろに続く全ての文字列を削除します。
     * {@primary Remove the last newline, and everything after it from a String.}
     *
     * @param str  改行以降を削除する対象となる文字列
     * {@primary String to chomp the newline from}
     * @return 改行以降が削除された文字列
     * {@primary String without chomped newline}
     * @throws NullPointerException str が null だった場合
     * {@primary if str is null}
     */
    public static String chomp(String str) {
        return chomp(str, "\n");
    }
    
    /** 
     * 最後に見つかった指定された文字列とその後ろに続く全ての文字列を削除し返します。
     * {@primary Remove the last value of a supplied String, and everything after it 
     * from a String.}
     *
     * @param str  末尾を削除する対象となる文字列
     * {@primary String to chomp from}
     * @param sep  削除する文字列
     * {@primary String to chomp}
     * @return 末尾が削除された文字列
     * {@primary String without chomped ending}
     * @throws NullPointerException str または sep が null だった場合
     * {@primary if str or sep is null}
     */
    public static String chomp(String str, String sep) {
        int idx = str.lastIndexOf(sep);
        if (idx != -1) {
            return str.substring(0, idx);
        } else {
            return str;
        }
    }
    
    /**
     * 文字列の末尾が改行だった場合のみ、改行を削除します。
     * {@primary Remove a newline if and only if it is at the end 
     * of the supplied string.}
     * 
     * @param str  末尾を削除する対象となる文字列
     * {@primary String to chomp from}
     * @return 末尾が削除された文字列
     * {@primary String without chomped ending}
     * @throws NullPointerException  str が null だった場合
     * {@primary if str is null}
     */
    public static String chompLast(String str) {
        return chompLast(str, "\n");
    }
    
    /**
     * 文字列の末尾が指定された文字列だった場合にのみ、末尾のこの文字列を削除します。
     * {@primary Remove a value if and only if the string ends with that value.}
     * 
     * @param str  末尾を削除する対象となる文字列
     * {@primary String to chomp from}
     * @param sep  削除する文字列
     * {@primary String to chomp}
     * @return 末尾が削除された文字列
     * {@primary String without chomped ending}
     * @throws NullPointerException str または sep が null だった場合
     * {@primary if str or sep is null}
     */
    public static String chompLast(String str, String sep) {
        if (str.length() == 0) {
            return str;
        }
        String sub = str.substring(str.length() - sep.length());
        if (sep.equals(sub)) {
            return str.substring(0, str.length() - sep.length());
        } else {
            return str;
        }
    }

    /** 
     * 最後に見つかった指定された文字列の前の部分を全て削除し返します。
     * {@primary Remove everything and return the last value of a supplied String, and 
     * everything after it from a String.}
     *
     * @param str 末尾を取得する対象となる文字列
     * {@primary String to chomp from}
     * @param sep 末尾を取得する際に検索対象となる文字列
     * {@primary String to chomp}
     * @return 末尾の文字列
     * {@primary String chomped}
     * @throws NullPointerException str または sep が null だった場合
     * {@primary if str or sep is null}
     */
    public static String getChomp(String str, String sep) {
        int idx = str.lastIndexOf(sep);
        if (idx == str.length() - sep.length()) {
            return sep;
        } else if (idx != -1) {
            return str.substring(idx);
        } else {
            return "";
        }
    }

    /** 
     * 最初に見つかった指定された文字列までを削除し、その後ろに続く文字列を返します。
     * {@primary Remove the first value of a supplied String, and everything before it 
     * from a String.}
     *
     * @param str 先頭をを削除する対象となる文字列
     * {@primary String to chomp from}
     * @param sep 先頭をを削除する際に検索対象となる文字列
     * {@primary String to chomp}
     * @return 先頭をを削除された文字列
     * {@primary String without chomped beginning}
     * @throws NullPointerException str または sep が null だった場合
     * {@primary if str or sep is null}
     */
    public static String prechomp(String str, String sep) {
        int idx = str.indexOf(sep);
        if (idx != -1) {
            return str.substring(idx + sep.length());
        } else {
            return str;
        }
    }

    /** 
     * 最初に見つかった指定された文字列より後ろを全て削除し返します。
     * {@primary Remove and return everything before the first value of a 
     * supplied String from another String.}
     *
     * @param str 先頭を取得する対象となる文字列
     * {@primary String to chomp from}
     * @param sep 先頭を取得する際に検索対象となる文字列
     * {@primary String to chomp}
     * @return 先頭の文字列
     * {@primary String prechomped}
     * @throws NullPointerException str または sep が null だった場合
     * {@primary if str or sep is null}
     */
    public static String getPrechomp(String str, String sep) {
        int idx = str.indexOf(sep);
        if (idx != -1) {
            return str.substring(0, idx + sep.length());
        } else {
            return "";
        }
    }

    // Chopping
    //--------------------------------------------------------------------------
    
    /**
     * 文字列の最後の1文字を削除し返します。 If the String 
     * 文字列の最後が \r\n の場合には、この両方を削除します。
     * {@primary Remove the last character from a String. If the String 
     * ends in \r\n, then remove both of them.}
     *
     * @param str 末尾を削除する対象となる文字列
     * {@primary String to chop last character from}
     * @return 末尾が削除された文字列
     * {@primary String without last character}
     * @throws NullPointerException str が null だった場合
     * {@primary if str is null}
     */
    public static String chop(String str) {
        if ("".equals(str)) {
            return "";
        }
        if (str.length() == 1) {
            return "";
        }
        int lastIdx = str.length() - 1;
        String ret = str.substring(0, lastIdx);
        char last = str.charAt(lastIdx);
        if (last == '\n') {
            if (ret.charAt(lastIdx - 1) == '\r') {
                return ret.substring(0, lastIdx - 1);
            }
        }
        return ret;
    }

    /**
     * 文字列の最後が \n たった場合にこれを削除します。
     * その前が \r だった場合にはこれも削除します。
     * {@primary Remove \n from end of a String if it's there.
     * If a \r precedes it, then remove that too.}
     *
     * @param str 末尾の改行を削除する対象となる文字列
     * {@primary String to chop a newline from}
     * @return 末尾の改行が削除された文字列
     * {@primary String without newline}
     * @throws NullPointerException str が null だった場合
     * {@primary if str is null}
     */
    public static String chopNewline(String str) {
        int lastIdx = str.length() - 1;
        char last = str.charAt(lastIdx);
        if (last == '\n') {
            if (str.charAt(lastIdx - 1) == '\r') {
                lastIdx--;
            }
        } else {
            lastIdx++;
        }
        return str.substring(0, lastIdx);
    }


    // Conversion
    //--------------------------------------------------------------------------
    
    // spec 3.10.6
    /**
     * 文字列の中の全ての値をエスケープします。
     * 例えばタブは '\\' と 't' の文字になります。
     * {@primary Escapes any values it finds into their String form.
     * So a tab becomes the characters '\\' and 't'.}
     *
     * @param str エスケープの対象となる文字列
     * {@primary String to escape values in}
     *
     * @return エスケープされた文字列
     * {@primary String with escaped values}
     * @throws NullPointerException if str is null
     */
    public static String escape(String str) {
        // improved with code from  cybertiger@cyberiantiger.org
        // unicode from him, and defaul for < 32's.
        int sz = str.length();
        StringBuffer buffer = new StringBuffer(2 * sz);
        for (int i = 0; i < sz; i++) {
            char ch = str.charAt(i);

            // handle unicode
            if (ch > 0xfff) {
                buffer.append("\\u" + Integer.toHexString(ch));
            } else if (ch > 0xff) {
                buffer.append("\\u0" + Integer.toHexString(ch));
            } else if (ch > 0x7f) {
                buffer.append("\\u00" + Integer.toHexString(ch));
            } else if (ch < 32) {
                switch (ch) {
                    case '\b' :
                        buffer.append('\\');
                        buffer.append('b');
                        break;
                    case '\n' :
                        buffer.append('\\');
                        buffer.append('n');
                        break;
                    case '\t' :
                        buffer.append('\\');
                        buffer.append('t');
                        break;
                    case '\f' :
                        buffer.append('\\');
                        buffer.append('f');
                        break;
                    case '\r' :
                        buffer.append('\\');
                        buffer.append('r');
                        break;
                    default :
                        if (ch > 0xf) {
                            buffer.append("\\u00" + Integer.toHexString(ch));
                        } else {
                            buffer.append("\\u000" + Integer.toHexString(ch));
                        }
                        break;
                }
            } else {
                switch (ch) {
                    case '\'' :
                        buffer.append('\\');
                        buffer.append('\'');
                        break;
                    case '"' :
                        buffer.append('\\');
                        buffer.append('"');
                        break;
                    case '\\' :
                        buffer.append('\\');
                        buffer.append('\\');
                        break;
                    default :
                        buffer.append(ch);
                        break;
                }
            }
        }
        return buffer.toString();
    }

    // Padding
    //--------------------------------------------------------------------------
    
    /**
     * 指定された文字列を指定された回数繰り返し作成された文字列を返します。
     * {@primary Repeat a string n times to form a new string.}
     *
     * @param str  繰り返す文字列
     * {@primary String to repeat}
     * @param repeat  繰り返す回数
     * {@primary int number of times to repeat}
     * @return 繰り返された文字列
     * {@primary String with repeated string}
     * @throws NegativeArraySizeException repeat が 0 よりも小さかった場合
     * {@primary if repeat < 0}
     * @throws NullPointerException str が null だった場合
     * {@primary if str is null}
     */
    public static String repeat(String str, int repeat) {
        StringBuffer buffer = new StringBuffer(repeat * str.length());
        for (int i = 0; i < repeat; i++) {
            buffer.append(str);
        }
        return buffer.toString();
    }

    /**
     * 指定された長さになるまで文字列の右側(末尾)をスペースで埋めます。
     * {@primary Right pad a String with spaces. Pad to a size of n.}
     * 
     * @param str  埋め込む対象となる文字列
     * {@primary String to repeat}
     * @param size  埋め込んだ後の文字列の長さ
     * {@primary int number of times to repeat}
     * @return 右側を埋められた文字列
     * {@primary right padded String}
     * @throws NullPointerException str が null だった場合
     * {@primary if str is null}
     */
    public static String rightPad(String str, int size) {
        return rightPad(str, size, " ");
    }
    
    /**
     * 指定された長さになるまで文字列の右側(末尾)を指定された文字列で埋めます。
     * {@primary Right pad a String with a specified string. Pad to a size of n.}
     *
     * @param str  埋め込む対象となる文字列
     * {@primary String to pad out}
     * @param size  埋め込んだ後の文字列の長さ
     * {@primary int size to pad to}
     * @param delim  埋め込まれる文字列
     * {@primary String to pad with}
     * @return 右側を埋められた文字列
     * {@primary right padded String}
     * @throws NullPointerException str または delim が null だった場合
     * {@primary if str or delim is null}
     * @throws ArithmeticException delim が殻の文字列だった場合
     * {@primary if delim is the empty string}
     */
    public static String rightPad(String str, int size, String delim) {
        size = (size - str.length()) / delim.length();
        if (size > 0) {
            str += repeat(delim, size);
        }
        return str;
    }

    /**
     * 指定された長さになるまで文字列の左側(先頭)をスペースで埋めます。
     * {@primary Left pad a String with spaces. Pad to a size of n.}
     *
     * @param str  埋め込む対象となる文字列
     * {@primary String to pad out}
     * @param size  埋め込んだ後の文字列の長さ
     * {@primary int size to pad to}
     * @return 左側を埋められた文字列
     * {@primary left padded String}
     * @throws NullPointerException str が null だった場合
     * {@primary if str or delim is null}
     */
    public static String leftPad(String str, int size) {
        return leftPad(str, size, " ");
    }
    /**
     * 指定された長さになるまで文字列の左側(先頭)を指定された文字列で埋めます。
     * {@primary Left pad a String with a specified string. Pad to a size of n.}
     *
     * @param str  埋め込む対象となる文字列
     * {@primary String to pad out}
     * @param size  埋め込んだ後の文字列の長さ
     * {@primary int size to pad to}
     * @param delim  埋め込まれる文字列
     * {@primary String to pad with}
     * @return 左側を埋められた文字列
     * {@primary left padded String}
     * @throws NullPointerException str または delim が null だった場合
     * {@primary if str or delim is null}
     * @throws ArithmeticException delim が殻の文字列だった場合
     * {@primary if delim is the empty string}
     */
    public static String leftPad(String str, int size, String delim) {
        size = (size - str.length()) / delim.length();
        if (size > 0) {
            str = repeat(delim, size) + str;
        }
        return str;
    }

    // Stripping
    //--------------------------------------------------------------------------
    
    /**
     * 文字列の前後から空白を削除します。
     * {@primary Remove whitespace from the front and back of a String.}
     * 
     * @param str  空白を削除する対象となる文字列
     * {@primary the string to remove whitespace from}
     * @return 空白の削除された文字列
     * {@primary the stripped string}
     */
    public static String strip(String str) {
        return strip(str, null);
    }
    /**
     * 文字列の前後から指定された文字列を削除します。
     * 空白を削除したい場合には strip(String) メソッドを使用してください。
     * {@primary Remove a specified String from the front and back of a 
     * String. If Whitespace is wanted to be removed, used the 
     * strip(String) method.}
     * 
     * @param str  指定された文字列を削除する対象となる文字列
     * {@primary the string to remove a string from}
     * @param delim  前後から削除する文字列
     * {@primary the string to remove at start and end}
     * @return 指定された文字列の削除された文字列
     * {@primary the stripped string}
     */
    public static String strip(String str, String delim) {
        str = stripStart(str, delim);
        return stripEnd(str, delim);
    }

    /**
     * 配列の中の全ての文字列の前後から空白を削除します。
     * {@primary Strip whitespace from the front and back of every string
     * in the array.}
     * 
     * @param strs  空白を削除する対象となる文字列の配列
     * {@primary the strings to remove whitespace from}
     * @return the 空白の削除された文字列の配列
     * {@primary the stripped strings}
     */
    public static String[] stripAll(String[] strs) {
        return stripAll(strs, null);
    }
 
    /**
     * 配列の中の全ての文字列の前後から指定された文字列を削除します。
     * {@primary Strip the specified delimiter from the front and back of
     * every String in the array.}
     * 
     * @param strs  指定された文字列を削除する対象となる文字列の配列
     * {@primary the strings to remove a string from}
     * @param delimiter  前後から削除する文字列
     * {@primary the string to remove at start and end}
     * @return 指定された文字列の削除された文字列の配列
     * {@primary the stripped strings}
     */
    public static String[] stripAll(String[] strs, String delimiter) {
        if ((strs == null) || (strs.length == 0)) {
            return strs;
        }
        int sz = strs.length;
        String[] newArr = new String[sz];
        for (int i = 0; i < sz; i++) {
            newArr[i] = strip(strs[i], delimiter);
        }
        return newArr;
    }   

    /**
     * 文字列の最後から指定された文字列を削除します。
     * 削除する文字列が null だった場合、空白が削除されます。
     * {@primary Strip any of a supplied string from the end of a String..
     * If the strip string is null, whitespace is stripped.}
     * 
     * @param str  指定された文字を削除する対象となる文字列
     * {@primary the string to remove characters from}
     * @param strip  削除する文字列
     * {@primary the string to remove}
     * @return 指定された文字列の削除された文字列
     * {@primary the stripped string}
     */
    public static String stripEnd(String str, String strip) {
        if (str == null) {
            return null;
        }
        int end = str.length();
 
        if (strip == null) {
            while ((end != 0) && Character.isWhitespace(str.charAt(end - 1))) {
                end--;
            }
        } else {
            while ((end != 0) && (strip.indexOf(str.charAt(end - 1)) != -1)) {
                end--;
            }
        }
        return str.substring(0, end);
    }

    /**
     * 文字列の最初から指定された文字列を削除します。
     * 削除する文字列が null だった場合、空白が削除されます。
     * {@primary Strip any of a supplied string from the start of a String.
     * If the strip string is null, whitespace is stripped.}
     * 
     * @param str 指定された文字を削除する対象となる文字列
     * {@primary the string to remove characters from}
     * @param strip  削除する文字列
     * {@primary the string to remove}
     * @return 指定された文字列の削除された文字列
     * {@primary the stripped string}
     */
    public static String stripStart(String str, String strip) {
        if (str == null) {
            return null;
        }
 
        int start = 0;
 
        int sz = str.length();
 
        if (strip == null) {
            while ((start != sz) && Character.isWhitespace(str.charAt(start))) {
                start++;
            }
        } else {
            while ((start != sz) && (strip.indexOf(str.charAt(start)) != -1)) {
                start++;
            }
        }
        return str.substring(start);
    }

    // Case conversion
    //--------------------------------------------------------------------------
    
    /**
     * 文字列内の小文字を大文字に変換します。
     * null が指定された場合には null を返します。
     * {@primary Convert a String to upper case, null string returns null.}
     * 
     * @param str  小文字を大文字に変換する対象となる文字列
     * {@primary the string to uppercase}
     * @return 小文字が大文字に変換された文字列
     * {@primary the upper cased string}
     */
    public static String upperCase(String str) {
        if (str == null) {
            return null;
        }
        return str.toUpperCase();
    }

    /**
     * 文字列内の大文字を小文字に変換します。
     * null が指定された場合には null を返します。
     * {@primary Convert a String to lower case, null string returns null.}
     * 
     * @param str  大文字を小文字に変換する対象となる文字列
     * {@primary the string to lowercase}
     * @return 大文字が小文字に変換された文字列
     * {@primary lower cased string}
     */
    public static String lowerCase(String str) {
        if (str == null) {
            return null;
        }
        return str.toLowerCase();
    }

    /**
     * 文字列の先頭を小文字にします。null が指定された場合には null を返します。
     * {@primary Uncapitalise a string. That is, convert the first character into 
     * lower-case. Null is returned as null.}
     *
     * @param str  先頭を小文字にする文字列
     * {@primary the string to uncapitalise}
     * @return 先頭が小文字となった文字列
     * {@primary uncapitalised string}
     */
    public static String uncapitalise(String str) {
        if (str == null) {
            return null;
        }
        if (str.length() == 0) {
            return "";
        }
        return new StringBuffer(str.length())
            .append(Character.toLowerCase(str.charAt(0)))
            .append(str.substring(1))
            .toString();
    }

    /**
     * 文字列の先頭を大文字にします。null が指定された場合には null を返します。
     * {@primary Capitalise a string. That is, convert the first character into 
     * title-case. Null is returned as null.}
     *
     * @param str  先頭を大文字にする文字列
     * {@primary the string to capitalise}
     * @return 先頭が大文字となった文字列
     * {@primary capitalised string}
     */
    public static String capitalise(String str) {
        if (str == null) {
            return null;
        }
        if (str.length() == 0) {
            return "";
        }
        return new StringBuffer(str.length())
            .append(Character.toTitleCase(str.charAt(0)))
            .append(str.substring(1))
            .toString();
    }

    /**
     * 大文字と小文字を入れ替えます。単語の開始文字は大文字ではなく
     * タイトルケースとして扱われます。
     * null が指定された場合には null を返します。
     * {@primary Swaps the case of String. Properly looks after 
     * making sure the start of words are Titlecase and not 
     * Uppercase. Null is returned as null.}
     * 
     * @param str  大文字と小文字を入れ替える文字列
     * {@primary the string to swap the case of}
     * @return 処理された文字列
     * {@primary the modified string}
     */
    public static String swapCase(String str) {
        if (str == null) {
            return null;
        }
        int sz = str.length();
        StringBuffer buffer = new StringBuffer(sz);

        boolean whitespace = false;
        char ch = 0;
        char tmp = 0;

        for (int i = 0; i < sz; i++) {
            ch = str.charAt(i);
            if (Character.isUpperCase(ch)) {
                tmp = Character.toLowerCase(ch);
            } else if (Character.isTitleCase(ch)) {
                tmp = Character.toLowerCase(ch);
            } else if (Character.isLowerCase(ch)) {
                if (whitespace) {
                    tmp = Character.toTitleCase(ch);
                } else {
                    tmp = Character.toUpperCase(ch);
                }
            } else {
                tmp = ch;
            }
            buffer.append(tmp);
            whitespace = Character.isWhitespace(ch);
        }
        return buffer.toString();
    }


    /**
     * 文字列内の全ての単語の先頭を大文字にします。
     * 単語と単語の区切り文字を判定するために Character.isWhitespace を使用します。
     * null が指定された場合には null を返します。
     * {@primary Capitalise all the words in a string. Uses Character.isWhitespace 
     * as a separator between words. Null will return null.}
     *
     * @param str  先頭を大文字にする文字列
     * {@primary the string to capitalise}
     * @return 先頭が大文字となった文字列
     * {@primary capitalised string}
     */
    public static String capitaliseAllWords(String str) {
        if (str == null) {
            return null;
        }
        int sz = str.length();
        StringBuffer buffer = new StringBuffer(sz);
        boolean space = true;
        for (int i = 0; i < sz; i++) {
            char ch = str.charAt(i);
            if (Character.isWhitespace(ch)) {
                buffer.append(ch);
                space = true;
            } else if (space) {
                buffer.append(Character.toTitleCase(ch));
                space = false;
            } else {
                buffer.append(ch);
            }
        }
        return buffer.toString();
    }

    // Nested extraction
    //--------------------------------------------------------------------------
    
    /**
     * 同じ文字列の間に挟まれ、ネストされた文字列を返します。
     * null が指定された場合には null を返します。
     * {@primary Get the String that is nested in between two instances of the 
     * same String. If str is null, will return null}
     *
     * @param str  ネストされた文字列を含む文字列
     * {@primary the string containing nested-string}
     * @param tag  ネストされた文字列の前後を挟んでいる文字列
     * {@primary the string before and after nested-string}
     * @return ネストされた文字列、または null
     * {@primary the string that was nested, or null}
     * @throws NullPointerException tag が null だった場合
     * {@primary NullPointerException if tag is null}
     */
    public static String getNestedString(String str, String tag) {
        return getNestedString(str, tag, tag);
    }
    
    /**
     * 2つの文字列の間に挟まれ、ネストされた文字列を返します。
     * {@primary Get the string that is nested in between two strings.}
     *
     * @param str  ネストされた文字列を含む文字列
     * {@primary the string containing nested-string}
     * @param open  ネストされた文字列の前にある文字列
     * {@primary the string before nested-string}
     * @param close  ネストされた文字列の後ろにある文字列
     * {@primary the string after nested-string}
     * @return ネストされた文字列、または null
     * {@primary the string that was nested, or null}
     * @throws NullPointerException open または close が null だった場合
     * {@primary if open or close  is null}
     */
    public static String getNestedString(String str, String open, String close) {
        if (str == null) {
            return null;
        }
        int start = str.indexOf(open);
        if (start != -1) {
            int end = str.indexOf(close, start + open.length());
            if (end != -1) {
                return str.substring(start + open.length(), end);
            }
        }
        return null;
    }

    /**
     * 文字列の中に指定された文字列がいくつあるかを返します。
     * null が指定された場合には0を返します。
     * {@primary How many times is the substring in the larger string.
     * Null returns 0.}
     * 
     * @param str  チェックを行う対象となる文字列
     * {@primary the string to check}
     * @param sub  いくつあるか数える文字列
     * {@primary the substring to count}
     * @return 文字列の見つかった数、チェック対照の文字列が null だった場合0
     * {@primary the number of occurances, 0 if the string is null}
     * @throws NullPointerException sub が null だった場合
     * {@primary if sub is null}
     */
    public static int countMatches(String str, String sub) {
        if (str == null) {
            return 0;
        }
        int count = 0;
        int idx = 0;
        while ((idx = str.indexOf(sub, idx)) != -1) {
            count++;
            idx += sub.length();
        }
        return count;
    }

    // Character Tests
    //--------------------------------------------------------------------------
    
    /**
     * 文字列がユニコードの汎用文字だけで構成されているかをチェックします。
     * null が指定された場合には false を返します。空の文字列が指定された場合には true を返します。
     * {@primary Checks if the string contains only unicode letters.
     * Null will return false. The empty string will return true.}
     * 
     * @param str  チェックを行う対象となる文字列
     * {@primary the string to check}
     * @return null でなく、汎用文字だけで構成されている場合、true
     * {@primary true if only contains letters, and is non-null}
     */
    public static boolean isAlpha(String str) {
        if (str == null) {
            return false;
        }
        int sz = str.length();
        for (int i = 0; i < sz; i++) {
            if (Character.isLetter(str.charAt(i)) == false) {
                return false;
            }
        }
        return true;
    }

    /**
     * 文字列がユニコードの汎用文字とスペース(' ')だけで構成されているかをチェックします。
     * null が指定された場合には false を返します。空の文字列が指定された場合には true を返します。
     * {@primary Checks if the string contains only unicode letters and space (' ').
     * Null will return false.  The empty string will return true.}
     * 
     * @param str  チェックを行う対象となる文字列
     * {@primary the string to check}
     * @return null でなく、汎用文字とスペースだけで構成されている場合、true
     * {@primary true if only contains letters and space, and is non-null}
     */
    public static boolean isAlphaSpace(String str) {
        if (str == null) {
            return false;
        }
        int sz = str.length();
        for (int i = 0; i < sz; i++) {
            if ((Character.isLetter(str.charAt(i)) == false) &&
                (str.charAt(i) != ' ')) {
                return false;
            }
        }
        return true;
    }

    /**
     * 文字列がユニコードの汎用文字と数字だけで構成されているかをチェックします。
     * null が指定された場合には false を返します。空の文字列が指定された場合には true を返します。
     * {@primary Checks if the string contains only unicode letters or digits.
     * Null will return false. The empty string will return true.}
     * 
     * @param str  チェックを行う対象となる文字列
     * {@primary the string to check}
     * @return null でなく、汎用文字と数字だけで構成されている場合、true
     * {@primary true if only contains letters or digits, and is non-null}
     */
    public static boolean isAlphanumeric(String str) {
        if (str == null) {
            return false;
        }
        int sz = str.length();
        for (int i = 0; i < sz; i++) {
            if (Character.isLetterOrDigit(str.charAt(i)) == false) {
                return false;
            }
        }
        return true;
    }

    /**
     * 文字列がユニコードの汎用文字と数字とスペース(' ')だけで構成されているかをチェックします。
     * null が指定された場合には false を返します。空の文字列が指定された場合には true を返します。
     * {@primary Checks if the string contains only unicode letters, digits or space (' ').
     * Null will return false. The empty string will return true.}
     * 
     * @param str  チェックを行う対象となる文字列
     * {@primary the string to check}
     * @return null でなく、汎用文字と数字とスペースだけで構成されている場合、true
     * {@primary true if only contains letters, digits or space, and is non-null}
     */
    public static boolean isAlphanumericSpace(String str) {
        if (str == null) {
            return false;
        }
        int sz = str.length();
        for (int i = 0; i < sz; i++) {
            if ((Character.isLetterOrDigit(str.charAt(i)) == false) &&
                (str.charAt(i) != ' ')) {
                return false;
            }
        }
        return true;
    }

    /**
     * 文字列がユニコードの数字だけで構成されているかをチェックします。
     * null が指定された場合には false を返します。空の文字列が指定された場合には true を返します。
     * {@primary Checks if the string contains only unicode digits.
     * Null will return false. The empty string will return true.}
     * 
     * @param str  チェックを行う対象となる文字列
     * {@primary the string to check}
     * @return null でなく、数字だけで構成されている場合、true
     * {@primary true if only contains digits, and is non-null}
     */
    public static boolean isNumeric(String str) {
        if (str == null) {
            return false;
        }
        int sz = str.length();
        for (int i = 0; i < sz; i++) {
            if (Character.isDigit(str.charAt(i)) == false) {
                return false;
            }
        }
        return true;
    }

    /**
     * 文字列がユニコードの数字とスペース(' ')だけで構成されているかをチェックします。
     * null が指定された場合には false を返します。空の文字列が指定された場合には true を返します。
     * {@primary Checks if the string contains only unicode digits or space (' ').
     * Null will return false. The empty string will return true.}
     * 
     * @param str  チェックを行う対象となる文字列
     * {@primary the string to check}
     * @return null でなく、数字とスペースだけで構成されている場合、true
     * {@primary true if only contains digits or space, and is non-null}
     */
    public static boolean isNumericSpace(String str) {
        if (str == null) {
            return false;
        }
        int sz = str.length();
        for (int i = 0; i < sz; i++) {
            if ((Character.isDigit(str.charAt(i)) == false) &&
                (str.charAt(i) != ' ')) {
                return false;
            }
        }
        return true;
    }

    // Defaults
    //--------------------------------------------------------------------------
    
    /**
     * 渡された文字列をチェックし、null だった場合には空の文字列を返します。
     * それ以外の場合には渡された文字列を返します。
     * {@primary Return either the passed in String, or if it is null, 
     * then an empty String.}
     * 
     * @param str  チェックを行う対象となる文字列
     * {@primary the string to check}
     * @return 渡された文字列、null の場合には空の文字列
     * {@primary the passed in string, or blank if it was null}
     */
    public static String defaultString(String str) {
        return defaultString(str, "");
    }

    /**
     * 渡された文字列をチェックし、null だった場合には指定されたデフォルトの文字列を返します。
     * それ以外の場合には渡された文字列を返します。
     * {@primary Return either the passed in String, or if it is null, 
     * then a passed in default String.}
     * 
     * @param str  チェックを行う対象となる文字列
     * {@primary the string to check}
     * @param defaultString  渡された文字列が null の場合に返されるデフォルトの文字列
     * {@primary the default string to return is str is null}
     * @return 渡された文字列、null の場合には空の文字列
     * {@primary the passed in string, or the default if it was null}
     */
    public static String defaultString(String str, String defaultString) {
        return (str == null) ? defaultString : str;
    }

    // Reversing
    //--------------------------------------------------------------------------

    /**
     * 文字列内の並び順を逆にします。
     * null が指定された場合には null を返します。
     * {@primary Reverse a String, null string returns null.}
     * 
     * @param str  逆の並び順にする文字列
     * {@primary the string to reverse}
     * @return 逆の並び順にされた文字列
     * {@primary the reversed string}
     */
    public static String reverse(String str) {
        if (str == null) {
            return null;
        }
        return new StringBuffer(str).reverse().toString();
    }

    /**
     * 指定された区切り文字で文字列を分割し、その並び順を逆にします。
     * 区切り文字の間の文字の並び順は逆にはなりません。
     * 従って java.lang.String は('.' を区切り文字にした場合) String.lang.java になります。
     * {@primary Reverses a string that is delimited by a specific character.
     * The strings between the delimiters are not reversed.
     * Thus java.lang.String becomes String.lang.java (if the delimiter is '.').}
     * 
     * @param str  逆の並び順にする文字列
     * {@primary the string to reverse}
     * @param delimiter  使用される区切り文字
     * {@primary the delimiter to use}
     * @return 逆の並び順にされた文字列
     * {@primary the reversed string}
     */
    public static String reverseDelimitedString(String str, String delimiter) {
        // could implement manually, but simple way is to reuse other, 
        // probably slower, methods.
        String[] strs = split(str, delimiter);
        reverseArray(strs);
        return join(strs, delimiter);
    }

    /**
     * 配列の並び順を逆にします。
     * CollectionsUtils から取り入れられました。
     * {@primary Reverses an array.
     * TAKEN FROM CollectionsUtils.}
     * @param array  逆の並び順にする配列
     * {@primary the array to reverse}
     */
    private static void reverseArray(Object[] array) {
        int i = 0;
        int j = array.length - 1;
        Object tmp;

        while (j > i) {
            tmp = array[j];
            array[j] = array[i];
            array[i] = tmp;
            j--;
            i++;
        }
    }


    // Misc
    //--------------------------------------------------------------------------

    /**
     * 2つの文字列の間の Levenshtein 距離を調べます。
     * これは文字列をもう一方に変えるために必要となる数を示します。
     * 1つの変更は1つの文字の修正を意味します。
     *
     * Levenshtein 距離を調べるこの実装のアルゴリズムは
     *  http://www.merriampark.com/ld.htm から取り入られました。
     * {@primary Find the Levenshtein distance between two strings.
     * This is the number of changes needed to change one string into 
     * another. Where each change is a single character modification.
     *
     * This implemmentation of the levenshtein distance algorithm 
     * is from http://www.merriampark.com/ld.htm}
     * 
     * @param s  最初の文字列
     * {@primary the first String}
     * @param t  次の文字列
     * {@primary the second String}
     * @return 結果となる距離の int
     * {@primary int result distance}
     * @throws NullPointerException s または t が null だった場合
     * {@primary if s or t is null}
     */
    public static int getLevenshteinDistance(String s, String t) {
        int d[][]; // matrix
        int n; // length of s
        int m; // length of t
        int i; // iterates through s
        int j; // iterates through t
        char s_i; // ith character of s
        char t_j; // jth character of t
        int cost; // cost

        // Step 1
        n = s.length();
        m = t.length();
        if (n == 0) {
            return m;
        }
        if (m == 0) {
            return n;
        }
        d = new int[n + 1][m + 1];

        // Step 2
        for (i = 0; i <= n; i++) {
            d[i][0] = i;
        }

        for (j = 0; j <= m; j++) {
            d[0][j] = j;
        }

        // Step 3
        for (i = 1; i <= n; i++) {
            s_i = s.charAt(i - 1);

            // Step 4
            for (j = 1; j <= m; j++) {
                t_j = t.charAt(j - 1);

                // Step 5
                if (s_i == t_j) {
                    cost = 0;
                } else {
                    cost = 1;
                }

                // Step 6
                d[i][j] = NumberUtils.minimum(d[i - 1][j] + 1, d[i][j - 1] + 1, d[i - 1][j - 1] + cost);
            }
        }

        // Step 7
        return d[n][m];
    }

    /**
     * 文字列が指定された文字だけで構成されているかをチェックします。
     * {@primary Checks if the String contains only certain chars.}
     *
     * @param str チェックを行う対象となる文字列
     * {@primary the String to check}
     * @param valid 有効な文字を示す配列
     * {@primary an array of valid chars}
     * @return true 有効な文字のみで構成されており、null でなかった場合、 true
     * {@primary true if it only contains valid chars and is non-null}
     */
    public static boolean containsOnly(String str, char[] valid) {
        if (str == null || valid == null) {
            return false;
        }

        int strSize = str.length();
        int validSize = valid.length;

        for (int i = 0; i < strSize; i++) {
            boolean contains = false;
            for (int j = 0; j < validSize; j++) {
                if (valid[j] == str.charAt(i)) {
                    contains = true;
                    break;
                }
            }
            if (!contains) {
                return false;
            }
        }

        return true;
    }
}
