/*
 * This file is part of Nuts Framework.
 * Copyright(C) 2009-2012 Nuts Develop Team.
 *
 * Nuts Framework is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License any later version.
 * 
 * Nuts Framework is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Nuts Framework. If not, see <http://www.gnu.org/licenses/>.
 */
package nuts.core.lang;

import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.regex.Pattern;

import nuts.core.io.CsvReader;
import nuts.core.io.CsvWriter;


/**
 * Utility class for String.
 */
public class StringUtils extends org.apache.commons.lang3.StringUtils {

	/**
	 * LINE_SEPARATOR = System.getProperty("line.separator");
	 */
	public static final String LINE_SEPARATOR = System.getProperty("line.separator");
	
	// ---------------------------------------------------------------------
	// General convenience methods for working with Strings
	// ---------------------------------------------------------------------
	/**
	 * <p>
	 * Checks if a String is a control character(c < 0x20 && c != '\t' && c != '\r' &&& c != '\n') text.
	 * </p>
	 * 
	 * @param str the String to check, may be null
	 * @return <code>true</code> if the String is a control string
	 */
	public static boolean isControl(CharSequence str) {
		int strLen;
		if (str == null || (strLen = str.length()) == 0) {
			return false;
		}
		for (int i = 0; i < strLen; i++) {
			char c = str.charAt(i);
			if (c >= 0x20 || c == '\t' || c == '\r' || c == '\n') {
				return false;
			}
		}
		return true;
	}

	/**
	 * <p>
	 * Checks if a String is a printable text.
	 * </p>
	 * 
	 * <pre>
	 * StringUtils.isPrintable(null)      = false
	 * StringUtils.isPrintable("")        = false
	 * StringUtils.isPrintable(" ")       = false
	 * StringUtils.isPrintable("bob")     = true
	 * StringUtils.isPrintable("\u0003 \r\n") = false
	 * </pre>
	 * 
	 * @param str the String to check, may be null
	 * @return <code>true</code> if the String is a printable string
	 */
	public static boolean isPrintable(CharSequence str) {
		int strLen;
		if (str == null || (strLen = str.length()) == 0) {
			return false;
		}
		for (int i = 0; i < strLen; i++) {
			char c = str.charAt(i);
			if (c > 0x20 && !Character.isWhitespace(c)) {
				return true;
			}
		}
		return false;
	}

	/**
	 * Trim all occurences of the leading/trailing space character from the given String[].
	 * @param strs the String[] to trim
	 * @return the trimmed String[]
	 */
	public static String[] trimAll(String[] strs) {
		if (strs == null) {
			return null;
		}

		for (int i = 0; i < strs.length; i++) {
			strs[i] = trim(strs[i]);
		}

		return strs;
	}

    /**
     * <p>Strips whitespace from the start of every String in an array.
     * Whitespace is defined by {@link Character#isWhitespace(char)}.</p>
     *
     * <p>A new array is returned each time, except for length zero.
     * A <code>null</code> array will return <code>null</code>.
     * An empty array will return itself.
     * A <code>null</code> array entry will be ignored.</p>
     *
     * <pre>
     * StringUtils.stripAll(null)             = null
     * StringUtils.stripAll([])               = []
     * StringUtils.stripAll(["abc", "  abc"]) = ["abc", "abc"]
     * StringUtils.stripAll(["  abc", null])  = ["abc", null]
     * StringUtils.stripAll(["  abc  ", null])  = ["abc  ", null]
     * </pre>
     *
     * @param strs  the array to remove whitespace from, may be null
     * @return the stripped Strings, <code>null</code> if null array input
     */
    public static String[] stripAllStart(String[] strs) {
        return stripAllStart(strs, null);
    }

    /**
     * <p>Strips any of a set of characters from the start of every
     * String in an array.</p>
     * Whitespace is defined by {@link Character#isWhitespace(char)}.</p>
     *
     * <p>A new array is returned each time, except for length zero.
     * A <code>null</code> array will return <code>null</code>.
     * An empty array will return itself.
     * A <code>null</code> array entry will be ignored.
     * A <code>null</code> stripChars will strip whitespace as defined by
     * {@link Character#isWhitespace(char)}.</p>
     *
     * <pre>
     * StringUtils.stripAll(null, *)                = null
     * StringUtils.stripAll([], *)                  = []
     * StringUtils.stripAll(["abc", "  abc"], null) = ["abc", "abc"]
     * StringUtils.stripAll(["  abc", null], null)  = ["abc", null]
     * StringUtils.stripAll(["  abc  ", null], null)  = ["  abc", null]
     * StringUtils.stripAll(["  abc  ", null], "yz")= ["  abc  ", null]
     * StringUtils.stripAll(["yabcz", null], "yz")  = ["abcz", null]
     * </pre>
     *
     * @param strs  the array to remove characters from, may be null
     * @param stripChars  the characters to remove, null treated as whitespace
     * @return the stripped Strings, <code>null</code> if null array input
     */
    public static String[] stripAllStart(String[] strs, String stripChars) {
        int strsLen;
        if (strs == null || (strsLen = strs.length) == 0) {
            return strs;
        }
        String[] newArr = new String[strsLen];
        for (int i = 0; i < strsLen; i++) {
            newArr[i] = stripStart(strs[i], stripChars);
        }
        return newArr;
    }

    /**
     * <p>Strips whitespace from the end of every String in an array.
     * Whitespace is defined by {@link Character#isWhitespace(char)}.</p>
     *
     * <p>A new array is returned each time, except for length zero.
     * A <code>null</code> array will return <code>null</code>.
     * An empty array will return itself.
     * A <code>null</code> array entry will be ignored.</p>
     *
     * <pre>
     * StringUtils.stripAll(null)             = null
     * StringUtils.stripAll([])               = []
     * StringUtils.stripAll(["abc", "  abc"]) = ["abc", "abc"]
     * StringUtils.stripAll(["abc  ", null])  = ["abc", null]
     * StringUtils.stripAll(["  abc  ", null])  = ["  abc", null]
     * </pre>
     *
     * @param strs  the array to remove whitespace from, may be null
     * @return the stripped Strings, <code>null</code> if null array input
     */
    public static String[] stripAllEnd(String[] strs) {
        return stripAllEnd(strs, null);
    }

    /**
     * <p>Strips any of a set of characters from the end of every
     * String in an array.</p>
     * Whitespace is defined by {@link Character#isWhitespace(char)}.</p>
     *
     * <p>A new array is returned each time, except for length zero.
     * A <code>null</code> array will return <code>null</code>.
     * An empty array will return itself.
     * A <code>null</code> array entry will be ignored.
     * A <code>null</code> stripChars will strip whitespace as defined by
     * {@link Character#isWhitespace(char)}.</p>
     *
     * <pre>
     * StringUtils.stripAll(null, *)                = null
     * StringUtils.stripAll([], *)                  = []
     * StringUtils.stripAll(["abc", "  abc"], null) = ["abc", "abc"]
     * StringUtils.stripAll(["abc  ", null], null)  = ["abc", null]
     * StringUtils.stripAll(["  abc  ", null], null)  = ["  abc", null]
     * StringUtils.stripAll(["  abc  ", null], "yz")= ["  abc  ", null]
     * StringUtils.stripAll(["yabcz", null], "yz")  = ["yabc", null]
     * </pre>
     *
     * @param strs  the array to remove characters from, may be null
     * @param stripChars  the characters to remove, null treated as whitespace
     * @return the stripped Strings, <code>null</code> if null array input
     */
    public static String[] stripAllEnd(String[] strs, String stripChars) {
        int strsLen;
        if (strs == null || (strsLen = strs.length) == 0) {
            return strs;
        }
        String[] newArr = new String[strsLen];
        for (int i = 0; i < strsLen; i++) {
            newArr[i] = stripEnd(strs[i], stripChars);
        }
        return newArr;
    }
    
