/******************************************************************************* * 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; } }