content: /*
  * Licensed to the Apache Software Foundation (ASF) under one or more
  * contributor license agreements.  See the NOTICE file distributed with
  * this work for additional information regarding copyright ownership.
  * The ASF licenses this file to You under the Apache License, Version 2.0
  * (the "License"); you may not use this file except in compliance with
  * the License.  You may obtain a copy of the License at
  *
  *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
  
  package co.aikar.commands.apachecommonslang;
  
  import java.lang.reflect.Array;
  import java.util.Iterator;
  import java.util.Locale;
  
  /**
   * Select methods copied from Apache Commons to avoid importing entire lib
   * No changes to logic
   */
  public class ApacheCommonsLangUtil {
  
      /**
       * The empty String {@code ""}.
       * @since 2.0
       */
      public static final String EMPTY = "";
      /**
       * <p>Shallow clones an array returning a typecast result and handling
       * {@code null}.
       *
       * <p>The objects in the array are not cloned, thus there is no special
       * handling for multi-dimensional arrays.
       *
       * <p>This method returns {@code null} for a {@code null} input array.
       *
       * @param <T> the component type of the array
       * @param array  the array to shallow clone, may be {@code null}
       * @return the cloned array, {@code null} if {@code null} input
       */
      public static <T> T[] clone(final T[] array) {
          if (array == null) {
              return null;
          }
          return array.clone();
      }
  
      /**
       * <p>Adds all the elements of the given arrays into a new array.
       * <p>The new array contains all of the element of {@code array1} followed
       * by all of the elements {@code array2}. When an array is returned, it is always
       * a new array.
       *
       * <pre>
       * ArrayUtils.addAll(null, null)     = null
       * ArrayUtils.addAll(array1, null)   = cloned copy of array1
       * ArrayUtils.addAll(null, array2)   = cloned copy of array2
       * ArrayUtils.addAll([], [])         = []
       * ArrayUtils.addAll([null], [null]) = [null, null]
       * ArrayUtils.addAll(["a", "b", "c"], ["1", "2", "3"]) = ["a", "b", "c", "1", "2", "3"]
       * </pre>
       *
       * @param <T> the component type of the array
       * @param array1  the first array whose elements are added to the new array, may be {@code null}
       * @param array2  the second array whose elements are added to the new array, may be {@code null}
       * @return The new array, {@code null} if both arrays are {@code null}.
       *      The type of the new array is the type of the first array,
       *      unless the first array is null, in which case the type is the same as the second array.
       * @since 2.1
       * @throws IllegalArgumentException if the array types are incompatible
       */
      public static <T> T[] addAll(final T[] array1, final T... array2) {
          if (array1 == null) {
              return clone(array2);
          } else if (array2 == null) {
              return clone(array1);
          }
          final Class<?> type1 = array1.getClass().getComponentType();
          @SuppressWarnings("unchecked") // OK, because array is of type T
          final T[] joinedArray = (T[]) Array.newInstance(type1, array1.length + array2.length);
          System.arraycopy(array1, 0, joinedArray, 0, array1.length);
          try {
              System.arraycopy(array2, 0, joinedArray, array1.length, array2.length);
          } catch (final ArrayStoreException ase) {
              // Check if problem was due to incompatible types
              /*
               * We do this here, rather than before the copy because:
               * - it would be a wasted check most of the time
               * - safer, in case check turns out to be too strict
               */
              final Class<?> type2 = array2.getClass().getComponentType();
              if (!type1.isAssignableFrom(type2)) {
                  throw new IllegalArgumentException("Cannot store " + type2.getName() + " in an array of "
                          + type1.getName(), ase);
              }
              throw ase; // No, so rethrow original
          }
          return joinedArray;
      }
  
      //-----------------------------------------------------------------------
      /**
       * <p>Converts all the whitespace separated words in a String into capitalized words,
       * that is each word is made up of a titlecase character and then a series of
       * lowercase characters.  </p>
       *
       * <p>Whitespace is defined by {@link Character#isWhitespace(char)}.
       * A <code>null</code> input String returns <code>null</code>.
       * Capitalization uses the Unicode title case, normally equivalent to
       * upper case.</p>
       *
       * <pre>
       * WordUtils.capitalizeFully(null)        = null
       * WordUtils.capitalizeFully("")          = ""
       * WordUtils.capitalizeFully("i am FINE") = "I Am Fine"
       * </pre>
       *
       * @param str  the String to capitalize, may be null
       * @return capitalized String, <code>null</code> if null String input
       */
      public static String capitalizeFully(final String str) {
          return capitalizeFully(str, null);
      }
  
      /**
       * <p>Converts all the delimiter separated words in a String into capitalized words,
       * that is each word is made up of a titlecase character and then a series of
       * lowercase characters. </p>
       *
       * <p>The delimiters represent a set of characters understood to separate words.
       * The first string character and the first non-delimiter character after a
       * delimiter will be capitalized. </p>
       *
       * <p>A <code>null</code> input String returns <code>null</code>.
       * Capitalization uses the Unicode title case, normally equivalent to
       * upper case.</p>
       *
       * <pre>
       * WordUtils.capitalizeFully(null, *)            = null
       * WordUtils.capitalizeFully("", *)              = ""
       * WordUtils.capitalizeFully(*, null)            = *
       * WordUtils.capitalizeFully(*, new char[0])     = *
       * WordUtils.capitalizeFully("i aM.fine", {'.'}) = "I am.Fine"
       * </pre>
       *
       * @param str  the String to capitalize, may be null
       * @param delimiters  set of characters to determine capitalization, null means whitespace
       * @return capitalized String, <code>null</code> if null String input
       * @since 2.1
       */
      public static String capitalizeFully(String str, final char... delimiters) {
          final int delimLen = delimiters == null ? -1 : delimiters.length;
          if (str == null || str.isEmpty() || delimLen == 0) {
              return str;
          }
          str = str.toLowerCase(Locale.ENGLISH);
          return capitalize(str, delimiters);
      }
  
      // Capitalizing
      //-----------------------------------------------------------------------
      /**
       * <p>Capitalizes all the whitespace separated words in a String.
       * Only the first character of each word is changed. To convert the
       * rest of each word to lowercase at the same time,
       * use {@link #capitalizeFully(String)}.</p>
       *
       * <p>Whitespace is defined by {@link Character#isWhitespace(char)}.
       * A <code>null</code> input String returns <code>null</code>.
       * Capitalization uses the Unicode title case, normally equivalent to
       * upper case.</p>
       *
       * <pre>
       * WordUtils.capitalize(null)        = null
       * WordUtils.capitalize("")          = ""
       * WordUtils.capitalize("i am FINE") = "I Am FINE"
       * </pre>
       *
       * @param str  the String to capitalize, may be null
       * @return capitalized String, <code>null</code> if null String input
       * @see #capitalizeFully(String)
       */
      public static String capitalize(final String str) {
          return capitalize(str, null);
      }
  
      /**
       * <p>Capitalizes all the delimiter separated words in a String.
       * Only the first character of each word is changed. To convert the
       * rest of each word to lowercase at the same time,
       * use {@link #capitalizeFully(String, char[])}.</p>
       *
       * <p>The delimiters represent a set of characters understood to separate words.
       * The first string character and the first non-delimiter character after a
       * delimiter will be capitalized. </p>
       *
       * <p>A <code>null</code> input String returns <code>null</code>.
       * Capitalization uses the Unicode title case, normally equivalent to
       * upper case.</p>
       *
       * <pre>
       * WordUtils.capitalize(null, *)            = null
       * WordUtils.capitalize("", *)              = ""
       * WordUtils.capitalize(*, new char[0])     = *
       * WordUtils.capitalize("i am fine", null)  = "I Am Fine"
       * WordUtils.capitalize("i aM.fine", {'.'}) = "I aM.Fine"
       * </pre>
       *
       * @param str  the String to capitalize, may be null
       * @param delimiters  set of characters to determine capitalization, null means whitespace
       * @return capitalized String, <code>null</code> if null String input
       * @see #capitalizeFully(String)
       * @since 2.1
       */
      public static String capitalize(final String str, final char... delimiters) {
          final int delimLen = delimiters == null ? -1 : delimiters.length;
          if (str == null || str.isEmpty() || delimLen == 0) {
              return str;
          }
          final char[] buffer = str.toCharArray();
          boolean capitalizeNext = true;
          for (int i = 0; i < buffer.length; i++) {
              final char ch = buffer[i];
              if (isDelimiter(ch, delimiters)) {
                  capitalizeNext = true;
              } else if (capitalizeNext) {
                  buffer[i] = Character.toTitleCase(ch);
                  capitalizeNext = false;
              }
          }
          return new String(buffer);
      }
      //-----------------------------------------------------------------------
      /**
       * Is the character a delimiter.
       *
       * @param ch  the character to check
       * @param delimiters  the delimiters
       * @return true if it is a delimiter
       */
      public static boolean isDelimiter(final char ch, final char[] delimiters) {
          if (delimiters == null) {
              return Character.isWhitespace(ch);
          }
          for (final char delimiter : delimiters) {
              if (ch == delimiter) {
                  return true;
              }
          }
          return false;
      }
  
      // Joining
      //-----------------------------------------------------------------------
      /**
       * <p>Joins the elements of the provided array into a single String
       * containing the provided list of elements.</p>
       *
       * <p>No separator is added to the joined 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"]) = "abc"
       * StringUtils.join([null, "", "a"]) = "a"
       * </pre>
       *
       * @param <T> the specific type of values to join together
       * @param elements  the values to join together, may be null
       * @return the joined String, {@code null} if null array input
       * @since 2.0
       * @since 3.0 Changed signature to use varargs
       */
      @SafeVarargs
      public static <T> String join(final T... elements) {
          return join(elements, null);
      }
  
      /**
       * <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.
       * 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([null, "", "a"], ';')  = ";;a"
       * </pre>
       *
       * @param array  the array of values to join together, may be null
       * @param separator  the separator character to use
       * @return the joined String, {@code null} if null array input
       * @since 2.0
       */
      public static String join(final Object[] array, final char 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. 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([1, 2, 3], ';')  = "1;2;3"
       * StringUtils.join([1, 2, 3], null) = "123"
       * </pre>
       *
       * @param array
       *            the array of values to join together, may be null
       * @param separator
       *            the separator character to use
       * @return the joined String, {@code null} if null array input
       * @since 3.2
       */
      public static String join(final long[] array, final char 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. 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([1, 2, 3], ';')  = "1;2;3"
       * StringUtils.join([1, 2, 3], null) = "123"
       * </pre>
       *
       * @param array
       *            the array of values to join together, may be null
       * @param separator
       *            the separator character to use
       * @return the joined String, {@code null} if null array input
       * @since 3.2
       */
      public static String join(final int[] array, final char 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. 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([1, 2, 3], ';')  = "1;2;3"
       * StringUtils.join([1, 2, 3], null) = "123"
       * </pre>
       *
       * @param array
       *            the array of values to join together, may be null
       * @param separator
       *            the separator character to use
       * @return the joined String, {@code null} if null array input
       * @since 3.2
       */
      public static String join(final short[] array, final char 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. 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([1, 2, 3], ';')  = "1;2;3"
       * StringUtils.join([1, 2, 3], null) = "123"
       * </pre>
       *
       * @param array
       *            the array of values to join together, may be null
       * @param separator
       *            the separator character to use
       * @return the joined String, {@code null} if null array input
       * @since 3.2
       */
      public static String join(final byte[] array, final char 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. 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([1, 2, 3], ';')  = "1;2;3"
       * StringUtils.join([1, 2, 3], null) = "123"
       * </pre>
       *
       * @param array
       *            the array of values to join together, may be null
       * @param separator
       *            the separator character to use
       * @return the joined String, {@code null} if null array input
       * @since 3.2
       */
      public static String join(final char[] array, final char 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. 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([1, 2, 3], ';')  = "1;2;3"
       * StringUtils.join([1, 2, 3], null) = "123"
       * </pre>
       *
       * @param array
       *            the array of values to join together, may be null
       * @param separator
       *            the separator character to use
       * @return the joined String, {@code null} if null array input
       * @since 3.2
       */
      public static String join(final float[] array, final char 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. 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([1, 2, 3], ';')  = "1;2;3"
       * StringUtils.join([1, 2, 3], null) = "123"
       * </pre>
       *
       * @param array
       *            the array of values to join together, may be null
       * @param separator
       *            the separator character to use
       * @return the joined String, {@code null} if null array input
       * @since 3.2
       */
      public static String join(final double[] array, final char 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.
       * 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([null, "", "a"], ';')  = ";;a"
       * </pre>
       *
       * @param array  the array of values to join together, may be null
       * @param separator  the separator character to use
       * @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} if null array input
       * @since 2.0
       */
      public static String join(final Object[] array, final char separator, final int startIndex, final int endIndex) {
          if (array == null) {
              return null;
          }
          final int noOfItems = endIndex - startIndex;
          if (noOfItems <= 0) {
              return EMPTY;
          }
          final StringBuilder buf = new StringBuilder(noOfItems * 16);
          for (int i = startIndex; i < endIndex; i++) {
              if (i > startIndex) {
                  buf.append(separator);
              }
              if (array[i] != null) {
                  buf.append(array[i]);
              }
          }
          return buf.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. 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([1, 2, 3], ';')  = "1;2;3"
       * StringUtils.join([1, 2, 3], null) = "123"
       * </pre>
       *
       * @param array
       *            the array of values to join together, may be null
       * @param separator
       *            the separator character to use
       * @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} if null array input
       * @since 3.2
       */
      public static String join(final long[] array, final char separator, final int startIndex, final int endIndex) {
          if (array == null) {
              return null;
          }
          final int noOfItems = endIndex - startIndex;
          if (noOfItems <= 0) {
              return EMPTY;
          }
          final StringBuilder buf = new StringBuilder(noOfItems * 16);
          for (int i = startIndex; i < endIndex; i++) {
              if (i > startIndex) {
                  buf.append(separator);
              }
              buf.append(array[i]);
          }
          return buf.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. 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([1, 2, 3], ';')  = "1;2;3"
       * StringUtils.join([1, 2, 3], null) = "123"
       * </pre>
       *
       * @param array
       *            the array of values to join together, may be null
       * @param separator
       *            the separator character to use
       * @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} if null array input
       * @since 3.2
       */
      public static String join(final int[] array, final char separator, final int startIndex, final int endIndex) {
          if (array == null) {
              return null;
          }
          final int noOfItems = endIndex - startIndex;
          if (noOfItems <= 0) {
              return EMPTY;
          }
          final StringBuilder buf = new StringBuilder(noOfItems * 16);
          for (int i = startIndex; i < endIndex; i++) {
              if (i > startIndex) {
                  buf.append(separator);
              }
              buf.append(array[i]);
          }
          return buf.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. 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([1, 2, 3], ';')  = "1;2;3"
       * StringUtils.join([1, 2, 3], null) = "123"
       * </pre>
       *
       * @param array
       *            the array of values to join together, may be null
       * @param separator
       *            the separator character to use
       * @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} if null array input
       * @since 3.2
       */
      public static String join(final byte[] array, final char separator, final int startIndex, final int endIndex) {
          if (array == null) {
              return null;
          }
          final int noOfItems = endIndex - startIndex;
          if (noOfItems <= 0) {
              return EMPTY;
          }
          final StringBuilder buf = new StringBuilder(noOfItems * 16);
          for (int i = startIndex; i < endIndex; i++) {
              if (i > startIndex) {
                  buf.append(separator);
              }
              buf.append(array[i]);
          }
          return buf.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. 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([1, 2, 3], ';')  = "1;2;3"
       * StringUtils.join([1, 2, 3], null) = "123"
       * </pre>
       *
       * @param array
       *            the array of values to join together, may be null
       * @param separator
       *            the separator character to use
       * @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} if null array input
       * @since 3.2
       */
      public static String join(final short[] array, final char separator, final int startIndex, final int endIndex) {
          if (array == null) {
              return null;
          }
          final int noOfItems = endIndex - startIndex;
          if (noOfItems <= 0) {
              return EMPTY;
          }
          final StringBuilder buf = new StringBuilder(noOfItems * 16);
          for (int i = startIndex; i < endIndex; i++) {
              if (i > startIndex) {
                  buf.append(separator);
              }
              buf.append(array[i]);
          }
          return buf.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. 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([1, 2, 3], ';')  = "1;2;3"
       * StringUtils.join([1, 2, 3], null) = "123"
       * </pre>
       *
       * @param array
       *            the array of values to join together, may be null
       * @param separator
       *            the separator character to use
       * @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} if null array input
       * @since 3.2
       */
      public static String join(final char[] array, final char separator, final int startIndex, final int endIndex) {
          if (array == null) {
              return null;
          }
          final int noOfItems = endIndex - startIndex;
          if (noOfItems <= 0) {
              return EMPTY;
          }
          final StringBuilder buf = new StringBuilder(noOfItems * 16);
          for (int i = startIndex; i < endIndex; i++) {
              if (i > startIndex) {
                  buf.append(separator);
              }
              buf.append(array[i]);
          }
          return buf.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. 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([1, 2, 3], ';')  = "1;2;3"
       * StringUtils.join([1, 2, 3], null) = "123"
       * </pre>
       *
       * @param array
       *            the array of values to join together, may be null
       * @param separator
       *            the separator character to use
       * @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} if null array input
       * @since 3.2
       */
      public static String join(final double[] array, final char separator, final int startIndex, final int endIndex) {
          if (array == null) {
              return null;
          }
          final int noOfItems = endIndex - startIndex;
          if (noOfItems <= 0) {
              return EMPTY;
          }
          final StringBuilder buf = new StringBuilder(noOfItems * 16);
          for (int i = startIndex; i < endIndex; i++) {
              if (i > startIndex) {
                  buf.append(separator);
              }
              buf.append(array[i]);
          }
          return buf.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. 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([1, 2, 3], ';')  = "1;2;3"
       * StringUtils.join([1, 2, 3], null) = "123"
       * </pre>
       *
       * @param array
       *            the array of values to join together, may be null
       * @param separator
       *            the separator character to use
       * @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} if null array input
       * @since 3.2
       */
      public static String join(final float[] array, final char separator, final int startIndex, final int endIndex) {
          if (array == null) {
              return null;
          }
          final int noOfItems = endIndex - startIndex;
          if (noOfItems <= 0) {
              return EMPTY;
          }
          final StringBuilder buf = new StringBuilder(noOfItems * 16);
          for (int i = startIndex; i < endIndex; i++) {
              if (i > startIndex) {
                  buf.append(separator);
              }
              buf.append(array[i]);
          }
          return buf.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} 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 ""
       * @return the joined String, {@code null} if null array input
       */
      public static String join(final Object[] array, final 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} 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"], "--", 0, 3)  = "a--b--c"
       * StringUtils.join(["a", "b", "c"], "--", 1, 3)  = "b--c"
       * StringUtils.join(["a", "b", "c"], "--", 2, 3)  = "c"
       * StringUtils.join(["a", "b", "c"], "--", 2, 2)  = ""
       * StringUtils.join(["a", "b", "c"], null, 0, 3)  = "abc"
       * StringUtils.join(["a", "b", "c"], "", 0, 3)    = "abc"
       * StringUtils.join([null, "", "a"], ',', 0, 3)   = ",,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.
       * @param endIndex the index to stop joining from (exclusive).
       * @return the joined String, {@code null} if null array input; or the empty string
       * if {@code endIndex - startIndex <= 0}. The number of joined entries is given by
       * {@code endIndex - startIndex}
       * @throws ArrayIndexOutOfBoundsException ife<br>
       * {@code startIndex < 0} or <br>
       * {@code startIndex >= array.length()} or <br>
       * {@code endIndex < 0} or <br>
       * {@code endIndex > array.length()}
       */
      public static String join(final Object[] array, String separator, final int startIndex, final 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)
          final int noOfItems = endIndex - startIndex;
          if (noOfItems <= 0) {
              return EMPTY;
          }
  
          final StringBuilder buf = new StringBuilder(noOfItems * 16);
  
          for (int i = startIndex; i < endIndex; i++) {
              if (i > startIndex) {
                  buf.append(separator);
              }
              if (array[i] != null) {
                  buf.append(array[i]);
              }
          }
          return buf.toString();
      }
  
      /**
       * <p>Joins the elements of the provided {@code Iterator} into
       * a single String containing the provided elements.</p>
       *
       * <p>No delimiter is added before or after the list. Null objects or empty
       * strings within the iteration are represented by empty strings.</p>
       *
       * <p>See the examples here: {@link #join(Object[],char)}. </p>
       *
       * @param iterator  the {@code Iterator} of values to join together, may be null
       * @param separator  the separator character to use
       * @return the joined String, {@code null} if null iterator input
       * @since 2.0
       */
      public static String join(final Iterator<?> iterator, final char separator) {
  
          // handle null, zero and one elements before building a buffer
          if (iterator == null) {
              return null;
          }
          if (!iterator.hasNext()) {
              return EMPTY;
          }
          final Object first = iterator.next();
          if (!iterator.hasNext()) {
              final String result = first != null ? first.toString() : "";
              return result;
          }
  
          // two or more elements
          final StringBuilder buf = new StringBuilder(256); // Java default is 16, probably too small
          if (first != null) {
              buf.append(first);
          }
  
          while (iterator.hasNext()) {
              buf.append(separator);
              final Object obj = iterator.next();
              if (obj != null) {
                  buf.append(obj);
              }
          }
  
          return buf.toString();
      }
  
      /**
       * <p>Joins the elements of the provided {@code Iterator} into
       * a single String containing the provided elements.</p>
       *
       * <p>No delimiter is added before or after the list.
       * A {@code null} separator is the same as an empty String ("").</p>
       *
       * <p>See the examples here: {@link #join(Object[],String)}. </p>
       *
       * @param iterator  the {@code Iterator} of values to join together, may be null
       * @param separator  the separator character to use, null treated as ""
       * @return the joined String, {@code null} if null iterator input
       */
      public static String join(final Iterator<?> iterator, final String separator) {
  
          // handle null, zero and one elements before building a buffer
          if (iterator == null) {
              return null;
          }
          if (!iterator.hasNext()) {
              return EMPTY;
          }
          final Object first = iterator.next();
          if (!iterator.hasNext()) {
              final String result = first != null ? first.toString() : "";
              return result;
          }
  
          // two or more elements
          final StringBuilder buf = new StringBuilder(256); // Java default is 16, probably too small
          if (first != null) {
              buf.append(first);
          }
  
          while (iterator.hasNext()) {
              if (separator != null) {
                  buf.append(separator);
              }
              final Object obj = iterator.next();
              if (obj != null) {
                  buf.append(obj);
              }
          }
          return buf.toString();
      }
  
      /**
       * <p>Joins the elements of the provided {@code Iterable} into
       * a single String containing the provided elements.</p>
       *
       * <p>No delimiter is added before or after the list. Null objects or empty
       * strings within the iteration are represented by empty strings.</p>
       *
       * <p>See the examples here: {@link #join(Object[],char)}. </p>
       *
       * @param iterable  the {@code Iterable} providing the values to join together, may be null
       * @param separator  the separator character to use
       * @return the joined String, {@code null} if null iterator input
       * @since 2.3
       */
      public static String join(final Iterable<?> iterable, final char separator) {
          if (iterable == null) {
              return null;
          }
          return join(iterable.iterator(), separator);
      }
  
      /**
       * <p>Joins the elements of the provided {@code Iterable} into
       * a single String containing the provided elements.</p>
       *
       * <p>No delimiter is added before or after the list.
       * A {@code null} separator is the same as an empty String ("").</p>
       *
       * <p>See the examples here: {@link #join(Object[],String)}. </p>
       *
       * @param iterable  the {@code Iterable} providing the values to join together, may be null
       * @param separator  the separator character to use, null treated as ""
       * @return the joined String, {@code null} if null iterator input
       * @since 2.3
       */
      public static String join(final Iterable<?> iterable, final String separator) {
          if (iterable == null) {
              return null;
          }
          return join(iterable.iterator(), separator);
      }
  
  
      /**
       * <p>Checks if the CharSequence contains only Unicode digits.
       * A decimal point is not a Unicode digit and returns false.</p>
       *
       * <p>{@code null} will return {@code false}.
       * An empty CharSequence (length()=0) will return {@code false}.</p>
       *
       * <p>Note that the method does not allow for a leading sign, either positive or negative.
       * Also, if a String passes the numeric test, it may still generate a NumberFormatException
       * when parsed by Integer.parseInt or Long.parseLong, e.g. if the value is outside the range
       * for int or long respectively.</p>
       *
       * <pre>
       * StringUtils.isNumeric(null)   = false
       * StringUtils.isNumeric("")     = false
       * StringUtils.isNumeric("  ")   = false
       * StringUtils.isNumeric("123")  = true
       * StringUtils.isNumeric("\u0967\u0968\u0969")  = true
       * StringUtils.isNumeric("12 3") = false
       * StringUtils.isNumeric("ab2c") = false
       * StringUtils.isNumeric("12-3") = false
       * StringUtils.isNumeric("12.3") = false
       * StringUtils.isNumeric("-123") = false
       * StringUtils.isNumeric("+123") = false
       * </pre>
       *
       * @param cs  the CharSequence to check, may be null
       * @return {@code true} if only contains digits, and is non-null
       * @since 3.0 Changed signature from isNumeric(String) to isNumeric(CharSequence)
       * @since 3.0 Changed "" to return false and not true
       */
      public static boolean isNumeric(final CharSequence cs) {
          if (cs == null || cs.length() == 0) {
              return false;
          }
          final int sz = cs.length();
          for (int i = 0; i < sz; i++) {
              if (!Character.isDigit(cs.charAt(i))) {
                  return false;
              }
          }
          return true;
      }
  
  
      // startsWith
      //-----------------------------------------------------------------------
  
      /**
       * <p>Check if a CharSequence starts with a specified prefix.</p>
       *
       * <p>{@code null}s are handled without exceptions. Two {@code null}
       * references are considered to be equal. The comparison is case sensitive.</p>
       *
       * <pre>
       * StringUtils.startsWith(null, null)      = true
       * StringUtils.startsWith(null, "abc")     = false
       * StringUtils.startsWith("abcdef", null)  = false
       * StringUtils.startsWith("abcdef", "abc") = true
       * StringUtils.startsWith("ABCDEF", "abc") = false
       * </pre>
       *
       * @see java.lang.String#startsWith(String)
       * @param str  the CharSequence to check, may be null
       * @param prefix the prefix to find, may be null
       * @return {@code true} if the CharSequence starts with the prefix, case sensitive, or
       *  both {@code null}
       * @since 2.4
       * @since 3.0 Changed signature from startsWith(String, String) to startsWith(CharSequence, CharSequence)
       */
      public static boolean startsWith(final CharSequence str, final CharSequence prefix) {
          return startsWith(str, prefix, false);
      }
  
      /**
       * <p>Case insensitive check if a CharSequence starts with a specified prefix.</p>
       *
       * <p>{@code null}s are handled without exceptions. Two {@code null}
       * references are considered to be equal. The comparison is case insensitive.</p>
       *
       * <pre>
       * StringUtils.startsWithIgnoreCase(null, null)      = true
       * StringUtils.startsWithIgnoreCase(null, "abc")     = false
       * StringUtils.startsWithIgnoreCase("abcdef", null)  = false
       * StringUtils.startsWithIgnoreCase("abcdef", "abc") = true
       * StringUtils.startsWithIgnoreCase("ABCDEF", "abc") = true
       * </pre>
       *
       * @see java.lang.String#startsWith(String)
       * @param str  the CharSequence to check, may be null
       * @param prefix the prefix to find, may be null
       * @return {@code true} if the CharSequence starts with the prefix, case insensitive, or
       *  both {@code null}
       * @since 2.4
       * @since 3.0 Changed signature from startsWithIgnoreCase(String, String) to startsWithIgnoreCase(CharSequence, CharSequence)
       */
      public static boolean startsWithIgnoreCase(final CharSequence str, final CharSequence prefix) {
          return startsWith(str, prefix, true);
      }
  
      /**
       * <p>Check if a CharSequence starts with a specified prefix (optionally case insensitive).</p>
       *
       * @see java.lang.String#startsWith(String)
       * @param str  the CharSequence to check, may be null
       * @param prefix the prefix to find, may be null
       * @param ignoreCase indicates whether the compare should ignore case
       *  (case insensitive) or not.
       * @return {@code true} if the CharSequence starts with the prefix or
       *  both {@code null}
       */
      private static boolean startsWith(final CharSequence str, final CharSequence prefix, final boolean ignoreCase) {
          if (str == null || prefix == null) {
              return str == null && prefix == null;
          }
          if (prefix.length() > str.length()) {
              return false;
          }
          return regionMatches(str, ignoreCase, 0, prefix, 0, prefix.length());
      }
  
      /**
       * Green implementation of regionMatches.
       *
       * @param cs the {@code CharSequence} to be processed
       * @param ignoreCase whether or not to be case insensitive
       * @param thisStart the index to start on the {@code cs} CharSequence
       * @param substring the {@code CharSequence} to be looked for
       * @param start the index to start on the {@code substring} CharSequence
       * @param length character length of the region
       * @return whether the region matched
       */
      static boolean regionMatches(final CharSequence cs, final boolean ignoreCase, final int thisStart,
                                   final CharSequence substring, final int start, final int length)    {
          if (cs instanceof String && substring instanceof String) {
              return ((String) cs).regionMatches(ignoreCase, thisStart, (String) substring, start, length);
          }
          int index1 = thisStart;
          int index2 = start;
          int tmpLen = length;
  
          // Extract these first so we detect NPEs the same as the java.lang.String version
          final int srcLen = cs.length() - thisStart;
          final int otherLen = substring.length() - start;
  
          // Check for invalid parameters
          if (thisStart < 0 || start < 0 || length < 0) {
              return false;
          }
  
          // Check that the regions are long enough
          if (srcLen < length || otherLen < length) {
              return false;
          }
  
          while (tmpLen-- > 0) {
              final char c1 = cs.charAt(index1++);
              final char c2 = substring.charAt(index2++);
  
              if (c1 == c2) {
                  continue;
              }
  
              if (!ignoreCase) {
                  return false;
              }
  
              // The same check as in String.regionMatches():
              if (Character.toUpperCase(c1) != Character.toUpperCase(c2)
                      && Character.toLowerCase(c1) != Character.toLowerCase(c2)) {
                  return false;
              }
          }
  
          return true;
      }
  
      /**
       * The index value when an element is not found in a list or array: <code>-1</code>.
       * This value is returned by methods in this class and can also be used in comparisons with values returned by
       * various method from {@link java.util.List}.
       */
      public static final int INDEX_NOT_FOUND = -1;
  
      // IndexOf search
      // ----------------------------------------------------------------------
  
      // Object IndexOf
      //-----------------------------------------------------------------------
      /**
       * <p>Finds the index of the given object in the array.</p>
       *
       * <p>This method returns {@link #INDEX_NOT_FOUND} (<code>-1</code>) for a <code>null</code> input array.</p>
       *
       * @param array  the array to search through for the object, may be <code>null</code>
       * @param objectToFind  the object to find, may be <code>null</code>
       * @return the index of the object within the array,
       *  {@link #INDEX_NOT_FOUND} (<code>-1</code>) if not found or <code>null</code> array input
       */
      public static int indexOf(Object[] array, Object objectToFind) {
          return indexOf(array, objectToFind, 0);
      }
  
      /**
       * <p>Finds the index of the given object in the array starting at the given index.</p>
       *
       * <p>This method returns {@link #INDEX_NOT_FOUND} (<code>-1</code>) for a <code>null</code> input array.</p>
       *
       * <p>A negative startIndex is treated as zero. A startIndex larger than the array
       * length will return {@link #INDEX_NOT_FOUND} (<code>-1</code>).</p>
       *
       * @param array  the array to search through for the object, may be <code>null</code>
       * @param objectToFind  the object to find, may be <code>null</code>
       * @param startIndex  the index to start searching at
       * @return the index of the object within the array starting at the index,
       *  {@link #INDEX_NOT_FOUND} (<code>-1</code>) if not found or <code>null</code> array input
       */
      public static int indexOf(Object[] array, Object objectToFind, int startIndex) {
          if (array == null) {
              return INDEX_NOT_FOUND;
          }
          if (startIndex < 0) {
              startIndex = 0;
          }
          if (objectToFind == null) {
              for (int i = startIndex; i < array.length; i++) {
                  if (array[i] == null) {
                      return i;
                  }
              }
          } else {
              for (int i = startIndex; i < array.length; i++) {
                  if (objectToFind.equals(array[i])) {
                      return i;
                  }
              }
          }
          return INDEX_NOT_FOUND;
      }
  }
  