    /**
     * <p>Splits the provided text into an array.
     *
     * <pre>
     * StringUtils.splitChars(null)         = null
     * StringUtils.splitChars("")           = []
     * StringUtils.splitChars("a d")        = ["a", " ", "d"]
     * </pre>
     *
     * @param str  the String to parse, may be null
     * @return an array of parsed Strings, <code>null</code> if null String input
     */
    public static String[] splitChars(String str) {
    	if (str == null) {
    		return null;
    	}
    	
    	int len = str.length();
    	String[] ss = new String[len];
    	for (int i = 0; i < len; i++) {
    		ss[i] = str.substring(i, i + 1);
    	}
    	
    	return ss;
    }
    
	/**
	 * <p>
	 * Check if a String starts with any char of the specified chars.
	 * </p>
	 * 
	 * <pre>
	 * StringUtils.startsWithChars(null, null)      = false
	 * StringUtils.startsWithChars(null, "abc")     = false
	 * StringUtils.startsWithChars("abcxyz", null)  = false
	 * StringUtils.startsWithChars("abcxyz", "")    = false
	 * StringUtils.startsWithChars("abcxyz", "abc") = true
	 * </pre>
	 * 
	 * @param string the String to check, may be null
	 * @param chars the chars to find, may be null or empty
	 * @return <code>true</code> if the String starts with any of the chars
	 */
	public static boolean startsWithChars(String string, String chars) {
		return startsWithChars(string, chars, false);
	}
    
	/**
	 * <p>
	 * Check if a String starts with any char of the specified chars.
	 * </p>
	 * 
	 * <pre>
	 * StringUtils.startsWithChars(null, null)      = false
	 * StringUtils.startsWithChars(null, "abc")     = false
	 * StringUtils.startsWithChars("abcxyz", null)  = false
	 * StringUtils.startsWithChars("abcxyz", "")    = false
	 * StringUtils.startsWithChars("abcxyz", "abc") = true
	 * </pre>
	 * 
	 * @param string the String to check, may be null
	 * @param chars the chars to find, may be null or empty
	 * @param ignoreCase inidicates whether the compare should ignore case (case
	 *            insensitive) or not.
	 * @return <code>true</code> if the String starts with any of the the
	 *         prefixes
	 */
	public static boolean startsWithChars(String string, String chars,
			boolean ignoreCase) {
		if (isEmpty(string) || isEmpty(chars)) {
			return false;
		}

		char s = string.charAt(0);
		if (ignoreCase) {
			s = Character.toLowerCase(s);
		}
		for (int i = 0; i < chars.length(); i++) {
			char ch = chars.charAt(i);
			if (ignoreCase) {
				ch = Character.toLowerCase(ch);
			}
			if (s == ch) {
				return true;
			}
		}
		return false;
	}

	/**
	 * <p>
	 * Check if a String starts with any char of the specified chars.
	 * </p>
	 * 
	 * <pre>
	 * StringUtils.startsWithChar(null, 'a')     = false
	 * StringUtils.startsWithChar("abcxyz", 'a') = true
	 * </pre>
	 * 
	 * @param string the String to check, may be null
	 * @param find the char to find, may be null
	 * @return <code>true</code> if the String starts with the char
	 */
	public static boolean startsWithChar(String string, char find) {
		return startsWithChar(string, find, false);
	}

	/**
	 * <p>
	 * Check if a String starts with any char of the specified chars.
	 * </p>
	 * 
	 * <pre>
	 * StringUtils.startsWithChar(null, 'a')     = false
	 * StringUtils.startsWithChar("abcxyz", 'a') = true
	 * </pre>
	 * 
	 * @param string the String to check, may be null
	 * @param find the char to find, may be null
	 * @param ignoreCase inidicates whether the compare should ignore case (case
	 *            insensitive) or not.
	 * @return <code>true</code> if the String starts with the char
	 */
	public static boolean startsWithChar(String string, char find, boolean ignoreCase) {
		if (isEmpty(string)) {
			return false;
		}

		char s = string.charAt(0);
		if (ignoreCase) {
			s = Character.toLowerCase(s);
		}
		if (ignoreCase) {
			find = Character.toLowerCase(find);
		}
		return s == find;
	}

	/**
	 * <p>
	 * Check if a String starts with any char of the specified chars.
	 * </p>
	 * 
	 * <pre>
	 * StringUtils.endsWithChars(null, null)      = false
	 * StringUtils.endsWithChars(null, "abc")     = false
	 * StringUtils.endsWithChars("abcxyz", null)  = false
	 * StringUtils.endsWithChars("abcxyz", "")    = false
	 * StringUtils.endsWithChars("abcxyz", "abc") = true
	 * </pre>
	 * 
	 * @param string the String to check, may be null
	 * @param chars the chars to find, may be null or empty
	 * @return <code>true</code> if the String ends with any of the chars
	 */
	public static boolean endsWithChars(String string, String chars) {
		return endsWithChars(string, chars, false);
	}
    
	/**
	 * <p>
	 * Check if a String starts with any char of the specified chars.
	 * </p>
	 * 
	 * <pre>
	 * StringUtils.endsWithChars(null, null)      = false
	 * StringUtils.endsWithChars(null, "abc")     = false
	 * StringUtils.endsWithChars("abcxyz", null)  = false
	 * StringUtils.endsWithChars("abcxyz", "")    = false
	 * StringUtils.endsWithChars("abcxyz", "abc") = true
	 * </pre>
	 * 
	 * @param string the String to check, may be null
	 * @param chars the chars to find, may be null or empty
	 * @param ignoreCase inidicates whether the compare should ignore case (case
	 *            insensitive) or not.
	 * @return <code>true</code> if the String ends with any of the the
	 *         prefixes
	 */
	public static boolean endsWithChars(String string, String chars,
			boolean ignoreCase) {
		if (isEmpty(string) || isEmpty(chars)) {
			return false;
		}

		char e = string.charAt(string.length() - 1);
		if (ignoreCase) {
			e = Character.toLowerCase(e);
		}
		for (int i = 0; i < chars.length(); i++) {
			char ch = chars.charAt(i);
			if (ignoreCase) {
				ch = Character.toLowerCase(ch);
			}
			if (e == ch) {
				return true;
			}
		}
		return false;
	}

	/**
	 * <p>
	 * Check if a String starts with any char of the specified chars.
	 * </p>
	 * 
	 * <pre>
	 * StringUtils.endsWithChar(null, 'a')     = false
	 * StringUtils.endsWithChar("abcxyz", 'a') = true
	 * </pre>
	 * 
	 * @param string the String to check, may be null
	 * @param find the char to find, may be null
	 * @return <code>true</code> if the String starts with the char
	 */
	public static boolean endsWithChar(String string, char find) {
		return startsWithChar(string, find, false);
	}

