/*******************************************************************************
 * Copyright (c) 2000, 2020 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *     Luiz-Otavio Zorzella <zorzella at gmail dot com> - Improve CamelCase algorithm
 *     Gábor Kövesdán - Contribution for Bug 350000 - [content assist] Include non-prefix matches in auto-complete suggestions
 *     Stefan Xenos <sxenos@gmail.com> (Google) - Bug 501283 - Lots of hash collisions during indexing
 *******************************************************************************/
package org.eclipse.jdt.core.compiler;

import java.util.Arrays;
import java.util.List;

import org.eclipse.jdt.internal.compiler.parser.ScannerHelper;

/**
 * This class is a collection of helper methods to manipulate char arrays.
 *
 * @since 2.1
 * @noinstantiate This class is not intended to be instantiated by clients.
 */
public final class CharOperation {

	/**
	 * Constant for an empty char array
	 */
	public static final char[] NO_CHAR = new char[0];

	/**
	 * Constant for an empty char array with two dimensions.
	 */
	public static final char[][] NO_CHAR_CHAR = new char[0][];

	/**
	 * Constant for an empty String array.
	 * @since 3.1
	 */
	public static final String[] NO_STRINGS = new String[0];

	/**
	 * Constant for all Prefix
	 * @since 3.14
	 */
	public static final char[] ALL_PREFIX = new char[] {'*'};

	/**
	 * Constant for comma
	 * @since 3.14
	 */
	public static final char[] COMMA_SEPARATOR = new char[] {','};

