/*******************************************************************************
* Copyright (c) 2000, 2009 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
* Luiz-Otavio Zorzella <zorzella at gmail dot com> - Improve CamelCase algorithm
*******************************************************************************/
package org.eclipse.jdt.core.compiler;
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];
/**
* 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;
}
/**
* 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 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>
*
* <pre>
* 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>
* </pre>
*
* @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>
*
* <pre>
* 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>
* </pre>
*
* @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>
* 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>
*
* <pre>
* 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>
* </pre>
*
* @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>
* 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
}
}
/**
* 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);
}
/**
* 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 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>
* </p>
*
* @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 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 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 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;
}
/**
* 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) {
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 (first[i] != second[i])
return false;
return true;
}
/**
* 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
* @throws NullPointerException if array is null
*/
public static final int hashCode(char[] array) {
int length= array.length;
int hash= length == 0 ? 31 : array[0];
if (length < 8) {
for (int i= length; --i > 0;)
hash= (hash * 31) + array[i];
} else {
// 8 characters is enough to compute a decent hash code, don't waste time examining every character
for (int i= length - 1, last= i > 16 ? i - 16 : 0; i > last; i-= 2)
hash= (hash * 31) + array[i];
}
return hash & 0x7FFFFFFF;
}
/**
* Answers true if c is a whitespace according to the JLS (\u000a, \u000c, \u000d,
* \u0009), otherwise false. <br>
* <br>
* For example:
* <ol>
* <li>
*
* <pre>
* c = ' '
* result => true
* </pre>
*
* </li>
* <li>
*
* <pre>
* c = '\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)
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);
}
/**
* Answers true if the pattern matches the given name, false otherwise. This char[] pattern
* matching accepts wild-cards '*' and '?'.
*
* When not case sensitive, the pattern is assumed to already be lowercased, the name will be
* lowercased character per character as comparing. If name is null, the answer is false. If
* pattern is null, the answer is true if name is not null. <br>
* <br>
* 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 = 2
* 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 ((iPattern < patternEnd)
&& (patternChar= pattern[iPattern]) != '*') {
if (iName == nameEnd)
return false;
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) {
int max= prefix.length;
if (name.length < max)
return false;
if (isCaseSensitive) {
for (int i= max; --i >= 0;)
// assumes the prefix is not larger than the name
if (prefix[i] != name[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[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;
}
/**
* 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;
}
}