	/**
	 * <p>
	 * Check if a String ends with any char of the specified chars.
	 * </p>
	 * 
	 * <pre>
	 * StringUtils.endsWithChar(null, 'a')     = false
	 * StringUtils.endsWithChar("abcxyz", 'z') = true
	 * </pre>
	 * 
	 * @param string the String to check, may be null
	 * @param find the char to find, may be null
	 * @param ignoreCase inidicates whether the compare should ignore case (case
	 *            insensitive) or not.
	 * @return <code>true</code> if the String ends with the char
	 */
	public static boolean endsWithChar(String string, char find, boolean ignoreCase) {
		if (isEmpty(string)) {
			return false;
		}

		char ch = string.charAt(string.length() - 1);
		if (ignoreCase) {
			ch = Character.toLowerCase(ch);
		}
		if (ignoreCase) {
			find = Character.toLowerCase(find);
		}
		return ch == find;
	}

	/**
	 * Test whether the given string matches the given substring at the given index.
	 * 
	 * @param str the original string (or StringBuffer)
	 * @param index the index in the original string to start matching against
	 * @param substring the substring to match at the given index
	 * @return true if the given string matches the given substring at the given index
	 */
	public static boolean substringMatch(CharSequence str, int index, CharSequence substring) {
		for (int j = 0; j < substring.length(); j++) {
			int i = index + j;
			if (i >= str.length() || str.charAt(i) != substring.charAt(j)) {
				return false;
			}
		}
		return true;
	}

	/**
	 * Count the occurrences of the substring in string s.
	 * @param str string to search in. Return 0 if this is null.
	 * @param sub string to search for. Return 0 if this is null.
	 * @return count
	 */
	public static int countOccurrencesOf(String str, String sub) {
		if (str == null || sub == null || str.length() == 0 || sub.length() == 0) {
			return 0;
		}
		int count = 0, pos = 0, idx = 0;
		while ((idx = str.indexOf(sub, pos)) != -1) {
			++count;
			pos = idx + sub.length();
		}
		return count;
	}


	//---------------------------------------------------------------------
	// Convenience methods for working with formatted Strings
	//---------------------------------------------------------------------

	/**
	 * Quote the given String with single quotes.
	 * @param str the input String (e.g. "myString")
	 * @return the quoted String (e.g. "'myString'"),
	 * or <code>null<code> if the input was <code>null</code>
	 */
	public static String quote(String str) {
		return (str != null ? "'" + str + "'" : null);
	}

	/**
	 * Turn the given Object into a String with single quotes
	 * if it is a String; keeping the Object as-is else.
	 * @param obj the input Object (e.g. "myString")
	 * @return the quoted String (e.g. "'myString'"),
	 * or the input object as-is if not a String
	 */
	public static Object quoteIfString(Object obj) {
		return (obj instanceof String ? quote((String) obj) : obj);
	}

	/**
	 * Unqualify a string qualified by a '.' dot character. For example,
	 * "this.name.is.qualified", returns "qualified".
	 * @param qualifiedName the qualified name
	 * @return unqualified string
	 */
	public static String unqualify(String qualifiedName) {
		return unqualify(qualifiedName, '.');
	}

	/**
	 * Unqualify a string qualified by a separator character. For example,
	 * "this:name:is:qualified" returns "qualified" if using a ':' separator.
	 * @param qualifiedName the qualified name
	 * @param separator the separator
	 * @return unqualified string
	 */
	public static String unqualify(String qualifiedName, char separator) {
		return qualifiedName.substring(qualifiedName.lastIndexOf(separator) + 1);
	}

	//---------------------------------------------------------------------
	// Convenience methods for working with String arrays
	//---------------------------------------------------------------------
	/**
	 * Convenience method to return a Collection as a delimited (e.g. CSV)
	 * String. E.g. useful for <code>toString()</code> implementations.
	 * @param coll the Collection to display
	 * @return the delimited String
	 */
	public static String join(Collection<?> coll) {
		return join(coll, EMPTY);
	}

	/**
	 * Convenience method to return a Collection as a delimited (e.g. CSV)
	 * String. E.g. useful for <code>toString()</code> implementations.
	 * @param coll the Collection to display
	 * @param delimiter the delimiter to use (probably a ",")
	 * @return the delimited String
	 */
	public static String join(Collection<?> coll, String delimiter) {
		return join(coll, delimiter, EMPTY, EMPTY);
	}

	/**
	 * Convenience method to return a Collection as a delimited (e.g. CSV)
	 * String. E.g. useful for <code>toString()</code> implementations.
	 * @param coll the Collection to display
	 * @param delimiter the delimiter to use (probably a ",")
	 * @param prefix the String to start each element with
	 * @param suffix the String to end each element with
	 * @return the delimited String
	 */
	public static String join(Collection<?> coll, String delimiter, String prefix, String suffix) {
		if (coll == null || coll.isEmpty()) {
			return "";
		}
		return join(coll.iterator(), delimiter, prefix, suffix);
	}

	/**
	 * Convenience method to return a Iterator as a delimited (e.g. CSV)
	 * String. E.g. useful for <code>toString()</code> implementations.
	 * @param it the iterator to display
	 * @param delimiter the delimiter to use (probably a ",")
	 * @return the delimited String
	 */
	public static String join(Iterator<?> it, String delimiter) {
		return join(it, delimiter, EMPTY, EMPTY);
	}

	/**
	 * Convenience method to return a Iterator as a delimited (e.g. CSV)
	 * String. E.g. useful for <code>toString()</code> implementations.
	 * @param it the iterator to display
	 * @param delimiter the delimiter to use (probably a ",")
	 * @param prefix the String to start each element with
	 * @param suffix the String to end each element with
	 * @return the delimited String
	 */
	public static String join(Iterator<?> it, String delimiter, String prefix, String suffix) {
		if (it == null) {
			return EMPTY;
		}
		if (delimiter == null) {
			delimiter = EMPTY;
		}
		if (prefix == null) {
			prefix = EMPTY;
		}
		if (suffix == null) {
			suffix = EMPTY;
		}

        StringBuilder sb = new StringBuilder();
		while (it.hasNext()) {
			sb.append(prefix).append(it.next()).append(suffix);
			if (it.hasNext()) {
				sb.append(delimiter);
			}
		}
		return sb.toString();
	}

    /**
     * <p>Joins the elements of the provided array into a single String
     * containing the provided list of elements.</p>
     *
     * <p>No delimiter is added before or after the list.
     * A <code>null</code> separator is the same as an empty String ("").
     * Null objects or empty strings within the array are represented by
     * empty strings.</p>
     *
     * @param array  the array of values to join together, may be null
     * @return the joined String, <code>null</code> if null array input
     */
    public static String join(boolean[] array) {
        return join(array, EMPTY);
    }

    /**
     * <p>Joins the elements of the provided array into a single String
     * containing the provided list of elements.</p>
     *
     * <p>No delimiter is added before or after the list.
     * A <code>null</code> separator is the same as an empty String ("").
     * Null objects or empty strings within the array are represented by
     * empty strings.</p>
     *
     * @param array  the array of values to join together, may be null
     * @param separator  the separator character to use, null treated as ""
     * @return the joined String, <code>null</code> if null array input
     */
    public static String join(boolean[] array, String separator) {
        if (array == null) {
            return null;
        }
        return join(array, separator, 0, array.length);
    }