	private static final int[] EMPTY_REGIONS = new int[0];

/**
 * Answers a new array with appending the suffix character at the end of the array.
 * <br>
 * <br>
 * For example:<br>
 * <ol>
 * <li><pre>
 *    array = { 'a', 'b' }
 *    suffix = 'c'
 *    => result = { 'a', 'b' , 'c' }
 * </pre>
 * </li>
 * <li><pre>
 *    array = null
 *    suffix = 'c'
 *    => result = { 'c' }
 * </pre></li>
 * </ol>
 *
 * @param array the array that is concatenated with the suffix character
 * @param suffix the suffix character
 * @return the new array
 */
public static final char[] append(char[] array, char suffix) {
	if (array == null)
		return new char[] { suffix };
	int length = array.length;
	System.arraycopy(array, 0, array = new char[length + 1], 0, length);
	array[length] = suffix;
	return array;
}

/**
 * Answers a new array with appending the sub-array at the end of the array.
 * <br>
 * <br>
 * For example:<br>
 * <ol>
 * <li><pre>
 *    array = { 'a', 'b' }
 *    suffix = { 'c', 'd' }
 *    => result = { 'a', 'b' , 'c' , d' }
 * </pre>
 * </li>
 * <li><pre>
 *    array = null
 *    suffix = { 'c' }
 *    => result = { 'c' }
 * </pre></li>
 * </ol>
 *
 * @param target the array that is concatenated with the suffix array.
 * @param suffix the array that will be concatenated to the target
 * @return the new array
 * @throws NullPointerException if the target array is null
 * @since 3.11
 */
public static final char[] append(char[] target, char[] suffix) {
	if(suffix == null || suffix.length == 0)
		return target;
	int targetLength = target.length;
	int subLength = suffix.length;
	int newTargetLength = targetLength + subLength;
	if (newTargetLength > targetLength) {
		System.arraycopy(target, 0, target = new char[newTargetLength], 0, targetLength);
	}
	System.arraycopy(suffix, 0, target, targetLength, subLength);
	return target;
}

/**
 * Append the given sub-array to the target array starting at the given index in the target array.
 * The start of the sub-array is inclusive, the end is exclusive.
 * Answers a new target array if it needs to grow, otherwise answers the same target array.
 * <br>
 * For example:<br>
 * <ol>
 * <li><pre>
 *    target = { 'a', 'b', '0' }
 *    index = 2
 *    array = { 'c', 'd' }
 *    start = 0
 *    end = 1
 *    => result = { 'a', 'b' , 'c' }
 * </pre>
 * </li>
 * <li><pre>
 *    target = { 'a', 'b' }
 *    index = 2
 *    array = { 'c', 'd' }
 *    start = 0
 *    end = 1
 *    => result = { 'a', 'b' , 'c', '0', '0' , '0' } (new array)
 * </pre></li>
 * <li><pre>
 *    target = { 'a', 'b', 'c' }
 *    index = 1
 *    array = { 'c', 'd', 'e', 'f' }
 *    start = 1
 *    end = 4
 *    => result = { 'a', 'd' , 'e', 'f', '0', '0', '0', '0' } (new array)
 * </pre></li>
 * </ol>
 *
 * @param target the given target
 * @param index the given index
 * @param array the given array
 * @param start the given start index
 * @param end the given end index
 *
 * @return the new array
 * @throws NullPointerException if the target array is null
 */
public static final char[] append(char[] target, int index, char[] array, int start, int end) {
	int targetLength = target.length;
	int subLength = end-start;
	int newTargetLength = subLength+index;
	if (newTargetLength > targetLength) {
		System.arraycopy(target, 0, target = new char[newTargetLength*2], 0, index);
	}
	System.arraycopy(array, start, target, index, subLength);
	return target;
}

/**
 * Answers a new array with prepending the prefix character at the start of the array.
 * <br>
 * <br>
 * For example:<br>
 * <ol>
 * <li><pre>
 *    prefix = 'c'
 *    array = { 'a', 'b' }
 *    => result = { 'c' , 'a', 'b' }
 * </pre>
 * </li>
 * <li><pre>
 *    prefix = 'c'
 *    array = null
 *    => result = { 'c' }
 * </pre></li>
 * </ol>
 *
 * @param array the array that is concatenated with the prefix character
 * @param prefix the prefix character
 * @return the new array
 * @since 3.14
 */
public static final char[] prepend(char prefix, char[] array) {
	if (array == null)
		return new char[] { prefix };
	int length = array.length;
	System.arraycopy(array, 0, array = new char[length + 1], 1, length);
	array[0] = prefix;
	return array;
}

/**
 * Answers the concatenation of the two arrays. It answers null if the two arrays are null.
 * If the first array is null, then the second array is returned.
 * If the second array is null, then the first array is returned.
 * <br>
 * <br>
 * For example:
 * <ol>
 * <li><pre>
 *    first = null
 *    second = null
 *    => result = null
 * </pre>
 * </li>
 * <li><pre>
 *    first = { { ' a' } }
 *    second = null
 *    => result = { { ' a' } }
 * </pre>
 * </li>
 * <li><pre>
 *    first = null
 *    second = { { ' a' } }
 *    => result = { { ' a' } }
 * </pre>
 * </li>
 * <li><pre>
 *    first = { { ' b' } }
 *    second = { { ' a' } }
 *    => result = { { ' b' }, { ' a' } }
 * </pre>
 * </li>
 * </ol>
 *
 * @param first the first array to concatenate
 * @param second the second array to concatenate
 * @return the concatenation of the two arrays, or null if the two arrays are null.
 */
public static final char[][] arrayConcat(char[][] first, char[][] second) {
	if (first == null)
		return second;
	if (second == null)
		return first;

	int length1 = first.length;
	int length2 = second.length;
	char[][] result = new char[length1 + length2][];
	System.arraycopy(first, 0, result, 0, length1);
	System.arraycopy(second, 0, result, length1, length2);
	return result;
}

/**
 * Answers true if the pattern matches the given name using CamelCase rules, or
 * false otherwise. char[] CamelCase matching does NOT accept explicit wild-cards
 * '*' and '?' and is inherently case sensitive.
 * <p>
 * CamelCase denotes the convention of writing compound names without spaces,
 * and capitalizing every term. This function recognizes both upper and lower
 * CamelCase, depending whether the leading character is capitalized or not.
 * The leading part of an upper CamelCase pattern is assumed to contain a
 * sequence of capitals which are appearing in the matching name; e.g. 'NPE' will
 * match 'NullPointerException', but not 'NewPerfData'. A lower CamelCase pattern
 * uses a lowercase first character. In Java, type names follow the upper
 * CamelCase convention, whereas method or field names follow the lower
 * CamelCase convention.
 * <p>
 * The pattern may contain lowercase characters, which will be matched in a case
 * sensitive way. These characters must appear in sequence in the name.
 * For instance, 'NPExcep' will match 'NullPointerException', but not
 * 'NullPointerExCEPTION' or 'NuPoEx' will match 'NullPointerException', but not
 * 'NoPointerException'.
 * <p>
 * Digit characters are treated in a special way. They can be used in the pattern
 * but are not always considered as leading character. For instance, both
 * 'UTF16DSS' and 'UTFDSS' patterns will match 'UTF16DocumentScannerSupport'.
 * <p>
 * Using this method allows matching names to have more parts than the specified
 * pattern (see {@link #camelCaseMatch(char[], char[], boolean)}).<br>
 * For instance, 'HM' , 'HaMa' and  'HMap' patterns will match 'HashMap',
 * 'HatMapper' <b>and also</b> 'HashMapEntry'.
 * <p>
 * Examples:
 * <ol>
 * <li> pattern = "NPE".toCharArray()
 * name = "NullPointerException".toCharArray()
 * result => true</li>
 * <li> pattern = "NPE".toCharArray()
 * name = "NoPermissionException".toCharArray()
 * result => true</li>
 * <li> pattern = "NuPoEx".toCharArray()
 * name = "NullPointerException".toCharArray()
 * result => true</li>
 * <li> pattern = "NuPoEx".toCharArray()
 * name = "NoPermissionException".toCharArray()
 * result => false</li>
 * <li> pattern = "npe".toCharArray()
 * name = "NullPointerException".toCharArray()
 * result => false</li>
 * <li> pattern = "IPL3".toCharArray()
 * name = "IPerspectiveListener3".toCharArray()
 * result => true</li>
 * <li> pattern = "HM".toCharArray()
 * name = "HashMapEntry".toCharArray()
 * result => true</li>
 * </ol>
 *
 * @param pattern the given pattern
 * @param name the given name
 * @return true if the pattern matches the given name, false otherwise
 * @since 3.2
 */
public static final boolean camelCaseMatch(char[] pattern, char[] name) {
	if (pattern == null)
		return true; // null pattern is equivalent to '*'
	if (name == null)
		return false; // null name cannot match

	return camelCaseMatch(pattern, 0, pattern.length, name, 0, name.length, false/*not the same count of parts*/);
}

/**
 * Answers true if the pattern matches the given name using CamelCase rules, or
 * false otherwise. char[] CamelCase matching does NOT accept explicit wild-cards
 * '*' and '?' and is inherently case sensitive.
 * <p>
 * CamelCase denotes the convention of writing compound names without spaces,
 * and capitalizing every term. This function recognizes both upper and lower
 * CamelCase, depending whether the leading character is capitalized or not.
 * The leading part of an upper CamelCase pattern is assumed to contain a
 * sequence of capitals which are appearing in the matching name; e.g. 'NPE' will
 * match 'NullPointerException', but not 'NewPerfData'. A lower CamelCase pattern
 * uses a lowercase first character. In Java, type names follow the upper
 * CamelCase convention, whereas method or field names follow the lower
 * CamelCase convention.
 * <p>
 * The pattern may contain lowercase characters, which will be matched in a case
 * sensitive way. These characters must appear in sequence in the name.
 * For instance, 'NPExcep' will match 'NullPointerException', but not
 * 'NullPointerExCEPTION' or 'NuPoEx' will match 'NullPointerException', but not
 * 'NoPointerException'.
 * <p>
 * Digit characters are treated in a special way. They can be used in the pattern
 * but are not always considered as leading character. For instance, both
 * 'UTF16DSS' and 'UTFDSS' patterns will match 'UTF16DocumentScannerSupport'.
 * <p>
 * CamelCase can be restricted to match only the same count of parts. When this
 * restriction is specified the given pattern and the given name must have <b>exactly</b>
 * the same number of parts (i.e. the same number of uppercase characters).<br>
 * For instance, 'HM' , 'HaMa' and  'HMap' patterns will match 'HashMap' and
 * 'HatMapper' <b>but not</b> 'HashMapEntry'.
 * <p>
 * Examples:<ol>
 * <li> pattern = "NPE".toCharArray()
 * name = "NullPointerException".toCharArray()
 * result => true</li>
 * <li> pattern = "NPE".toCharArray()
 * name = "NoPermissionException".toCharArray()
 * result => true</li>
 * <li> pattern = "NuPoEx".toCharArray()
 * name = "NullPointerException".toCharArray()
 * result => true</li>
 * <li> pattern = "NuPoEx".toCharArray()
 * name = "NoPermissionException".toCharArray()
 * result => false</li>
 * <li> pattern = "npe".toCharArray()
 * name = "NullPointerException".toCharArray()
 * result => false</li>
 * <li> pattern = "IPL3".toCharArray()
 * name = "IPerspectiveListener3".toCharArray()
 * result => true</li>
 * <li> pattern = "HM".toCharArray()
 * name = "HashMapEntry".toCharArray()
 * result => (samePartCount == false)</li>
 * </ol>
 *
 * @param pattern the given pattern
 * @param name the given name
 * @param samePartCount flag telling whether the pattern and the name should
 * 	have the same count of parts or not.<br>
 * 	&nbsp;&nbsp;For example:
 * 	<ul>
 * 		<li>'HM' type string pattern will match 'HashMap' and 'HtmlMapper' types,
 * 				but not 'HashMapEntry'</li>
 * 		<li>'HMap' type string pattern will still match previous 'HashMap' and
 * 				'HtmlMapper' types, but not 'HighMagnitude'</li>
 * 	</ul>
 * @return true if the pattern matches the given name, false otherwise
 * @since 3.4
 */
public static final boolean camelCaseMatch(char[] pattern, char[] name, boolean samePartCount) {
	if (pattern == null)
		return true; // null pattern is equivalent to '*'
	if (name == null)
		return false; // null name cannot match

	return camelCaseMatch(pattern, 0, pattern.length, name, 0, name.length, samePartCount);
}

/**
 * Answers true if a sub-pattern matches the sub-part of the given name using
 * CamelCase rules, or false otherwise.  char[] CamelCase matching does NOT
 * accept explicit wild-cards '*' and '?' and is inherently case sensitive.
 * Can match only subset of name/pattern, considering end positions as non-inclusive.
 * The sub-pattern is defined by the patternStart and patternEnd positions.
 * <p>
 * CamelCase denotes the convention of writing compound names without spaces,
 * and capitalizing every term. This function recognizes both upper and lower
 * CamelCase, depending whether the leading character is capitalized or not.
 * The leading part of an upper CamelCase pattern is assumed to contain a
 * sequence of capitals which are appearing in the matching name; e.g. 'NPE' will
 * match 'NullPointerException', but not 'NewPerfData'. A lower CamelCase pattern
 * uses a lowercase first character. In Java, type names follow the upper
 * CamelCase convention, whereas method or field names follow the lower
 * CamelCase convention.
 * <p>
 * The pattern may contain lowercase characters, which will be matched in a case
 * sensitive way. These characters must appear in sequence in the name.
 * For instance, 'NPExcep' will match 'NullPointerException', but not
 * 'NullPointerExCEPTION' or 'NuPoEx' will match 'NullPointerException', but not
 * 'NoPointerException'.
 * <p>
 * Digit characters are treated in a special way. They can be used in the pattern
 * but are not always considered as leading character. For instance, both
 * 'UTF16DSS' and 'UTFDSS' patterns will match 'UTF16DocumentScannerSupport'.
 * <p>
 * Digit characters are treated in a special way. They can be used in the pattern
 * but are not always considered as leading character. For instance, both
 * 'UTF16DSS' and 'UTFDSS' patterns will match 'UTF16DocumentScannerSupport'.
 * <p>
 * Using this method allows matching names to have more parts than the specified
 * pattern (see {@link #camelCaseMatch(char[], int, int, char[], int, int, boolean)}).<br>
 * For instance, 'HM' , 'HaMa' and  'HMap' patterns will match 'HashMap',
 * 'HatMapper' <b>and also</b> 'HashMapEntry'.
 * <p>
 * Examples:
 * <ol>
 * <li> pattern = "NPE".toCharArray()
 * patternStart = 0
 * patternEnd = 3
 * name = "NullPointerException".toCharArray()
 * nameStart = 0
 * nameEnd = 20
 * result => true</li>
 * <li> pattern = "NPE".toCharArray()
 * patternStart = 0
 * patternEnd = 3
 * name = "NoPermissionException".toCharArray()
 * nameStart = 0
 * nameEnd = 21
 * result => true</li>
 * <li> pattern = "NuPoEx".toCharArray()
 * patternStart = 0
 * patternEnd = 6
 * name = "NullPointerException".toCharArray()
 * nameStart = 0
 * nameEnd = 20
 * result => true</li>
 * <li> pattern = "NuPoEx".toCharArray()
 * patternStart = 0
 * patternEnd = 6
 * name = "NoPermissionException".toCharArray()
 * nameStart = 0
 * nameEnd = 21
 * result => false</li>
 * <li> pattern = "npe".toCharArray()
 * patternStart = 0
 * patternEnd = 3
 * name = "NullPointerException".toCharArray()
 * nameStart = 0
 * nameEnd = 20
 * result => false</li>
 * <li> pattern = "IPL3".toCharArray()
 * patternStart = 0
 * patternEnd = 4
 * name = "IPerspectiveListener3".toCharArray()
 * nameStart = 0
 * nameEnd = 21
 * result => true</li>
 * <li> pattern = "HM".toCharArray()
 * patternStart = 0
 * patternEnd = 2
 * name = "HashMapEntry".toCharArray()
 * nameStart = 0
 * nameEnd = 12
 * result => true</li>
 * </ol>
 *
 * @param pattern the given pattern
 * @param patternStart the start index of the pattern, inclusive
 * @param patternEnd the end index of the pattern, exclusive
 * @param name the given name
 * @param nameStart the start index of the name, inclusive
 * @param nameEnd the end index of the name, exclusive
 * @return true if a sub-pattern matches the sub-part of the given name, false otherwise
 * @since 3.2
 */
public static final boolean camelCaseMatch(char[] pattern, int patternStart, int patternEnd, char[] name, int nameStart, int nameEnd) {
	return camelCaseMatch(pattern, patternStart, patternEnd, name, nameStart, nameEnd, false/*not the same count of parts*/);
}

/**
 * Answers true if a sub-pattern matches the sub-part of the given name using
 * CamelCase rules, or false otherwise.  char[] CamelCase matching does NOT
 * accept explicit wild-cards '*' and '?' and is inherently case sensitive.
 * Can match only subset of name/pattern, considering end positions as
 * non-inclusive. The sub-pattern is defined by the patternStart and patternEnd
 * positions.
 * <p>
 * CamelCase denotes the convention of writing compound names without spaces,
 * and capitalizing every term. This function recognizes both upper and lower
 * CamelCase, depending whether the leading character is capitalized or not.
 * The leading part of an upper CamelCase pattern is assumed to contain
 * a sequence of capitals which are appearing in the matching name; e.g. 'NPE' will
 * match 'NullPointerException', but not 'NewPerfData'. A lower CamelCase pattern
 * uses a lowercase first character. In Java, type names follow the upper
 * CamelCase convention, whereas method or field names follow the lower
 * CamelCase convention.
 * <p>
 * The pattern may contain lowercase characters, which will be matched in a case
 * sensitive way. These characters must appear in sequence in the name.
 * For instance, 'NPExcep' will match 'NullPointerException', but not
 * 'NullPointerExCEPTION' or 'NuPoEx' will match 'NullPointerException', but not
 * 'NoPointerException'.
 * <p>
 * Digit characters are treated in a special way. They can be used in the pattern
 * but are not always considered as leading character. For instance, both
 * 'UTF16DSS' and 'UTFDSS' patterns will match 'UTF16DocumentScannerSupport'.
 * <p>
 * CamelCase can be restricted to match only the same count of parts. When this
 * restriction is specified the given pattern and the given name must have <b>exactly</b>
 * the same number of parts (i.e. the same number of uppercase characters).<br>
 * For instance, 'HM' , 'HaMa' and  'HMap' patterns will match 'HashMap' and
 * 'HatMapper' <b>but not</b> 'HashMapEntry'.
 * <p>
 * Examples:
 * <ol>
 * <li> pattern = "NPE".toCharArray()
 * patternStart = 0
 * patternEnd = 3
 * name = "NullPointerException".toCharArray()
 * nameStart = 0
 * nameEnd = 20
 * result => true</li>
 * <li> pattern = "NPE".toCharArray()
 * patternStart = 0
 * patternEnd = 3
 * name = "NoPermissionException".toCharArray()
 * nameStart = 0
 * nameEnd = 21
 * result => true</li>
 * <li> pattern = "NuPoEx".toCharArray()
 * patternStart = 0
 * patternEnd = 6
 * name = "NullPointerException".toCharArray()
 * nameStart = 0
 * nameEnd = 20
 * result => true</li>
 * <li> pattern = "NuPoEx".toCharArray()
 * patternStart = 0
 * patternEnd = 6
 * name = "NoPermissionException".toCharArray()
 * nameStart = 0
 * nameEnd = 21
 * result => false</li>
 * <li> pattern = "npe".toCharArray()
 * patternStart = 0
 * patternEnd = 3
 * name = "NullPointerException".toCharArray()
 * nameStart = 0
 * nameEnd = 20
 * result => false</li>
 * <li> pattern = "IPL3".toCharArray()
 * patternStart = 0
 * patternEnd = 4
 * name = "IPerspectiveListener3".toCharArray()
 * nameStart = 0
 * nameEnd = 21
 * result => true</li>
 * <li> pattern = "HM".toCharArray()
 * patternStart = 0
 * patternEnd = 2
 * name = "HashMapEntry".toCharArray()
 * nameStart = 0
 * nameEnd = 12
 * result => (samePartCount == false)</li>
 * </ol>
 *
 * @param pattern the given pattern
 * @param patternStart the start index of the pattern, inclusive
 * @param patternEnd the end index of the pattern, exclusive
 * @param name the given name
 * @param nameStart the start index of the name, inclusive
 * @param nameEnd the end index of the name, exclusive
 * @param samePartCount flag telling whether the pattern and the name should
 * 	have the same count of parts or not.<br>
 * 	&nbsp;&nbsp;For example:
 * 	<ul>
 * 		<li>'HM' type string pattern will match 'HashMap' and 'HtmlMapper' types,
 * 				but not 'HashMapEntry'</li>
 * 		<li>'HMap' type string pattern will still match previous 'HashMap' and
 * 				'HtmlMapper' types, but not 'HighMagnitude'</li>
 * 	</ul>
 * @return true if a sub-pattern matches the sub-part of the given name, false otherwise
 * @since 3.4
 */
public static final boolean camelCaseMatch(char[] pattern, int patternStart, int patternEnd, char[] name, int nameStart, int nameEnd, boolean samePartCount) {

	/* !!!!!!!!!! WARNING !!!!!!!!!!
	 * The algorithm implemented in this method has been heavily used in
	 * StringOperation#getCamelCaseMatchingRegions(String, int, int, String, int, int, boolean)
	 * method.
	 *
	 * So, if any change needs to be applied in the current algorithm,
	 * do NOT forget to also apply the same change in the StringOperation method!
	 */

	if (name == null)
		return false; // null name cannot match
	if (pattern == null)
		return true; // null pattern is equivalent to '*'
	if (patternEnd < 0) 	patternEnd = pattern.length;
	if (nameEnd < 0) nameEnd = name.length;

	if (patternEnd <= patternStart) return nameEnd <= nameStart;
	if (nameEnd <= nameStart) return false;
	// check first pattern char
	if (name[nameStart] != pattern[patternStart]) {
		// first char must strictly match (upper/lower)
		return false;
	}

	char patternChar, nameChar;
	int iPattern = patternStart;
	int iName = nameStart;

	// Main loop is on pattern characters
	while (true) {

		iPattern++;
		iName++;

		if (iPattern == patternEnd) { // we have exhausted pattern...
			// it's a match if the name can have additional parts (i.e. uppercase characters) or is also exhausted
			if (!samePartCount || iName == nameEnd) return true;

			// otherwise it's a match only if the name has no more uppercase characters
			while (true) {
				if (iName == nameEnd) {
					// we have exhausted the name, so it's a match
					return true;
				}
				nameChar = name[iName];
				// test if the name character is uppercase
				if (nameChar < ScannerHelper.MAX_OBVIOUS) {
					if ((ScannerHelper.OBVIOUS_IDENT_CHAR_NATURES[nameChar] & ScannerHelper.C_UPPER_LETTER) != 0) {
						return false;
					}
				}
				else if (!Character.isJavaIdentifierPart(nameChar) || Character.isUpperCase(nameChar)) {
					return false;
				}
				iName++;
			}
		}

		if (iName == nameEnd){
			// We have exhausted the name (and not the pattern), so it's not a match
			return false;
		}

		// For as long as we're exactly matching, bring it on (even if it's a lower case character)
		if ((patternChar = pattern[iPattern]) == name[iName]) {
			continue;
		}

		// If characters are not equals, then it's not a match if patternChar is lowercase
		if (patternChar < ScannerHelper.MAX_OBVIOUS) {
			if ((ScannerHelper.OBVIOUS_IDENT_CHAR_NATURES[patternChar] & (ScannerHelper.C_UPPER_LETTER | ScannerHelper.C_DIGIT)) == 0) {
				return false;
			}
		}
		else if (Character.isJavaIdentifierPart(patternChar) && !Character.isUpperCase(patternChar) && !Character.isDigit(patternChar)) {
			return false;
		}

		// patternChar is uppercase, so let's find the next uppercase in name
		while (true) {
			if (iName == nameEnd){
	            //	We have exhausted name (and not pattern), so it's not a match
				return false;
			}

			nameChar = name[iName];
			if (nameChar < ScannerHelper.MAX_OBVIOUS) {
				int charNature = ScannerHelper.OBVIOUS_IDENT_CHAR_NATURES[nameChar];
				if ((charNature & (ScannerHelper.C_LOWER_LETTER | ScannerHelper.C_SPECIAL)) != 0) {
					// nameChar is lowercase
					iName++;
				} else if ((charNature & ScannerHelper.C_DIGIT) != 0) {
					// nameChar is digit => break if the digit is current pattern character otherwise consume it
					if (patternChar == nameChar) break;
					iName++;
				// nameChar is uppercase...
				} else  if (patternChar != nameChar) {
					//.. and it does not match patternChar, so it's not a match
					return false;
				} else {
					//.. and it matched patternChar. Back to the big loop
					break;
				}
			}
			// Same tests for non-obvious characters
			else if (Character.isJavaIdentifierPart(nameChar) && !Character.isUpperCase(nameChar)) {
				iName++;
			} else if (Character.isDigit(nameChar)) {
				if (patternChar == nameChar) break;
				iName++;
			} else  if (patternChar != nameChar) {
				return false;
			} else {
				break;
			}
		}
		// At this point, either name has been exhausted, or it is at an uppercase letter.
		// Since pattern is also at an uppercase letter
	}
}

/**
 * Answers true if the characters of the pattern are contained in the
 * name as a subword, in a case-insensitive way.
 *
 * @param pattern the given pattern
 * @param name the given name
 * @return true if the pattern matches the given name, false otherwise
 * @since 3.21
 */
public static final boolean subWordMatch(char[] pattern, char[] name) {
	if (name == null)
		return false; // null name cannot match
	if (pattern == null)
		return true; // null pattern is equivalent to '*'

	int[] matchingRegions = getSubWordMatchingRegions(new String(pattern), new String(name));
	return matchingRegions != null;
}

/**
 * Answers all the regions in a given name matching a subword pattern.
 * <p>
 * Each of these regions is made of its starting index and its length in the given
 * name. They are all concatenated in a single array of <code>int</code>
 * which therefore always has an even length.
 * <p>
 * Note that each region is disjointed from the following one.<br>
 * E.g. if the regions are <code>{ start1, length1, start2, length2 }</code>,
 * then <code>start1+length1</code> will always be smaller than
 * <code>start2</code>.
 * <p>
 * Examples:
 * <ol>
 * <li><pre>
 *    pattern = "linkedmap"
 *    name = LinkedHashMap
 *    result:  { 0, 6, 10, 3 }
 * </pre></li>
 * </ol>
 *
 * @see CharOperation#subWordMatch(char[], char[])
 * 	for more details on the subword behavior
 *
 * @param pattern the given pattern
 * @param name the given name
 * @return an array of <code>int</code> having two slots per returned
 * 	regions (first one is the starting index of the region and the second
 * 	one the length of the region).<br>
 * 	Note that it may be <code>null</code> if the given name does not match
 * 	the pattern
 * @since 3.21
 */
public static final int[] getSubWordMatchingRegions(String pattern, String name) {

	if (name == null)
		return null; // null name cannot match
	if (pattern == null) {
		// null pattern cannot match any region
		// see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=264816
		return EMPTY_REGIONS;
	}

	return new SubwordMatcher(name).getMatchingRegions(pattern);
}

/**
 * Answers true if the characters of the pattern are contained in the
 * name as a substring, in a case-insensitive way.
 *
 * @param pattern the given pattern
 * @param name the given name
 * @return true if the pattern matches the given name, false otherwise
 * @since 3.12
 */
public static final boolean substringMatch(String pattern, String name) {
	if (pattern == null || pattern.length() == 0) {
		return true;
	}
	if (name == null) {
		return false;
	}
	return checkSubstringMatch(pattern.toCharArray(), name.toCharArray());
}

/**
 * Answers true if the characters of the pattern are contained in the
 * name as a substring, in a case-insensitive way.
 *
 * @param pattern the given pattern
 * @param name the given name
 * @return true if the pattern matches the given name, false otherwise
 * @since 3.12
 */
public static final boolean substringMatch(char[] pattern, char[] name) {
	if (pattern == null || pattern.length == 0) {
		return true;
	}
	if (name == null) {
		return false;
	}
	return checkSubstringMatch(pattern, name);
}

/**
 * Internal substring matching method; called after the null and length
 * checks are performed.
 *
 * @param pattern the given pattern
 * @param name the given name
 * @return true if the pattern matches the given name, false otherwise
 *
 * @see CharOperation#substringMatch(char[], char[])
 */
private static final boolean checkSubstringMatch(char[] pattern, char[] name) {

/* XXX: to be revised/enabled

	// allow non-consecutive occurrence of pattern characters
	if (pattern.length >= 3) {
		int pidx = 0;

		for (int nidx = 0; nidx < name.length; nidx++) {
			if (Character.toLowerCase(name[nidx]) ==
					Character.toLowerCase(pattern[pidx]))
				pidx++;
			if (pidx == pattern.length)
				return true;
		}

	// for short patterns only allow consecutive occurrence
	} else {
*/
		// outer loop iterates on the characters of the name; trying to
		// match at any possible position
		outer: for (int nidx = 0; nidx < name.length - pattern.length + 1; nidx++) {
			// inner loop iterates on pattern characters
			for (int pidx = 0; pidx < pattern.length; pidx++) {
				if (Character.toLowerCase(name[nidx + pidx]) !=
						Character.toLowerCase(pattern[pidx])) {
					// no match until parameter list; do not match parameter list
					if ((name[nidx + pidx] == '(') || (name[nidx + pidx] == ':'))
						return false;
					continue outer;
				}
				if (pidx == pattern.length - 1)
					return true;
			}
		}
	// XXX: }

	return false;
}

/**
 * Returns the char arrays as an array of Strings
 *
 * @param charArrays the char array to convert
 * @return the char arrays as an array of Strings or null if the given char arrays is null.
 * @since 3.0
 */
public static String[] charArrayToStringArray(char[][] charArrays) {
	if (charArrays == null)
		return null;
	int length = charArrays.length;
	if (length == 0)
		return NO_STRINGS;
	String[] strings= new String[length];
	for (int i= 0; i < length; i++)
		strings[i]= new String(charArrays[i]);
	return strings;
}

/**
 * Returns the char array as a String

 * @param charArray the char array to convert
 * @return the char array as a String or null if the given char array is null.
 * @since 3.0
 */
public static String charToString(char[] charArray) {
	if (charArray == null) return null;
	return new String(charArray);
}

/**
 * Converts the given list of strings to an array of equal size,
 * containing the individual strings converted to char[] each.
 *
 * @return an array of char[], representing the elements in the input list, or {@code null} if the list was {@code null}.
 * @since 3.14
 */
public static char[][] toCharArrays(List<String> stringList) {
	if (stringList == null)
		return null;
	char[][] result = new char[stringList.size()][];
	for (int i = 0; i < result.length; i++)
		result[i] = stringList.get(i).toCharArray();
	return result;
}
/**
 * Answers a new array adding the second array at the end of first array.
 * It answers null if the first and second are null.
 * If the first array is null, then a new array char[][] is created with second.
 * If the second array is null, then the first array is returned.
 * <br>
 * <br>
 * For example:
 * <ol>
 * <li><pre>
 *    first = null
 *    second = { 'a' }
 *    => result = { { ' a' } }
 * </pre>
 * <li><pre>
 *    first = { { ' a' } }
 *    second = null
 *    => result = { { ' a' } }
 * </pre>
 * </li>
 * <li><pre>
 *    first = { { ' a' } }
 *    second = { ' b' }
 *    => result = { { ' a' } , { ' b' } }
 * </pre>
 * </li>
 * </ol>
 *
 * @param first the first array to concatenate
 * @param second the array to add at the end of the first array
 * @return a new array adding the second array at the end of first array, or null if the two arrays are null.
 */
public static final char[][] arrayConcat(char[][] first, char[] second) {
	if (second == null)
		return first;
	if (first == null)
		return new char[][] { second };

	int length = first.length;
	char[][] result = new char[length + 1][];
	System.arraycopy(first, 0, result, 0, length);
	result[length] = second;
	return result;
}
/**
 * Compares the two char arrays lexicographically.
 *
 * Returns a negative integer if array1 lexicographically precedes the array2,
 * a positive integer if this array1 lexicographically follows the array2, or
 * zero if both arrays are equal.
 *
 * @param array1 the first given array
 * @param array2 the second given array
 * @return the returned value of the comparison between array1 and array2
 * @throws NullPointerException if one of the arrays is null
 * @since 3.3
 */
public static final int compareTo(char[] array1, char[] array2) {
	int length1 = array1.length;
	int length2 = array2.length;
	int min = Math.min(length1, length2);
	for (int i = 0; i < min; i++) {
		if (array1[i] != array2[i]) {
			return array1[i] - array2[i];
		}
	}
	return length1 - length2;
}
/**
 * Compares the two char arrays lexicographically between the given start and end positions.
 *
 * Returns a negative integer if array1 lexicographically precedes the array2,
 * a positive integer if this array1 lexicographically follows the array2, or
 * zero if both arrays are equal.
 * <p>The comparison is done between start and end positions.</p>
 *
 * @param array1 the first given array
 * @param array2 the second given array
 * @param start the starting position to compare (inclusive)
 * @param end the ending position to compare (exclusive)
 *
 * @return the returned value of the comparison between array1 and array2
 * @throws NullPointerException if one of the arrays is null
 * @since 3.7.1
 */
public static final int compareTo(char[] array1, char[] array2, int start, int end) {
	int length1 = array1.length;
	int length2 = array2.length;
	int min = Math.min(length1, length2);
	min = Math.min(min, end);
	for (int i = start; i < min; i++) {
		if (array1[i] != array2[i]) {
			return array1[i] - array2[i];
		}
	}
	return length1 - length2;
}
/**
 * Compares the contents of the two arrays array and prefix. Returns
 * <ul>
 * <li>zero if the array starts with the prefix contents</li>
 * <li>the difference between the first two characters that are not equal </li>
 * <li>one if array length is lower than the prefix length and that the prefix starts with the
 * array contents.</li>
 * </ul>
 * <p>
 * For example:
 * <ol>
 * <li><pre>
 *    array = null
 *    prefix = null
 *    => result = NullPointerException
 * </pre>
 * </li>
 * <li><pre>
 *    array = { 'a', 'b', 'c', 'd', 'e' }
 *    prefix = { 'a', 'b', 'c'}
 *    => result = 0
 * </pre>
 * </li>
 * <li><pre>
 *    array = { 'a', 'b', 'c', 'd', 'e' }
 *    prefix = { 'a', 'B', 'c'}
 *    => result = 32
 * </pre>
 * </li>
 * <li><pre>
 *    array = { 'd', 'b', 'c', 'd', 'e' }
 *    prefix = { 'a', 'b', 'c'}
 *    => result = 3
 * </pre>
 * </li>
 * <li><pre>
 *    array = { 'a', 'b', 'c', 'd', 'e' }
 *    prefix = { 'd', 'b', 'c'}
 *    => result = -3
 * </pre>
 * </li>
 * <li><pre>
 *    array = { 'a', 'a', 'c', 'd', 'e' }
 *    prefix = { 'a', 'e', 'c'}
 *    => result = -4
 * </pre>
 * </li>
 * </ol>
 *
 * @param array the given array
 * @param prefix the given prefix
 * @return the result of the comparison (>=0 if array>prefix)
 * @throws NullPointerException if either array or prefix is null
 */
public static final int compareWith(char[] array, char[] prefix) {
	int arrayLength = array.length;
	int prefixLength = prefix.length;
	int min = Math.min(arrayLength, prefixLength);
	int i = 0;
	while (min-- != 0) {
		char c1 = array[i];
		char c2 = prefix[i++];
		if (c1 != c2)
			return c1 - c2;
	}
	if (prefixLength == i)
		return 0;
	return -1;	// array is shorter than prefix (e.g. array:'ab' < prefix:'abc').
}

/**
 * Answers the concatenation of the two arrays. It answers null if the two arrays are null.
 * If the first array is null, then the second array is returned.
 * If the second array is null, then the first array is returned.
 * <br>
 * <br>
 * For example:
 * <ol>
 * <li><pre>
 *    first = null
 *    second = { 'a' }
 *    => result = { ' a' }
 * </pre>
 * </li>
 * <li><pre>
 *    first = { ' a' }
 *    second = null
 *    => result = { ' a' }
 * </pre>
 * </li>
 * <li><pre>
 *    first = { ' a' }
 *    second = { ' b' }
 *    => result = { ' a' , ' b' }
 * </pre>
 * </li>
 * </ol>
 *
 * @param first the first array to concatenate
 * @param second the second array to concatenate
 * @return the concatenation of the two arrays, or null if the two arrays are null.
 */
public static final char[] concat(char[] first, char[] second) {
	if (first == null)
		return second;
	if (second == null)
		return first;

	int length1 = first.length;
	int length2 = second.length;
	char[] result = new char[length1 + length2];
	System.arraycopy(first, 0, result, 0, length1);
	System.arraycopy(second, 0, result, length1, length2);
	return result;
}

/**
 * Answers the concatenation of the three arrays. It answers null if the three arrays are null.
 * If first is null, it answers the concatenation of second and third.
 * If second is null, it answers the concatenation of first and third.
 * If third is null, it answers the concatenation of first and second.
 * <br>
 * <br>
 * For example:
 * <ol>
 * <li><pre>
 *    first = null
 *    second = { 'a' }
 *    third = { 'b' }
 *    => result = { ' a', 'b' }
 * </pre>
 * </li>
 * <li><pre>
 *    first = { 'a' }
 *    second = null
 *    third = { 'b' }
 *    => result = { ' a', 'b' }
 * </pre>
 * </li>
 * <li><pre>
 *    first = { 'a' }
 *    second = { 'b' }
 *    third = null
 *    => result = { ' a', 'b' }
 * </pre>
 * </li>
 * <li><pre>
 *    first = null
 *    second = null
 *    third = null
 *    => result = null
 * </pre>
 * </li>
 * <li><pre>
 *    first = { 'a' }
 *    second = { 'b' }
 *    third = { 'c' }
 *    => result = { 'a', 'b', 'c' }
 * </pre>
 * </li>
 * </ol>
 *
 * @param first the first array to concatenate
 * @param second the second array to concatenate
 * @param third the third array to concatenate
 *
 * @return the concatenation of the three arrays, or null if the three arrays are null.
 */
public static final char[] concat(
	char[] first,
	char[] second,
	char[] third) {
	if (first == null)
		return concat(second, third);
	if (second == null)
		return concat(first, third);
	if (third == null)
		return concat(first, second);

	int length1 = first.length;
	int length2 = second.length;
	int length3 = third.length;
	char[] result = new char[length1 + length2 + length3];
	System.arraycopy(first, 0, result, 0, length1);
	System.arraycopy(second, 0, result, length1, length2);
	System.arraycopy(third, 0, result, length1 + length2, length3);
	return result;
}

/**
 * Answers the concatenation of the two arrays inserting the separator character between the two arrays.
 * It answers null if the two arrays are null.
 * If the first array is null, then the second array is returned.
 * If the second array is null, then the first array is returned.
 * <br>
 * <br>
 * For example:
 * <ol>
 * <li><pre>
 *    first = null
 *    second = { 'a' }
 *    separator = '/'
 *    => result = { ' a' }
 * </pre>
 * </li>
 * <li><pre>
 *    first = { ' a' }
 *    second = null
 *    separator = '/'
 *    => result = { ' a' }
 * </pre>
 * </li>
 * <li><pre>
 *    first = { ' a' }
 *    second = { ' b' }
 *    separator = '/'
 *    => result = { ' a' , '/', 'b' }
 * </pre>
 * </li>
 * </ol>
 *
 * @param first the first array to concatenate
 * @param second the second array to concatenate
 * @param separator the character to insert
 * @return the concatenation of the two arrays inserting the separator character
 * between the two arrays , or null if the two arrays are null.
 */
public static final char[] concat(
	char[] first,
	char[] second,
	char separator) {
	if (first == null)
		return second;
	if (second == null)
		return first;

	int length1 = first.length;
	if (length1 == 0)
		return second;
	int length2 = second.length;
	if (length2 == 0)
		return first;

	char[] result = new char[length1 + length2 + 1];
	System.arraycopy(first, 0, result, 0, length1);
	result[length1] = separator;
	System.arraycopy(second, 0, result, length1 + 1, length2);
	return result;
}

/**
 * Answers the concatenation of the two arrays inserting the separator character between the two arrays. Differs from
 * {@link CharOperation#contains(char, char[])} in case second array is a zero length array.
 * It answers null if the two arrays are null.
 * If the first array is null, then the second array is returned.
 * If the second array is null, then the first array is returned.
 * if the second array is zero length array, the separator is appended.
 * <br>
 * <br>
 * For example:
 * <ol>
 * <li><pre>
 *    first = null
 *    second = { 'a' }
 *    separator = '/'
 *    => result = { ' a' }
 * </pre>
 * </li>
 * <li><pre>
 *    first = { ' a' }
 *    second = null
 *    separator = '/'
 *    => result = { ' a' }
 * </pre>
 * </li>
 * <li><pre>
 *    first = { ' a' }
 *    second = { ' b' }
 *    separator = '/'
 *    => result = { ' a' , '/', 'b' }
 * </pre>
 * </li>
 * <li><pre>
 *    first = { ' a' }
 *    second = { '' }
 *    separator = '.'
 *    => result = { ' a' , '.', }
 * </pre>
 * </li>
 * </ol>
 *
 * @param first the first array to concatenate
 * @param second the second array to concatenate
 * @param separator the character to insert
 * @return the concatenation of the two arrays inserting the separator character
 * between the two arrays , or null if the two arrays are null. If second array
 * is of zero length, the separator is appended to the first array and returned.
 * @since 3.14
 */
public static final char[] concatAll(
	char[] first,
	char[] second,
	char separator) {
	if (first == null)
		return second;
	if (second == null)
		return first;

	int length1 = first.length;
	if (length1 == 0)
		return second;
	int length2 = second.length;

	char[] result = new char[length1 + length2 + 1];
	System.arraycopy(first, 0, result, 0, length1);
	result[length1] = separator;
	if (length2 > 0)
		System.arraycopy(second, 0, result, length1 + 1, length2);
	return result;
}

/**
 * Answers the concatenation of the three arrays inserting the sep1 character between the
 * first two arrays and sep2 between the last two.
 * It answers null if the three arrays are null.
 * If the first array is null, then it answers the concatenation of second and third inserting
 * the sep2 character between them.
 * If the second array is null, then it answers the concatenation of first and third inserting
 * the sep1 character between them.
 * If the third array is null, then it answers the concatenation of first and second inserting
 * the sep1 character between them.
 * <br>
 * <br>
 * For example:
 * <ol>
 * <li><pre>
 *    first = null
 *    sep1 = '/'
 *    second = { 'a' }
 *    sep2 = ':'
 *    third = { 'b' }
 *    => result = { ' a' , ':', 'b' }
 * </pre>
 * </li>
 * <li><pre>
 *    first = { 'a' }
 *    sep1 = '/'
 *    second = null
 *    sep2 = ':'
 *    third = { 'b' }
 *    => result = { ' a' , '/', 'b' }
 * </pre>
 * </li>
 * <li><pre>
 *    first = { 'a' }
 *    sep1 = '/'
 *    second = { 'b' }
 *    sep2 = ':'
 *    third = null
 *    => result = { ' a' , '/', 'b' }
 * </pre>
 * </li>
 * <li><pre>
 *    first = { 'a' }
 *    sep1 = '/'
 *    second = { 'b' }
 *    sep2 = ':'
 *    third = { 'c' }
 *    => result = { ' a' , '/', 'b' , ':', 'c' }
 * </pre>
 * </li>
 * </ol>
 *
 * @param first the first array to concatenate
 * @param sep1 the character to insert
 * @param second the second array to concatenate
 * @param sep2 the character to insert
 * @param third the second array to concatenate
 * @return the concatenation of the three arrays inserting the sep1 character between the
 * two arrays and sep2 between the last two.
 */
public static final char[] concat(
	char[] first,
	char sep1,
	char[] second,
	char sep2,
	char[] third) {
	if (first == null)
		return concat(second, third, sep2);
	if (second == null)
		return concat(first, third, sep1);
	if (third == null)
		return concat(first, second, sep1);

	int length1 = first.length;
	int length2 = second.length;
	int length3 = third.length;
	char[] result = new char[length1 + length2 + length3 + 2];
	System.arraycopy(first, 0, result, 0, length1);
	result[length1] = sep1;
	System.arraycopy(second, 0, result, length1 + 1, length2);
	result[length1 + length2 + 1] = sep2;
	System.arraycopy(third, 0, result, length1 + length2 + 2, length3);
	return result;
}
/**
 * Answers the concatenation of the two arrays inserting the separator character between the two arrays.
 * It answers null if the two arrays are null.
 * If the first array is null or is empty, then the second array is returned.
 * If the second array is null or is empty, then the first array is returned.
 * <br>
 * <br>
 * For example:
 * <ol>
 * <li><pre>
 *    first = null
 *    second = { 'a' }
 *    separator = '/'
 *    => result = { ' a' }
 * </pre>
 * </li>
 * <li><pre>
 *    first = { ' a' }
 *    second = null
 *    separator = '/'
 *    => result = { ' a' }
 * </pre>
 * </li>
 * <li><pre>
 *    first = { ' a' }
 *    second = { ' b' }
 *    separator = '/'
 *    => result = { ' a' , '/', 'b' }
 * </pre>
 * </li>
 * <li><pre>
 *    first = { ' a' }
 *    second = {  }
 *    separator = '/'
 *    => result = { ' a'}
 * </pre>
 * </li>

 * </ol>
 *
 * @param first the first array to concatenate
 * @param second the second array to concatenate
 * @param separator the character to insert
 * @return the concatenation of the two arrays inserting the separator character
 * between the two arrays , or null if the two arrays are null.
 * @since 3.12
 */
public static final char[] concatNonEmpty(
	char[] first,
	char[] second,
	char separator) {
	if (first == null || first.length == 0)
		return second;
	if (second == null || second.length == 0)
		return first;
	return concat(first, second, separator);
}
/**
 * Answers the concatenation of the three arrays inserting the sep1 character between the
 * first two arrays and sep2 between the last two.
 * It answers null if the three arrays are null.
 * If the first array is null or empty, then it answers the concatenation of second and third inserting
 * the sep2 character between them.
 * If the second array is null or empty, then it answers the concatenation of first and third inserting
 * the sep1 character between them.
 * If the third array is null or empty, then it answers the concatenation of first and second inserting
 * the sep1 character between them.
 * <br>
 * <br>
 * For example:
 * <ol>
 * <li><pre>
 *    first = null
 *    sep1 = '/'
 *    second = { 'a' }
 *    sep2 = ':'
 *    third = { 'b' }
 *    => result = { ' a' , ':', 'b' }
 * </pre>
 * </li>
 * <li><pre>
 *    first = { 'a' }
 *    sep1 = '/'
 *    second = null
 *    sep2 = ':'
 *    third = { 'b' }
 *    => result = { ' a' , '/', 'b' }
 * </pre>
 * </li>
 * <li><pre>
 *    first = { 'a' }
 *    sep1 = '/'
 *    second = { 'b' }
 *    sep2 = ':'
 *    third = null
 *    => result = { ' a' , '/', 'b' }
 * </pre>
 * </li>
 * <li><pre>
 *    first = { 'a' }
 *    sep1 = '/'
 *    second = { 'b' }
 *    sep2 = ':'
 *    third = { 'c' }
 *    => result = { ' a' , '/', 'b' , ':', 'c' }
 * </pre>
 * </li>
 * <li><pre>
 *    first = { 'a' }
 *    sep1 = '/'
 *    second = { }
 *    sep2 = ':'
 *    third = { 'c' }
 *    => result = { ' a', ':', 'c' }
 * </pre>
 * </li>
 * </ol>
 *
 * @param first the first array to concatenate
 * @param sep1 the character to insert
 * @param second the second array to concatenate
 * @param sep2 the character to insert
 * @param third the second array to concatenate
 * @return the concatenation of the three arrays inserting the sep1 character between the
 * two arrays and sep2 between the last two.
 * @since 3.12
 */
public static final char[] concatNonEmpty(
	char[] first,
	char sep1,
	char[] second,
	char sep2,
	char[] third) {
	if (first == null || first.length  == 0)
		return concatNonEmpty(second, third, sep2);
	if (second == null || second.length == 0)
		return concatNonEmpty(first, third, sep1);
	if (third == null || third.length == 0)
		return concatNonEmpty(first, second, sep1);

	return concat(first, sep1, second, sep2, third);
}

/**
 * Answers a new array with prepending the prefix character and appending the suffix
 * character at the end of the array. If array is null, it answers a new array containing the
 * prefix and the suffix characters.
 * <br>
 * <br>
 * For example:<br>
 * <ol>
 * <li><pre>
 *    prefix = 'a'
 *    array = { 'b' }
 *    suffix = 'c'
 *    => result = { 'a', 'b' , 'c' }
 * </pre>
 * </li>
 * <li><pre>
 *    prefix = 'a'
 *    array = null
 *    suffix = 'c'
 *    => result = { 'a', 'c' }
 * </pre></li>
 * </ol>
 *
 * @param prefix the prefix character
 * @param array the array that is concatenated with the prefix and suffix characters
 * @param suffix the suffix character
 * @return the new array
 */
public static final char[] concat(char prefix, char[] array, char suffix) {
	if (array == null)
		return new char[] { prefix, suffix };

	int length = array.length;
	char[] result = new char[length + 2];
	result[0] = prefix;
	System.arraycopy(array, 0, result, 1, length);
	result[length + 1] = suffix;
	return result;
}

/**
 * Answers the concatenation of the given array parts using the given separator between each
 * part and prepending the given name at the beginning.
 * <br>
 * <br>
 * For example:<br>
 * <ol>
 * <li><pre>
 *    name = { 'c' }
 *    array = { { 'a' }, { 'b' } }
 *    separator = '.'
 *    => result = { 'a', '.', 'b' , '.', 'c' }
 * </pre>
 * </li>
 * <li><pre>
 *    name = null
 *    array = { { 'a' }, { 'b' } }
 *    separator = '.'
 *    => result = { 'a', '.', 'b' }
 * </pre></li>
 * <li><pre>
 *    name = { ' c' }
 *    array = null
 *    separator = '.'
 *    => result = { 'c' }
 * </pre></li>
 * </ol>
 *
 * @param name the given name
 * @param array the given array
 * @param separator the given separator
 * @return the concatenation of the given array parts using the given separator between each
 * part and prepending the given name at the beginning
 */
public static final char[] concatWith(
	char[] name,
	char[][] array,
	char separator) {
	int nameLength = name == null ? 0 : name.length;
	if (nameLength == 0)
		return concatWith(array, separator);

	int length = array == null ? 0 : array.length;
	if (length == 0)
		return name;

	int size = nameLength;
	int index = length;
	while (--index >= 0)
		if (array[index].length > 0)
			size += array[index].length + 1;
	char[] result = new char[size];
	index = size;
	for (int i = length - 1; i >= 0; i--) {
		int subLength = array[i].length;
		if (subLength > 0) {
			index -= subLength;
			System.arraycopy(array[i], 0, result, index, subLength);
			result[--index] = separator;
		}
	}
	System.arraycopy(name, 0, result, 0, nameLength);
	return result;
}

/**
 * Answers the concatenation of the given array parts using the given separator between each
 * part and appending the given name at the end.
 * <br>
 * <br>
 * For example:<br>
 * <ol>
 * <li><pre>
 *    name = { 'c' }
 *    array = { { 'a' }, { 'b' } }
 *    separator = '.'
 *    => result = { 'a', '.', 'b' , '.', 'c' }
 * </pre>
 * </li>
 * <li><pre>
 *    name = null
 *    array = { { 'a' }, { 'b' } }
 *    separator = '.'
 *    => result = { 'a', '.', 'b' }
 * </pre></li>
 * <li><pre>
 *    name = { ' c' }
 *    array = null
 *    separator = '.'
 *    => result = { 'c' }
 * </pre></li>
 * </ol>
 *
 * @param array the given array
 * @param name the given name
 * @param separator the given separator
 * @return the concatenation of the given array parts using the given separator between each
 * part and appending the given name at the end
 */
public static final char[] concatWith(
	char[][] array,
	char[] name,
	char separator) {
	int nameLength = name == null ? 0 : name.length;
	if (nameLength == 0)
		return concatWith(array, separator);

	int length = array == null ? 0 : array.length;
	if (length == 0)
		return name;

	int size = nameLength;
	int index = length;
	while (--index >= 0)
		if (array[index].length > 0)
			size += array[index].length + 1;
	char[] result = new char[size];
	index = 0;
	for (int i = 0; i < length; i++) {
		int subLength = array[i].length;
		if (subLength > 0) {
			System.arraycopy(array[i], 0, result, index, subLength);
			index += subLength;
			result[index++] = separator;
		}
	}
	System.arraycopy(name, 0, result, index, nameLength);
	return result;
}

/**
 * Answers the concatenation of the given array parts using the given separator between each part.
 * <br>
 * <br>
 * For example:<br>
 * <ol>
 * <li><pre>
 *    array = { { 'a' }, { 'b' } }
 *    separator = '.'
 *    => result = { 'a', '.', 'b' }
 * </pre>
 * </li>
 * <li><pre>
 *    array = null
 *    separator = '.'
 *    => result = { }
 * </pre></li>
 * </ol>
 *
 * @param array the given array
 * @param separator the given separator
 * @return the concatenation of the given array parts using the given separator between each part
 */
public static final char[] concatWith(char[][] array, char separator) {
	int length = array == null ? 0 : array.length;
	if (length == 0)
		return CharOperation.NO_CHAR;

	int size = length - 1;
	int index = length;
	while (--index >= 0) {
		if (array[index].length == 0)
			size--;
		else
			size += array[index].length;
	}
	if (size <= 0)
		return CharOperation.NO_CHAR;
	char[] result = new char[size];
	index = length;
	while (--index >= 0) {
		length = array[index].length;
		if (length > 0) {
			System.arraycopy(
				array[index],
				0,
				result,
				(size -= length),
				length);
			if (--size >= 0)
				result[size] = separator;
		}
	}
	return result;
}

/**
 * Answers the concatenation of the given array parts using the given separator between each part
 * irrespective of whether an element is a zero length array or not.
 * <br>
 * <br>
 * For example:<br>
 * <ol>
 * <li><pre>
 *    array = { { 'a' }, {}, { 'b' } }
 *    separator = ''
 *    => result = { 'a', '/', '/', 'b' }
 * </pre>
 * </li>
 * <li><pre>
 *    array = { { 'a' }, { 'b' } }
 *    separator = '.'
 *    => result = { 'a', '.', 'b' }
 * </pre>
 * </li>
 * <li><pre>
 *    array = null
 *    separator = '.'
 *    => result = { }
 * </pre></li>
 * </ol>
 *
 * @param array the given array
 * @param separator the given separator
 * @return the concatenation of the given array parts using the given separator between each part
 * @since 3.12
 */
public static final char[] concatWithAll(char[][] array, char separator) {
	int length = array == null ? 0 : array.length;
	if (length == 0)
		return CharOperation.NO_CHAR;

	int size = length - 1;
	int index = length;
	while (--index >= 0) {
		size += array[index].length;
	}
	char[] result = new char[size];
	index = length;
	while (--index >= 0) {
		length = array[index].length;
		if (length > 0) {
			System.arraycopy(
				array[index],
				0,
				result,
				(size -= length),
				length);
		}
		if (--size >= 0)
			result[size] = separator;
	}
	return result;
}

/**
 * Answers true if the array contains an occurrence of character, false otherwise.
 *
 * <br>
 * <br>
 * For example:
 * <ol>
 * <li><pre>
 *    character = 'c'
 *    array = { { ' a' }, { ' b' } }
 *    result => false
 * </pre>
 * </li>
 * <li><pre>
 *    character = 'a'
 *    array = { { ' a' }, { ' b' } }
 *    result => true
 * </pre>
 * </li>
 * </ol>
 *
 * @param character the character to search
 * @param array the array in which the search is done
 * @return true if the array contains an occurrence of character, false otherwise.
 * @throws NullPointerException if array is null.
 */
public static final boolean contains(char character, char[][] array) {
	for (int i = array.length; --i >= 0;) {
		char[] subarray = array[i];
		for (int j = subarray.length; --j >= 0;)
			if (subarray[j] == character)
				return true;
	}
	return false;
}

/**
 * Answers true if the array contains an occurrence of character, false otherwise.
 *
 * <br>
 * <br>
 * For example:
 * <ol>
 * <li><pre>
 *    character = 'c'
 *    array = { ' b'  }
 *    result => false
 * </pre>
 * </li>
 * <li><pre>
 *    character = 'a'
 *    array = { ' a' , ' b' }
 *    result => true
 * </pre>
 * </li>
 * </ol>
 *
 * @param character the character to search
 * @param array the array in which the search is done
 * @return true if the array contains an occurrence of character, false otherwise.
 * @throws NullPointerException if array is null.
 */
public static final boolean contains(char character, char[] array) {
	for (int i = array.length; --i >= 0;)
		if (array[i] == character)
			return true;
	return false;
}

/**
 * Answers true if the array contains an occurrence of one of the characters, false otherwise.
 *
 * <br>
 * <br>
 * For example:
 * <ol>
 * <li><pre>
 *    characters = { 'c', 'd' }
 *    array = { 'a', ' b'  }
 *    result => false
 * </pre>
 * </li>
 * <li><pre>
 *    characters = { 'c', 'd' }
 *    array = { 'a', ' b', 'c'  }
 *    result => true
 * </pre>
 * </li>
 * </ol>
 *
 * @param characters the characters to search
 * @param array the array in which the search is done
 * @return true if the array contains an occurrence of one of the characters, false otherwise.
 * @throws NullPointerException if array is null.
 * @since 3.1
 */
public static final boolean contains(char[] characters, char[] array) {
	for (int i = array.length; --i >= 0;)
		for (int j = characters.length; --j >= 0;)
			if (array[i] == characters[j])
				return true;
	return false;
}

/**
 * Does the given array contain a char sequence that is equal to the give sequence?
 * @return true if sequence is equal to an element in array
 * @since 3.14
 */
public static boolean containsEqual(char[][] array, char[] sequence) {
	for (int i = 0; i < array.length; i++) {
		if (equals(array[i], sequence))
			return true;
	}
	return false;
}

/**
 * Answers a deep copy of the toCopy array.
 *
 * @param toCopy the array to copy
 * @return a deep copy of the toCopy array.
 */

public static final char[][] deepCopy(char[][] toCopy) {
	int toCopyLength = toCopy.length;
	char[][] result = new char[toCopyLength][];
	for (int i = 0; i < toCopyLength; i++) {
		char[] toElement = toCopy[i];
		int toElementLength = toElement.length;
		char[] resultElement = new char[toElementLength];
		System.arraycopy(toElement, 0, resultElement, 0, toElementLength);
		result[i] = resultElement;
	}
	return result;
}

/**
 * Return true if array ends with the sequence of characters contained in toBeFound,
 * otherwise false.
 * <br>
 * <br>
 * For example:
 * <ol>
 * <li><pre>
 *    array = { 'a', 'b', 'c', 'd' }
 *    toBeFound = { 'b', 'c' }
 *    result => false
 * </pre>
 * </li>
 * <li><pre>
 *    array = { 'a', 'b', 'c' }
 *    toBeFound = { 'b', 'c' }
 *    result => true
 * </pre>
 * </li>
 * </ol>
 *
 * @param array the array to check
 * @param toBeFound the array to find
 * @return true if array ends with the sequence of characters contained in toBeFound,
 * otherwise false.
 * @throws NullPointerException if array is null or toBeFound is null
 */
public static final boolean endsWith(char[] array, char[] toBeFound) {
	int i = toBeFound.length;
	int j = array.length - i;

	if (j < 0)
		return false;
	while (--i >= 0)
		if (toBeFound[i] != array[i + j])
			return false;
	return true;
}

/**
 * Answers true if the two arrays are identical character by character, otherwise false.
 * The equality is case sensitive.
 * <br>
 * <br>
 * For example:
 * <ol>
 * <li><pre>
 *    first = null
 *    second = null
 *    result => true
 * </pre>
 * </li>
 * <li><pre>
 *    first = { { } }
 *    second = null
 *    result => false
 * </pre>
 * </li>
 * <li><pre>
 *    first = { { 'a' } }
 *    second = { { 'a' } }
 *    result => true
 * </pre>
 * </li>
 * <li><pre>
 *    first = { { 'A' } }
 *    second = { { 'a' } }
 *    result => false
 * </pre>
 * </li>
 * </ol>
 * @param first the first array
 * @param second the second array
 * @return true if the two arrays are identical character by character, otherwise false
 */
public static final boolean equals(char[][] first, char[][] second) {
	if (first == second)
		return true;
	if (first == null || second == null)
		return false;
	if (first.length != second.length)
		return false;

	for (int i = first.length; --i >= 0;)
		if (!equals(first[i], second[i]))
			return false;
	return true;
}

/**
 * If isCaseSensite is true, answers true if the two arrays are identical character
 * by character, otherwise false.
 * If it is false, answers true if the two arrays are identical character by
 * character without checking the case, otherwise false.
 * <br>
 * <br>
 * For example:
 * <ol>
 * <li><pre>
 *    first = null
 *    second = null
 *    isCaseSensitive = true
 *    result => true
 * </pre>
 * </li>
 * <li><pre>
 *    first = { { } }
 *    second = null
 *    isCaseSensitive = true
 *    result => false
 * </pre>
 * </li>
 * <li><pre>
 *    first = { { 'A' } }
 *    second = { { 'a' } }
 *    isCaseSensitive = true
 *    result => false
 * </pre>
 * </li>
 * <li><pre>
 *    first = { { 'A' } }
 *    second = { { 'a' } }
 *    isCaseSensitive = false
 *    result => true
 * </pre>
 * </li>
 * </ol>
 *
 * @param first the first array
 * @param second the second array
 * @param isCaseSensitive check whether or not the equality should be case sensitive
 * @return true if the two arrays are identical character by character according to the value
 * of isCaseSensitive, otherwise false
 */
public static final boolean equals(
	char[][] first,
	char[][] second,
	boolean isCaseSensitive) {

	if (isCaseSensitive) {
		return equals(first, second);
	}
	if (first == second)
		return true;
	if (first == null || second == null)
		return false;
	if (first.length != second.length)
		return false;

	for (int i = first.length; --i >= 0;)
		if (!equals(first[i], second[i], false))
			return false;
	return true;
}

/**
 * Answers true if the two arrays are identical character by character, otherwise false.
 * The equality is case sensitive.
 * <br>
 * <br>
 * For example:
 * <ol>
 * <li><pre>
 *    first = null
 *    second = null
 *    result => true
 * </pre>
 * </li>
 * <li><pre>
 *    first = { }
 *    second = null
 *    result => false
 * </pre>
 * </li>
 * <li><pre>
 *    first = { 'a' }
 *    second = { 'a' }
 *    result => true
 * </pre>
 * </li>
 * <li><pre>
 *    first = { 'a' }
 *    second = { 'A' }
 *    result => false
 * </pre>
 * </li>
 * </ol>
 * @param first the first array
 * @param second the second array
 * @return true if the two arrays are identical character by character, otherwise false
 */
public static final boolean equals(char[] first, char[] second) {
	return Arrays.equals(first, second);
}

/**
 * Answers true if the first array is identical character by character to a portion of the second array
 * delimited from position secondStart (inclusive) to secondEnd(exclusive), otherwise false.
 * The equality is case sensitive.
 * <br>
 * <br>
 * For example:
 * <ol>
 * <li><pre>
 *    first = null
 *    second = null
 *    secondStart = 0
 *    secondEnd = 0
 *    result => true
 * </pre>
 * </li>
 * <li><pre>
 *    first = { }
 *    second = null
 *    secondStart = 0
 *    secondEnd = 0
 *    result => false
 * </pre>
 * </li>
 * <li><pre>
 *    first = { 'a' }
 *    second = { 'a' }
 *    secondStart = 0
 *    secondEnd = 1
 *    result => true
 * </pre>
 * </li>
 * <li><pre>
 *    first = { 'a' }
 *    second = { 'A' }
 *    secondStart = 0
 *    secondEnd = 1
 *    result => false
 * </pre>
 * </li>
 * </ol>
 * @param first the first array
 * @param second the second array
 * @param secondStart inclusive start position in the second array to compare
 * @param secondEnd exclusive end position in the second array to compare
 * @return true if the first array is identical character by character to fragment of second array ranging from secondStart to secondEnd-1, otherwise false
 * @since 3.0
 */
public static final boolean equals(char[] first, char[] second, int secondStart, int secondEnd) {
	return equals(first, second, secondStart, secondEnd, true);
}
/**
 * <p>Answers true if the first array is identical character by character to a portion of the second array
 * delimited from position secondStart (inclusive) to secondEnd(exclusive), otherwise false. The equality could be either
 * case sensitive or case insensitive according to the value of the <code>isCaseSensitive</code> parameter.
 * </p>
 * <p>For example:</p>
 * <ol>
 * <li><pre>
 *    first = null
 *    second = null
 *    secondStart = 0
 *    secondEnd = 0
 *    isCaseSensitive = false
 *    result => true
 * </pre>
 * </li>
 * <li><pre>
 *    first = { }
 *    second = null
 *    secondStart = 0
 *    secondEnd = 0
 *    isCaseSensitive = false
 *    result => false
 * </pre>
 * </li>
 * <li><pre>
 *    first = { 'a' }
 *    second = { 'a' }
 *    secondStart = 0
 *    secondEnd = 1
 *    isCaseSensitive = true
 *    result => true
 * </pre>
 * </li>
 * <li><pre>
 *    first = { 'a' }
 *    second = { 'A' }
 *    secondStart = 0
 *    secondEnd = 1
 *    isCaseSensitive = true
 *    result => false
 * </pre>
 * </li>
 * <li><pre>
 *    first = { 'a' }
 *    second = { 'A' }
 *    secondStart = 0
 *    secondEnd = 1
 *    isCaseSensitive = false
 *    result => true
 * </pre>
 * </li>
 * </ol>
 * @param first the first array
 * @param second the second array
 * @param secondStart inclusive start position in the second array to compare
 * @param secondEnd exclusive end position in the second array to compare
 * @param isCaseSensitive check whether or not the equality should be case sensitive
 * @return true if the first array is identical character by character to fragment of second array ranging from secondStart to secondEnd-1, otherwise false
 * @since 3.2
 */
public static final boolean equals(char[] first, char[] second, int secondStart, int secondEnd, boolean isCaseSensitive) {
	if (first == second)
		return true;
	if (first == null || second == null)
		return false;
	if (first.length != secondEnd - secondStart)
		return false;
	if (isCaseSensitive) {
		for (int i = first.length; --i >= 0;)
			if (first[i] != second[i+secondStart])
				return false;
	} else {
		for (int i = first.length; --i >= 0;)
			if (ScannerHelper.toLowerCase(first[i]) != ScannerHelper.toLowerCase(second[i+secondStart]))
				return false;
	}
	return true;
}

/**
 * If isCaseSensite is true, answers true if the two arrays are identical character
 * by character, otherwise false.
 * If it is false, answers true if the two arrays are identical character by
 * character without checking the case, otherwise false.
 * <br>
 * <br>
 * For example:
 * <ol>
 * <li><pre>
 *    first = null
 *    second = null
 *    isCaseSensitive = true
 *    result => true
 * </pre>
 * </li>
 * <li><pre>
 *    first = { }
 *    second = null
 *    isCaseSensitive = true
 *    result => false
 * </pre>
 * </li>
 * <li><pre>
 *    first = { 'A' }
 *    second = { 'a' }
 *    isCaseSensitive = true
 *    result => false
 * </pre>
 * </li>
 * <li><pre>
 *    first = { 'A' }
 *    second = { 'a' }
 *    isCaseSensitive = false
 *    result => true
 * </pre>
 * </li>
 * </ol>
 *
 * @param first the first array
 * @param second the second array
 * @param isCaseSensitive check whether or not the equality should be case sensitive
 * @return true if the two arrays are identical character by character according to the value
 * of isCaseSensitive, otherwise false
 */
public static final boolean equals(
	char[] first,
	char[] second,
	boolean isCaseSensitive) {

	if (isCaseSensitive) {
		return equals(first, second);
	}
	if (first == second)
		return true;
	if (first == null || second == null)
		return false;
	if (first.length != second.length)
		return false;

	for (int i = first.length; --i >= 0;)
		if (ScannerHelper.toLowerCase(first[i])
			!= ScannerHelper.toLowerCase(second[i]))
			return false;
	return true;
}

/**
 * If isCaseSensite is true, the equality is case sensitive, otherwise it is case insensitive.
 *
 * Answers true if the name contains the fragment at the starting index startIndex, otherwise false.
 * <br>
 * <br>
 * For example:
 * <ol>
 * <li><pre>
 *    fragment = { 'b', 'c' , 'd' }
 *    name = { 'a', 'b', 'c' , 'd' }
 *    startIndex = 1
 *    isCaseSensitive = true
 *    result => true
 * </pre>
 * </li>
 * <li><pre>
 *    fragment = { 'b', 'c' , 'd' }
 *    name = { 'a', 'b', 'C' , 'd' }
 *    startIndex = 1
 *    isCaseSensitive = true
 *    result => false
 * </pre>
 * </li>
 * <li><pre>
 *    fragment = { 'b', 'c' , 'd' }
 *    name = { 'a', 'b', 'C' , 'd' }
 *    startIndex = 0
 *    isCaseSensitive = false
 *    result => false
 * </pre>
 * </li>
 * <li><pre>
 *    fragment = { 'b', 'c' , 'd' }
 *    name = { 'a', 'b'}
 *    startIndex = 0
 *    isCaseSensitive = true
 *    result => false
 * </pre>
 * </li>
 * </ol>
 *
 * @param fragment the fragment to check
 * @param name the array to check
 * @param startIndex the starting index
 * @param isCaseSensitive check whether or not the equality should be case sensitive
 * @return true if the name contains the fragment at the starting index startIndex according to the
 * value of isCaseSensitive, otherwise false.
 * @throws NullPointerException if fragment or name is null.
 */
public static final boolean fragmentEquals(
	char[] fragment,
	char[] name,
	int startIndex,
	boolean isCaseSensitive) {

	int max = fragment.length;
	if (name.length < max + startIndex)
		return false;
	if (isCaseSensitive) {
		for (int i = max;
			--i >= 0;
			) // assumes the prefix is not larger than the name
			if (fragment[i] != name[i + startIndex])
				return false;
		return true;
	}
	for (int i = max;
		--i >= 0;
		) // assumes the prefix is not larger than the name
		if (ScannerHelper.toLowerCase(fragment[i])
			!= ScannerHelper.toLowerCase(name[i + startIndex]))
			return false;
	return true;
}

/**
 * Answers a hashcode for the array
 *
 * @param array the array for which a hashcode is required
 * @return the hashcode
 */
public static final int hashCode(char[] array) {
	int hash = Arrays.hashCode(array);
	return hash & 0x7FFFFFFF;
}

/**
 * Answers true if c is a whitespace according to the JLS (&#92;u0009, &#92;u000a, &#92;u000c, &#92;u000d, &#92;u0020), otherwise false.
 * <br>
 * <br>
 * For example:
 * <ol>
 * <li><pre>
 *    c = ' '
 *    result => true
 * </pre>
 * </li>
 * <li><pre>
 *    c = '&#92;u3000'
 *    result => false
 * </pre>
 * </li>
 * </ol>
 *
 * @param c the character to check
 * @return true if c is a whitespace according to the JLS, otherwise false.
 */
public static boolean isWhitespace(char c) {
	return c < ScannerHelper.MAX_OBVIOUS && ((ScannerHelper.OBVIOUS_IDENT_CHAR_NATURES[c] & ScannerHelper.C_JLS_SPACE) != 0);
}

/**
 * Answers the first index in the array for which the corresponding character is
 * equal to toBeFound. Answers -1 if no occurrence of this character is found.
 * <br>
 * <br>
 * For example:
 * <ol>
 * <li><pre>
 *    toBeFound = 'c'
 *    array = { ' a', 'b', 'c', 'd' }
 *    result => 2
 * </pre>
 * </li>
 * <li><pre>
 *    toBeFound = 'e'
 *    array = { ' a', 'b', 'c', 'd' }
 *    result => -1
 * </pre>
 * </li>
 * </ol>
 *
 * @param toBeFound the character to search
 * @param array the array to be searched
 * @return the first index in the array for which the corresponding character is
 * equal to toBeFound, -1 otherwise
 * @throws NullPointerException if array is null
 */
public static final int indexOf(char toBeFound, char[] array) {
	return indexOf(toBeFound, array, 0);
}

/**
 * Answers the first index in the array for which the toBeFound array is a matching
 * subarray following the case rule. Answers -1 if no match is found.
 * <br>
 * <br>
 * For example:
 * <ol>
 * <li><pre>
 *    toBeFound = { 'c' }
 *    array = { ' a', 'b', 'c', 'd' }
 *    result => 2
 * </pre>
 * </li>
 * <li><pre>
 *    toBeFound = { 'e' }
 *    array = { ' a', 'b', 'c', 'd' }
 *    result => -1
 * </pre>
 * </li>
 * </ol>
 *
 * @param toBeFound the subarray to search
 * @param array the array to be searched
 * @param isCaseSensitive flag to know if the matching should be case sensitive
 * @return the first index in the array for which the toBeFound array is a matching
 * subarray following the case rule, -1 otherwise
 * @throws NullPointerException if array is null or toBeFound is null
 * @since 3.2
 */
public static final int indexOf(char[] toBeFound, char[] array, boolean isCaseSensitive) {
	return indexOf(toBeFound, array, isCaseSensitive, 0);
}

/**
 * Answers the first index in the array for which the toBeFound array is a matching
 * subarray following the case rule starting at the index start. Answers -1 if no match is found.
 * <br>
 * <br>
 * For example:
 * <ol>
 * <li><pre>
 *    toBeFound = { 'c' }
 *    array = { ' a', 'b', 'c', 'd' }
 *    result => 2
 * </pre>
 * </li>
 * <li><pre>
 *    toBeFound = { 'e' }
 *    array = { ' a', 'b', 'c', 'd' }
 *    result => -1
 * </pre>
 * </li>
 * </ol>
 *
 * @param toBeFound the subarray to search
 * @param array the array to be searched
 * @param isCaseSensitive flag to know if the matching should be case sensitive
 * @param start the starting index
 * @return the first index in the array for which the toBeFound array is a matching
 * subarray following the case rule starting at the index start, -1 otherwise
 * @throws NullPointerException if array is null or toBeFound is null
 * @since 3.2
 */
public static final int indexOf(final char[] toBeFound, final char[] array, final boolean isCaseSensitive, final int start) {
	return indexOf(toBeFound, array, isCaseSensitive, start, array.length);
}

/**
 * Answers the first index in the array for which the toBeFound array is a matching
 * subarray following the case rule starting at the index start. Answers -1 if no match is found.
 * <br>
 * <br>
 * For example:
 * <ol>
 * <li><pre>
 *    toBeFound = { 'c' }
 *    array = { ' a', 'b', 'c', 'd' }
 *    result => 2
 * </pre>
 * </li>
 * <li><pre>
 *    toBeFound = { 'e' }
 *    array = { ' a', 'b', 'c', 'd' }
 *    result => -1
 * </pre>
 * </li>
 * </ol>
 *
 * @param toBeFound the subarray to search
 * @param array the array to be searched
 * @param isCaseSensitive flag to know if the matching should be case sensitive
 * @param start the starting index (inclusive)
 * @param end the end index (exclusive)
 * @return the first index in the array for which the toBeFound array is a matching
 * subarray following the case rule starting at the index start, -1 otherwise
 * @throws NullPointerException if array is null or toBeFound is null
 * @since 3.2
 */
public static final int indexOf(final char[] toBeFound, final char[] array, final boolean isCaseSensitive, final int start, final int end) {
	final int arrayLength = end;
	final int toBeFoundLength = toBeFound.length;
	if (toBeFoundLength > arrayLength || start < 0) return -1;
	if (toBeFoundLength == 0) return 0;
	if (toBeFoundLength == arrayLength) {
		if (isCaseSensitive) {
			for (int i = start; i < arrayLength; i++) {
				if (array[i] != toBeFound[i]) return -1;
			}
			return 0;
		} else {
			for (int i = start; i < arrayLength; i++) {
				if (ScannerHelper.toLowerCase(array[i]) != ScannerHelper.toLowerCase(toBeFound[i])) return -1;
			}
			return 0;
		}
	}
	if (isCaseSensitive) {
		arrayLoop: for (int i = start, max = arrayLength - toBeFoundLength + 1; i < max; i++) {
			if (array[i] == toBeFound[0]) {
				for (int j = 1; j < toBeFoundLength; j++) {
					if (array[i + j] != toBeFound[j]) continue arrayLoop;
				}
				return i;
			}
		}
	} else {
		arrayLoop: for (int i = start, max = arrayLength - toBeFoundLength + 1; i < max; i++) {
			if (ScannerHelper.toLowerCase(array[i]) == ScannerHelper.toLowerCase(toBeFound[0])) {
				for (int j = 1; j < toBeFoundLength; j++) {
					if (ScannerHelper.toLowerCase(array[i + j]) != ScannerHelper.toLowerCase(toBeFound[j])) continue arrayLoop;
				}
				return i;
			}
		}
	}
	return -1;
}

/**
 * Answers the first index in the array for which the corresponding character is
 * equal to toBeFound starting the search at index start.
 * Answers -1 if no occurrence of this character is found.
 * <br>
 * <br>
 * For example:
 * <ol>
 * <li><pre>
 *    toBeFound = 'c'
 *    array = { ' a', 'b', 'c', 'd' }
 *    start = 2
 *    result => 2
 * </pre>
 * </li>
 * <li><pre>
 *    toBeFound = 'c'
 *    array = { ' a', 'b', 'c', 'd' }
 *    start = 3
 *    result => -1
 * </pre>
 * </li>
 * <li><pre>
 *    toBeFound = 'e'
 *    array = { ' a', 'b', 'c', 'd' }
 *    start = 1
 *    result => -1
 * </pre>
 * </li>
 * </ol>
 *
 * @param toBeFound the character to search
 * @param array the array to be searched
 * @param start the starting index
 * @return the first index in the array for which the corresponding character is
 * equal to toBeFound, -1 otherwise
 * @throws NullPointerException if array is null
 * @throws ArrayIndexOutOfBoundsException if  start is lower than 0
 */
public static final int indexOf(char toBeFound, char[] array, int start) {
	for (int i = start; i < array.length; i++)
		if (toBeFound == array[i])
			return i;
	return -1;
}

/**
 * Answers the first index in the array for which the corresponding character is
 * equal to toBeFound starting the search at index start and before the ending index.
 * Answers -1 if no occurrence of this character is found.
 * <br>
 * <br>
 * For example:
 * <ol>
 * <li><pre>
 *    toBeFound = 'c'
 *    array = { ' a', 'b', 'c', 'd' }
 *    start = 2
 *    result => 2
 * </pre>
 * </li>
 * <li><pre>
 *    toBeFound = 'c'
 *    array = { ' a', 'b', 'c', 'd' }
 *    start = 3
 *    result => -1
 * </pre>
 * </li>
 * <li><pre>
 *    toBeFound = 'e'
 *    array = { ' a', 'b', 'c', 'd' }
 *    start = 1
 *    result => -1
 * </pre>
 * </li>
 * </ol>
 *
 * @param toBeFound the character to search
 * @param array the array to be searched
 * @param start the starting index (inclusive)
 * @param end the ending index (exclusive)
 * @return the first index in the array for which the corresponding character is
 * equal to toBeFound, -1 otherwise
 * @throws NullPointerException if array is null
 * @throws ArrayIndexOutOfBoundsException if  start is lower than 0 or ending greater than array length
 * @since 3.2
 */
public static final int indexOf(char toBeFound, char[] array, int start, int end) {
	for (int i = start; i < end; i++)
		if (toBeFound == array[i])
			return i;
	return -1;
}

/**
 * Answers the last index in the array for which the corresponding character is
 * equal to toBeFound starting from the end of the array.
 * Answers -1 if no occurrence of this character is found.
 * <br>
 * <br>
 * For example:
 * <ol>
 * <li><pre>
 *    toBeFound = 'c'
 *    array = { ' a', 'b', 'c', 'd' , 'c', 'e' }
 *    result => 4
 * </pre>
 * </li>
 * <li><pre>
 *    toBeFound = 'e'
 *    array = { ' a', 'b', 'c', 'd' }
 *    result => -1
 * </pre>
 * </li>
 * </ol>
 *
 * @param toBeFound the character to search
 * @param array the array to be searched
 * @return the last index in the array for which the corresponding character is
 * equal to toBeFound starting from the end of the array, -1 otherwise
 * @throws NullPointerException if array is null
 */
public static final int lastIndexOf(char toBeFound, char[] array) {
	for (int i = array.length; --i >= 0;)
		if (toBeFound == array[i])
			return i;
	return -1;
}

/**
 * Answers the last index in the array for which the corresponding character is
 * equal to toBeFound stopping at the index startIndex.
 * Answers -1 if no occurrence of this character is found.
 * <br>
 * <br>
 * For example:
 * <ol>
 * <li><pre>
 *    toBeFound = 'c'
 *    array = { ' a', 'b', 'c', 'd' }
 *    startIndex = 2
 *    result => 2
 * </pre>
 * </li>
 * <li><pre>
 *    toBeFound = 'c'
 *    array = { ' a', 'b', 'c', 'd', 'e' }
 *    startIndex = 3
 *    result => -1
 * </pre>
 * </li>
 * <li><pre>
 *    toBeFound = 'e'
 *    array = { ' a', 'b', 'c', 'd' }
 *    startIndex = 0
 *    result => -1
 * </pre>
 * </li>
 * </ol>
 *
 * @param toBeFound the character to search
 * @param array the array to be searched
 * @param startIndex the stopping index
 * @return the last index in the array for which the corresponding character is
 * equal to toBeFound stopping at the index startIndex, -1 otherwise
 * @throws NullPointerException if array is null
 * @throws ArrayIndexOutOfBoundsException if startIndex is lower than 0
 */
public static final int lastIndexOf(
	char toBeFound,
	char[] array,
	int startIndex) {
	for (int i = array.length; --i >= startIndex;)
		if (toBeFound == array[i])
			return i;
	return -1;
}

/**
 * Answers the last index in the array for which the corresponding character is
 * equal to toBeFound starting from endIndex to startIndex.
 * Answers -1 if no occurrence of this character is found.
 * <br>
 * <br>
 * For example:
 * <ol>
 * <li><pre>
 *    toBeFound = 'c'
 *    array = { ' a', 'b', 'c', 'd' }
 *    startIndex = 2
 *    endIndex = 2
 *    result => 2
 * </pre>
 * </li>
 * <li><pre>
 *    toBeFound = 'c'
 *    array = { ' a', 'b', 'c', 'd', 'e' }
 *    startIndex = 3
 *    endIndex = 4
 *    result => -1
 * </pre>
 * </li>
 * <li><pre>
 *    toBeFound = 'e'
 *    array = { ' a', 'b', 'c', 'd' }
 *    startIndex = 0
 *    endIndex = 3
 *    result => -1
 * </pre>
 * </li>
 * </ol>
 *
 * @param toBeFound the character to search
 * @param array the array to be searched
 * @param startIndex the stopping index
 * @param endIndex the starting index
 * @return the last index in the array for which the corresponding character is
 * equal to toBeFound starting from endIndex to startIndex, -1 otherwise
 * @throws NullPointerException if array is null
 * @throws ArrayIndexOutOfBoundsException if endIndex is greater or equals to array length or starting is lower than 0
 */
public static final int lastIndexOf(
	char toBeFound,
	char[] array,
	int startIndex,
	int endIndex) {
	for (int i = endIndex; --i >= startIndex;)
		if (toBeFound == array[i])
			return i;
	return -1;
}

/**
 * Answers the last portion of a name given a separator.
 * <br>
 * <br>
 * For example,
 * <pre>
 * 	lastSegment("java.lang.Object".toCharArray(),'.') --> Object
 * </pre>
 *
 * @param array the array
 * @param separator the given separator
 * @return the last portion of a name given a separator
 * @throws NullPointerException if array is null
 */
final static public char[] lastSegment(char[] array, char separator) {
	int pos = lastIndexOf(separator, array);
	if (pos < 0)
		return array;
	return subarray(array, pos + 1, array.length);
}

/**
 * <p>Answers true if the pattern matches the given name, false otherwise. This char[] pattern matching
 * accepts wild-cards '*' and '?'.</p>
 *
 * <p>When not case sensitive, the pattern is assumed to already be lowercased, the
 * name will be lowercased character per character as comparing.<br>
 * If name is null, the answer is false.<br>
 * If pattern is null, the answer is true if name is not null.
 * </p>
 * For example:
 * <ol>
 * <li><pre>
 *    pattern = { '?', 'b', '*' }
 *    name = { 'a', 'b', 'c' , 'd' }
 *    isCaseSensitive = true
 *    result => true
 * </pre>
 * </li>
 * <li><pre>
 *    pattern = { '?', 'b', '?' }
 *    name = { 'a', 'b', 'c' , 'd' }
 *    isCaseSensitive = true
 *    result => false
 * </pre>
 * </li>
 * <li><pre>
 *    pattern = { 'b', '*' }
 *    name = { 'a', 'b', 'c' , 'd' }
 *    isCaseSensitive = true
 *    result => false
 * </pre>
 * </li>
 * </ol>
 *
 * @param pattern the given pattern
 * @param name the given name
 * @param isCaseSensitive flag to know whether or not the matching should be case sensitive
 * @return true if the pattern matches the given name, false otherwise
 */
public static final boolean match(
	char[] pattern,
	char[] name,
	boolean isCaseSensitive) {

	if (name == null)
		return false; // null name cannot match
	if (pattern == null)
		return true; // null pattern is equivalent to '*'

	return match(
		pattern,
		0,
		pattern.length,
		name,
		0,
		name.length,
		isCaseSensitive);
}

/**
 * Answers true if a sub-pattern matches the subpart of the given name, false otherwise.
 * char[] pattern matching, accepting wild-cards '*' and '?'. Can match only subset of name/pattern.
 * end positions are non-inclusive.
 * The subpattern is defined by the patternStart and pattternEnd positions.
 * When not case sensitive, the pattern is assumed to already be lowercased, the
 * name will be lowercased character per character as comparing.
 * <br>
 * <br>
 * For example:
 * <ol>
 * <li><pre>
 *    pattern = { '?', 'b', '*' }
 *    patternStart = 1
 *    patternEnd = 3
 *    name = { 'a', 'b', 'c' , 'd' }
 *    nameStart = 1
 *    nameEnd = 4
 *    isCaseSensitive = true
 *    result => true
 * </pre>
 * </li>
 * <li><pre>
 *    pattern = { '?', 'b', '*' }
 *    patternStart = 1
 *    patternEnd = 2
 *    name = { 'a', 'b', 'c' , 'd' }
 *    nameStart = 1
 *    nameEnd = 4
 *    isCaseSensitive = true
 *    result => false
 * </pre>
 * </li>
 * </ol>
 *
 * @param pattern the given pattern
 * @param patternStart the given pattern start
 * @param patternEnd the given pattern end
 * @param name the given name
 * @param nameStart the given name start
 * @param nameEnd the given name end
 * @param isCaseSensitive flag to know if the matching should be case sensitive
 * @return true if a sub-pattern matches the subpart of the given name, false otherwise
 */
public static final boolean match(
	char[] pattern,
	int patternStart,
	int patternEnd,
	char[] name,
	int nameStart,
	int nameEnd,
	boolean isCaseSensitive) {

	if (name == null)
		return false; // null name cannot match
	if (pattern == null)
		return true; // null pattern is equivalent to '*'
	int iPattern = patternStart;
	int iName = nameStart;

	if (patternEnd < 0)
		patternEnd = pattern.length;
	if (nameEnd < 0)
		nameEnd = name.length;

	/* check first segment */
	char patternChar = 0;
	while (true) {
		if (iPattern == patternEnd) {
			if (iName == nameEnd) return true; // the chars match
			return false; // pattern has ended but not the name, no match
		}
		if ((patternChar = pattern[iPattern]) == '*') {
			break;
		}
		if (iName == nameEnd) {
			return false; // name has ended but not the pattern
		}
		if (patternChar
			!= (isCaseSensitive
				? name[iName]
				: ScannerHelper.toLowerCase(name[iName]))
			&& patternChar != '?') {
			return false;
		}
		iName++;
		iPattern++;
	}
	/* check sequence of star+segment */
	int segmentStart;
	if (patternChar == '*') {
		segmentStart = ++iPattern; // skip star
	} else {
		segmentStart = 0; // force iName check
	}
	int prefixStart = iName;
	checkSegment : while (iName < nameEnd) {
		if (iPattern == patternEnd) {
			iPattern = segmentStart; // mismatch - restart current segment
			iName = ++prefixStart;
			continue checkSegment;
		}
		/* segment is ending */
		if ((patternChar = pattern[iPattern]) == '*') {
			segmentStart = ++iPattern; // skip start
			if (segmentStart == patternEnd) {
				return true;
			}
			prefixStart = iName;
			continue checkSegment;
		}
		/* check current name character */
		if ((isCaseSensitive ? name[iName] : ScannerHelper.toLowerCase(name[iName]))
					!= patternChar
				&& patternChar != '?') {
			iPattern = segmentStart; // mismatch - restart current segment
			iName = ++prefixStart;
			continue checkSegment;
		}
		iName++;
		iPattern++;
	}

	return (segmentStart == patternEnd)
		|| (iName == nameEnd && iPattern == patternEnd)
		|| (iPattern == patternEnd - 1 && pattern[iPattern] == '*');
}

/**
 * Answers true if the pattern matches the filepath using the pathSepatator, false otherwise.
 *
 * Path char[] pattern matching, accepting wild-cards '**', '*' and '?' (using Ant directory tasks
 * conventions, also see "http://jakarta.apache.org/ant/manual/dirtasks.html#defaultexcludes").
 * Path pattern matching is enhancing regular pattern matching in supporting extra rule where '**' represent
 * any folder combination.
 * Special rule:
 * - foo\  is equivalent to foo\**
 * When not case sensitive, the pattern is assumed to already be lowercased, the
 * name will be lowercased character per character as comparing.
 *
 * @param pattern the given pattern
 * @param filepath the given path
 * @param isCaseSensitive to find out whether or not the matching should be case sensitive
 * @param pathSeparator the given path separator
 * @return true if the pattern matches the filepath using the pathSepatator, false otherwise
 */
public static final boolean pathMatch(
	char[] pattern,
	char[] filepath,
	boolean isCaseSensitive,
	char pathSeparator) {

	if (filepath == null)
		return false; // null name cannot match
	if (pattern == null)
		return true; // null pattern is equivalent to '*'

	// offsets inside pattern
	int pSegmentStart = pattern[0] == pathSeparator ? 1 : 0;
	int pLength = pattern.length;
	int pSegmentEnd = CharOperation.indexOf(pathSeparator, pattern, pSegmentStart+1);
	if (pSegmentEnd < 0) pSegmentEnd = pLength;

	// special case: pattern foo\ is equivalent to foo\**
	boolean freeTrailingDoubleStar = pattern[pLength - 1] == pathSeparator;

	// offsets inside filepath
	int fSegmentStart, fLength = filepath.length;
	if (filepath[0] != pathSeparator){
		fSegmentStart = 0;
	} else {
		fSegmentStart = 1;
	}
	if (fSegmentStart != pSegmentStart) {
		return false; // both must start with a separator or none.
	}
	int fSegmentEnd = CharOperation.indexOf(pathSeparator, filepath, fSegmentStart+1);
	if (fSegmentEnd < 0) fSegmentEnd = fLength;

	// first segments
	while (pSegmentStart < pLength
		&& !(pSegmentEnd == pLength && freeTrailingDoubleStar
				|| (pSegmentEnd == pSegmentStart + 2
						&& pattern[pSegmentStart] == '*'
						&& pattern[pSegmentStart + 1] == '*'))) {

		if (fSegmentStart >= fLength)
			return false;
		if (!CharOperation
			.match(
				pattern,
				pSegmentStart,
				pSegmentEnd,
				filepath,
				fSegmentStart,
				fSegmentEnd,
				isCaseSensitive)) {
			return false;
		}

		// jump to next segment
		pSegmentEnd =
			CharOperation.indexOf(
				pathSeparator,
				pattern,
				pSegmentStart = pSegmentEnd + 1);
		// skip separator
		if (pSegmentEnd < 0)
			pSegmentEnd = pLength;

		fSegmentEnd =
			CharOperation.indexOf(
				pathSeparator,
				filepath,
				fSegmentStart = fSegmentEnd + 1);
		// skip separator
		if (fSegmentEnd < 0) fSegmentEnd = fLength;
	}

	/* check sequence of doubleStar+segment */
	int pSegmentRestart;
	if ((pSegmentStart >= pLength && freeTrailingDoubleStar)
			|| (pSegmentEnd == pSegmentStart + 2
				&& pattern[pSegmentStart] == '*'
				&& pattern[pSegmentStart + 1] == '*')) {
		pSegmentEnd =
			CharOperation.indexOf(
				pathSeparator,
				pattern,
				pSegmentStart = pSegmentEnd + 1);
		// skip separator
		if (pSegmentEnd < 0) pSegmentEnd = pLength;
		pSegmentRestart = pSegmentStart;
	} else {
		if (pSegmentStart >= pLength) return fSegmentStart >= fLength; // true if filepath is done too.
		pSegmentRestart = 0; // force fSegmentStart check
	}
	int fSegmentRestart = fSegmentStart;
	checkSegment : while (fSegmentStart < fLength) {

		if (pSegmentStart >= pLength) {
			if (freeTrailingDoubleStar) return true;
			// mismatch - restart current path segment
			pSegmentEnd =
				CharOperation.indexOf(pathSeparator, pattern, pSegmentStart = pSegmentRestart);
			if (pSegmentEnd < 0) pSegmentEnd = pLength;

			fSegmentRestart =
				CharOperation.indexOf(pathSeparator, filepath, fSegmentRestart + 1);
			// skip separator
			if (fSegmentRestart < 0) {
				fSegmentRestart = fLength;
			} else {
				fSegmentRestart++;
			}
			fSegmentEnd =
				CharOperation.indexOf(pathSeparator, filepath, fSegmentStart = fSegmentRestart);
			if (fSegmentEnd < 0) fSegmentEnd = fLength;
			continue checkSegment;
		}

		/* path segment is ending */
		if (pSegmentEnd == pSegmentStart + 2
			&& pattern[pSegmentStart] == '*'
			&& pattern[pSegmentStart + 1] == '*') {
			pSegmentEnd =
				CharOperation.indexOf(pathSeparator, pattern, pSegmentStart = pSegmentEnd + 1);
			// skip separator
			if (pSegmentEnd < 0) pSegmentEnd = pLength;
			pSegmentRestart = pSegmentStart;
			fSegmentRestart = fSegmentStart;
			if (pSegmentStart >= pLength) return true;
			continue checkSegment;
		}
		/* chech current path segment */
		if (!CharOperation.match(
							pattern,
							pSegmentStart,
							pSegmentEnd,
							filepath,
							fSegmentStart,
							fSegmentEnd,
							isCaseSensitive)) {
			// mismatch - restart current path segment
			pSegmentEnd =
				CharOperation.indexOf(pathSeparator, pattern, pSegmentStart = pSegmentRestart);
			if (pSegmentEnd < 0) pSegmentEnd = pLength;

			fSegmentRestart =
				CharOperation.indexOf(pathSeparator, filepath, fSegmentRestart + 1);
			// skip separator
			if (fSegmentRestart < 0) {
				fSegmentRestart = fLength;
			} else {
				fSegmentRestart++;
			}
			fSegmentEnd =
				CharOperation.indexOf(pathSeparator, filepath, fSegmentStart = fSegmentRestart);
			if (fSegmentEnd < 0) fSegmentEnd = fLength;
			continue checkSegment;
		}
		// jump to next segment
		pSegmentEnd =
			CharOperation.indexOf(
				pathSeparator,
				pattern,
				pSegmentStart = pSegmentEnd + 1);
		// skip separator
		if (pSegmentEnd < 0)
			pSegmentEnd = pLength;

		fSegmentEnd =
			CharOperation.indexOf(
				pathSeparator,
				filepath,
				fSegmentStart = fSegmentEnd + 1);
		// skip separator
		if (fSegmentEnd < 0)
			fSegmentEnd = fLength;
	}

	return (pSegmentRestart >= pSegmentEnd)
		|| (fSegmentStart >= fLength && pSegmentStart >= pLength)
		|| (pSegmentStart == pLength - 2
			&& pattern[pSegmentStart] == '*'
			&& pattern[pSegmentStart + 1] == '*')
		|| (pSegmentStart == pLength && freeTrailingDoubleStar);
}

/**
 * Answers the number of occurrences of the given character in the given array, 0 if any.
 *
 * <br>
 * <br>
 * For example:
 * <ol>
 * <li><pre>
 *    toBeFound = 'b'
 *    array = { 'a' , 'b', 'b', 'a', 'b', 'a' }
 *    result => 3
 * </pre>
 * </li>
 * <li><pre>
 *    toBeFound = 'c'
 *    array = { 'a' , 'b', 'b', 'a', 'b', 'a' }
 *    result => 0
 * </pre>
 * </li>
 * </ol>
 *
 * @param toBeFound the given character
 * @param array the given array
 * @return the number of occurrences of the given character in the given array, 0 if any
 * @throws NullPointerException if array is null
 */
public static final int occurencesOf(char toBeFound, char[] array) {
	int count = 0;
	for (int i = 0; i < array.length; i++)
		if (toBeFound == array[i])
			count++;
	return count;
}

/**
 * Answers the number of occurrences of the given character in the given array starting
 * at the given index, 0 if any.
 *
 * <br>
 * <br>
 * For example:
 * <ol>
 * <li><pre>
 *    toBeFound = 'b'
 *    array = { 'a' , 'b', 'b', 'a', 'b', 'a' }
 *    start = 2
 *    result => 2
 * </pre>
 * </li>
 * <li><pre>
 *    toBeFound = 'c'
 *    array = { 'a' , 'b', 'b', 'a', 'b', 'a' }
 *    start = 0
 *    result => 0
 * </pre>
 * </li>
 * </ol>
 *
 * @param toBeFound the given character
 * @param array the given array
 * @param start the given index
 * @return the number of occurrences of the given character in the given array, 0 if any
 * @throws NullPointerException if array is null
 * @throws ArrayIndexOutOfBoundsException if start is lower than 0
 */
public static final int occurencesOf(
	char toBeFound,
	char[] array,
	int start) {
	int count = 0;
	for (int i = start; i < array.length; i++)
		if (toBeFound == array[i])
			count++;
	return count;
}
/**
 * Return the int value represented by the designated subpart of array. The
 * calculation of the result for single-digit positive integers is optimized in
 * time.
 * @param array the array within which the int value is to be parsed
 * @param start first character of the int value in array
 * @param length length of the int value in array
 * @return the int value of a subpart of array
 * @throws NumberFormatException if the designated subpart of array does not
 *         parse to an int
 * @since 3.4
 */
public static final int parseInt(char[] array, int start, int length) throws NumberFormatException {
	if (length == 1) {
		int result = array[start] - '0';
		if (result < 0 || result > 9) {
			throw new NumberFormatException("invalid digit"); //$NON-NLS-1$
		}
		return result;
	} else {
		return Integer.parseInt(new String(array, start, length));
	}
}
/**
 * Answers true if the given name starts with the given prefix, false otherwise.
 * The comparison is case sensitive.
 * <br>
 * <br>
 * For example:
 * <ol>
 * <li><pre>
 *    prefix = { 'a' , 'b' }
 *    name = { 'a' , 'b', 'b', 'a', 'b', 'a' }
 *    result => true
 * </pre>
 * </li>
 * <li><pre>
 *    prefix = { 'a' , 'c' }
 *    name = { 'a' , 'b', 'b', 'a', 'b', 'a' }
 *    result => false
 * </pre>
 * </li>
 * </ol>
 *
 * @param prefix the given prefix
 * @param name the given name
 * @return true if the given name starts with the given prefix, false otherwise
 * @throws NullPointerException if the given name is null or if the given prefix is null
 */
public static final boolean prefixEquals(char[] prefix, char[] name) {

	int max = prefix.length;
	if (name.length < max)
		return false;
	for (int i = max;
		--i >= 0;
		) // assumes the prefix is not larger than the name
		if (prefix[i] != name[i])
			return false;
	return true;
}

/**
 * Answers true if the given name starts with the given prefix, false otherwise.
 * isCaseSensitive is used to find out whether or not the comparison should be case sensitive.
 * <br>
 * <br>
 * For example:
 * <ol>
 * <li><pre>
 *    prefix = { 'a' , 'B' }
 *    name = { 'a' , 'b', 'b', 'a', 'b', 'a' }
 *    isCaseSensitive = false
 *    result => true
 * </pre>
 * </li>
 * <li><pre>
 *    prefix = { 'a' , 'B' }
 *    name = { 'a' , 'b', 'b', 'a', 'b', 'a' }
 *    isCaseSensitive = true
 *    result => false
 * </pre>
 * </li>
 * </ol>
 *
 * @param prefix the given prefix
 * @param name the given name
 * @param isCaseSensitive to find out whether or not the comparison should be case sensitive
 * @return true if the given name starts with the given prefix, false otherwise
 * @throws NullPointerException if the given name is null or if the given prefix is null
 */
public static final boolean prefixEquals(
	char[] prefix,
	char[] name,
	boolean isCaseSensitive) {
	return prefixEquals(prefix, name, isCaseSensitive, 0);
}

/**
 * Answers true if the given name, starting from the given index, starts with the given prefix,
 * false otherwise. isCaseSensitive is used to find out whether or not the comparison should be
 * case sensitive.
 * <br>
 * <br>
 * For example:
 * <ol>
 * <li><pre>
 *    prefix = { 'a' , 'B' }
 *    name = { 'c', 'd', 'a' , 'b', 'b', 'a', 'b', 'a' }
 *    startIndex = 2
 *    isCaseSensitive = false
 *    result => true
 * </pre>
 * </li>
 * <li><pre>
 *    prefix = { 'a' , 'B' }
 *    name = { 'c', 'd', 'a' , 'b', 'b', 'a', 'b', 'a' }
 *    startIndex = 2
 *    isCaseSensitive = true
 *    result => false
 * </pre>
 * </li>
 * </ol>
 *
 * @param prefix the given prefix
 * @param name the given name
 * @param isCaseSensitive to find out whether or not the comparison should be case sensitive
 * @param startIndex index from which the prefix should be searched in the name
 * @return true if the given name starts with the given prefix, false otherwise
 * @throws NullPointerException if the given name is null or if the given prefix is null
 * @since 3.7
 */
public static final boolean prefixEquals(
	char[] prefix,
	char[] name,
	boolean isCaseSensitive,
	int startIndex) {

	int max = prefix.length;
	if (name.length - startIndex < max)
		return false;
	if (isCaseSensitive) {
		for (int i = max; --i >= 0;) // assumes the prefix is not larger than the name
			if (prefix[i] != name[startIndex + i])
				return false;
		return true;
	}

	for (int i = max; --i >= 0;) // assumes the prefix is not larger than the name
		if (ScannerHelper.toLowerCase(prefix[i])
			!= ScannerHelper.toLowerCase(name[startIndex + i]))
			return false;
	return true;
}

/**
 * Answers a new array removing a given character. Answers the given array if there is
 * no occurrence of the character to remove.
 * <br>
 * <br>
 * For example:
 * <ol>
 * <li><pre>
 *    array = { 'a' , 'b', 'b', 'c', 'b', 'a' }
 *    toBeRemoved = 'b'
 *    return { 'a' , 'c', 'a' }
 * </pre>
 * </li>
 * <li><pre>
 *    array = { 'a' , 'b', 'b', 'a', 'b', 'a' }
 *    toBeRemoved = 'c'
 *    return array
 * </pre>
 * </li>
 * </ol>
 *
 * @param array the given array
 * @param toBeRemoved the character to be removed
 * @return a new array removing given character
 * @since 3.2
 */
public static final char[] remove(char[] array, char toBeRemoved) {

	if (array == null) return null;
	int length = array.length;
	if (length == 0) return array;
	char[] result = null;
	int count = 0;
	for (int i = 0; i < length; i++) {
		char c = array[i];
		if (c == toBeRemoved) {
			if (result == null) {
				result = new char[length];
				System.arraycopy(array, 0, result, 0, i);
				count = i;
			}
		} else if (result != null) {
			result[count++] = c;
		}
	}
	if (result == null) return array;
	System.arraycopy(result, 0, result = new char[count], 0, count);
	return result;
}

/**
 * Replace all occurrence of the character to be replaced with the replacement character in the
 * given array.
 * <br>
 * <br>
 * For example:
 * <ol>
 * <li><pre>
 *    array = { 'a' , 'b', 'b', 'a', 'b', 'a' }
 *    toBeReplaced = 'b'
 *    replacementChar = 'a'
 *    result => No returned value, but array is now equals to { 'a' , 'a', 'a', 'a', 'a', 'a' }
 * </pre>
 * </li>
 * <li><pre>
 *    array = { 'a' , 'b', 'b', 'a', 'b', 'a' }
 *    toBeReplaced = 'c'
 *    replacementChar = 'a'
 *    result => No returned value, but array is now equals to { 'a' , 'b', 'b', 'a', 'b', 'a' }
 * </pre>
 * </li>
 * </ol>
 *
 * @param array the given array
 * @param toBeReplaced the character to be replaced
 * @param replacementChar the replacement character
 * @throws NullPointerException if the given array is null
 */
public static final void replace(
	char[] array,
	char toBeReplaced,
	char replacementChar) {
	if (toBeReplaced != replacementChar) {
		for (int i = 0, max = array.length; i < max; i++) {
			if (array[i] == toBeReplaced)
				array[i] = replacementChar;
		}
	}
}

/**
 * Replace all occurrences of characters to be replaced with the replacement character in the
 * given array.
 * <br>
 * <br>
 * For example:
 * <ol>
 * <li><pre>
 *    array = { 'a' , 'b', 'b', 'c', 'a', 'b', 'c', 'a' }
 *    toBeReplaced = { 'b', 'c' }
 *    replacementChar = 'a'
 *    result => No returned value, but array is now equals to { 'a' , 'a', 'a', 'a', 'a', 'a', 'a', 'a' }
 * </pre>
 * </li>
 * </ol>
 *
 * @param array the given array
 * @param toBeReplaced characters to be replaced
 * @param replacementChar the replacement character
 * @throws NullPointerException if arrays are null.
 * @since 3.1
 */
public static final void replace(char[] array, char[] toBeReplaced, char replacementChar) {
	replace(array, toBeReplaced, replacementChar, 0, array.length);
}

/**
 * Replace all occurrences of characters to be replaced with the replacement character in the
 * given array from the start position (inclusive) to the end position (exclusive).
 * <br>
 * <br>
 * For example:
 * <ol>
 * <li><pre>
 *    array = { 'a' , 'b', 'b', 'c', 'a', 'b', 'c', 'a' }
 *    toBeReplaced = { 'b', 'c' }
 *    replacementChar = 'a'
 *    start = 4
 *    end = 8
 *    result => No returned value, but array is now equals to { 'a' , 'b', 'b', 'c', 'a', 'a', 'a', 'a' }
 * </pre>
 * </li>
 * </ol>
 *
 * @param array the given array
 * @param toBeReplaced characters to be replaced
 * @param replacementChar the replacement character
 * @param start the given start position (inclusive)
 * @param end  the given end position (exclusive)
 * @throws NullPointerException if arrays are null.
 * @since 3.2
 */
public static final void replace(char[] array, char[] toBeReplaced, char replacementChar, int start, int end) {
	for (int i = end; --i >= start;)
		for (int j = toBeReplaced.length; --j >= 0;)
			if (array[i] == toBeReplaced[j])
				array[i] = replacementChar;
}
/**
 * Answers a new array of characters with substitutions. No side-effect is operated on the original
 * array, in case no substitution happened, then the result is the same as the
 * original one.
 * <br>
 * <br>
 * For example:
 * <ol>
 * <li><pre>
 *    array = { 'a' , 'b', 'b', 'a', 'b', 'a' }
 *    toBeReplaced = { 'b' }
 *    replacementChar = { 'a', 'a' }
 *    result => { 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a' }
 * </pre>
 * </li>
 * <li><pre>
 *    array = { 'a' , 'b', 'b', 'a', 'b', 'a' }
 *    toBeReplaced = { 'c' }
 *    replacementChar = { 'a' }
 *    result => { 'a' , 'b', 'b', 'a', 'b', 'a' }
 * </pre>
 * </li>
 * </ol>
 *
 * @param array the given array
 * @param toBeReplaced characters to be replaced
 * @param replacementChars the replacement characters
 * @return a new array of characters with substitutions or the given array if none
 * @throws NullPointerException if the given array is null
 */
public static final char[] replace(
	char[] array,
	char[] toBeReplaced,
	char[] replacementChars) {

	int max = array.length;
	int replacedLength = toBeReplaced.length;
	int replacementLength = replacementChars.length;

	int[] starts = new int[5];
	int occurrenceCount = 0;

	if (!equals(toBeReplaced, replacementChars)) {

		next : for (int i = 0; i < max;) {
			int index = indexOf(toBeReplaced, array, true, i);
			if (index == -1) {
				i++;
				continue next;
			}
			if (occurrenceCount == starts.length) {
				System.arraycopy(
					starts,
					0,
					starts = new int[occurrenceCount * 2],
					0,
					occurrenceCount);
			}
			starts[occurrenceCount++] = index;
			i = index + replacedLength;
		}
	}
	if (occurrenceCount == 0)
		return array;
	char[] result =
		new char[max
			+ occurrenceCount * (replacementLength - replacedLength)];
	int inStart = 0, outStart = 0;
	for (int i = 0; i < occurrenceCount; i++) {
		int offset = starts[i] - inStart;
		System.arraycopy(array, inStart, result, outStart, offset);
		inStart += offset;
		outStart += offset;
		System.arraycopy(
			replacementChars,
			0,
			result,
			outStart,
			replacementLength);
		inStart += replacedLength;
		outStart += replacementLength;
	}
	System.arraycopy(array, inStart, result, outStart, max - inStart);
	return result;
}

/**
 * Replace all occurrence of the character to be replaced with the replacement character
 * in a copy of the given array. Returns the given array if no occurrences of the character
 * to be replaced are found.
 * <br>
 * <br>
 * For example:
 * <ol>
 * <li><pre>
 *    array = { 'a' , 'b', 'b', 'a', 'b', 'a' }
 *    toBeReplaced = 'b'
 *    replacementChar = 'a'
 *    result => A new array that is equals to { 'a' , 'a', 'a', 'a', 'a', 'a' }
 * </pre>
 * </li>
 * <li><pre>
 *    array = { 'a' , 'b', 'b', 'a', 'b', 'a' }
 *    toBeReplaced = 'c'
 *    replacementChar = 'a'
 *    result => The original array that remains unchanged.
 * </pre>
 * </li>
 * </ol>
 *
 * @param array the given array
 * @param toBeReplaced the character to be replaced
 * @param replacementChar the replacement character
 * @throws NullPointerException if the given array is null
 * @since 3.1
 */
public static final char[] replaceOnCopy(
	char[] array,
	char toBeReplaced,
	char replacementChar) {

	char[] result = null;
	for (int i = 0, length = array.length; i < length; i++) {
		char c = array[i];
		if (c == toBeReplaced) {
			if (result == null) {
				result = new char[length];
				System.arraycopy(array, 0, result, 0, i);
			}
			result[i] = replacementChar;
		} else if (result != null) {
			result[i] = c;
		}
	}
	if (result == null) return array;
	return result;
}

/**
 * Return a new array which is the split of the given array using the given divider and trimming each subarray to remove
 * whitespaces equals to ' '.
 * <br>
 * <br>
 * For example:
 * <ol>
 * <li><pre>
 *    divider = 'b'
 *    array = { 'a' , 'b', 'b', 'a', 'b', 'a' }
 *    result => { { 'a' }, {  }, { 'a' }, { 'a' } }
 * </pre>
 * </li>
 * <li><pre>
 *    divider = 'c'
 *    array = { 'a' , 'b', 'b', 'a', 'b', 'a' }
 *    result => { { 'a', 'b', 'b', 'a', 'b', 'a' } }
 * </pre>
 * </li>
 * <li><pre>
 *    divider = 'b'
 *    array = { 'a' , ' ', 'b', 'b', 'a', 'b', 'a' }
 *    result => { { 'a' }, {  }, { 'a' }, { 'a' } }
 * </pre>
 * </li>
 * <li><pre>
 *    divider = 'c'
 *    array = { ' ', ' ', 'a' , 'b', 'b', 'a', 'b', 'a', ' ' }
 *    result => { { 'a', 'b', 'b', 'a', 'b', 'a' } }
 * </pre>
 * </li>
 * </ol>
 *
 * @param divider the given divider
 * @param array the given array
 * @return a new array which is the split of the given array using the given divider and trimming each subarray to remove
 * whitespaces equals to ' '
 */
public static final char[][] splitAndTrimOn(char divider, char[] array) {
	int length = array == null ? 0 : array.length;
	if (length == 0)
		return NO_CHAR_CHAR;

	int wordCount = 1;
	for (int i = 0; i < length; i++)
		if (array[i] == divider)
			wordCount++;
	char[][] split = new char[wordCount][];
	int last = 0, currentWord = 0;
	for (int i = 0; i < length; i++) {
		if (array[i] == divider) {
			int start = last, end = i - 1;
			while (start < i && array[start] == ' ')
				start++;
			while (end > start && array[end] == ' ')
				end--;
			split[currentWord] = new char[end - start + 1];
			System.arraycopy(
				array,
				start,
				split[currentWord++],
				0,
				end - start + 1);
			last = i + 1;
		}
	}
	int start = last, end = length - 1;
	while (start < length && array[start] == ' ')
		start++;
	while (end > start && array[end] == ' ')
		end--;
	split[currentWord] = new char[end - start + 1];
	System.arraycopy(
		array,
		start,
		split[currentWord++],
		0,
		end - start + 1);
	return split;
}

/**
 * Return a new array which is the split of the given array using the given divider.
 * <br>
 * <br>
 * For example:
 * <ol>
 * <li><pre>
 *    divider = 'b'
 *    array = { 'a' , 'b', 'b', 'a', 'b', 'a' }
 *    result => { { 'a' }, {  }, { 'a' }, { 'a' } }
 * </pre>
 * </li>
 * <li><pre>
 *    divider = 'c'
 *    array = { 'a' , 'b', 'b', 'a', 'b', 'a' }
 *    result => { { 'a', 'b', 'b', 'a', 'b', 'a' } }
 * </pre>
 * </li>
 * <li><pre>
 *    divider = 'c'
 *    array = { ' ', ' ', 'a' , 'b', 'b', 'a', 'b', 'a', ' ' }
 *    result => { { ' ', 'a', 'b', 'b', 'a', 'b', 'a', ' ' } }
 * </pre>
 * </li>
 * </ol>
 *
 * @param divider the given divider
 * @param array the given array
 * @return a new array which is the split of the given array using the given divider
 */
public static final char[][] splitOn(char divider, char[] array) {
	int length = array == null ? 0 : array.length;
	if (length == 0)
		return NO_CHAR_CHAR;

	int wordCount = 1;
	for (int i = 0; i < length; i++)
		if (array[i] == divider)
			wordCount++;
	char[][] split = new char[wordCount][];
	int last = 0, currentWord = 0;
	for (int i = 0; i < length; i++) {
		if (array[i] == divider) {
			split[currentWord] = new char[i - last];
			System.arraycopy(
				array,
				last,
				split[currentWord++],
				0,
				i - last);
			last = i + 1;
		}
	}
	split[currentWord] = new char[length - last];
	System.arraycopy(array, last, split[currentWord], 0, length - last);
	return split;
}

/**
 * Return a new array which is the split of the given array using the given divider. The given end
 * is exclusive and the given start is inclusive.
 * <br>
 * <br>
 * For example:
 * <ol>
 * <li><pre>
 *    divider = 'b'
 *    array = { 'a' , 'b', 'b', 'a', 'b', 'a' }
 *    start = 2
 *    end = 5
 *    result => { {  }, { 'a' }, {  } }
 * </pre>
 * </li>
 * </ol>
 *
 * @param divider the given divider
 * @param array the given array
 * @param start the given starting index
 * @param end the given ending index
 * @return a new array which is the split of the given array using the given divider
 * @throws ArrayIndexOutOfBoundsException if start is lower than 0 or end is greater than the array length
 */
public static final char[][] splitOn(
	char divider,
	char[] array,
	int start,
	int end) {
	int length = array == null ? 0 : array.length;
	if (length == 0 || start > end)
		return NO_CHAR_CHAR;

	int wordCount = 1;
	for (int i = start; i < end; i++)
		if (array[i] == divider)
			wordCount++;
	char[][] split = new char[wordCount][];
	int last = start, currentWord = 0;
	for (int i = start; i < end; i++) {
		if (array[i] == divider) {
			split[currentWord] = new char[i - last];
			System.arraycopy(
				array,
				last,
				split[currentWord++],
				0,
				i - last);
			last = i + 1;
		}
	}
	split[currentWord] = new char[end - last];
	System.arraycopy(array, last, split[currentWord], 0, end - last);
	return split;
}

/**
 * Return a new array which is the split of the given array using the given divider ignoring the
 * text between (possibly nested) openEncl and closingEncl. If there are no openEncl in the code
 * this is identical to {@link CharOperation#splitOn(char, char[], int, int)}. The given end
 * is exclusive and the given start is inclusive.
 * <br>
 * <br>
 * For example:
 * <ol>
 * <li><pre>
 *    divider = ','
 *    array = { 'A' , '<', 'B', ',', 'C', '>', ',', 'D' }
 *    start = 0
 *    end = 8
 *    result => { {  'A' , '<', 'B', ',', 'C', '>'}, { 'D' }}
 * </pre>
 * </li>
 * </ol>
 *
 * @param divider the given divider
 * @param openEncl the opening enclosure
 * @param closeEncl the closing enclosure
 * @param array the given array
 * @param start the given starting index
 * @param end the given ending index
 * @return a new array which is the split of the given array using the given divider
 * @throws ArrayIndexOutOfBoundsException if start is lower than 0 or end is greater than the array length
 * @since 3.12
 */
public static final char[][] splitOnWithEnclosures(
		char divider,
		char openEncl,
		char closeEncl,
		char[] array,
		int start,
		int end) {
		int length = array == null ? 0 : array.length;
		if (length == 0 || start > end)
			return NO_CHAR_CHAR;

		int wordCount = 1;
		int enclCount = 0;
		for (int i = start; i < end; i++) {
			if (array[i] == openEncl)
				enclCount++;
			else if (array[i] == divider)
				wordCount++;
		}
		if (enclCount == 0)
			return CharOperation.splitOn(divider, array, start, end);

		int nesting = 0;
		if (openEncl == divider || closeEncl == divider) // divider should be distinct
			return CharOperation.NO_CHAR_CHAR;

		int[][] splitOffsets = new int[wordCount][2]; //maximum
		int last = start, currentWord = 0, prevOffset = start;
		for (int i = start; i < end; i++) {
			if (array[i] == openEncl) {
				++nesting;
				continue;
			}
			if (array[i] == closeEncl) {
				if (nesting > 0)
					--nesting;
				continue;
			}
			if (array[i] == divider && nesting == 0) {
				splitOffsets[currentWord][0] = prevOffset;
				last = splitOffsets[currentWord++][1] = i;
				prevOffset = last + 1;
			}
		}
		if (last < end - 1) {
			splitOffsets[currentWord][0] = prevOffset;
			splitOffsets[currentWord++][1] = end;
		}
		char[][] split = new char[currentWord][];
		for (int i = 0; i < currentWord; ++i) {
			int sStart = splitOffsets[i][0];
			int sEnd = splitOffsets[i][1];
			int size = sEnd - sStart;
			split[i] = new char[size];
			System.arraycopy(array, sStart, split[i], 0, size);
		}
		return split;
	}

/**
 * Answers a new array which is a copy of the given array starting at the given start and
 * ending at the given end. The given start is inclusive and the given end is exclusive.
 * Answers null if start is greater than end, if start is lower than 0 or if end is greater
 * than the length of the given array. If end  equals -1, it is converted to the array length.
 * <br>
 * <br>
 * For example:
 * <ol>
 * <li><pre>
 *    array = { { 'a' } , { 'b' } }
 *    start = 0
 *    end = 1
 *    result => { { 'a' } }
 * </pre>
 * </li>
 * <li><pre>
 *    array = { { 'a' } , { 'b' } }
 *    start = 0
 *    end = -1
 *    result => { { 'a' }, { 'b' } }
 * </pre>
 * </li>
 * </ol>
 *
 * @param array the given array
 * @param start the given starting index
 * @param end the given ending index
 * @return a new array which is a copy of the given array starting at the given start and
 * ending at the given end
 * @throws NullPointerException if the given array is null
 */
public static final char[][] subarray(char[][] array, int start, int end) {
	if (end == -1)
		end = array.length;
	if (start > end)
		return null;
	if (start < 0)
		return null;
	if (end > array.length)
		return null;

	char[][] result = new char[end - start][];
	System.arraycopy(array, start, result, 0, end - start);
	return result;
}

/**
 * Answers a new array which is a copy of the given array starting at the given start and
 * ending at the given end. The given start is inclusive and the given end is exclusive.
 * Answers null if start is greater than end, if start is lower than 0 or if end is greater
 * than the length of the given array. If end  equals -1, it is converted to the array length.
 * <br>
 * <br>
 * For example:
 * <ol>
 * <li><pre>
 *    array = { 'a' , 'b' }
 *    start = 0
 *    end = 1
 *    result => { 'a' }
 * </pre>
 * </li>
 * <li><pre>
 *    array = { 'a', 'b' }
 *    start = 0
 *    end = -1
 *    result => { 'a' , 'b' }
 * </pre>
 * </li>
 * </ol>
 *
 * @param array the given array
 * @param start the given starting index
 * @param end the given ending index
 * @return a new array which is a copy of the given array starting at the given start and
 * ending at the given end
 * @throws NullPointerException if the given array is null
 */
public static final char[] subarray(char[] array, int start, int end) {
	if (end == -1)
		end = array.length;
	if (start > end)
		return null;
	if (start < 0)
		return null;
	if (end > array.length)
		return null;

	char[] result = new char[end - start];
	System.arraycopy(array, start, result, 0, end - start);
	return result;
}

/**
 * Answers the result of a char[] conversion to lowercase. Answers null if the given chars array is null.
 * <br>
 * NOTE: If no conversion was necessary, then answers back the argument one.
 * <br>
 * <br>
 * For example:
 * <ol>
 * <li><pre>
 *    chars = { 'a' , 'b' }
 *    result => { 'a' , 'b' }
 * </pre>
 * </li>
 * <li><pre>
 *    array = { 'A', 'b' }
 *    result => { 'a' , 'b' }
 * </pre>
 * </li>
 * </ol>
 *
 * @param chars the chars to convert
 * @return the result of a char[] conversion to lowercase
 */
final static public char[] toLowerCase(char[] chars) {
	if (chars == null)
		return null;
	int length = chars.length;
	char[] lowerChars = null;
	for (int i = 0; i < length; i++) {
		char c = chars[i];
		char lc = ScannerHelper.toLowerCase(c);
		if ((c != lc) || (lowerChars != null)) {
			if (lowerChars == null) {
				System.arraycopy(
					chars,
					0,
					lowerChars = new char[length],
					0,
					i);
			}
			lowerChars[i] = lc;
		}
	}
	return lowerChars == null ? chars : lowerChars;
}

/**
 * Answers the result of a char[] conversion to uppercase. Answers null if the given chars array is null.
 * <br>
 * NOTE: If no conversion was necessary, then answers back the argument one.
 * <br>
 * <br>
 * For example:
 * <ol>
 * <li><pre>
 *    chars = { 'A' , 'B' }
 *    result => { 'A' , 'B' }
 * </pre>
 * </li>
 * <li><pre>
 *    array = { 'a', 'B' }
 *    result => { 'A' , 'B' }
 * </pre>
 * </li>
 * </ol>
 *
 * @param chars the chars to convert
 * @return the result of a char[] conversion to uppercase
 *
 * @since 3.5
 */
final static public char[] toUpperCase(char[] chars) {
	if (chars == null)
		return null;
	int length = chars.length;
	char[] upperChars = null;
	for (int i = 0; i < length; i++) {
		char c = chars[i];
		char lc = ScannerHelper.toUpperCase(c);
		if ((c != lc) || (upperChars != null)) {
			if (upperChars == null) {
				System.arraycopy(
					chars,
					0,
					upperChars = new char[length],
					0,
					i);
			}
			upperChars[i] = lc;
		}
	}
	return upperChars == null ? chars : upperChars;
}

/**
 * Answers a new array removing leading and trailing spaces (' '). Answers the given array if there is no
 * space characters to remove.
 * <br>
 * <br>
 * For example:
 * <ol>
 * <li><pre>
 *    chars = { ' ', 'a' , 'b', ' ',  ' ' }
 *    result => { 'a' , 'b' }
 * </pre>
 * </li>
 * <li><pre>
 *    array = { 'A', 'b' }
 *    result => { 'A' , 'b' }
 * </pre>
 * </li>
 * </ol>
 *
 * @param chars the given array
 * @return a new array removing leading and trailing spaces (' ')
 */
final static public char[] trim(char[] chars) {

	if (chars == null)
		return null;

	int start = 0, length = chars.length, end = length - 1;
	while (start < length && chars[start] == ' ') {
		start++;
	}
	while (end > start && chars[end] == ' ') {
		end--;
	}
	if (start != 0 || end != length - 1) {
		return subarray(chars, start, end + 1);
	}
	return chars;
}

/**
 * Answers a string which is the concatenation of the given array using the '.' as a separator.
 * <br>
 * <br>
 * For example:
 * <ol>
 * <li><pre>
 *    array = { { 'a' } , { 'b' } }
 *    result => "a.b"
 * </pre>
 * </li>
 * <li><pre>
 *    array = { { ' ',  'a' } , { 'b' } }
 *    result => " a.b"
 * </pre>
 * </li>
 * </ol>
 *
 * @param array the given array
 * @return a string which is the concatenation of the given array using the '.' as a separator
 */
final static public String toString(char[][] array) {
	char[] result = concatWith(array, '.');
	return new String(result);
}

/**
 * Answers an array of strings from the given array of char array.
 *
 * @param array the given array
 * @return an array of strings
 * @since 3.0
 */
final static public String[] toStrings(char[][] array) {
	if (array == null) return NO_STRINGS;
	int length = array.length;
	if (length == 0) return NO_STRINGS;
	String[] result = new String[length];
	for (int i = 0; i < length; i++)
		result[i] = new String(array[i]);
	return result;
}
}