	/**
     * <p>Joins the elements of the provided array into a single String
     * containing the provided list of elements.</p>
     *
     * <p>No delimiter is added before or after the list.
     * A <code>null</code> separator is the same as an empty String ("").
     * Null objects or empty strings within the array are represented by
     * empty strings.</p>
     * 
     * @param array  the array of values to join together, may be null
     * @param separator  the separator character to use, null treated as ""
     * @param startIndex the first index to start joining from.  It is
     * an error to pass in an end index past the end of the array
     * @param endIndex the index to stop joining from (exclusive). It is
     * an error to pass in an end index past the end of the array
     * @return the joined String, <code>null</code> if null array input
	 */
	public static String join(boolean[] array, String separator, int startIndex, int endIndex) {
        if (array == null) {
            return null;
        }
        if (separator == null) {
            separator = EMPTY;
        }

        // endIndex - startIndex > 0:   Len = NofStrings *(len(firstString) + len(separator))
        //           (Assuming that all Strings are roughly equally long)
        int bufSize = (endIndex - startIndex);
        if (bufSize <= 0) {
            return EMPTY;
        }

        StringBuilder sb = new StringBuilder();

        for (int i = startIndex; i < endIndex; i++) {
            if (i > startIndex) {
            	sb.append(separator);
            }
           	sb.append(array[i]);
        }
        return sb.toString();
	}

    /**
     * <p>Joins the elements of the provided array into a single String
     * containing the provided list of elements.</p>
     *
     * <p>No delimiter is added before or after the list.
     * A <code>null</code> separator is the same as an empty String ("").
     * Null objects or empty strings within the array are represented by
     * empty strings.</p>
     *
     * @param array  the array of values to join together, may be null
     * @param separator  the separator character to use, null treated as ""
     * @return the joined String, <code>null</code> if null array input
     */
    public static String join(byte[] array, String separator) {
        if (array == null) {
            return null;
        }
        return join(array, separator, 0, array.length);
    }

    /**
     * <p>Joins the elements of the provided array into a single String
     * containing the provided list of elements.</p>
     *
     * <p>No delimiter is added before or after the list.
     * A <code>null</code> separator is the same as an empty String ("").
     * Null objects or empty strings within the array are represented by
     * empty strings.</p>
     *
     * @param array  the array of values to join together, may be null
     * @return the joined String, <code>null</code> if null array input
     */
    public static String join(byte[] array) {
        return join(array, EMPTY);
    }

	/**
     * <p>Joins the elements of the provided array into a single String
     * containing the provided list of elements.</p>
     *
     * <p>No delimiter is added before or after the list.
     * A <code>null</code> separator is the same as an empty String ("").
     * Null objects or empty strings within the array are represented by
     * empty strings.</p>
     * 
     * @param array  the array of values to join together, may be null
     * @param separator  the separator character to use, null treated as ""
     * @param startIndex the first index to start joining from.  It is
     * an error to pass in an end index past the end of the array
     * @param endIndex the index to stop joining from (exclusive). It is
     * an error to pass in an end index past the end of the array
     * @return the joined String, <code>null</code> if null array input
	 */
	public static String join(byte[] array, String separator, int startIndex, int endIndex) {
        if (array == null) {
            return null;
        }
        if (separator == null) {
            separator = EMPTY;
        }

        // endIndex - startIndex > 0:   Len = NofStrings *(len(firstString) + len(separator))
        //           (Assuming that all Strings are roughly equally long)
        int bufSize = (endIndex - startIndex);
        if (bufSize <= 0) {
            return EMPTY;
        }

        StringBuilder sb = new StringBuilder();

        for (int i = startIndex; i < endIndex; i++) {
            if (i > startIndex) {
            	sb.append(separator);
            }
           	sb.append(array[i]);
        }
        return sb.toString();
	}

    /**
     * <p>Joins the elements of the provided array into a single String
     * containing the provided list of elements.</p>
     *
     * <p>No delimiter is added before or after the list.
     * A <code>null</code> separator is the same as an empty String ("").
     * Null objects or empty strings within the array are represented by
     * empty strings.</p>
     *
     * @param array  the array of values to join together, may be null
     * @return the joined String, <code>null</code> if null array input
     */
    public static String join(char[] array) {
        return join(array);
    }

    /**
     * <p>Joins the elements of the provided array into a single String
     * containing the provided list of elements.</p>
     *
     * <p>No delimiter is added before or after the list.
     * A <code>null</code> separator is the same as an empty String ("").
     * Null objects or empty strings within the array are represented by
     * empty strings.</p>
     *
     * @param array  the array of values to join together, may be null
     * @param separator  the separator character to use, null treated as ""
     * @return the joined String, <code>null</code> if null array input
     */
    public static String join(char[] array, String separator) {
        if (array == null) {
            return null;
        }
        return join(array, separator, 0, array.length);
    }

	/**
     * <p>Joins the elements of the provided array into a single String
     * containing the provided list of elements.</p>
     *
     * <p>No delimiter is added before or after the list.
     * A <code>null</code> separator is the same as an empty String ("").
     * Null objects or empty strings within the array are represented by
     * empty strings.</p>
     * 
     * @param array  the array of values to join together, may be null
     * @param separator  the separator character to use, null treated as ""
     * @param startIndex the first index to start joining from.  It is
     * an error to pass in an end index past the end of the array
     * @param endIndex the index to stop joining from (exclusive). It is
     * an error to pass in an end index past the end of the array
     * @return the joined String, <code>null</code> if null array input
	 */
	public static String join(char[] array, String separator, int startIndex, int endIndex) {
        if (array == null) {
            return null;
        }
        if (separator == null) {
            separator = EMPTY;
        }

        // endIndex - startIndex > 0:   Len = NofStrings *(len(firstString) + len(separator))
        //           (Assuming that all Strings are roughly equally long)
        int bufSize = (endIndex - startIndex);
        if (bufSize <= 0) {
            return EMPTY;
        }

        StringBuilder sb = new StringBuilder();

        for (int i = startIndex; i < endIndex; i++) {
            if (i > startIndex) {
            	sb.append(separator);
            }
           	sb.append(array[i]);
        }
        return sb.toString();
	}

    /**
     * <p>Joins the elements of the provided array into a single String
     * containing the provided list of elements.</p>
     *
     * <p>No delimiter is added before or after the list.
     * A <code>null</code> separator is the same as an empty String ("").
     * Null objects or empty strings within the array are represented by
     * empty strings.</p>
     *
     * @param array  the array of values to join together, may be null
     * @return the joined String, <code>null</code> if null array input
     */
    public static String join(double[] array) {
        return join(array, EMPTY);
    }

    /**
     * <p>Joins the elements of the provided array into a single String
     * containing the provided list of elements.</p>
     *
     * <p>No delimiter is added before or after the list.
     * A <code>null</code> separator is the same as an empty String ("").
     * Null objects or empty strings within the array are represented by
     * empty strings.</p>
     *
     * @param array  the array of values to join together, may be null
     * @param separator  the separator character to use, null treated as ""
     * @return the joined String, <code>null</code> if null array input
     */
    public static String join(double[] array, String separator) {
        if (array == null) {
            return null;
        }
        return join(array, separator, 0, array.length);
    }

	/**
     * <p>Joins the elements of the provided array into a single String
     * containing the provided list of elements.</p>
     *
     * <p>No delimiter is added before or after the list.
     * A <code>null</code> separator is the same as an empty String ("").
     * Null objects or empty strings within the array are represented by
     * empty strings.</p>
     * 
     * @param array  the array of values to join together, may be null
     * @param separator  the separator character to use, null treated as ""
     * @param startIndex the first index to start joining from.  It is
     * an error to pass in an end index past the end of the array
     * @param endIndex the index to stop joining from (exclusive). It is
     * an error to pass in an end index past the end of the array
     * @return the joined String, <code>null</code> if null array input
	 */
	public static String join(double[] array, String separator, int startIndex, int endIndex) {
        if (array == null) {
            return null;
        }
        if (separator == null) {
            separator = EMPTY;
        }

        // endIndex - startIndex > 0:   Len = NofStrings *(len(firstString) + len(separator))
        //           (Assuming that all Strings are roughly equally long)
        int bufSize = (endIndex - startIndex);
        if (bufSize <= 0) {
            return EMPTY;
        }

        StringBuilder sb = new StringBuilder();

        for (int i = startIndex; i < endIndex; i++) {
            if (i > startIndex) {
            	sb.append(separator);
            }
           	sb.append(array[i]);
        }
        return sb.toString();
	}

    /**
     * <p>Joins the elements of the provided array into a single String
     * containing the provided list of elements.</p>
     *
     * <p>No delimiter is added before or after the list.
     * A <code>null</code> separator is the same as an empty String ("").
     * Null objects or empty strings within the array are represented by
     * empty strings.</p>
     *
     * @param array  the array of values to join together, may be null
     * @return the joined String, <code>null</code> if null array input
     */
    public static String join(float[] array) {
        return join(array, EMPTY);
    }

    /**
     * <p>Joins the elements of the provided array into a single String
     * containing the provided list of elements.</p>
     *
     * <p>No delimiter is added before or after the list.
     * A <code>null</code> separator is the same as an empty String ("").
     * Null objects or empty strings within the array are represented by
     * empty strings.</p>
     *
     * @param array  the array of values to join together, may be null
     * @param separator  the separator character to use, null treated as ""
     * @return the joined String, <code>null</code> if null array input
     */
    public static String join(float[] array, String separator) {
        if (array == null) {
            return null;
        }
        return join(array, separator, 0, array.length);
    }

	/**
     * <p>Joins the elements of the provided array into a single String
     * containing the provided list of elements.</p>
     *
     * <p>No delimiter is added before or after the list.
     * A <code>null</code> separator is the same as an empty String ("").
     * Null objects or empty strings within the array are represented by
     * empty strings.</p>
     * 
     * @param array  the array of values to join together, may be null
     * @param separator  the separator character to use, null treated as ""
     * @param startIndex the first index to start joining from.  It is
     * an error to pass in an end index past the end of the array
     * @param endIndex the index to stop joining from (exclusive). It is
     * an error to pass in an end index past the end of the array
     * @return the joined String, <code>null</code> if null array input
	 */
	public static String join(float[] array, String separator, int startIndex, int endIndex) {
        if (array == null) {
            return null;
        }
        if (separator == null) {
            separator = EMPTY;
        }

        // endIndex - startIndex > 0:   Len = NofStrings *(len(firstString) + len(separator))
        //           (Assuming that all Strings are roughly equally long)
        int bufSize = (endIndex - startIndex);
        if (bufSize <= 0) {
            return EMPTY;
        }

        StringBuilder sb = new StringBuilder();

        for (int i = startIndex; i < endIndex; i++) {
            if (i > startIndex) {
            	sb.append(separator);
            }
           	sb.append(array[i]);
        }
        return sb.toString();
	}

    /**
     * <p>Joins the elements of the provided array into a single String
     * containing the provided list of elements.</p>
     *
     * <p>No delimiter is added before or after the list.
     * A <code>null</code> separator is the same as an empty String ("").
     * Null objects or empty strings within the array are represented by
     * empty strings.</p>
     *
     * @param array  the array of values to join together, may be null
     * @return the joined String, <code>null</code> if null array input
     */
    public static String join(short[] array) {
        return join(array, EMPTY);
    }

    /**
     * <p>Joins the elements of the provided array into a single String
     * containing the provided list of elements.</p>
     *
     * <p>No delimiter is added before or after the list.
     * A <code>null</code> separator is the same as an empty String ("").
     * Null objects or empty strings within the array are represented by
     * empty strings.</p>
     *
     * @param array  the array of values to join together, may be null
     * @param separator  the separator character to use, null treated as ""
     * @return the joined String, <code>null</code> if null array input
     */
    public static String join(short[] array, String separator) {
        if (array == null) {
            return null;
        }
        return join(array, separator, 0, array.length);
    }

	/**
     * <p>Joins the elements of the provided array into a single String
     * containing the provided list of elements.</p>
     *
     * <p>No delimiter is added before or after the list.
     * A <code>null</code> separator is the same as an empty String ("").
     * Null objects or empty strings within the array are represented by
     * empty strings.</p>
     * 
     * @param array  the array of values to join together, may be null
     * @param separator  the separator character to use, null treated as ""
     * @param startIndex the first index to start joining from.  It is
     * an error to pass in an end index past the end of the array
     * @param endIndex the index to stop joining from (exclusive). It is
     * an error to pass in an end index past the end of the array
     * @return the joined String, <code>null</code> if null array input
	 */
	public static String join(short[] array, String separator, int startIndex, int endIndex) {
        if (array == null) {
            return null;
        }
        if (separator == null) {
            separator = EMPTY;
        }

        // endIndex - startIndex > 0:   Len = NofStrings *(len(firstString) + len(separator))
        //           (Assuming that all Strings are roughly equally long)
        int bufSize = (endIndex - startIndex);
        if (bufSize <= 0) {
            return EMPTY;
        }

        StringBuilder sb = new StringBuilder();

        for (int i = startIndex; i < endIndex; i++) {
            if (i > startIndex) {
            	sb.append(separator);
            }
           	sb.append(array[i]);
        }
        return sb.toString();
	}

    /**
     * <p>Joins the elements of the provided array into a single String
     * containing the provided list of elements.</p>
     *
     * <p>No delimiter is added before or after the list.
     * A <code>null</code> separator is the same as an empty String ("").
     * Null objects or empty strings within the array are represented by
     * empty strings.</p>
     *
     * @param array  the array of values to join together, may be null
     * @return the joined String, <code>null</code> if null array input
     */
    public static String join(int[] array) {
        return join(array, EMPTY);
    }

    /**
     * <p>Joins the elements of the provided array into a single String
     * containing the provided list of elements.</p>
     *
     * <p>No delimiter is added before or after the list.
     * A <code>null</code> separator is the same as an empty String ("").
     * Null objects or empty strings within the array are represented by
     * empty strings.</p>
     *
     * @param array  the array of values to join together, may be null
     * @param separator  the separator character to use, null treated as ""
     * @return the joined String, <code>null</code> if null array input
     */
    public static String join(int[] array, String separator) {
        if (array == null) {
            return null;
        }
        return join(array, separator, 0, array.length);
    }

	/**
     * <p>Joins the elements of the provided array into a single String
     * containing the provided list of elements.</p>
     *
     * <p>No delimiter is added before or after the list.
     * A <code>null</code> separator is the same as an empty String ("").
     * Null objects or empty strings within the array are represented by
     * empty strings.</p>
     * 
     * @param array  the array of values to join together, may be null
     * @param separator  the separator character to use, null treated as ""
     * @param startIndex the first index to start joining from.  It is
     * an error to pass in an end index past the end of the array
     * @param endIndex the index to stop joining from (exclusive). It is
     * an error to pass in an end index past the end of the array
     * @return the joined String, <code>null</code> if null array input
	 */
	public static String join(int[] array, String separator, int startIndex, int endIndex) {
        if (array == null) {
            return null;
        }
        if (separator == null) {
            separator = EMPTY;
        }

        // endIndex - startIndex > 0:   Len = NofStrings *(len(firstString) + len(separator))
        //           (Assuming that all Strings are roughly equally long)
        int bufSize = (endIndex - startIndex);
        if (bufSize <= 0) {
            return EMPTY;
        }

        StringBuilder sb = new StringBuilder();

        for (int i = startIndex; i < endIndex; i++) {
            if (i > startIndex) {
            	sb.append(separator);
            }
           	sb.append(array[i]);
        }
        return sb.toString();
	}

    /**
     * <p>Joins the elements of the provided array into a single String
     * containing the provided list of elements.</p>
     *
     * <p>No delimiter is added before or after the list.
     * A <code>null</code> separator is the same as an empty String ("").
     * Null objects or empty strings within the array are represented by
     * empty strings.</p>
     *
     * @param array  the array of values to join together, may be null
     * @return the joined String, <code>null</code> if null array input
     */
    public static String join(long[] array) {
        return join(array, EMPTY);
    }

    /**
     * <p>Joins the elements of the provided array into a single String
     * containing the provided list of elements.</p>
     *
     * <p>No delimiter is added before or after the list.
     * A <code>null</code> separator is the same as an empty String ("").
     * Null objects or empty strings within the array are represented by
     * empty strings.</p>
     *
     * @param array  the array of values to join together, may be null
     * @param separator  the separator character to use, null treated as ""
     * @return the joined String, <code>null</code> if null array input
     */
    public static String join(long[] array, String separator) {
        if (array == null) {
            return null;
        }
        return join(array, separator, 0, array.length);
    }

	/**
     * <p>Joins the elements of the provided array into a single String
     * containing the provided list of elements.</p>
     *
     * <p>No delimiter is added before or after the list.
     * A <code>null</code> separator is the same as an empty String ("").
     * Null objects or empty strings within the array are represented by
     * empty strings.</p>
     * 
     * @param array  the array of values to join together, may be null
     * @param separator  the separator character to use, null treated as ""
     * @param startIndex the first index to start joining from.  It is
     * an error to pass in an end index past the end of the array
     * @param endIndex the index to stop joining from (exclusive). It is
     * an error to pass in an end index past the end of the array
     * @return the joined String, <code>null</code> if null array input
	 */
	public static String join(long[] array, String separator, int startIndex, int endIndex) {
        if (array == null) {
            return null;
        }
        if (separator == null) {
            separator = EMPTY;
        }

        // endIndex - startIndex > 0:   Len = NofStrings *(len(firstString) + len(separator))
        //           (Assuming that all Strings are roughly equally long)
        int bufSize = (endIndex - startIndex);
        if (bufSize <= 0) {
            return EMPTY;
        }

        StringBuilder sb = new StringBuilder();

        for (int i = startIndex; i < endIndex; i++) {
            if (i > startIndex) {
            	sb.append(separator);
            }
           	sb.append(array[i]);
        }
        return sb.toString();
	}

//    /**
//     * <p>Joins the elements of the provided array into a single String
//     * containing the provided list of elements.</p>
//     *
//     * <p>No delimiter is added before or after the list.
//     * A <code>null</code> separator is the same as an empty String ("").
//     * Null objects or empty strings within the array are represented by
//     * empty strings.</p>
//     *
//     * @param array  the array of values to join together, may be null
//     * @return the joined String, <code>null</code> if null array input
//     */
//    public static String join(Object[] array) {
//        return join(array, EMPTY);
//    }

    /**
     * <p>Joins the elements of the provided array into a single String
     * containing the provided list of elements.</p>
     *
     * <p>No delimiter is added before or after the list.
     * A <code>null</code> separator is the same as an empty String ("").
     * Null objects or empty strings within the array are represented by
     * empty strings.</p>
     *
     * @param array  the array of values to join together, may be null
     * @param separator  the separator character to use, null treated as ""
     * @return the joined String, <code>null</code> if null array input
     */
    public static String join(Object[] array, String separator) {
        if (array == null) {
            return null;
        }
        return join(array, separator, 0, array.length);
    }

    /**
     * <p>Joins the elements of the provided array into a single String
     * containing the provided list of elements.</p>
     *
     * <p>No delimiter is added before or after the list.
     * A <code>null</code> separator is the same as an empty String ("").
     * Null objects or empty strings within the array are represented by
     * empty strings.</p>
     *
     * <pre>
     * StringUtils.join(null, *)                = null
     * StringUtils.join([], *)                  = ""
     * StringUtils.join([null], *)              = ""
     * StringUtils.join(["a", "b", "c"], "--")  = "a--b--c"
     * StringUtils.join(["a", "b", "c"], null)  = "abc"
     * StringUtils.join(["a", "b", "c"], "")    = "abc"
     * StringUtils.join([null, "", "a"], ',')   = ",,a"
     * </pre>
     *
     * @param array  the array of values to join together, may be null
     * @param separator  the separator character to use, null treated as ""
     * @param startIndex the first index to start joining from.  It is
     * an error to pass in an end index past the end of the array
     * @param endIndex the index to stop joining from (exclusive). It is
     * an error to pass in an end index past the end of the array
     * @return the joined String, <code>null</code> if null array input
     */
    public static String join(Object[] array, String separator, int startIndex, int endIndex) {
        if (array == null) {
            return null;
        }
        if (separator == null) {
            separator = EMPTY;
        }

        // endIndex - startIndex > 0:   Len = NofStrings *(len(firstString) + len(separator))
        //           (Assuming that all Strings are roughly equally long)
        int bufSize = (endIndex - startIndex);
        if (bufSize <= 0) {
            return EMPTY;
        }

        bufSize *= ((array[startIndex] == null ? 16 : array[startIndex].toString().length())
                        + separator.length());

        StringBuilder sb = new StringBuilder(bufSize);

        for (int i = startIndex; i < endIndex; i++) {
            if (i > startIndex) {
                sb.append(separator);
            }
            if (array[i] != null) {
                sb.append(array[i]);
            }
        }
        return sb.toString();
    }

	/**
	 * Copy the given Collection into a String array.
	 * The Collection must contain String elements only.
	 * @param collection the Collection to copy
	 * @return the String array (<code>null</code> if the passed-in
	 * Collection was <code>null</code>)
	 */
	public static String[] toStringArray(Collection<String> collection) {
		if (collection == null) {
			return null;
		}
		return collection.toArray(new String[collection.size()]);
	}

	/**
	 * Copy the given Enumeration into a String array.
	 * The Enumeration must contain String elements only.
	 * @param enumeration the Enumeration to copy
	 * @return the String array (<code>null</code> if the passed-in
	 * Enumeration was <code>null</code>)
	 */
	public static String[] toStringArray(Enumeration<String> enumeration) {
		if (enumeration == null) {
			return null;
		}
		List<String> list = Collections.list(enumeration);
		return list.toArray(new String[list.size()]);
	}

	/**
	 * Take an array Strings and split each element based on the given delimiter.
	 * A <code>Properties</code> instance is then generated, with the left of the
	 * delimiter providing the key, and the right of the delimiter providing the value.
	 * <p>Will trim both the key and value before adding them to the
	 * <code>Properties</code> instance.
	 * @param array the array to process
	 * @param delimiter to split each element using (typically the equals symbol)
	 * @return a <code>Properties</code> instance representing the array contents,
	 * or <code>null</code> if the array to process was null or empty
	 */
	public static Properties splitArrayElementsIntoProperties(String[] array, String delimiter) {
		return splitArrayElementsIntoProperties(array, delimiter, null);
	}

	/**
	 * Take an array Strings and split each element based on the given delimiter.
	 * A <code>Properties</code> instance is then generated, with the left of the
	 * delimiter providing the key, and the right of the delimiter providing the value.
	 * <p>Will trim both the key and value before adding them to the
	 * <code>Properties</code> instance.
	 * @param array the array to process
	 * @param delimiter to split each element using (typically the equals symbol)
	 * @param charsToDelete one or more characters to remove from each element
	 * prior to attempting the split operation (typically the quotation mark
	 * symbol), or <code>null</code> if no removal should occur
	 * @return a <code>Properties</code> instance representing the array contents,
	 * or <code>null</code> if the array to process was <code>null</code> or empty
	 */
	public static Properties splitArrayElementsIntoProperties(
			String[] array, String delimiter, String charsToDelete) {

		if (ArrayUtils.isEmpty(array)) {
			return null;
		}
		Properties result = new Properties();
		for (int i = 0; i < array.length; i++) {
			String element = array[i];
			if (charsToDelete != null) {
				element = remove(array[i], charsToDelete);
			}
			String[] splittedElement = split(element, delimiter);
			if (splittedElement == null) {
				continue;
			}
			result.setProperty(splittedElement[0].trim(), splittedElement[1].trim());
		}
		return result;
	}

	/**
	 * Tokenize the given String into a String array via a StringTokenizer.
	 * Trims tokens and omits empty tokens.
	 * <p>The given delimiters string is supposed to consist of any number of
	 * delimiter characters. Each of those characters can be used to separate
	 * tokens. A delimiter is always a single character; for multi-character
	 * delimiters, consider using <code>delimitedListToStringArray</code>
	 * @param str the String to tokenize
	 * @param delimiters the delimiter characters, assembled as String
	 * (each of those characters is individually considered as delimiter).
	 * @return an array of the tokens
	 * @see java.util.StringTokenizer
	 * @see java.lang.String#trim()
	 * @see #toStringArray
	 */
	public static String[] tokenizeToStringArray(String str, String delimiters) {
		return tokenizeToStringArray(str, delimiters, true, true);
	}

	/**
	 * Tokenize the given String into a String array via a StringTokenizer.
	 * <p>The given delimiters string is supposed to consist of any number of
	 * delimiter characters. Each of those characters can be used to separate
	 * tokens. A delimiter is always a single character; for multi-character
	 * delimiters, consider using <code>delimitedListToStringArray</code>
	 * @param str the String to tokenize
	 * @param delimiters the delimiter characters, assembled as String
	 * (each of those characters is individually considered as delimiter)
	 * @param trimTokens trim the tokens via String's <code>trim</code>
	 * @param ignoreEmptyTokens omit empty tokens from the result array
	 * (only applies to tokens that are empty after trimming; StringTokenizer
	 * will not consider subsequent delimiters as token in the first place).
	 * @return an array of the tokens (<code>null</code> if the input String
	 * was <code>null</code>)
	 * @see java.util.StringTokenizer
	 * @see java.lang.String#trim()
	 * @see #toStringArray
	 */
	public static String[] tokenizeToStringArray(
			String str, String delimiters, boolean trimTokens, boolean ignoreEmptyTokens) {

		if (str == null) {
			return null;
		}
		StringTokenizer st = new StringTokenizer(str, delimiters);
		List<String> tokens = new ArrayList<String>();
		while (st.hasMoreTokens()) {
			String token = st.nextToken();
			if (trimTokens) {
				token = token.trim();
			}
			if (!ignoreEmptyTokens || token.length() > 0) {
				tokens.add(token);
			}
		}
		return toStringArray(tokens);
	}

	/**
	 * getByteLength
	 * @param str string
	 * @return byte length
	 */
	public static int getByteLength(String str) {
		if (isEmpty(str)) {
			return 0;
		}

		byte bytes[] = str.getBytes();

		return bytes != null ? bytes.length : 0;
	}

	/**
	 * getByteLength
	 * @param str string
	 * @param encode encode
	 * @return string byte array length
	 * @throws UnsupportedEncodingException if the encode is not supported
	 */
	public static int getByteLength(String str, String encode) throws UnsupportedEncodingException {
		if (isEmpty(str)) {
			return 0;
		}

		byte bytes[] = null;
		if (isEmpty(encode)) {
			bytes = str.getBytes();
		}
		else {
			bytes = str.getBytes(encode);
		}

		return bytes != null ? bytes.length : 0;
	}

	/**
	 * toCsv
	 * @param lines lines
	 * @return csv string
	 */
	public static String toCsv(List<String[]> lines) {
		if (lines == null) {
			return null;
		}
		
		StringWriter sw = new StringWriter();
		CsvWriter cw = new CsvWriter(sw);
		cw.writeAll(lines);
		return sw.toString();
	}
	
	/**
	 * parse csv string
	 * @param str string
	 * @return string list
	 */
	public static List<String> parseCsv(String str) {
		if (str == null) {
			return null;
		}
		try {
			CsvReader cr = new CsvReader(new StringReader(str));
			return cr.readNext();
		} 
		catch (IOException e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * parse csv string
	 * @param str string
	 * @param separator the delimiter to use for separating entries.
	 * @return string list
	 */
	public static List<String> parseCsv(String str, char separator) {
		if (str == null) {
			return null;
		}
		try {
			CsvReader cr = new CsvReader(new StringReader(str), separator);
			return cr.readNext();
		} 
		catch (IOException e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * parse csv string
	 * @param str string
	 * @param separator the delimiter to use for separating entries.
	 * @param quotechar the character to use for quoted elements
	 * @return string list
	 */
	public static List<String> parseCsv(String str, char separator, char quotechar) {
		if (str == null) {
			return null;
		}
		try {
			CsvReader cr = new CsvReader(new StringReader(str), separator, quotechar);
			return cr.readNext();
		} 
		catch (IOException e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * match a String with the wild card pattern (case sensitive).
	 *
	 * @param str string
	 * @param pattern wild card pattern
	 * @return true if matches
	 */
	public static boolean wildcardMatch(String str, String pattern) {
		return wildcardMatch(str, pattern, true);
	}
	
	/**
	 * match a String with the wild card pattern (ignore case sensitive).
	 *
	 * @param str string
	 * @param pattern wild card pattern
	 * @return true if matches
	 */
	public static boolean wildcardMatchIgnoreCase(String str, String pattern) {
		return wildcardMatch(str, pattern, false);
	}
	
	/**
	 * match a String with the wild card pattern.
	 *
	 * @param str string
	 * @param pattern wild card pattern
	 * @param isCaseSensitive case sensitive
	 * @return true if matches
	 */
	public static boolean wildcardMatch(String str, String pattern, boolean isCaseSensitive) {
		if (str == null || pattern == null) {
			return false;
		}
		
		char[] patArr = pattern.toCharArray();
		char[] strArr = str.toCharArray();
		int patIdxStart = 0;
		int patIdxEnd = patArr.length - 1;
		int strIdxStart = 0;
		int strIdxEnd = strArr.length - 1;
		char ch;

		boolean containsStar = false;
		for (int i = 0; i < patArr.length; i++) {
			if (patArr[i] == '*') {
				containsStar = true;
				break;
			}
		}

		if (!containsStar) {
			// No '*'s, so we make a shortcut
			if (patIdxEnd != strIdxEnd) {
				return false; // Pattern and string do not have the same size
			}
			for (int i = 0; i <= patIdxEnd; i++) {
				ch = patArr[i];
				if (ch != '?') {
					if (isCaseSensitive && ch != strArr[i]) {
						return false; // Character mismatch
					}
					if (!isCaseSensitive
							&& Character.toUpperCase(ch) != Character.toUpperCase(strArr[i])) {
						return false; // Character mismatch
					}
				}
			}
			return true; // String matches against pattern
		}

		if (patIdxEnd == 0) {
			return true; // Pattern contains only '*', which matches anything
		}

		// Process characters before first star
		while ((ch = patArr[patIdxStart]) != '*' && strIdxStart <= strIdxEnd) {
			if (ch != '?') {
				if (isCaseSensitive && ch != strArr[strIdxStart]) {
					return false; // Character mismatch
				}
				if (!isCaseSensitive
						&& Character.toUpperCase(ch) != Character.toUpperCase(strArr[strIdxStart])) {
					return false; // Character mismatch
				}
			}
			patIdxStart++;
			strIdxStart++;
		}
		if (strIdxStart > strIdxEnd) {
			// All characters in the string are used. Check if only '*'s are
			// left in the pattern. If so, we succeeded. Otherwise failure.
			for (int i = patIdxStart; i <= patIdxEnd; i++) {
				if (patArr[i] != '*') {
					return false;
				}
			}
			return true;
		}

		// Process characters after last star
		while ((ch = patArr[patIdxEnd]) != '*' && strIdxStart <= strIdxEnd) {
			if (ch != '?') {
				if (isCaseSensitive && ch != strArr[strIdxEnd]) {
					return false; // Character mismatch
				}
				if (!isCaseSensitive
						&& Character.toUpperCase(ch) != Character.toUpperCase(strArr[strIdxEnd])) {
					return false; // Character mismatch
				}
			}
			patIdxEnd--;
			strIdxEnd--;
		}
		if (strIdxStart > strIdxEnd) {
			// All characters in the string are used. Check if only '*'s are
			// left in the pattern. If so, we succeeded. Otherwise failure.
			for (int i = patIdxStart; i <= patIdxEnd; i++) {
				if (patArr[i] != '*') {
					return false;
				}
			}
			return true;
		}

		// process pattern between stars. padIdxStart and patIdxEnd point
		// always to a '*'.
		while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd) {
			int patIdxTmp = -1;
			for (int i = patIdxStart + 1; i <= patIdxEnd; i++) {
				if (patArr[i] == '*') {
					patIdxTmp = i;
					break;
				}
			}
			if (patIdxTmp == patIdxStart + 1) {
				// Two stars next to each other, skip the first one.
				patIdxStart++;
				continue;
			}
			// Find the pattern between padIdxStart & padIdxTmp in str between
			// strIdxStart & strIdxEnd
			int patLength = (patIdxTmp - patIdxStart - 1);
			int strLength = (strIdxEnd - strIdxStart + 1);
			int foundIdx = -1;
			strLoop: for (int i = 0; i <= strLength - patLength; i++) {
				for (int j = 0; j < patLength; j++) {
					ch = patArr[patIdxStart + j + 1];
					if (ch != '?') {
						if (isCaseSensitive && ch != strArr[strIdxStart + i + j]) {
							continue strLoop;
						}
						if (!isCaseSensitive
								&& Character.toUpperCase(ch) != Character
										.toUpperCase(strArr[strIdxStart + i + j])) {
							continue strLoop;
						}
					}
				}

				foundIdx = strIdxStart + i;
				break;
			}

			if (foundIdx == -1) {
				return false;
			}

			patIdxStart = patIdxTmp;
			strIdxStart = foundIdx + patLength;
		}

		// All characters in the string are used. Check if only '*'s are left
		// in the pattern. If so, we succeeded. Otherwise failure.
		for (int i = patIdxStart; i <= patIdxEnd; i++) {
			if (patArr[i] != '*') {
				return false;
			}
		}
		return true;
	}

	/**
	 * prettify xml 
	 * @param xml xml string
	 * @return prettified xml
	 */
	public static String prettifyXml(String xml) {
		StringBuilder fmt = new StringBuilder();

        Pattern p0 = Pattern.compile(".+</\\w[^>]*>$");
        Pattern p1 = Pattern.compile("^</\\w*>$");
        Pattern p2 = Pattern.compile("^<\\w[^>]*[^/]>.*$");

        xml = xml.replaceAll("(>)(<)(/*)", "$1\n$2$3");
		
		String[] xs = split(xml, "\r\n");
		
		int pad = 0;
		for (int i = 0; i < xs.length; i++) {
			String node = xs[i];
			int indent = 0;
			if (p0.matcher(node).matches()) {
				indent = 0;
			}
			else if (p1.matcher(node).matches()) {
				if (pad != 0) {
					pad -= 1;
				}
			}
			else if (p2.matcher(node).matches()) {
				indent = 1;
			}
			else {
				indent = 0;
			}
	
			fmt.append(leftPad(EMPTY, pad * 2, ' ')).append(node).append("\r\n");
			pad += indent;
		};
	
		return fmt.toString();
	}
	
	/**
	 * format file size
	 * @param fs size
	 * @return (xxGB, xxMB, xxKB, xxB)
	 */
	public static String formatFileSize(Integer fs) {
		if (fs == null) {
			return "";
		}
		
		return formatFileSize(fs.longValue());
	}
	
	/**
	 * format file size
	 * @param fs size
	 * @return (xxGB, xxMB, xxKB, xxB)
	 */
	public static String formatFileSize(Long fs) {
		String sz = "";

		if (fs != null) {
			if (fs > 1024 * 1024 * 1024) {
				sz = Math.round(fs / 1024 / 1024 / 1024) + "GB";
			}
			if (fs > 1024 * 1024) {
				sz = Math.round(fs / 1024 / 1024) + "MB";
			}
			else if (fs > 1024) {
				sz = Math.round(fs / 1024) + "KB";
			}
			else {
				sz = fs + "B";
			}
		}
		return sz;
	}

	/**
	 * Truncate a string and add an ellipsiz ('...') to the end if it exceeds the specified length
	 * @param str value The string to truncate
	 * @param len length The maximum length to allow before truncating
	 * @return The converted text
	 */
	public static String ellipsis(String str, int len) {
		if (str == null) {
			return null;
		}
		
		if (str.length() > len) {
			return str.substring(0, len - 3) + "...";
		}
		return str;
	}

	/**
	 * Truncate a string and add an ellipsiz ('...') to the end if it exceeds the specified length
	 * the length of charCodeAt(i) > 0xFF will be treated as 2. 
	 * @param str value The string to truncate
	 * @param len length The maximum length to allow before truncating
	 * @return The converted text
	 */
	public static String ellipsiz(String str, int len) {
		if (str == null) {
			return null;
		}
		
		int sz = 0;
		for (int i = 0; i < str.length(); i++) {
			sz++;
			if (str.charAt(i) > 0xFF) {
				sz++;
			}
			if (sz > len) {
				return str.substring(0, i) + "...";
			}
		}
		return str;
	}
}
