| /******************************************************************************* |
| * Copyright (c) 2000, 2019 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| * Luiz-Otavio Zorzella <zorzella at gmail dot com> - Improve CamelCase algorithm |
| * Gábor Kövesdán - Contribution for Bug 350000 - [content assist] Include non-prefix matches in auto-complete suggestions |
| * Stefan Xenos <sxenos@gmail.com> (Google) - Bug 501283 - Lots of hash collisions during indexing |
| *******************************************************************************/ |
| package org.eclipse.jdt.core.compiler; |
| |
| import java.util.Arrays; |
| import java.util.List; |
| |
| import org.eclipse.jdt.internal.compiler.parser.ScannerHelper; |
| |
| /** |
| * This class is a collection of helper methods to manipulate char arrays. |
| * |
| * @since 2.1 |
| * @noinstantiate This class is not intended to be instantiated by clients. |
| */ |
| public final class CharOperation { |
| |
| /** |
| * Constant for an empty char array |
| */ |
| public static final char[] NO_CHAR = new char[0]; |
| |
| /** |
| * Constant for an empty char array with two dimensions. |
| */ |
| public static final char[][] NO_CHAR_CHAR = new char[0][]; |
| |
| /** |
| * Constant for an empty String array. |
| * @since 3.1 |
| */ |
| public static final String[] NO_STRINGS = new String[0]; |
| |
| /** |
| * Constant for all Prefix |
| * @since 3.14 |
| */ |
| public static final char[] ALL_PREFIX = new char[] {'*'}; |
| |
| /** |
| * Constant for comma |
| * @since 3.14 |
| */ |
| public static final char[] COMMA_SEPARATOR = new char[] {','}; |
| |
| /** |
| * Answers a new array with appending the suffix character at the end of the array. |
| * <br> |
| * <br> |
| * For example:<br> |
| * <ol> |
| * <li><pre> |
| * array = { 'a', 'b' } |
| * suffix = 'c' |
| * => result = { 'a', 'b' , 'c' } |
| * </pre> |
| * </li> |
| * <li><pre> |
| * array = null |
| * suffix = 'c' |
| * => result = { 'c' } |
| * </pre></li> |
| * </ol> |
| * |
| * @param array the array that is concatenated with the suffix character |
| * @param suffix the suffix character |
| * @return the new array |
| */ |
| public static final char[] append(char[] array, char suffix) { |
| if (array == null) |
| return new char[] { suffix }; |
| int length = array.length; |
| System.arraycopy(array, 0, array = new char[length + 1], 0, length); |
| array[length] = suffix; |
| return array; |
| } |
| |
| /** |
| * Answers a new array with appending the sub-array at the end of the array. |
| * <br> |
| * <br> |
| * For example:<br> |
| * <ol> |
| * <li><pre> |
| * array = { 'a', 'b' } |
| * suffix = { 'c', 'd' } |
| * => result = { 'a', 'b' , 'c' , d' } |
| * </pre> |
| * </li> |
| * <li><pre> |
| * array = null |
| * suffix = { 'c' } |
| * => result = { 'c' } |
| * </pre></li> |
| * </ol> |
| * |
| * @param target the array that is concatenated with the suffix array. |
| * @param suffix the array that will be concatenated to the target |
| * @return the new array |
| * @throws NullPointerException if the target array is null |
| * @since 3.11 |
| */ |
| public static final char[] append(char[] target, char[] suffix) { |
| if(suffix == null || suffix.length == 0) |
| return target; |
| int targetLength = target.length; |
| int subLength = suffix.length; |
| int newTargetLength = targetLength + subLength; |
| if (newTargetLength > targetLength) { |
| System.arraycopy(target, 0, target = new char[newTargetLength], 0, targetLength); |
| } |
| System.arraycopy(suffix, 0, target, targetLength, subLength); |
| return target; |
| } |
| |
| /** |
| * Append the given sub-array to the target array starting at the given index in the target array. |
| * The start of the sub-array is inclusive, the end is exclusive. |
| * Answers a new target array if it needs to grow, otherwise answers the same target array. |
| * <br> |
| * For example:<br> |
| * <ol> |
| * <li><pre> |
| * target = { 'a', 'b', '0' } |
| * index = 2 |
| * array = { 'c', 'd' } |
| * start = 0 |
| * end = 1 |
| * => result = { 'a', 'b' , 'c' } |
| * </pre> |
| * </li> |
| * <li><pre> |
| * target = { 'a', 'b' } |
| * index = 2 |
| * array = { 'c', 'd' } |
| * start = 0 |
| * end = 1 |
| * => result = { 'a', 'b' , 'c', '0', '0' , '0' } (new array) |
| * </pre></li> |
| * <li><pre> |
| * target = { 'a', 'b', 'c' } |
| * index = 1 |
| * array = { 'c', 'd', 'e', 'f' } |
| * start = 1 |
| * end = 4 |
| * => result = { 'a', 'd' , 'e', 'f', '0', '0', '0', '0' } (new array) |
| * </pre></li> |
| * </ol> |
| * |
| * @param target the given target |
| * @param index the given index |
| * @param array the given array |
| * @param start the given start index |
| * @param end the given end index |
| * |
| * @return the new array |
| * @throws NullPointerException if the target array is null |
| */ |
| public static final char[] append(char[] target, int index, char[] array, int start, int end) { |
| int targetLength = target.length; |
| int subLength = end-start; |
| int newTargetLength = subLength+index; |
| if (newTargetLength > targetLength) { |
| System.arraycopy(target, 0, target = new char[newTargetLength*2], 0, index); |
| } |
| System.arraycopy(array, start, target, index, subLength); |
| return target; |
| } |
| |
| /** |
| * Answers a new array with prepending the prefix character at the start of the array. |
| * <br> |
| * <br> |
| * For example:<br> |
| * <ol> |
| * <li><pre> |
| * prefix = 'c' |
| * array = { 'a', 'b' } |
| * => result = { 'c' , 'a', 'b' } |
| * </pre> |
| * </li> |
| * <li><pre> |
| * prefix = 'c' |
| * array = null |
| * => result = { 'c' } |
| * </pre></li> |
| * </ol> |
| * |
| * @param array the array that is concatenated with the prefix character |
| * @param prefix the prefix character |
| * @return the new array |
| * @since 3.14 |
| */ |
| public static final char[] prepend(char prefix, char[] array) { |
| if (array == null) |
| return new char[] { prefix }; |
| int length = array.length; |
| System.arraycopy(array, 0, array = new char[length + 1], 1, length); |
| array[0] = prefix; |
| return array; |
| } |
| |
| /** |
| * Answers the concatenation of the two arrays. It answers null if the two arrays are null. |
| * If the first array is null, then the second array is returned. |
| * If the second array is null, then the first array is returned. |
| * <br> |
| * <br> |
| * For example: |
| * <ol> |
| * <li><pre> |
| * first = null |
| * second = null |
| * => result = null |
| * </pre> |
| * </li> |
| * <li><pre> |
| * first = { { ' a' } } |
| * second = null |
| * => result = { { ' a' } } |
| * </pre> |
| * </li> |
| * <li><pre> |
| * first = null |
| * second = { { ' a' } } |
| * => result = { { ' a' } } |
| * </pre> |
| * </li> |
| * <li><pre> |
| * first = { { ' b' } } |
| * second = { { ' a' } } |
| * => result = { { ' b' }, { ' a' } } |
| * </pre> |
| * </li> |
| * </ol> |
| * |
| * @param first the first array to concatenate |
| * @param second the second array to concatenate |
| * @return the concatenation of the two arrays, or null if the two arrays are null. |
| */ |
| public static final char[][] arrayConcat(char[][] first, char[][] second) { |
| if (first == null) |
| return second; |
| if (second == null) |
| return first; |
| |
| int length1 = first.length; |
| int length2 = second.length; |
| char[][] result = new char[length1 + length2][]; |
| System.arraycopy(first, 0, result, 0, length1); |
| System.arraycopy(second, 0, result, length1, length2); |
| return result; |
| } |
| |
| /** |
| * Answers true if the pattern matches the given name using CamelCase rules, or |
| * false otherwise. char[] CamelCase matching does NOT accept explicit wild-cards |
| * '*' and '?' and is inherently case sensitive. |
| * <p> |
| * CamelCase denotes the convention of writing compound names without spaces, |
| * and capitalizing every term. This function recognizes both upper and lower |
| * CamelCase, depending whether the leading character is capitalized or not. |
| * The leading part of an upper CamelCase pattern is assumed to contain a |
| * sequence of capitals which are appearing in the matching name; e.g. 'NPE' will |
| * match 'NullPointerException', but not 'NewPerfData'. A lower CamelCase pattern |
| * uses a lowercase first character. In Java, type names follow the upper |
| * CamelCase convention, whereas method or field names follow the lower |
| * CamelCase convention. |
| * <p> |
| * The pattern may contain lowercase characters, which will be matched in a case |
| * sensitive way. These characters must appear in sequence in the name. |
| * For instance, 'NPExcep' will match 'NullPointerException', but not |
| * 'NullPointerExCEPTION' or 'NuPoEx' will match 'NullPointerException', but not |
| * 'NoPointerException'. |
| * <p> |
| * Digit characters are treated in a special way. They can be used in the pattern |
| * but are not always considered as leading character. For instance, both |
| * 'UTF16DSS' and 'UTFDSS' patterns will match 'UTF16DocumentScannerSupport'. |
| * <p> |
| * Using this method allows matching names to have more parts than the specified |
| * pattern (see {@link #camelCaseMatch(char[], char[], boolean)}).<br> |
| * For instance, 'HM' , 'HaMa' and 'HMap' patterns will match 'HashMap', |
| * 'HatMapper' <b>and also</b> 'HashMapEntry'. |
| * <p> |
| * Examples: |
| * <ol> |
| * <li> pattern = "NPE".toCharArray() |
| * name = "NullPointerException".toCharArray() |
| * result => true</li> |
| * <li> pattern = "NPE".toCharArray() |
| * name = "NoPermissionException".toCharArray() |
| * result => true</li> |
| * <li> pattern = "NuPoEx".toCharArray() |
| * name = "NullPointerException".toCharArray() |
| * result => true</li> |
| * <li> pattern = "NuPoEx".toCharArray() |
| * name = "NoPermissionException".toCharArray() |
| * result => false</li> |
| * <li> pattern = "npe".toCharArray() |
| * name = "NullPointerException".toCharArray() |
| * result => false</li> |
| * <li> pattern = "IPL3".toCharArray() |
| * name = "IPerspectiveListener3".toCharArray() |
| * result => true</li> |
| * <li> pattern = "HM".toCharArray() |
| * name = "HashMapEntry".toCharArray() |
| * result => true</li> |
| * </ol> |
| * |
| * @param pattern the given pattern |
| * @param name the given name |
| * @return true if the pattern matches the given name, false otherwise |
| * @since 3.2 |
| */ |
| public static final boolean camelCaseMatch(char[] pattern, char[] name) { |
| if (pattern == null) |
| return true; // null pattern is equivalent to '*' |
| if (name == null) |
| return false; // null name cannot match |
| |
| return camelCaseMatch(pattern, 0, pattern.length, name, 0, name.length, false/*not the same count of parts*/); |
| } |
| |
| /** |
| * Answers true if the pattern matches the given name using CamelCase rules, or |
| * false otherwise. char[] CamelCase matching does NOT accept explicit wild-cards |
| * '*' and '?' and is inherently case sensitive. |
| * <p> |
| * CamelCase denotes the convention of writing compound names without spaces, |
| * and capitalizing every term. This function recognizes both upper and lower |
| * CamelCase, depending whether the leading character is capitalized or not. |
| * The leading part of an upper CamelCase pattern is assumed to contain a |
| * sequence of capitals which are appearing in the matching name; e.g. 'NPE' will |
| * match 'NullPointerException', but not 'NewPerfData'. A lower CamelCase pattern |
| * uses a lowercase first character. In Java, type names follow the upper |
| * CamelCase convention, whereas method or field names follow the lower |
| * CamelCase convention. |
| * <p> |
| * The pattern may contain lowercase characters, which will be matched in a case |
| * sensitive way. These characters must appear in sequence in the name. |
| * For instance, 'NPExcep' will match 'NullPointerException', but not |
| * 'NullPointerExCEPTION' or 'NuPoEx' will match 'NullPointerException', but not |
| * 'NoPointerException'. |
| * <p> |
| * Digit characters are treated in a special way. They can be used in the pattern |
| * but are not always considered as leading character. For instance, both |
| * 'UTF16DSS' and 'UTFDSS' patterns will match 'UTF16DocumentScannerSupport'. |
| * <p> |
| * CamelCase can be restricted to match only the same count of parts. When this |
| * restriction is specified the given pattern and the given name must have <b>exactly</b> |
| * the same number of parts (i.e. the same number of uppercase characters).<br> |
| * For instance, 'HM' , 'HaMa' and 'HMap' patterns will match 'HashMap' and |
| * 'HatMapper' <b>but not</b> 'HashMapEntry'. |
| * <p> |
| * Examples:<ol> |
| * <li> pattern = "NPE".toCharArray() |
| * name = "NullPointerException".toCharArray() |
| * result => true</li> |
| * <li> pattern = "NPE".toCharArray() |
| * name = "NoPermissionException".toCharArray() |
| * result => true</li> |
| * <li> pattern = "NuPoEx".toCharArray() |
| * name = "NullPointerException".toCharArray() |
| * result => true</li> |
| * <li> pattern = "NuPoEx".toCharArray() |
| * name = "NoPermissionException".toCharArray() |
| * result => false</li> |
| * <li> pattern = "npe".toCharArray() |
| * name = "NullPointerException".toCharArray() |
| * result => false</li> |
| * <li> pattern = "IPL3".toCharArray() |
| * name = "IPerspectiveListener3".toCharArray() |
| * result => true</li> |
| * <li> pattern = "HM".toCharArray() |
| * name = "HashMapEntry".toCharArray() |
| * result => (samePartCount == false)</li> |
| * </ol> |
| * |
| * @param pattern the given pattern |
| * @param name the given name |
| * @param samePartCount flag telling whether the pattern and the name should |
| * have the same count of parts or not.<br> |
| * For example: |
| * <ul> |
| * <li>'HM' type string pattern will match 'HashMap' and 'HtmlMapper' types, |
| * but not 'HashMapEntry'</li> |
| * <li>'HMap' type string pattern will still match previous 'HashMap' and |
| * 'HtmlMapper' types, but not 'HighMagnitude'</li> |
| * </ul> |
| * @return true if the pattern matches the given name, false otherwise |
| * @since 3.4 |
| */ |
| public static final boolean camelCaseMatch(char[] pattern, char[] name, boolean samePartCount) { |
| if (pattern == null) |
| return true; // null pattern is equivalent to '*' |
| if (name == null) |
| return false; // null name cannot match |
| |
| return camelCaseMatch(pattern, 0, pattern.length, name, 0, name.length, samePartCount); |
| } |
| |
| /** |
| * Answers true if a sub-pattern matches the sub-part of the given name using |
| * CamelCase rules, or false otherwise. char[] CamelCase matching does NOT |
| * accept explicit wild-cards '*' and '?' and is inherently case sensitive. |
| * Can match only subset of name/pattern, considering end positions as non-inclusive. |
| * The sub-pattern is defined by the patternStart and patternEnd positions. |
| * <p> |
| * CamelCase denotes the convention of writing compound names without spaces, |
| * and capitalizing every term. This function recognizes both upper and lower |
| * CamelCase, depending whether the leading character is capitalized or not. |
| * The leading part of an upper CamelCase pattern is assumed to contain a |
| * sequence of capitals which are appearing in the matching name; e.g. 'NPE' will |
| * match 'NullPointerException', but not 'NewPerfData'. A lower CamelCase pattern |
| * uses a lowercase first character. In Java, type names follow the upper |
| * CamelCase convention, whereas method or field names follow the lower |
| * CamelCase convention. |
| * <p> |
| * The pattern may contain lowercase characters, which will be matched in a case |
| * sensitive way. These characters must appear in sequence in the name. |
| * For instance, 'NPExcep' will match 'NullPointerException', but not |
| * 'NullPointerExCEPTION' or 'NuPoEx' will match 'NullPointerException', but not |
| * 'NoPointerException'. |
| * <p> |
| * Digit characters are treated in a special way. They can be used in the pattern |
| * but are not always considered as leading character. For instance, both |
| * 'UTF16DSS' and 'UTFDSS' patterns will match 'UTF16DocumentScannerSupport'. |
| * <p> |
| * Digit characters are treated in a special way. They can be used in the pattern |
| * but are not always considered as leading character. For instance, both |
| * 'UTF16DSS' and 'UTFDSS' patterns will match 'UTF16DocumentScannerSupport'. |
| * <p> |
| * Using this method allows matching names to have more parts than the specified |
| * pattern (see {@link #camelCaseMatch(char[], int, int, char[], int, int, boolean)}).<br> |
| * For instance, 'HM' , 'HaMa' and 'HMap' patterns will match 'HashMap', |
| * 'HatMapper' <b>and also</b> 'HashMapEntry'. |
| * <p> |
| * Examples: |
| * <ol> |
| * <li> pattern = "NPE".toCharArray() |
| * patternStart = 0 |
| * patternEnd = 3 |
| * name = "NullPointerException".toCharArray() |
| * nameStart = 0 |
| * nameEnd = 20 |
| * result => true</li> |
| * <li> pattern = "NPE".toCharArray() |
| * patternStart = 0 |
| * patternEnd = 3 |
| * name = "NoPermissionException".toCharArray() |
| * nameStart = 0 |
| * nameEnd = 21 |
| * result => true</li> |
| * <li> pattern = "NuPoEx".toCharArray() |
| * patternStart = 0 |
| * patternEnd = 6 |
| * name = "NullPointerException".toCharArray() |
| * nameStart = 0 |
| * nameEnd = 20 |
| * result => true</li> |
| * <li> pattern = "NuPoEx".toCharArray() |
| * patternStart = 0 |
| * patternEnd = 6 |
| * name = "NoPermissionException".toCharArray() |
| * nameStart = 0 |
| * nameEnd = 21 |
| * result => false</li> |
| * <li> pattern = "npe".toCharArray() |
| * patternStart = 0 |
| * patternEnd = 3 |
| * name = "NullPointerException".toCharArray() |
| * nameStart = 0 |
| * nameEnd = 20 |
| * result => false</li> |
| * <li> pattern = "IPL3".toCharArray() |
| * patternStart = 0 |
| * patternEnd = 4 |
| * name = "IPerspectiveListener3".toCharArray() |
| * nameStart = 0 |
| * nameEnd = 21 |
| * result => true</li> |
| * <li> pattern = "HM".toCharArray() |
| * patternStart = 0 |
| * patternEnd = 2 |
| * name = "HashMapEntry".toCharArray() |
| * nameStart = 0 |
| * nameEnd = 12 |
| * result => true</li> |
| * </ol> |
| * |
| * @param pattern the given pattern |
| * @param patternStart the start index of the pattern, inclusive |
| * @param patternEnd the end index of the pattern, exclusive |
| * @param name the given name |
| * @param nameStart the start index of the name, inclusive |
| * @param nameEnd the end index of the name, exclusive |
| * @return true if a sub-pattern matches the sub-part of the given name, false otherwise |
| * @since 3.2 |
| */ |
| public static final boolean camelCaseMatch(char[] pattern, int patternStart, int patternEnd, char[] name, int nameStart, int nameEnd) { |
| return camelCaseMatch(pattern, patternStart, patternEnd, name, nameStart, nameEnd, false/*not the same count of parts*/); |
| } |
| |
| /** |
| * Answers true if a sub-pattern matches the sub-part of the given name using |
| * CamelCase rules, or false otherwise. char[] CamelCase matching does NOT |
| * accept explicit wild-cards '*' and '?' and is inherently case sensitive. |
| * Can match only subset of name/pattern, considering end positions as |
| * non-inclusive. The sub-pattern is defined by the patternStart and patternEnd |
| * positions. |
| * <p> |
| * CamelCase denotes the convention of writing compound names without spaces, |
| * and capitalizing every term. This function recognizes both upper and lower |
| * CamelCase, depending whether the leading character is capitalized or not. |
| * The leading part of an upper CamelCase pattern is assumed to contain |
| * a sequence of capitals which are appearing in the matching name; e.g. 'NPE' will |
| * match 'NullPointerException', but not 'NewPerfData'. A lower CamelCase pattern |
| * uses a lowercase first character. In Java, type names follow the upper |
| * CamelCase convention, whereas method or field names follow the lower |
| * CamelCase convention. |
| * <p> |
| * The pattern may contain lowercase characters, which will be matched in a case |
| * sensitive way. These characters must appear in sequence in the name. |
| * For instance, 'NPExcep' will match 'NullPointerException', but not |
| * 'NullPointerExCEPTION' or 'NuPoEx' will match 'NullPointerException', but not |
| * 'NoPointerException'. |
| * <p> |
| * Digit characters are treated in a special way. They can be used in the pattern |
| * but are not always considered as leading character. For instance, both |
| * 'UTF16DSS' and 'UTFDSS' patterns will match 'UTF16DocumentScannerSupport'. |
| * <p> |
| * CamelCase can be restricted to match only the same count of parts. When this |
| * restriction is specified the given pattern and the given name must have <b>exactly</b> |
| * the same number of parts (i.e. the same number of uppercase characters).<br> |
| * For instance, 'HM' , 'HaMa' and 'HMap' patterns will match 'HashMap' and |
| * 'HatMapper' <b>but not</b> 'HashMapEntry'. |
| * <p> |
| * Examples: |
| * <ol> |
| * <li> pattern = "NPE".toCharArray() |
| * patternStart = 0 |
| * patternEnd = 3 |
| * name = "NullPointerException".toCharArray() |
| * nameStart = 0 |
| * nameEnd = 20 |
| * result => true</li> |
| * <li> pattern = "NPE".toCharArray() |
| * patternStart = 0 |
| * patternEnd = 3 |
| * name = "NoPermissionException".toCharArray() |
| * nameStart = 0 |
| * nameEnd = 21 |
| * result => true</li> |
| * <li> pattern = "NuPoEx".toCharArray() |
| * patternStart = 0 |
| * patternEnd = 6 |
| * name = "NullPointerException".toCharArray() |
| * nameStart = 0 |
| * nameEnd = 20 |
| * result => true</li> |
| * <li> pattern = "NuPoEx".toCharArray() |
| * patternStart = 0 |
| * patternEnd = 6 |
| * name = "NoPermissionException".toCharArray() |
| * nameStart = 0 |
| * nameEnd = 21 |
| * result => false</li> |
| * <li> pattern = "npe".toCharArray() |
| * patternStart = 0 |
| * patternEnd = 3 |
| * name = "NullPointerException".toCharArray() |
| * nameStart = 0 |
| * nameEnd = 20 |
| * result => false</li> |
| * <li> pattern = "IPL3".toCharArray() |
| * patternStart = 0 |
| * patternEnd = 4 |
| * name = "IPerspectiveListener3".toCharArray() |
| * nameStart = 0 |
| * nameEnd = 21 |
| * result => true</li> |
| * <li> pattern = "HM".toCharArray() |
| * patternStart = 0 |
| * patternEnd = 2 |
| * name = "HashMapEntry".toCharArray() |
| * nameStart = 0 |
| * nameEnd = 12 |
| * result => (samePartCount == false)</li> |
| * </ol> |
| * |
| * @param pattern the given pattern |
| * @param patternStart the start index of the pattern, inclusive |
| * @param patternEnd the end index of the pattern, exclusive |
| * @param name the given name |
| * @param nameStart the start index of the name, inclusive |
| * @param nameEnd the end index of the name, exclusive |
| * @param samePartCount flag telling whether the pattern and the name should |
| * have the same count of parts or not.<br> |
| * For example: |
| * <ul> |
| * <li>'HM' type string pattern will match 'HashMap' and 'HtmlMapper' types, |
| * but not 'HashMapEntry'</li> |
| * <li>'HMap' type string pattern will still match previous 'HashMap' and |
| * 'HtmlMapper' types, but not 'HighMagnitude'</li> |
| * </ul> |
| * @return true if a sub-pattern matches the sub-part of the given name, false otherwise |
| * @since 3.4 |
| */ |
| public static final boolean camelCaseMatch(char[] pattern, int patternStart, int patternEnd, char[] name, int nameStart, int nameEnd, boolean samePartCount) { |
| |
| /* !!!!!!!!!! WARNING !!!!!!!!!! |
| * The algorithm implemented in this method has been heavily used in |
| * StringOperation#getCamelCaseMatchingRegions(String, int, int, String, int, int, boolean) |
| * method. |
| * |
| * So, if any change needs to be applied in the current algorithm, |
| * do NOT forget to also apply the same change in the StringOperation method! |
| */ |
| |
| if (name == null) |
| return false; // null name cannot match |
| if (pattern == null) |
| return true; // null pattern is equivalent to '*' |
| if (patternEnd < 0) patternEnd = pattern.length; |
| if (nameEnd < 0) nameEnd = name.length; |
| |
| if (patternEnd <= patternStart) return nameEnd <= nameStart; |
| if (nameEnd <= nameStart) return false; |
| // check first pattern char |
| if (name[nameStart] != pattern[patternStart]) { |
| // first char must strictly match (upper/lower) |
| return false; |
| } |
| |
| char patternChar, nameChar; |
| int iPattern = patternStart; |
| int iName = nameStart; |
| |
| // Main loop is on pattern characters |
| while (true) { |
| |
| iPattern++; |
| iName++; |
| |
| if (iPattern == patternEnd) { // we have exhausted pattern... |
| // it's a match if the name can have additional parts (i.e. uppercase characters) or is also exhausted |
| if (!samePartCount || iName == nameEnd) return true; |
| |
| // otherwise it's a match only if the name has no more uppercase characters |
| while (true) { |
| if (iName == nameEnd) { |
| // we have exhausted the name, so it's a match |
| return true; |
| } |
| nameChar = name[iName]; |
| // test if the name character is uppercase |
| if (nameChar < ScannerHelper.MAX_OBVIOUS) { |
| if ((ScannerHelper.OBVIOUS_IDENT_CHAR_NATURES[nameChar] & ScannerHelper.C_UPPER_LETTER) != 0) { |
| return false; |
| } |
| } |
| else if (!Character.isJavaIdentifierPart(nameChar) || Character.isUpperCase(nameChar)) { |
| return false; |
| } |
| iName++; |
| } |
| } |
| |
| if (iName == nameEnd){ |
| // We have exhausted the name (and not the pattern), so it's not a match |
| return false; |
| } |
| |
| // For as long as we're exactly matching, bring it on (even if it's a lower case character) |
| if ((patternChar = pattern[iPattern]) == name[iName]) { |
| continue; |
| } |
| |
| // If characters are not equals, then it's not a match if patternChar is lowercase |
| if (patternChar < ScannerHelper.MAX_OBVIOUS) { |
| if ((ScannerHelper.OBVIOUS_IDENT_CHAR_NATURES[patternChar] & (ScannerHelper.C_UPPER_LETTER | ScannerHelper.C_DIGIT)) == 0) { |
| return false; |
| } |
| } |
| else if (Character.isJavaIdentifierPart(patternChar) && !Character.isUpperCase(patternChar) && !Character.isDigit(patternChar)) { |
| return false; |
| } |
| |
| // patternChar is uppercase, so let's find the next uppercase in name |
| while (true) { |
| if (iName == nameEnd){ |
| // We have exhausted name (and not pattern), so it's not a match |
| return false; |
| } |
| |
| nameChar = name[iName]; |
| if (nameChar < ScannerHelper.MAX_OBVIOUS) { |
| int charNature = ScannerHelper.OBVIOUS_IDENT_CHAR_NATURES[nameChar]; |
| if ((charNature & (ScannerHelper.C_LOWER_LETTER | ScannerHelper.C_SPECIAL)) != 0) { |
| // nameChar is lowercase |
| iName++; |
| } else if ((charNature & ScannerHelper.C_DIGIT) != 0) { |
| // nameChar is digit => break if the digit is current pattern character otherwise consume it |
| if (patternChar == nameChar) break; |
| iName++; |
| // nameChar is uppercase... |
| } else if (patternChar != nameChar) { |
| //.. and it does not match patternChar, so it's not a match |
| return false; |
| } else { |
| //.. and it matched patternChar. Back to the big loop |
| break; |
| } |
| } |
| // Same tests for non-obvious characters |
| else if (Character.isJavaIdentifierPart(nameChar) && !Character.isUpperCase(nameChar)) { |
| iName++; |
| } else if (Character.isDigit(nameChar)) { |
| if (patternChar == nameChar) break; |
| iName++; |
| } else if (patternChar != nameChar) { |
| return false; |
| } else { |
| break; |
| } |
| } |
| // At this point, either name has been exhausted, or it is at an uppercase letter. |
| // Since pattern is also at an uppercase letter |
| } |
| } |
| |
| /** |
| * Answers true if the characters of the pattern are contained in the |
| * name as a substring, in a case-insensitive way. |
| * |
| * @param pattern the given pattern |
| * @param name the given name |
| * @return true if the pattern matches the given name, false otherwise |
| * @since 3.12 |
| */ |
| public static final boolean substringMatch(String pattern, String name) { |
| if (pattern == null || pattern.length() == 0) { |
| return true; |
| } |
| if (name == null) { |
| return false; |
| } |
| return checkSubstringMatch(pattern.toCharArray(), name.toCharArray()); |
| } |
| |
| /** |
| * Answers true if the characters of the pattern are contained in the |
| * name as a substring, in a case-insensitive way. |
| * |
| * @param pattern the given pattern |
| * @param name the given name |
| * @return true if the pattern matches the given name, false otherwise |
| * @since 3.12 |
| */ |
| public static final boolean substringMatch(char[] pattern, char[] name) { |
| if (pattern == null || pattern.length == 0) { |
| return true; |
| } |
| if (name == null) { |
| return false; |
| } |
| return checkSubstringMatch(pattern, name); |
| } |
| |
| /** |
| * Internal substring matching method; called after the null and length |
| * checks are performed. |
| * |
| * @param pattern the given pattern |
| * @param name the given name |
| * @return true if the pattern matches the given name, false otherwise |
| * |
| * @see CharOperation#substringMatch(char[], char[]) |
| */ |
| private static final boolean checkSubstringMatch(char[] pattern, char[] name) { |
| |
| /* XXX: to be revised/enabled |
| |
| // allow non-consecutive occurrence of pattern characters |
| if (pattern.length >= 3) { |
| int pidx = 0; |
| |
| for (int nidx = 0; nidx < name.length; nidx++) { |
| if (Character.toLowerCase(name[nidx]) == |
| Character.toLowerCase(pattern[pidx])) |
| pidx++; |
| if (pidx == pattern.length) |
| return true; |
| } |
| |
| // for short patterns only allow consecutive occurrence |
| } else { |
| */ |
| // outer loop iterates on the characters of the name; trying to |
| // match at any possible position |
| outer: for (int nidx = 0; nidx < name.length - pattern.length + 1; nidx++) { |
| // inner loop iterates on pattern characters |
| for (int pidx = 0; pidx < pattern.length; pidx++) { |
| if (Character.toLowerCase(name[nidx + pidx]) != |
| Character.toLowerCase(pattern[pidx])) { |
| // no match until parameter list; do not match parameter list |
| if ((name[nidx + pidx] == '(') || (name[nidx + pidx] == ':')) |
| return false; |
| continue outer; |
| } |
| if (pidx == pattern.length - 1) |
| return true; |
| } |
| } |
| // XXX: } |
| |
| return false; |
| } |
| |
| /** |
| * Returns the char arrays as an array of Strings |
| * |
| * @param charArrays the char array to convert |
| * @return the char arrays as an array of Strings or null if the given char arrays is null. |
| * @since 3.0 |
| */ |
| public static String[] charArrayToStringArray(char[][] charArrays) { |
| if (charArrays == null) |
| return null; |
| int length = charArrays.length; |
| if (length == 0) |
| return NO_STRINGS; |
| String[] strings= new String[length]; |
| for (int i= 0; i < length; i++) |
| strings[i]= new String(charArrays[i]); |
| return strings; |
| } |
| |
| /** |
| * Returns the char array as a String |
| |
| * @param charArray the char array to convert |
| * @return the char array as a String or null if the given char array is null. |
| * @since 3.0 |
| */ |
| public static String charToString(char[] charArray) { |
| if (charArray == null) return null; |
| return new String(charArray); |
| } |
| |
| /** |
| * Converts the given list of strings to an array of equal size, |
| * containing the individual strings converted to char[] each. |
| * |
| * @param stringList |
| * @return an array of char[], representing the elements in the input list, or {@code null} if the list was {@code null}. |
| * @since 3.14 |
| */ |
| public static char[][] toCharArrays(List<String> stringList) { |
| if (stringList == null) |
| return null; |
| char[][] result = new char[stringList.size()][]; |
| for (int i = 0; i < result.length; i++) |
| result[i] = stringList.get(i).toCharArray(); |
| return result; |
| } |
| /** |
| * Answers a new array adding the second array at the end of first array. |
| * It answers null if the first and second are null. |
| * If the first array is null, then a new array char[][] is created with second. |
| * If the second array is null, then the first array is returned. |
| * <br> |
| * <br> |
| * For example: |
| * <ol> |
| * <li><pre> |
| * first = null |
| * second = { 'a' } |
| * => result = { { ' a' } } |
| * </pre> |
| * <li><pre> |
| * first = { { ' a' } } |
| * second = null |
| * => result = { { ' a' } } |
| * </pre> |
| * </li> |
| * <li><pre> |
| * first = { { ' a' } } |
| * second = { ' b' } |
| * => result = { { ' a' } , { ' b' } } |
| * </pre> |
| * </li> |
| * </ol> |
| * |
| * @param first the first array to concatenate |
| * @param second the array to add at the end of the first array |
| * @return a new array adding the second array at the end of first array, or null if the two arrays are null. |
| */ |
| public static final char[][] arrayConcat(char[][] first, char[] second) { |
| if (second == null) |
| return first; |
| if (first == null) |
| return new char[][] { second }; |
| |
| int length = first.length; |
| char[][] result = new char[length + 1][]; |
| System.arraycopy(first, 0, result, 0, length); |
| result[length] = second; |
| return result; |
| } |
| /** |
| * Compares the two char arrays lexicographically. |
| * |
| * Returns a negative integer if array1 lexicographically precedes the array2, |
| * a positive integer if this array1 lexicographically follows the array2, or |
| * zero if both arrays are equal. |
| * |
| * @param array1 the first given array |
| * @param array2 the second given array |
| * @return the returned value of the comparison between array1 and array2 |
| * @throws NullPointerException if one of the arrays is null |
| * @since 3.3 |
| */ |
| public static final int compareTo(char[] array1, char[] array2) { |
| int length1 = array1.length; |
| int length2 = array2.length; |
| int min = Math.min(length1, length2); |
| for (int i = 0; i < min; i++) { |
| if (array1[i] != array2[i]) { |
| return array1[i] - array2[i]; |
| } |
| } |
| return length1 - length2; |
| } |
| /** |
| * Compares the two char arrays lexicographically between the given start and end positions. |
| * |
| * Returns a negative integer if array1 lexicographically precedes the array2, |
| * a positive integer if this array1 lexicographically follows the array2, or |
| * zero if both arrays are equal. |
| * <p>The comparison is done between start and end positions.</p> |
| * |
| * @param array1 the first given array |
| * @param array2 the second given array |
| * @param start the starting position to compare (inclusive) |
| * @param end the ending position to compare (exclusive) |
| * |
| * @return the returned value of the comparison between array1 and array2 |
| * @throws NullPointerException if one of the arrays is null |
| * @since 3.7.1 |
| */ |
| public static final int compareTo(char[] array1, char[] array2, int start, int end) { |
| int length1 = array1.length; |
| int length2 = array2.length; |
| int min = Math.min(length1, length2); |
| min = Math.min(min, end); |
| for (int i = start; i < min; i++) { |
| if (array1[i] != array2[i]) { |
| return array1[i] - array2[i]; |
| } |
| } |
| return length1 - length2; |
| } |
| /** |
| * Compares the contents of the two arrays array and prefix. Returns |
| * <ul> |
| * <li>zero if the array starts with the prefix contents</li> |
| * <li>the difference between the first two characters that are not equal </li> |
| * <li>one if array length is lower than the prefix length and that the prefix starts with the |
| * array contents.</li> |
| * </ul> |
| * <p> |
| * For example: |
| * <ol> |
| * <li><pre> |
| * array = null |
| * prefix = null |
| * => result = NullPointerException |
| * </pre> |
| * </li> |
| * <li><pre> |
| * array = { 'a', 'b', 'c', 'd', 'e' } |
| * prefix = { 'a', 'b', 'c'} |
| * => result = 0 |
| * </pre> |
| * </li> |
| * <li><pre> |
| * array = { 'a', 'b', 'c', 'd', 'e' } |
| * prefix = { 'a', 'B', 'c'} |
| * => result = 32 |
| * </pre> |
| * </li> |
| * <li><pre> |
| * array = { 'd', 'b', 'c', 'd', 'e' } |
| * prefix = { 'a', 'b', 'c'} |
| * => result = 3 |
| * </pre> |
| * </li> |
| * <li><pre> |
| * array = { 'a', 'b', 'c', 'd', 'e' } |
| * prefix = { 'd', 'b', 'c'} |
| * => result = -3 |
| * </pre> |
| * </li> |
| * <li><pre> |
| * array = { 'a', 'a', 'c', 'd', 'e' } |
| * prefix = { 'a', 'e', 'c'} |
| * => result = -4 |
| * </pre> |
| * </li> |
| * </ol> |
| * |
| * @param array the given array |
| * @param prefix the given prefix |
| * @return the result of the comparison (>=0 if array>prefix) |
| * @throws NullPointerException if either array or prefix is null |
| */ |
| public static final int compareWith(char[] array, char[] prefix) { |
| int arrayLength = array.length; |
| int prefixLength = prefix.length; |
| int min = Math.min(arrayLength, prefixLength); |
| int i = 0; |
| while (min-- != 0) { |
| char c1 = array[i]; |
| char c2 = prefix[i++]; |
| if (c1 != c2) |
| return c1 - c2; |
| } |
| if (prefixLength == i) |
| return 0; |
| return -1; // array is shorter than prefix (e.g. array:'ab' < prefix:'abc'). |
| } |
| |
| /** |
| * Answers the concatenation of the two arrays. It answers null if the two arrays are null. |
| * If the first array is null, then the second array is returned. |
| * If the second array is null, then the first array is returned. |
| * <br> |
| * <br> |
| * For example: |
| * <ol> |
| * <li><pre> |
| * first = null |
| * second = { 'a' } |
| * => result = { ' a' } |
| * </pre> |
| * </li> |
| * <li><pre> |
| * first = { ' a' } |
| * second = null |
| * => result = { ' a' } |
| * </pre> |
| * </li> |
| * <li><pre> |
| * first = { ' a' } |
| * second = { ' b' } |
| * => result = { ' a' , ' b' } |
| * </pre> |
| * </li> |
| * </ol> |
| * |
| * @param first the first array to concatenate |
| * @param second the second array to concatenate |
| * @return the concatenation of the two arrays, or null if the two arrays are null. |
| */ |
| public static final char[] concat(char[] first, char[] second) { |
| if (first == null) |
| return second; |
| if (second == null) |
| return first; |
| |
| int length1 = first.length; |
| int length2 = second.length; |
| char[] result = new char[length1 + length2]; |
| System.arraycopy(first, 0, result, 0, length1); |
| System.arraycopy(second, 0, result, length1, length2); |
| return result; |
| } |
| |
| /** |
| * Answers the concatenation of the three arrays. It answers null if the three arrays are null. |
| * If first is null, it answers the concatenation of second and third. |
| * If second is null, it answers the concatenation of first and third. |
| * If third is null, it answers the concatenation of first and second. |
| * <br> |
| * <br> |
| * For example: |
| * <ol> |
| * <li><pre> |
| * first = null |
| * second = { 'a' } |
| * third = { 'b' } |
| * => result = { ' a', 'b' } |
| * </pre> |
| * </li> |
| * <li><pre> |
| * first = { 'a' } |
| * second = null |
| * third = { 'b' } |
| * => result = { ' a', 'b' } |
| * </pre> |
| * </li> |
| * <li><pre> |
| * first = { 'a' } |
| * second = { 'b' } |
| * third = null |
| * => result = { ' a', 'b' } |
| * </pre> |
| * </li> |
| * <li><pre> |
| * first = null |
| * second = null |
| * third = null |
| * => result = null |
| * </pre> |
| * </li> |
| * <li><pre> |
| * first = { 'a' } |
| * second = { 'b' } |
| * third = { 'c' } |
| * => result = { 'a', 'b', 'c' } |
| * </pre> |
| * </li> |
| * </ol> |
| * |
| * @param first the first array to concatenate |
| * @param second the second array to concatenate |
| * @param third the third array to concatenate |
| * |
| * @return the concatenation of the three arrays, or null if the three arrays are null. |
| */ |
| public static final char[] concat( |
| char[] first, |
| char[] second, |
| char[] third) { |
| if (first == null) |
| return concat(second, third); |
| if (second == null) |
| return concat(first, third); |
| if (third == null) |
| return concat(first, second); |
| |
| int length1 = first.length; |
| int length2 = second.length; |
| int length3 = third.length; |
| char[] result = new char[length1 + length2 + length3]; |
| System.arraycopy(first, 0, result, 0, length1); |
| System.arraycopy(second, 0, result, length1, length2); |
| System.arraycopy(third, 0, result, length1 + length2, length3); |
| return result; |
| } |
| |
| /** |
| * Answers the concatenation of the two arrays inserting the separator character between the two arrays. |
| * It answers null if the two arrays are null. |
| * If the first array is null, then the second array is returned. |
| * If the second array is null, then the first array is returned. |
| * <br> |
| * <br> |
| * For example: |
| * <ol> |
| * <li><pre> |
| * first = null |
| * second = { 'a' } |
| * separator = '/' |
| * => result = { ' a' } |
| * </pre> |
| * </li> |
| * <li><pre> |
| * first = { ' a' } |
| * second = null |
| * separator = '/' |
| * => result = { ' a' } |
| * </pre> |
| * </li> |
| * <li><pre> |
| * first = { ' a' } |
| * second = { ' b' } |
| * separator = '/' |
| * => result = { ' a' , '/', 'b' } |
| * </pre> |
| * </li> |
| * </ol> |
| * |
| * @param first the first array to concatenate |
| * @param second the second array to concatenate |
| * @param separator the character to insert |
| * @return the concatenation of the two arrays inserting the separator character |
| * between the two arrays , or null if the two arrays are null. |
| */ |
| public static final char[] concat( |
| char[] first, |
| char[] second, |
| char separator) { |
| if (first == null) |
| return second; |
| if (second == null) |
| return first; |
| |
| int length1 = first.length; |
| if (length1 == 0) |
| return second; |
| int length2 = second.length; |
| if (length2 == 0) |
| return first; |
| |
| char[] result = new char[length1 + length2 + 1]; |
| System.arraycopy(first, 0, result, 0, length1); |
| result[length1] = separator; |
| System.arraycopy(second, 0, result, length1 + 1, length2); |
| return result; |
| } |
| |
| /** |
| * Answers the concatenation of the two arrays inserting the separator character between the two arrays. Differs from |
| * {@link CharOperation#contains(char, char[])} in case second array is a zero length array. |
| * It answers null if the two arrays are null. |
| * If the first array is null, then the second array is returned. |
| * If the second array is null, then the first array is returned. |
| * if the second array is zero length array, the separator is appended. |
| * <br> |
| * <br> |
| * For example: |
| * <ol> |
| * <li><pre> |
| * first = null |
| * second = { 'a' } |
| * separator = '/' |
| * => result = { ' a' } |
| * </pre> |
| * </li> |
| * <li><pre> |
| * first = { ' a' } |
| * second = null |
| * separator = '/' |
| * => result = { ' a' } |
| * </pre> |
| * </li> |
| * <li><pre> |
| * first = { ' a' } |
| * second = { ' b' } |
| * separator = '/' |
| * => result = { ' a' , '/', 'b' } |
| * </pre> |
| * </li> |
| * <li><pre> |
| * first = { ' a' } |
| * second = { '' } |
| * separator = '.' |
| * => result = { ' a' , '.', } |
| * </pre> |
| * </li> |
| * </ol> |
| * |
| * @param first the first array to concatenate |
| * @param second the second array to concatenate |
| * @param separator the character to insert |
| * @return the concatenation of the two arrays inserting the separator character |
| * between the two arrays , or null if the two arrays are null. If second array |
| * is of zero length, the separator is appended to the first array and returned. |
| * @since 3.14 |
| */ |
| public static final char[] concatAll( |
| char[] first, |
| char[] second, |
| char separator) { |
| if (first == null) |
| return second; |
| if (second == null) |
| return first; |
| |
| int length1 = first.length; |
| if (length1 == 0) |
| return second; |
| int length2 = second.length; |
| |
| char[] result = new char[length1 + length2 + 1]; |
| System.arraycopy(first, 0, result, 0, length1); |
| result[length1] = separator; |
| if (length2 > 0) |
| System.arraycopy(second, 0, result, length1 + 1, length2); |
| return result; |
| } |
| |
| /** |
| * Answers the concatenation of the three arrays inserting the sep1 character between the |
| * first two arrays and sep2 between the last two. |
| * It answers null if the three arrays are null. |
| * If the first array is null, then it answers the concatenation of second and third inserting |
| * the sep2 character between them. |
| * If the second array is null, then it answers the concatenation of first and third inserting |
| * the sep1 character between them. |
| * If the third array is null, then it answers the concatenation of first and second inserting |
| * the sep1 character between them. |
| * <br> |
| * <br> |
| * For example: |
| * <ol> |
| * <li><pre> |
| * first = null |
| * sep1 = '/' |
| * second = { 'a' } |
| * sep2 = ':' |
| * third = { 'b' } |
| * => result = { ' a' , ':', 'b' } |
| * </pre> |
| * </li> |
| * <li><pre> |
| * first = { 'a' } |
| * sep1 = '/' |
| * second = null |
| * sep2 = ':' |
| * third = { 'b' } |
| * => result = { ' a' , '/', 'b' } |
| * </pre> |
| * </li> |
| * <li><pre> |
| * first = { 'a' } |
| * sep1 = '/' |
| * second = { 'b' } |
| * sep2 = ':' |
| * third = null |
| * => result = { ' a' , '/', 'b' } |
| * </pre> |
| * </li> |
| * <li><pre> |
| * first = { 'a' } |
| * sep1 = '/' |
| * second = { 'b' } |
| * sep2 = ':' |
| * third = { 'c' } |
| * => result = { ' a' , '/', 'b' , ':', 'c' } |
| * </pre> |
| * </li> |
| * </ol> |
| * |
| * @param first the first array to concatenate |
| * @param sep1 the character to insert |
| * @param second the second array to concatenate |
| * @param sep2 the character to insert |
| * @param third the second array to concatenate |
| * @return the concatenation of the three arrays inserting the sep1 character between the |
| * two arrays and sep2 between the last two. |
| */ |
| public static final char[] concat( |
| char[] first, |
| char sep1, |
| char[] second, |
| char sep2, |
| char[] third) { |
| if (first == null) |
| return concat(second, third, sep2); |
| if (second == null) |
| return concat(first, third, sep1); |
| if (third == null) |
| return concat(first, second, sep1); |
| |
| int length1 = first.length; |
| int length2 = second.length; |
| int length3 = third.length; |
| char[] result = new char[length1 + length2 + length3 + 2]; |
| System.arraycopy(first, 0, result, 0, length1); |
| result[length1] = sep1; |
| System.arraycopy(second, 0, result, length1 + 1, length2); |
| result[length1 + length2 + 1] = sep2; |
| System.arraycopy(third, 0, result, length1 + length2 + 2, length3); |
| return result; |
| } |
| /** |
| * Answers the concatenation of the two arrays inserting the separator character between the two arrays. |
| * It answers null if the two arrays are null. |
| * If the first array is null or is empty, then the second array is returned. |
| * If the second array is null or is empty, then the first array is returned. |
| * <br> |
| * <br> |
| * For example: |
| * <ol> |
| * <li><pre> |
| * first = null |
| * second = { 'a' } |
| * separator = '/' |
| * => result = { ' a' } |
| * </pre> |
| * </li> |
| * <li><pre> |
| * first = { ' a' } |
| * second = null |
| * separator = '/' |
| * => result = { ' a' } |
| * </pre> |
| * </li> |
| * <li><pre> |
| * first = { ' a' } |
| * second = { ' b' } |
| * separator = '/' |
| * => result = { ' a' , '/', 'b' } |
| * </pre> |
| * </li> |
| * <li><pre> |
| * first = { ' a' } |
| * second = { } |
| * separator = '/' |
| * => result = { ' a'} |
| * </pre> |
| * </li> |
| |
| * </ol> |
| * |
| * @param first the first array to concatenate |
| * @param second the second array to concatenate |
| * @param separator the character to insert |
| * @return the concatenation of the two arrays inserting the separator character |
| * between the two arrays , or null if the two arrays are null. |
| * @since 3.12 |
| */ |
| public static final char[] concatNonEmpty( |
| char[] first, |
| char[] second, |
| char separator) { |
| if (first == null || first.length == 0) |
| return second; |
| if (second == null || second.length == 0) |
| return first; |
| return concat(first, second, separator); |
| } |
| /** |
| * Answers the concatenation of the three arrays inserting the sep1 character between the |
| * first two arrays and sep2 between the last two. |
| * It answers null if the three arrays are null. |
| * If the first array is null or empty, then it answers the concatenation of second and third inserting |
| * the sep2 character between them. |
| * If the second array is null or empty, then it answers the concatenation of first and third inserting |
| * the sep1 character between them. |
| * If the third array is null or empty, then it answers the concatenation of first and second inserting |
| * the sep1 character between them. |
| * <br> |
| * <br> |
| * For example: |
| * <ol> |
| * <li><pre> |
| * first = null |
| * sep1 = '/' |
| * second = { 'a' } |
| * sep2 = ':' |
| * third = { 'b' } |
| * => result = { ' a' , ':', 'b' } |
| * </pre> |
| * </li> |
| * <li><pre> |
| * first = { 'a' } |
| * sep1 = '/' |
| * second = null |
| * sep2 = ':' |
| * third = { 'b' } |
| * => result = { ' a' , '/', 'b' } |
| * </pre> |
| * </li> |
| * <li><pre> |
| * first = { 'a' } |
| * sep1 = '/' |
| * second = { 'b' } |
| * sep2 = ':' |
| * third = null |
| * => result = { ' a' , '/', 'b' } |
| * </pre> |
| * </li> |
| * <li><pre> |
| * first = { 'a' } |
| * sep1 = '/' |
| * second = { 'b' } |
| * sep2 = ':' |
| * third = { 'c' } |
| * => result = { ' a' , '/', 'b' , ':', 'c' } |
| * </pre> |
| * </li> |
| * <li><pre> |
| * first = { 'a' } |
| * sep1 = '/' |
| * second = { } |
| * sep2 = ':' |
| * third = { 'c' } |
| * => result = { ' a', ':', 'c' } |
| * </pre> |
| * </li> |
| * </ol> |
| * |
| * @param first the first array to concatenate |
| * @param sep1 the character to insert |
| * @param second the second array to concatenate |
| * @param sep2 the character to insert |
| * @param third the second array to concatenate |
| * @return the concatenation of the three arrays inserting the sep1 character between the |
| * two arrays and sep2 between the last two. |
| * @since 3.12 |
| */ |
| public static final char[] concatNonEmpty( |
| char[] first, |
| char sep1, |
| char[] second, |
| char sep2, |
| char[] third) { |
| if (first == null || first.length == 0) |
| return concatNonEmpty(second, third, sep2); |
| if (second == null || second.length == 0) |
| return concatNonEmpty(first, third, sep1); |
| if (third == null || third.length == 0) |
| return concatNonEmpty(first, second, sep1); |
| |
| return concat(first, sep1, second, sep2, third); |
| } |
| |
| /** |
| * Answers a new array with prepending the prefix character and appending the suffix |
| * character at the end of the array. If array is null, it answers a new array containing the |
| * prefix and the suffix characters. |
| * <br> |
| * <br> |
| * For example:<br> |
| * <ol> |
| * <li><pre> |
| * prefix = 'a' |
| * array = { 'b' } |
| * suffix = 'c' |
| * => result = { 'a', 'b' , 'c' } |
| * </pre> |
| * </li> |
| * <li><pre> |
| * prefix = 'a' |
| * array = null |
| * suffix = 'c' |
| * => result = { 'a', 'c' } |
| * </pre></li> |
| * </ol> |
| * |
| * @param prefix the prefix character |
| * @param array the array that is concatenated with the prefix and suffix characters |
| * @param suffix the suffix character |
| * @return the new array |
| */ |
| public static final char[] concat(char prefix, char[] array, char suffix) { |
| if (array == null) |
| return new char[] { prefix, suffix }; |
| |
| int length = array.length; |
| char[] result = new char[length + 2]; |
| result[0] = prefix; |
| System.arraycopy(array, 0, result, 1, length); |
| result[length + 1] = suffix; |
| return result; |
| } |
| |
| /** |
| * Answers the concatenation of the given array parts using the given separator between each |
| * part and prepending the given name at the beginning. |
| * <br> |
| * <br> |
| * For example:<br> |
| * <ol> |
| * <li><pre> |
| * name = { 'c' } |
| * array = { { 'a' }, { 'b' } } |
| * separator = '.' |
| * => result = { 'a', '.', 'b' , '.', 'c' } |
| * </pre> |
| * </li> |
| * <li><pre> |
| * name = null |
| * array = { { 'a' }, { 'b' } } |
| * separator = '.' |
| * => result = { 'a', '.', 'b' } |
| * </pre></li> |
| * <li><pre> |
| * name = { ' c' } |
| * array = null |
| * separator = '.' |
| * => result = { 'c' } |
| * </pre></li> |
| * </ol> |
| * |
| * @param name the given name |
| * @param array the given array |
| * @param separator the given separator |
| * @return the concatenation of the given array parts using the given separator between each |
| * part and prepending the given name at the beginning |
| */ |
| public static final char[] concatWith( |
| char[] name, |
| char[][] array, |
| char separator) { |
| int nameLength = name == null ? 0 : name.length; |
| if (nameLength == 0) |
| return concatWith(array, separator); |
| |
| int length = array == null ? 0 : array.length; |
| if (length == 0) |
| return name; |
| |
| int size = nameLength; |
| int index = length; |
| while (--index >= 0) |
| if (array[index].length > 0) |
| size += array[index].length + 1; |
| char[] result = new char[size]; |
| index = size; |
| for (int i = length - 1; i >= 0; i--) { |
| int subLength = array[i].length; |
| if (subLength > 0) { |
| index -= subLength; |
| System.arraycopy(array[i], 0, result, index, subLength); |
| result[--index] = separator; |
| } |
| } |
| System.arraycopy(name, 0, result, 0, nameLength); |
| return result; |
| } |
| |
| /** |
| * Answers the concatenation of the given array parts using the given separator between each |
| * part and appending the given name at the end. |
| * <br> |
| * <br> |
| * For example:<br> |
| * <ol> |
| * <li><pre> |
| * name = { 'c' } |
| * array = { { 'a' }, { 'b' } } |
| * separator = '.' |
| * => result = { 'a', '.', 'b' , '.', 'c' } |
| * </pre> |
| * </li> |
| * <li><pre> |
| * name = null |
| * array = { { 'a' }, { 'b' } } |
| * separator = '.' |
| * => result = { 'a', '.', 'b' } |
| * </pre></li> |
| * <li><pre> |
| * name = { ' c' } |
| * array = null |
| * separator = '.' |
| * => result = { 'c' } |
| * </pre></li> |
| * </ol> |
| * |
| * @param array the given array |
| * @param name the given name |
| * @param separator the given separator |
| * @return the concatenation of the given array parts using the given separator between each |
| * part and appending the given name at the end |
| */ |
| public static final char[] concatWith( |
| char[][] array, |
| char[] name, |
| char separator) { |
| int nameLength = name == null ? 0 : name.length; |
| if (nameLength == 0) |
| return concatWith(array, separator); |
| |
| int length = array == null ? 0 : array.length; |
| if (length == 0) |
| return name; |
| |
| int size = nameLength; |
| int index = length; |
| while (--index >= 0) |
| if (array[index].length > 0) |
| size += array[index].length + 1; |
| char[] result = new char[size]; |
| index = 0; |
| for (int i = 0; i < length; i++) { |
| int subLength = array[i].length; |
| if (subLength > 0) { |
| System.arraycopy(array[i], 0, result, index, subLength); |
| index += subLength; |
| result[index++] = separator; |
| } |
| } |
| System.arraycopy(name, 0, result, index, nameLength); |
| return result; |
| } |
| |
| /** |
| * Answers the concatenation of the given array parts using the given separator between each part. |
| * <br> |
| * <br> |
| * For example:<br> |
| * <ol> |
| * <li><pre> |
| * array = { { 'a' }, { 'b' } } |
| * separator = '.' |
| * => result = { 'a', '.', 'b' } |
| * </pre> |
| * </li> |
| * <li><pre> |
| * array = null |
| * separator = '.' |
| * => result = { } |
| * </pre></li> |
| * </ol> |
| * |
| * @param array the given array |
| * @param separator the given separator |
| * @return the concatenation of the given array parts using the given separator between each part |
| */ |
| public static final char[] concatWith(char[][] array, char separator) { |
| int length = array == null ? 0 : array.length; |
| if (length == 0) |
| return CharOperation.NO_CHAR; |
| |
| int size = length - 1; |
| int index = length; |
| while (--index >= 0) { |
| if (array[index].length == 0) |
| size--; |
| else |
| size += array[index].length; |
| } |
| if (size <= 0) |
| return CharOperation.NO_CHAR; |
| char[] result = new char[size]; |
| index = length; |
| while (--index >= 0) { |
| length = array[index].length; |
| if (length > 0) { |
| System.arraycopy( |
| array[index], |
| 0, |
| result, |
| (size -= length), |
| length); |
| if (--size >= 0) |
| result[size] = separator; |
| } |
| } |
| return result; |
| } |
| |
| /** |
| * Answers the concatenation of the given array parts using the given separator between each part |
| * irrespective of whether an element is a zero length array or not. |
| * <br> |
| * <br> |
| * For example:<br> |
| * <ol> |
| * <li><pre> |
| * array = { { 'a' }, {}, { 'b' } } |
| * separator = '' |
| * => result = { 'a', '/', '/', 'b' } |
| * </pre> |
| * </li> |
| * <li><pre> |
| * array = { { 'a' }, { 'b' } } |
| * separator = '.' |
| * => result = { 'a', '.', 'b' } |
| * </pre> |
| * </li> |
| * <li><pre> |
| * array = null |
| * separator = '.' |
| * => result = { } |
| * </pre></li> |
| * </ol> |
| * |
| * @param array the given array |
| * @param separator the given separator |
| * @return the concatenation of the given array parts using the given separator between each part |
| * @since 3.12 |
| */ |
| public static final char[] concatWithAll(char[][] array, char separator) { |
| int length = array == null ? 0 : array.length; |
| if (length == 0) |
| return CharOperation.NO_CHAR; |
| |
| int size = length - 1; |
| int index = length; |
| while (--index >= 0) { |
| size += array[index].length; |
| } |
| char[] result = new char[size]; |
| index = length; |
| while (--index >= 0) { |
| length = array[index].length; |
| if (length > 0) { |
| System.arraycopy( |
| array[index], |
| 0, |
| result, |
| (size -= length), |
| length); |
| } |
| if (--size >= 0) |
| result[size] = separator; |
| } |
| return result; |
| } |
| |
| /** |
| * Answers true if the array contains an occurrence of character, false otherwise. |
| * |
| * <br> |
| * <br> |
| * For example: |
| * <ol> |
| * <li><pre> |
| * character = 'c' |
| * array = { { ' a' }, { ' b' } } |
| * result => false |
| * </pre> |
| * </li> |
| * <li><pre> |
| * character = 'a' |
| * array = { { ' a' }, { ' b' } } |
| * result => true |
| * </pre> |
| * </li> |
| * </ol> |
| * |
| * @param character the character to search |
| * @param array the array in which the search is done |
| * @return true if the array contains an occurrence of character, false otherwise. |
| * @throws NullPointerException if array is null. |
| */ |
| public static final boolean contains(char character, char[][] array) { |
| for (int i = array.length; --i >= 0;) { |
| char[] subarray = array[i]; |
| for (int j = subarray.length; --j >= 0;) |
| if (subarray[j] == character) |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * Answers true if the array contains an occurrence of character, false otherwise. |
| * |
| * <br> |
| * <br> |
| * For example: |
| * <ol> |
| * <li><pre> |
| * character = 'c' |
| * array = { ' b' } |
| * result => false |
| * </pre> |
| * </li> |
| * <li><pre> |
| * character = 'a' |
| * array = { ' a' , ' b' } |
| * result => true |
| * </pre> |
| * </li> |
| * </ol> |
| * |
| * @param character the character to search |
| * @param array the array in which the search is done |
| * @return true if the array contains an occurrence of character, false otherwise. |
| * @throws NullPointerException if array is null. |
| */ |
| public static final boolean contains(char character, char[] array) { |
| for (int i = array.length; --i >= 0;) |
| if (array[i] == character) |
| return true; |
| return false; |
| } |
| |
| /** |
| * Answers true if the array contains an occurrence of one of the characters, false otherwise. |
| * |
| * <br> |
| * <br> |
| * For example: |
| * <ol> |
| * <li><pre> |
| * characters = { 'c', 'd' } |
| * array = { 'a', ' b' } |
| * result => false |
| * </pre> |
| * </li> |
| * <li><pre> |
| * characters = { 'c', 'd' } |
| * array = { 'a', ' b', 'c' } |
| * result => true |
| * </pre> |
| * </li> |
| * </ol> |
| * |
| * @param characters the characters to search |
| * @param array the array in which the search is done |
| * @return true if the array contains an occurrence of one of the characters, false otherwise. |
| * @throws NullPointerException if array is null. |
| * @since 3.1 |
| */ |
| public static final boolean contains(char[] characters, char[] array) { |
| for (int i = array.length; --i >= 0;) |
| for (int j = characters.length; --j >= 0;) |
| if (array[i] == characters[j]) |
| return true; |
| return false; |
| } |
| |
| /** |
| * Does the given array contain a char sequence that is equal to the give sequence? |
| * @param array |
| * @param sequence |
| * @return true if sequence is equal to an element in array |
| * @since 3.14 |
| */ |
| public static boolean containsEqual(char[][] array, char[] sequence) { |
| for (int i = 0; i < array.length; i++) { |
| if (equals(array[i], sequence)) |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * Answers a deep copy of the toCopy array. |
| * |
| * @param toCopy the array to copy |
| * @return a deep copy of the toCopy array. |
| */ |
| |
| public static final char[][] deepCopy(char[][] toCopy) { |
| int toCopyLength = toCopy.length; |
| char[][] result = new char[toCopyLength][]; |
| for (int i = 0; i < toCopyLength; i++) { |
| char[] toElement = toCopy[i]; |
| int toElementLength = toElement.length; |
| char[] resultElement = new char[toElementLength]; |
| System.arraycopy(toElement, 0, resultElement, 0, toElementLength); |
| result[i] = resultElement; |
| } |
| return result; |
| } |
| |
| /** |
| * Return true if array ends with the sequence of characters contained in toBeFound, |
| * otherwise false. |
| * <br> |
| * <br> |
| * For example: |
| * <ol> |
| * <li><pre> |
| * array = { 'a', 'b', 'c', 'd' } |
| * toBeFound = { 'b', 'c' } |
| * result => false |
| * </pre> |
| * </li> |
| * <li><pre> |
| * array = { 'a', 'b', 'c' } |
| * toBeFound = { 'b', 'c' } |
| * result => true |
| * </pre> |
| * </li> |
| * </ol> |
| * |
| * @param array the array to check |
| * @param toBeFound the array to find |
| * @return true if array ends with the sequence of characters contained in toBeFound, |
| * otherwise false. |
| * @throws NullPointerException if array is null or toBeFound is null |
| */ |
| public static final boolean endsWith(char[] array, char[] toBeFound) { |
| int i = toBeFound.length; |
| int j = array.length - i; |
| |
| if (j < 0) |
| return false; |
| while (--i >= 0) |
| if (toBeFound[i] != array[i + j]) |
| return false; |
| return true; |
| } |
| |
| /** |
| * Answers true if the two arrays are identical character by character, otherwise false. |
| * The equality is case sensitive. |
| * <br> |
| * <br> |
| * For example: |
| * <ol> |
| * <li><pre> |
| * first = null |
| * second = null |
| * result => true |
| * </pre> |
| * </li> |
| * <li><pre> |
| * first = { { } } |
| * second = null |
| * result => false |
| * </pre> |
| * </li> |
| * <li><pre> |
| * first = { { 'a' } } |
| * second = { { 'a' } } |
| * result => true |
| * </pre> |
| * </li> |
| * <li><pre> |
| * first = { { 'A' } } |
| * second = { { 'a' } } |
| * result => false |
| * </pre> |
| * </li> |
| * </ol> |
| * @param first the first array |
| * @param second the second array |
| * @return true if the two arrays are identical character by character, otherwise false |
| */ |
| public static final boolean equals(char[][] first, char[][] second) { |
| if (first == second) |
| return true; |
| if (first == null || second == null) |
| return false; |
| if (first.length != second.length) |
| return false; |
| |
| for (int i = first.length; --i >= 0;) |
| if (!equals(first[i], second[i])) |
| return false; |
| return true; |
| } |
| |
| /** |
| * If isCaseSensite is true, answers true if the two arrays are identical character |
| * by character, otherwise false. |
| * If it is false, answers true if the two arrays are identical character by |
| * character without checking the case, otherwise false. |
| * <br> |
| * <br> |
| * For example: |
| * <ol> |
| * <li><pre> |
| * first = null |
| * second = null |
| * isCaseSensitive = true |
| * result => true |
| * </pre> |
| * </li> |
| * <li><pre> |
| * first = { { } } |
| * second = null |
| * isCaseSensitive = true |
| * result => false |
| * </pre> |
| * </li> |
| * <li><pre> |
| * first = { { 'A' } } |
| * second = { { 'a' } } |
| * isCaseSensitive = true |
| * result => false |
| * </pre> |
| * </li> |
| * <li><pre> |
| * first = { { 'A' } } |
| * second = { { 'a' } } |
| * isCaseSensitive = false |
| * result => true |
| * </pre> |
| * </li> |
| * </ol> |
| * |
| * @param first the first array |
| * @param second the second array |
| * @param isCaseSensitive check whether or not the equality should be case sensitive |
| * @return true if the two arrays are identical character by character according to the value |
| * of isCaseSensitive, otherwise false |
| */ |
| public static final boolean equals( |
| char[][] first, |
| char[][] second, |
| boolean isCaseSensitive) { |
| |
| if (isCaseSensitive) { |
| return equals(first, second); |
| } |
| if (first == second) |
| return true; |
| if (first == null || second == null) |
| return false; |
| if (first.length != second.length) |
| return false; |
| |
| for (int i = first.length; --i >= 0;) |
| if (!equals(first[i], second[i], false)) |
| return false; |
| return true; |
| } |
| |
| /** |
| * Answers true if the two arrays are identical character by character, otherwise false. |
| * The equality is case sensitive. |
| * <br> |
| * <br> |
| * For example: |
| * <ol> |
| * <li><pre> |
| * first = null |
| * second = null |
| * result => true |
| * </pre> |
| * </li> |
| * <li><pre> |
| * first = { } |
| * second = null |
| * result => false |
| * </pre> |
| * </li> |
| * <li><pre> |
| * first = { 'a' } |
| * second = { 'a' } |
| * result => true |
| * </pre> |
| * </li> |
| * <li><pre> |
| * first = { 'a' } |
| * second = { 'A' } |
| * result => false |
| * </pre> |
| * </li> |
| * </ol> |
| * @param first the first array |
| * @param second the second array |
| * @return true if the two arrays are identical character by character, otherwise false |
| */ |
| public static final boolean equals(char[] first, char[] second) { |
| 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 |
| */ |
| public static final int hashCode(char[] array) { |
| int hash = Arrays.hashCode(array); |
| return hash & 0x7FFFFFFF; |
| } |
| |
| /** |
| * Answers true if c is a whitespace according to the JLS (\u0009, \u000a, \u000c, \u000d, \u0020), 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 || start < 0) return -1; |
| if (toBeFoundLength == 0) return 0; |
| if (toBeFoundLength == arrayLength) { |
| if (isCaseSensitive) { |
| for (int i = start; i < arrayLength; i++) { |
| if (array[i] != toBeFound[i]) return -1; |
| } |
| return 0; |
| } else { |
| for (int i = start; i < arrayLength; i++) { |
| if (ScannerHelper.toLowerCase(array[i]) != ScannerHelper.toLowerCase(toBeFound[i])) return -1; |
| } |
| return 0; |
| } |
| } |
| if (isCaseSensitive) { |
| arrayLoop: for (int i = start, max = arrayLength - toBeFoundLength + 1; i < max; i++) { |
| if (array[i] == toBeFound[0]) { |
| for (int j = 1; j < toBeFoundLength; j++) { |
| if (array[i + j] != toBeFound[j]) continue arrayLoop; |
| } |
| return i; |
| } |
| } |
| } else { |
| arrayLoop: for (int i = start, max = arrayLength - toBeFoundLength + 1; i < max; i++) { |
| if (ScannerHelper.toLowerCase(array[i]) == ScannerHelper.toLowerCase(toBeFound[0])) { |
| for (int j = 1; j < toBeFoundLength; j++) { |
| if (ScannerHelper.toLowerCase(array[i + j]) != ScannerHelper.toLowerCase(toBeFound[j])) continue arrayLoop; |
| } |
| return i; |
| } |
| } |
| } |
| return -1; |
| } |
| |
| /** |
| * Answers the first index in the array for which the corresponding character is |
| * equal to toBeFound starting the search at index start. |
| * Answers -1 if no occurrence of this character is found. |
| * <br> |
| * <br> |
| * For example: |
| * <ol> |
| * <li><pre> |
| * toBeFound = 'c' |
| * array = { ' a', 'b', 'c', 'd' } |
| * start = 2 |
| * result => 2 |
| * </pre> |
| * </li> |
| * <li><pre> |
| * toBeFound = 'c' |
| * array = { ' a', 'b', 'c', 'd' } |
| * start = 3 |
| * result => -1 |
| * </pre> |
| * </li> |
| * <li><pre> |
| * toBeFound = 'e' |
| * array = { ' a', 'b', 'c', 'd' } |
| * start = 1 |
| * result => -1 |
| * </pre> |
| * </li> |
| * </ol> |
| * |
| * @param toBeFound the character to search |
| * @param array the array to be searched |
| * @param start the starting index |
| * @return the first index in the array for which the corresponding character is |
| * equal to toBeFound, -1 otherwise |
| * @throws NullPointerException if array is null |
| * @throws ArrayIndexOutOfBoundsException if start is lower than 0 |
| */ |
| public static final int indexOf(char toBeFound, char[] array, int start) { |
| for (int i = start; i < array.length; i++) |
| if (toBeFound == array[i]) |
| return i; |
| return -1; |
| } |
| |
| /** |
| * Answers the first index in the array for which the corresponding character is |
| * equal to toBeFound starting the search at index start and before the ending index. |
| * Answers -1 if no occurrence of this character is found. |
| * <br> |
| * <br> |
| * For example: |
| * <ol> |
| * <li><pre> |
| * toBeFound = 'c' |
| * array = { ' a', 'b', 'c', 'd' } |
| * start = 2 |
| * result => 2 |
| * </pre> |
| * </li> |
| * <li><pre> |
| * toBeFound = 'c' |
| * array = { ' a', 'b', 'c', 'd' } |
| * start = 3 |
| * result => -1 |
| * </pre> |
| * </li> |
| * <li><pre> |
| * toBeFound = 'e' |
| * array = { ' a', 'b', 'c', 'd' } |
| * start = 1 |
| * result => -1 |
| * </pre> |
| * </li> |
| * </ol> |
| * |
| * @param toBeFound the character to search |
| * @param array the array to be searched |
| * @param start the starting index (inclusive) |
| * @param end the ending index (exclusive) |
| * @return the first index in the array for which the corresponding character is |
| * equal to toBeFound, -1 otherwise |
| * @throws NullPointerException if array is null |
| * @throws ArrayIndexOutOfBoundsException if start is lower than 0 or ending greater than array length |
| * @since 3.2 |
| */ |
| public static final int indexOf(char toBeFound, char[] array, int start, int end) { |
| for (int i = start; i < end; i++) |
| if (toBeFound == array[i]) |
| return i; |
| return -1; |
| } |
| |
| /** |
| * Answers the last index in the array for which the corresponding character is |
| * equal to toBeFound starting from the end of the array. |
| * Answers -1 if no occurrence of this character is found. |
| * <br> |
| * <br> |
| * For example: |
| * <ol> |
| * <li><pre> |
| * toBeFound = 'c' |
| * array = { ' a', 'b', 'c', 'd' , 'c', 'e' } |
| * result => 4 |
| * </pre> |
| * </li> |
| * <li><pre> |
| * toBeFound = 'e' |
| * array = { ' a', 'b', 'c', 'd' } |
| * result => -1 |
| * </pre> |
| * </li> |
| * </ol> |
| * |
| * @param toBeFound the character to search |
| * @param array the array to be searched |
| * @return the last index in the array for which the corresponding character is |
| * equal to toBeFound starting from the end of the array, -1 otherwise |
| * @throws NullPointerException if array is null |
| */ |
| public static final int lastIndexOf(char toBeFound, char[] array) { |
| for (int i = array.length; --i >= 0;) |
| if (toBeFound == array[i]) |
| return i; |
| return -1; |
| } |
| |
| /** |
| * Answers the last index in the array for which the corresponding character is |
| * equal to toBeFound stopping at the index startIndex. |
| * Answers -1 if no occurrence of this character is found. |
| * <br> |
| * <br> |
| * For example: |
| * <ol> |
| * <li><pre> |
| * toBeFound = 'c' |
| * array = { ' a', 'b', 'c', 'd' } |
| * startIndex = 2 |
| * result => 2 |
| * </pre> |
| * </li> |
| * <li><pre> |
| * toBeFound = 'c' |
| * array = { ' a', 'b', 'c', 'd', 'e' } |
| * startIndex = 3 |
| * result => -1 |
| * </pre> |
| * </li> |
| * <li><pre> |
| * toBeFound = 'e' |
| * array = { ' a', 'b', 'c', 'd' } |
| * startIndex = 0 |
| * result => -1 |
| * </pre> |
| * </li> |
| * </ol> |
| * |
| * @param toBeFound the character to search |
| * @param array the array to be searched |
| * @param startIndex the stopping index |
| * @return the last index in the array for which the corresponding character is |
| * equal to toBeFound stopping at the index startIndex, -1 otherwise |
| * @throws NullPointerException if array is null |
| * @throws ArrayIndexOutOfBoundsException if startIndex is lower than 0 |
| */ |
| public static final int lastIndexOf( |
| char toBeFound, |
| char[] array, |
| int startIndex) { |
| for (int i = array.length; --i >= startIndex;) |
| if (toBeFound == array[i]) |
| return i; |
| return -1; |
| } |
| |
| /** |
| * Answers the last index in the array for which the corresponding character is |
| * equal to toBeFound starting from endIndex to startIndex. |
| * Answers -1 if no occurrence of this character is found. |
| * <br> |
| * <br> |
| * For example: |
| * <ol> |
| * <li><pre> |
| * toBeFound = 'c' |
| * array = { ' a', 'b', 'c', 'd' } |
| * startIndex = 2 |
| * endIndex = 2 |
| * result => 2 |
| * </pre> |
| * </li> |
| * <li><pre> |
| * toBeFound = 'c' |
| * array = { ' a', 'b', 'c', 'd', 'e' } |
| * startIndex = 3 |
| * endIndex = 4 |
| * result => -1 |
| * </pre> |
| * </li> |
| * <li><pre> |
| * toBeFound = 'e' |
| * array = { ' a', 'b', 'c', 'd' } |
| * startIndex = 0 |
| * endIndex = 3 |
| * result => -1 |
| * </pre> |
| * </li> |
| * </ol> |
| * |
| * @param toBeFound the character to search |
| * @param array the array to be searched |
| * @param startIndex the stopping index |
| * @param endIndex the starting index |
| * @return the last index in the array for which the corresponding character is |
| * equal to toBeFound starting from endIndex to startIndex, -1 otherwise |
| * @throws NullPointerException if array is null |
| * @throws ArrayIndexOutOfBoundsException if endIndex is greater or equals to array length or starting is lower than 0 |
| */ |
| public static final int lastIndexOf( |
| char toBeFound, |
| char[] array, |
| int startIndex, |
| int endIndex) { |
| for (int i = endIndex; --i >= startIndex;) |
| if (toBeFound == array[i]) |
| return i; |
| return -1; |
| } |
| |
| /** |
| * Answers the last portion of a name given a separator. |
| * <br> |
| * <br> |
| * For example, |
| * <pre> |
| * lastSegment("java.lang.Object".toCharArray(),'.') --> Object |
| * </pre> |
| * |
| * @param array the array |
| * @param separator the given separator |
| * @return the last portion of a name given a separator |
| * @throws NullPointerException if array is null |
| */ |
| final static public char[] lastSegment(char[] array, char separator) { |
| int pos = lastIndexOf(separator, array); |
| if (pos < 0) |
| return array; |
| return subarray(array, pos + 1, array.length); |
| } |
| |
| /** |
| * <p>Answers true if the pattern matches the given name, false otherwise. This char[] pattern matching |
| * accepts wild-cards '*' and '?'.</p> |
| * |
| * <p>When not case sensitive, the pattern is assumed to already be lowercased, the |
| * name will be lowercased character per character as comparing.<br> |
| * If name is null, the answer is false.<br> |
| * If pattern is null, the answer is true if name is not null. |
| * </p> |
| * For example: |
| * <ol> |
| * <li><pre> |
| * pattern = { '?', 'b', '*' } |
| * name = { 'a', 'b', 'c' , 'd' } |
| * isCaseSensitive = true |
| * result => true |
| * </pre> |
| * </li> |
| * <li><pre> |
| * pattern = { '?', 'b', '?' } |
| * name = { 'a', 'b', 'c' , 'd' } |
| * isCaseSensitive = true |
| * result => false |
| * </pre> |
| * </li> |
| * <li><pre> |
| * pattern = { 'b', '*' } |
| * name = { 'a', 'b', 'c' , 'd' } |
| * isCaseSensitive = true |
| * result => false |
| * </pre> |
| * </li> |
| * </ol> |
| * |
| * @param pattern the given pattern |
| * @param name the given name |
| * @param isCaseSensitive flag to know whether or not the matching should be case sensitive |
| * @return true if the pattern matches the given name, false otherwise |
| */ |
| public static final boolean match( |
| char[] pattern, |
| char[] name, |
| boolean isCaseSensitive) { |
| |
| if (name == null) |
| return false; // null name cannot match |
| if (pattern == null) |
| return true; // null pattern is equivalent to '*' |
| |
| return match( |
| pattern, |
| 0, |
| pattern.length, |
| name, |
| 0, |
| name.length, |
| isCaseSensitive); |
| } |
| |
| /** |
| * Answers true if a sub-pattern matches the subpart of the given name, false otherwise. |
| * char[] pattern matching, accepting wild-cards '*' and '?'. Can match only subset of name/pattern. |
| * end positions are non-inclusive. |
| * The subpattern is defined by the patternStart and pattternEnd positions. |
| * When not case sensitive, the pattern is assumed to already be lowercased, the |
| * name will be lowercased character per character as comparing. |
| * <br> |
| * <br> |
| * For example: |
| * <ol> |
| * <li><pre> |
| * pattern = { '?', 'b', '*' } |
| * patternStart = 1 |
| * patternEnd = 3 |
| * name = { 'a', 'b', 'c' , 'd' } |
| * nameStart = 1 |
| * nameEnd = 4 |
| * isCaseSensitive = true |
| * result => true |
| * </pre> |
| * </li> |
| * <li><pre> |
| * pattern = { '?', 'b', '*' } |
| * patternStart = 1 |
| * patternEnd = 2 |
| * name = { 'a', 'b', 'c' , 'd' } |
| * nameStart = 1 |
| * nameEnd = 4 |
| * isCaseSensitive = true |
| * result => false |
| * </pre> |
| * </li> |
| * </ol> |
| * |
| * @param pattern the given pattern |
| * @param patternStart the given pattern start |
| * @param patternEnd the given pattern end |
| * @param name the given name |
| * @param nameStart the given name start |
| * @param nameEnd the given name end |
| * @param isCaseSensitive flag to know if the matching should be case sensitive |
| * @return true if a sub-pattern matches the subpart of the given name, false otherwise |
| */ |
| public static final boolean match( |
| char[] pattern, |
| int patternStart, |
| int patternEnd, |
| char[] name, |
| int nameStart, |
| int nameEnd, |
| boolean isCaseSensitive) { |
| |
| if (name == null) |
| return false; // null name cannot match |
| if (pattern == null) |
| return true; // null pattern is equivalent to '*' |
| int iPattern = patternStart; |
| int iName = nameStart; |
| |
| if (patternEnd < 0) |
| patternEnd = pattern.length; |
| if (nameEnd < 0) |
| nameEnd = name.length; |
| |
| /* check first segment */ |
| char patternChar = 0; |
| while (true) { |
| if (iPattern == patternEnd) { |
| if (iName == nameEnd) return true; // the chars match |
| return false; // pattern has ended but not the name, no match |
| } |
| if ((patternChar = pattern[iPattern]) == '*') { |
| break; |
| } |
| if (iName == nameEnd) { |
| return false; // name has ended but not the pattern |
| } |
| if (patternChar |
| != (isCaseSensitive |
| ? name[iName] |
| : ScannerHelper.toLowerCase(name[iName])) |
| && patternChar != '?') { |
| return false; |
| } |
| iName++; |
| iPattern++; |
| } |
| /* check sequence of star+segment */ |
| int segmentStart; |
| if (patternChar == '*') { |
| segmentStart = ++iPattern; // skip star |
| } else { |
| segmentStart = 0; // force iName check |
| } |
| int prefixStart = iName; |
| checkSegment : while (iName < nameEnd) { |
| if (iPattern == patternEnd) { |
| iPattern = segmentStart; // mismatch - restart current segment |
| iName = ++prefixStart; |
| continue checkSegment; |
| } |
| /* segment is ending */ |
| if ((patternChar = pattern[iPattern]) == '*') { |
| segmentStart = ++iPattern; // skip start |
| if (segmentStart == patternEnd) { |
| return true; |
| } |
| prefixStart = iName; |
| continue checkSegment; |
| } |
| /* check current name character */ |
| if ((isCaseSensitive ? name[iName] : ScannerHelper.toLowerCase(name[iName])) |
| != patternChar |
| && patternChar != '?') { |
| iPattern = segmentStart; // mismatch - restart current segment |
| iName = ++prefixStart; |
| continue checkSegment; |
| } |
| iName++; |
| iPattern++; |
| } |
| |
| return (segmentStart == patternEnd) |
| || (iName == nameEnd && iPattern == patternEnd) |
| || (iPattern == patternEnd - 1 && pattern[iPattern] == '*'); |
| } |
| |
| /** |
| * Answers true if the pattern matches the filepath using the pathSepatator, false otherwise. |
| * |
| * Path char[] pattern matching, accepting wild-cards '**', '*' and '?' (using Ant directory tasks |
| * conventions, also see "http://jakarta.apache.org/ant/manual/dirtasks.html#defaultexcludes"). |
| * Path pattern matching is enhancing regular pattern matching in supporting extra rule where '**' represent |
| * any folder combination. |
| * Special rule: |
| * - foo\ is equivalent to foo\** |
| * When not case sensitive, the pattern is assumed to already be lowercased, the |
| * name will be lowercased character per character as comparing. |
| * |
| * @param pattern the given pattern |
| * @param filepath the given path |
| * @param isCaseSensitive to find out whether or not the matching should be case sensitive |
| * @param pathSeparator the given path separator |
| * @return true if the pattern matches the filepath using the pathSepatator, false otherwise |
| */ |
| public static final boolean pathMatch( |
| char[] pattern, |
| char[] filepath, |
| boolean isCaseSensitive, |
| char pathSeparator) { |
| |
| if (filepath == null) |
| return false; // null name cannot match |
| if (pattern == null) |
| return true; // null pattern is equivalent to '*' |
| |
| // offsets inside pattern |
| int pSegmentStart = pattern[0] == pathSeparator ? 1 : 0; |
| int pLength = pattern.length; |
| int pSegmentEnd = CharOperation.indexOf(pathSeparator, pattern, pSegmentStart+1); |
| if (pSegmentEnd < 0) pSegmentEnd = pLength; |
| |
| // special case: pattern foo\ is equivalent to foo\** |
| boolean freeTrailingDoubleStar = pattern[pLength - 1] == pathSeparator; |
| |
| // offsets inside filepath |
| int fSegmentStart, fLength = filepath.length; |
| if (filepath[0] != pathSeparator){ |
| fSegmentStart = 0; |
| } else { |
| fSegmentStart = 1; |
| } |
| if (fSegmentStart != pSegmentStart) { |
| return false; // both must start with a separator or none. |
| } |
| int fSegmentEnd = CharOperation.indexOf(pathSeparator, filepath, fSegmentStart+1); |
| if (fSegmentEnd < 0) fSegmentEnd = fLength; |
| |
| // first segments |
| while (pSegmentStart < pLength |
| && !(pSegmentEnd == pLength && freeTrailingDoubleStar |
| || (pSegmentEnd == pSegmentStart + 2 |
| && pattern[pSegmentStart] == '*' |
| && pattern[pSegmentStart + 1] == '*'))) { |
| |
| if (fSegmentStart >= fLength) |
| return false; |
| if (!CharOperation |
| .match( |
| pattern, |
| pSegmentStart, |
| pSegmentEnd, |
| filepath, |
| fSegmentStart, |
| fSegmentEnd, |
| isCaseSensitive)) { |
| return false; |
| } |
| |
| // jump to next segment |
| pSegmentEnd = |
| CharOperation.indexOf( |
| pathSeparator, |
| pattern, |
| pSegmentStart = pSegmentEnd + 1); |
| // skip separator |
| if (pSegmentEnd < 0) |
| pSegmentEnd = pLength; |
| |
| fSegmentEnd = |
| CharOperation.indexOf( |
| pathSeparator, |
| filepath, |
| fSegmentStart = fSegmentEnd + 1); |
| // skip separator |
| if (fSegmentEnd < 0) fSegmentEnd = fLength; |
| } |
| |
| /* check sequence of doubleStar+segment */ |
| int pSegmentRestart; |
| if ((pSegmentStart >= pLength && freeTrailingDoubleStar) |
| || (pSegmentEnd == pSegmentStart + 2 |
| && pattern[pSegmentStart] == '*' |
| && pattern[pSegmentStart + 1] == '*')) { |
| pSegmentEnd = |
| CharOperation.indexOf( |
| pathSeparator, |
| pattern, |
| pSegmentStart = pSegmentEnd + 1); |
| // skip separator |
| if (pSegmentEnd < 0) pSegmentEnd = pLength; |
| pSegmentRestart = pSegmentStart; |
| } else { |
| if (pSegmentStart >= pLength) return fSegmentStart >= fLength; // true if filepath is done too. |
| pSegmentRestart = 0; // force fSegmentStart check |
| } |
| int fSegmentRestart = fSegmentStart; |
| checkSegment : while (fSegmentStart < fLength) { |
| |
| if (pSegmentStart >= pLength) { |
| if (freeTrailingDoubleStar) return true; |
| // mismatch - restart current path segment |
| pSegmentEnd = |
| CharOperation.indexOf(pathSeparator, pattern, pSegmentStart = pSegmentRestart); |
| if (pSegmentEnd < 0) pSegmentEnd = pLength; |
| |
| fSegmentRestart = |
| CharOperation.indexOf(pathSeparator, filepath, fSegmentRestart + 1); |
| // skip separator |
| if (fSegmentRestart < 0) { |
| fSegmentRestart = fLength; |
| } else { |
| fSegmentRestart++; |
| } |
| fSegmentEnd = |
| CharOperation.indexOf(pathSeparator, filepath, fSegmentStart = fSegmentRestart); |
| if (fSegmentEnd < 0) fSegmentEnd = fLength; |
| continue checkSegment; |
| } |
| |
| /* path segment is ending */ |
| if (pSegmentEnd == pSegmentStart + 2 |
| && pattern[pSegmentStart] == '*' |
| && pattern[pSegmentStart + 1] == '*') { |
| pSegmentEnd = |
| CharOperation.indexOf(pathSeparator, pattern, pSegmentStart = pSegmentEnd + 1); |
| // skip separator |
| if (pSegmentEnd < 0) pSegmentEnd = pLength; |
| pSegmentRestart = pSegmentStart; |
| fSegmentRestart = fSegmentStart; |
| if (pSegmentStart >= pLength) return true; |
| continue checkSegment; |
| } |
| /* chech current path segment */ |
| if (!CharOperation.match( |
| pattern, |
| pSegmentStart, |
| pSegmentEnd, |
| filepath, |
| fSegmentStart, |
| fSegmentEnd, |
| isCaseSensitive)) { |
| // mismatch - restart current path segment |
| pSegmentEnd = |
| CharOperation.indexOf(pathSeparator, pattern, pSegmentStart = pSegmentRestart); |
| if (pSegmentEnd < 0) pSegmentEnd = pLength; |
| |
| fSegmentRestart = |
| CharOperation.indexOf(pathSeparator, filepath, fSegmentRestart + 1); |
| // skip separator |
| if (fSegmentRestart < 0) { |
| fSegmentRestart = fLength; |
| } else { |
| fSegmentRestart++; |
| } |
| fSegmentEnd = |
| CharOperation.indexOf(pathSeparator, filepath, fSegmentStart = fSegmentRestart); |
| if (fSegmentEnd < 0) fSegmentEnd = fLength; |
| continue checkSegment; |
| } |
| // jump to next segment |
| pSegmentEnd = |
| CharOperation.indexOf( |
| pathSeparator, |
| pattern, |
| pSegmentStart = pSegmentEnd + 1); |
| // skip separator |
| if (pSegmentEnd < 0) |
| pSegmentEnd = pLength; |
| |
| fSegmentEnd = |
| CharOperation.indexOf( |
| pathSeparator, |
| filepath, |
| fSegmentStart = fSegmentEnd + 1); |
| // skip separator |
| if (fSegmentEnd < 0) |
| fSegmentEnd = fLength; |
| } |
| |
| return (pSegmentRestart >= pSegmentEnd) |
| || (fSegmentStart >= fLength && pSegmentStart >= pLength) |
| || (pSegmentStart == pLength - 2 |
| && pattern[pSegmentStart] == '*' |
| && pattern[pSegmentStart + 1] == '*') |
| || (pSegmentStart == pLength && freeTrailingDoubleStar); |
| } |
| |
| /** |
| * Answers the number of occurrences of the given character in the given array, 0 if any. |
| * |
| * <br> |
| * <br> |
| * For example: |
| * <ol> |
| * <li><pre> |
| * toBeFound = 'b' |
| * array = { 'a' , 'b', 'b', 'a', 'b', 'a' } |
| * result => 3 |
| * </pre> |
| * </li> |
| * <li><pre> |
| * toBeFound = 'c' |
| * array = { 'a' , 'b', 'b', 'a', 'b', 'a' } |
| * result => 0 |
| * </pre> |
| * </li> |
| * </ol> |
| * |
| * @param toBeFound the given character |
| * @param array the given array |
| * @return the number of occurrences of the given character in the given array, 0 if any |
| * @throws NullPointerException if array is null |
| */ |
| public static final int occurencesOf(char toBeFound, char[] array) { |
| int count = 0; |
| for (int i = 0; i < array.length; i++) |
| if (toBeFound == array[i]) |
| count++; |
| return count; |
| } |
| |
| /** |
| * Answers the number of occurrences of the given character in the given array starting |
| * at the given index, 0 if any. |
| * |
| * <br> |
| * <br> |
| * For example: |
| * <ol> |
| * <li><pre> |
| * toBeFound = 'b' |
| * array = { 'a' , 'b', 'b', 'a', 'b', 'a' } |
| * start = 2 |
| * result => 2 |
| * </pre> |
| * </li> |
| * <li><pre> |
| * toBeFound = 'c' |
| * array = { 'a' , 'b', 'b', 'a', 'b', 'a' } |
| * start = 0 |
| * result => 0 |
| * </pre> |
| * </li> |
| * </ol> |
| * |
| * @param toBeFound the given character |
| * @param array the given array |
| * @param start the given index |
| * @return the number of occurrences of the given character in the given array, 0 if any |
| * @throws NullPointerException if array is null |
| * @throws ArrayIndexOutOfBoundsException if start is lower than 0 |
| */ |
| public static final int occurencesOf( |
| char toBeFound, |
| char[] array, |
| int start) { |
| int count = 0; |
| for (int i = start; i < array.length; i++) |
| if (toBeFound == array[i]) |
| count++; |
| return count; |
| } |
| /** |
| * Return the int value represented by the designated subpart of array. The |
| * calculation of the result for single-digit positive integers is optimized in |
| * time. |
| * @param array the array within which the int value is to be parsed |
| * @param start first character of the int value in array |
| * @param length length of the int value in array |
| * @return the int value of a subpart of array |
| * @throws NumberFormatException if the designated subpart of array does not |
| * parse to an int |
| * @since 3.4 |
| */ |
| public static final int parseInt(char[] array, int start, int length) throws NumberFormatException { |
| if (length == 1) { |
| int result = array[start] - '0'; |
| if (result < 0 || result > 9) { |
| throw new NumberFormatException("invalid digit"); //$NON-NLS-1$ |
| } |
| return result; |
| } else { |
| return Integer.parseInt(new String(array, start, length)); |
| } |
| } |
| /** |
| * Answers true if the given name starts with the given prefix, false otherwise. |
| * The comparison is case sensitive. |
| * <br> |
| * <br> |
| * For example: |
| * <ol> |
| * <li><pre> |
| * prefix = { 'a' , 'b' } |
| * name = { 'a' , 'b', 'b', 'a', 'b', 'a' } |
| * result => true |
| * </pre> |
| * </li> |
| * <li><pre> |
| * prefix = { 'a' , 'c' } |
| * name = { 'a' , 'b', 'b', 'a', 'b', 'a' } |
| * result => false |
| * </pre> |
| * </li> |
| * </ol> |
| * |
| * @param prefix the given prefix |
| * @param name the given name |
| * @return true if the given name starts with the given prefix, false otherwise |
| * @throws NullPointerException if the given name is null or if the given prefix is null |
| */ |
| public static final boolean prefixEquals(char[] prefix, char[] name) { |
| |
| int max = prefix.length; |
| if (name.length < max) |
| return false; |
| for (int i = max; |
| --i >= 0; |
| ) // assumes the prefix is not larger than the name |
| if (prefix[i] != name[i]) |
| return false; |
| return true; |
| } |
| |
| /** |
| * Answers true if the given name starts with the given prefix, false otherwise. |
| * isCaseSensitive is used to find out whether or not the comparison should be case sensitive. |
| * <br> |
| * <br> |
| * For example: |
| * <ol> |
| * <li><pre> |
| * prefix = { 'a' , 'B' } |
| * name = { 'a' , 'b', 'b', 'a', 'b', 'a' } |
| * isCaseSensitive = false |
| * result => true |
| * </pre> |
| * </li> |
| * <li><pre> |
| * prefix = { 'a' , 'B' } |
| * name = { 'a' , 'b', 'b', 'a', 'b', 'a' } |
| * isCaseSensitive = true |
| * result => false |
| * </pre> |
| * </li> |
| * </ol> |
| * |
| * @param prefix the given prefix |
| * @param name the given name |
| * @param isCaseSensitive to find out whether or not the comparison should be case sensitive |
| * @return true if the given name starts with the given prefix, false otherwise |
| * @throws NullPointerException if the given name is null or if the given prefix is null |
| */ |
| public static final boolean prefixEquals( |
| char[] prefix, |
| char[] name, |
| boolean isCaseSensitive) { |
| return prefixEquals(prefix, name, isCaseSensitive, 0); |
| } |
| |
| /** |
| * Answers true if the given name, starting from the given index, starts with the given prefix, |
| * false otherwise. isCaseSensitive is used to find out whether or not the comparison should be |
| * case sensitive. |
| * <br> |
| * <br> |
| * For example: |
| * <ol> |
| * <li><pre> |
| * prefix = { 'a' , 'B' } |
| * name = { 'c', 'd', 'a' , 'b', 'b', 'a', 'b', 'a' } |
| * startIndex = 2 |
| * isCaseSensitive = false |
| * result => true |
| * </pre> |
| * </li> |
| * <li><pre> |
| * prefix = { 'a' , 'B' } |
| * name = { 'c', 'd', 'a' , 'b', 'b', 'a', 'b', 'a' } |
| * startIndex = 2 |
| * isCaseSensitive = true |
| * result => false |
| * </pre> |
| * </li> |
| * </ol> |
| * |
| * @param prefix the given prefix |
| * @param name the given name |
| * @param isCaseSensitive to find out whether or not the comparison should be case sensitive |
| * @param startIndex index from which the prefix should be searched in the name |
| * @return true if the given name starts with the given prefix, false otherwise |
| * @throws NullPointerException if the given name is null or if the given prefix is null |
| * @since 3.7 |
| */ |
| public static final boolean prefixEquals( |
| char[] prefix, |
| char[] name, |
| boolean isCaseSensitive, |
| int startIndex) { |
| |
| int max = prefix.length; |
| if (name.length - startIndex < max) |
| return false; |
| if (isCaseSensitive) { |
| for (int i = max; --i >= 0;) // assumes the prefix is not larger than the name |
| if (prefix[i] != name[startIndex + i]) |
| return false; |
| return true; |
| } |
| |
| for (int i = max; --i >= 0;) // assumes the prefix is not larger than the name |
| if (ScannerHelper.toLowerCase(prefix[i]) |
| != ScannerHelper.toLowerCase(name[startIndex + i])) |
| return false; |
| return true; |
| } |
| |
| /** |
| * Answers a new array removing a given character. Answers the given array if there is |
| * no occurrence of the character to remove. |
| * <br> |
| * <br> |
| * For example: |
| * <ol> |
| * <li><pre> |
| * array = { 'a' , 'b', 'b', 'c', 'b', 'a' } |
| * toBeRemoved = 'b' |
| * return { 'a' , 'c', 'a' } |
| * </pre> |
| * </li> |
| * <li><pre> |
| * array = { 'a' , 'b', 'b', 'a', 'b', 'a' } |
| * toBeRemoved = 'c' |
| * return array |
| * </pre> |
| * </li> |
| * </ol> |
| * |
| * @param array the given array |
| * @param toBeRemoved the character to be removed |
| * @return a new array removing given character |
| * @since 3.2 |
| */ |
| public static final char[] remove(char[] array, char toBeRemoved) { |
| |
| if (array == null) return null; |
| int length = array.length; |
| if (length == 0) return array; |
| char[] result = null; |
| int count = 0; |
| for (int i = 0; i < length; i++) { |
| char c = array[i]; |
| if (c == toBeRemoved) { |
| if (result == null) { |
| result = new char[length]; |
| System.arraycopy(array, 0, result, 0, i); |
| count = i; |
| } |
| } else if (result != null) { |
| result[count++] = c; |
| } |
| } |
| if (result == null) return array; |
| System.arraycopy(result, 0, result = new char[count], 0, count); |
| return result; |
| } |
| |
| /** |
| * Replace all occurrence of the character to be replaced with the replacement character in the |
| * given array. |
| * <br> |
| * <br> |
| * For example: |
| * <ol> |
| * <li><pre> |
| * array = { 'a' , 'b', 'b', 'a', 'b', 'a' } |
| * toBeReplaced = 'b' |
| * replacementChar = 'a' |
| * result => No returned value, but array is now equals to { 'a' , 'a', 'a', 'a', 'a', 'a' } |
| * </pre> |
| * </li> |
| * <li><pre> |
| * array = { 'a' , 'b', 'b', 'a', 'b', 'a' } |
| * toBeReplaced = 'c' |
| * replacementChar = 'a' |
| * result => No returned value, but array is now equals to { 'a' , 'b', 'b', 'a', 'b', 'a' } |
| * </pre> |
| * </li> |
| * </ol> |
| * |
| * @param array the given array |
| * @param toBeReplaced the character to be replaced |
| * @param replacementChar the replacement character |
| * @throws NullPointerException if the given array is null |
| */ |
| public static final void replace( |
| char[] array, |
| char toBeReplaced, |
| char replacementChar) { |
| if (toBeReplaced != replacementChar) { |
| for (int i = 0, max = array.length; i < max; i++) { |
| if (array[i] == toBeReplaced) |
| array[i] = replacementChar; |
| } |
| } |
| } |
| |
| /** |
| * Replace all occurrences of characters to be replaced with the replacement character in the |
| * given array. |
| * <br> |
| * <br> |
| * For example: |
| * <ol> |
| * <li><pre> |
| * array = { 'a' , 'b', 'b', 'c', 'a', 'b', 'c', 'a' } |
| * toBeReplaced = { 'b', 'c' } |
| * replacementChar = 'a' |
| * result => No returned value, but array is now equals to { 'a' , 'a', 'a', 'a', 'a', 'a', 'a', 'a' } |
| * </pre> |
| * </li> |
| * </ol> |
| * |
| * @param array the given array |
| * @param toBeReplaced characters to be replaced |
| * @param replacementChar the replacement character |
| * @throws NullPointerException if arrays are null. |
| * @since 3.1 |
| */ |
| public static final void replace(char[] array, char[] toBeReplaced, char replacementChar) { |
| replace(array, toBeReplaced, replacementChar, 0, array.length); |
| } |
| |
| /** |
| * Replace all occurrences of characters to be replaced with the replacement character in the |
| * given array from the start position (inclusive) to the end position (exclusive). |
| * <br> |
| * <br> |
| * For example: |
| * <ol> |
| * <li><pre> |
| * array = { 'a' , 'b', 'b', 'c', 'a', 'b', 'c', 'a' } |
| * toBeReplaced = { 'b', 'c' } |
| * replacementChar = 'a' |
| * start = 4 |
| * end = 8 |
| * result => No returned value, but array is now equals to { 'a' , 'b', 'b', 'c', 'a', 'a', 'a', 'a' } |
| * </pre> |
| * </li> |
| * </ol> |
| * |
| * @param array the given array |
| * @param toBeReplaced characters to be replaced |
| * @param replacementChar the replacement character |
| * @param start the given start position (inclusive) |
| * @param end the given end position (exclusive) |
| * @throws NullPointerException if arrays are null. |
| * @since 3.2 |
| */ |
| public static final void replace(char[] array, char[] toBeReplaced, char replacementChar, int start, int end) { |
| for (int i = end; --i >= start;) |
| for (int j = toBeReplaced.length; --j >= 0;) |
| if (array[i] == toBeReplaced[j]) |
| array[i] = replacementChar; |
| } |
| /** |
| * Answers a new array of characters with substitutions. No side-effect is operated on the original |
| * array, in case no substitution happened, then the result is the same as the |
| * original one. |
| * <br> |
| * <br> |
| * For example: |
| * <ol> |
| * <li><pre> |
| * array = { 'a' , 'b', 'b', 'a', 'b', 'a' } |
| * toBeReplaced = { 'b' } |
| * replacementChar = { 'a', 'a' } |
| * result => { 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a' } |
| * </pre> |
| * </li> |
| * <li><pre> |
| * array = { 'a' , 'b', 'b', 'a', 'b', 'a' } |
| * toBeReplaced = { 'c' } |
| * replacementChar = { 'a' } |
| * result => { 'a' , 'b', 'b', 'a', 'b', 'a' } |
| * </pre> |
| * </li> |
| * </ol> |
| * |
| * @param array the given array |
| * @param toBeReplaced characters to be replaced |
| * @param replacementChars the replacement characters |
| * @return a new array of characters with substitutions or the given array if none |
| * @throws NullPointerException if the given array is null |
| */ |
| public static final char[] replace( |
| char[] array, |
| char[] toBeReplaced, |
| char[] replacementChars) { |
| |
| int max = array.length; |
| int replacedLength = toBeReplaced.length; |
| int replacementLength = replacementChars.length; |
| |
| int[] starts = new int[5]; |
| int occurrenceCount = 0; |
| |
| if (!equals(toBeReplaced, replacementChars)) { |
| |
| next : for (int i = 0; i < max;) { |
| int index = indexOf(toBeReplaced, array, true, i); |
| if (index == -1) { |
| i++; |
| continue next; |
| } |
| if (occurrenceCount == starts.length) { |
| System.arraycopy( |
| starts, |
| 0, |
| starts = new int[occurrenceCount * 2], |
| 0, |
| occurrenceCount); |
| } |
| starts[occurrenceCount++] = index; |
| i = index + replacedLength; |
| } |
| } |
| if (occurrenceCount == 0) |
| return array; |
| char[] result = |
| new char[max |
| + occurrenceCount * (replacementLength - replacedLength)]; |
| int inStart = 0, outStart = 0; |
| for (int i = 0; i < occurrenceCount; i++) { |
| int offset = starts[i] - inStart; |
| System.arraycopy(array, inStart, result, outStart, offset); |
| inStart += offset; |
| outStart += offset; |
| System.arraycopy( |
| replacementChars, |
| 0, |
| result, |
| outStart, |
| replacementLength); |
| inStart += replacedLength; |
| outStart += replacementLength; |
| } |
| System.arraycopy(array, inStart, result, outStart, max - inStart); |
| return result; |
| } |
| |
| /** |
| * Replace all occurrence of the character to be replaced with the replacement character |
| * in a copy of the given array. Returns the given array if no occurrences of the character |
| * to be replaced are found. |
| * <br> |
| * <br> |
| * For example: |
| * <ol> |
| * <li><pre> |
| * array = { 'a' , 'b', 'b', 'a', 'b', 'a' } |
| * toBeReplaced = 'b' |
| * replacementChar = 'a' |
| * result => A new array that is equals to { 'a' , 'a', 'a', 'a', 'a', 'a' } |
| * </pre> |
| * </li> |
| * <li><pre> |
| * array = { 'a' , 'b', 'b', 'a', 'b', 'a' } |
| * toBeReplaced = 'c' |
| * replacementChar = 'a' |
| * result => The original array that remains unchanged. |
| * </pre> |
| * </li> |
| * </ol> |
| * |
| * @param array the given array |
| * @param toBeReplaced the character to be replaced |
| * @param replacementChar the replacement character |
| * @throws NullPointerException if the given array is null |
| * @since 3.1 |
| */ |
| public static final char[] replaceOnCopy( |
| char[] array, |
| char toBeReplaced, |
| char replacementChar) { |
| |
| char[] result = null; |
| for (int i = 0, length = array.length; i < length; i++) { |
| char c = array[i]; |
| if (c == toBeReplaced) { |
| if (result == null) { |
| result = new char[length]; |
| System.arraycopy(array, 0, result, 0, i); |
| } |
| result[i] = replacementChar; |
| } else if (result != null) { |
| result[i] = c; |
| } |
| } |
| if (result == null) return array; |
| return result; |
| } |
| |
| /** |
| * Return a new array which is the split of the given array using the given divider and trimming each subarray to remove |
| * whitespaces equals to ' '. |
| * <br> |
| * <br> |
| * For example: |
| * <ol> |
| * <li><pre> |
| * divider = 'b' |
| * array = { 'a' , 'b', 'b', 'a', 'b', 'a' } |
| * result => { { 'a' }, { }, { 'a' }, { 'a' } } |
| * </pre> |
| * </li> |
| * <li><pre> |
| * divider = 'c' |
| * array = { 'a' , 'b', 'b', 'a', 'b', 'a' } |
| * result => { { 'a', 'b', 'b', 'a', 'b', 'a' } } |
| * </pre> |
| * </li> |
| * <li><pre> |
| * divider = 'b' |
| * array = { 'a' , ' ', 'b', 'b', 'a', 'b', 'a' } |
| * result => { { 'a' }, { }, { 'a' }, { 'a' } } |
| * </pre> |
| * </li> |
| * <li><pre> |
| * divider = 'c' |
| * array = { ' ', ' ', 'a' , 'b', 'b', 'a', 'b', 'a', ' ' } |
| * result => { { 'a', 'b', 'b', 'a', 'b', 'a' } } |
| * </pre> |
| * </li> |
| * </ol> |
| * |
| * @param divider the given divider |
| * @param array the given array |
| * @return a new array which is the split of the given array using the given divider and trimming each subarray to remove |
| * whitespaces equals to ' ' |
| */ |
| public static final char[][] splitAndTrimOn(char divider, char[] array) { |
| int length = array == null ? 0 : array.length; |
| if (length == 0) |
| return NO_CHAR_CHAR; |
| |
| int wordCount = 1; |
| for (int i = 0; i < length; i++) |
| if (array[i] == divider) |
| wordCount++; |
| char[][] split = new char[wordCount][]; |
| int last = 0, currentWord = 0; |
| for (int i = 0; i < length; i++) { |
| if (array[i] == divider) { |
| int start = last, end = i - 1; |
| while (start < i && array[start] == ' ') |
| start++; |
| while (end > start && array[end] == ' ') |
| end--; |
| split[currentWord] = new char[end - start + 1]; |
| System.arraycopy( |
| array, |
| start, |
| split[currentWord++], |
| 0, |
| end - start + 1); |
| last = i + 1; |
| } |
| } |
| int start = last, end = length - 1; |
| while (start < length && array[start] == ' ') |
| start++; |
| while (end > start && array[end] == ' ') |
| end--; |
| split[currentWord] = new char[end - start + 1]; |
| System.arraycopy( |
| array, |
| start, |
| split[currentWord++], |
| 0, |
| end - start + 1); |
| return split; |
| } |
| |
| /** |
| * Return a new array which is the split of the given array using the given divider. |
| * <br> |
| * <br> |
| * For example: |
| * <ol> |
| * <li><pre> |
| * divider = 'b' |
| * array = { 'a' , 'b', 'b', 'a', 'b', 'a' } |
| * result => { { 'a' }, { }, { 'a' }, { 'a' } } |
| * </pre> |
| * </li> |
| * <li><pre> |
| * divider = 'c' |
| * array = { 'a' , 'b', 'b', 'a', 'b', 'a' } |
| * result => { { 'a', 'b', 'b', 'a', 'b', 'a' } } |
| * </pre> |
| * </li> |
| * <li><pre> |
| * divider = 'c' |
| * array = { ' ', ' ', 'a' , 'b', 'b', 'a', 'b', 'a', ' ' } |
| * result => { { ' ', 'a', 'b', 'b', 'a', 'b', 'a', ' ' } } |
| * </pre> |
| * </li> |
| * </ol> |
| * |
| * @param divider the given divider |
| * @param array the given array |
| * @return a new array which is the split of the given array using the given divider |
| */ |
| public static final char[][] splitOn(char divider, char[] array) { |
| int length = array == null ? 0 : array.length; |
| if (length == 0) |
| return NO_CHAR_CHAR; |
| |
| int wordCount = 1; |
| for (int i = 0; i < length; i++) |
| if (array[i] == divider) |
| wordCount++; |
| char[][] split = new char[wordCount][]; |
| int last = 0, currentWord = 0; |
| for (int i = 0; i < length; i++) { |
| if (array[i] == divider) { |
| split[currentWord] = new char[i - last]; |
| System.arraycopy( |
| array, |
| last, |
| split[currentWord++], |
| 0, |
| i - last); |
| last = i + 1; |
| } |
| } |
| split[currentWord] = new char[length - last]; |
| System.arraycopy(array, last, split[currentWord], 0, length - last); |
| return split; |
| } |
| |
| /** |
| * Return a new array which is the split of the given array using the given divider. The given end |
| * is exclusive and the given start is inclusive. |
| * <br> |
| * <br> |
| * For example: |
| * <ol> |
| * <li><pre> |
| * divider = 'b' |
| * array = { 'a' , 'b', 'b', 'a', 'b', 'a' } |
| * start = 2 |
| * end = 5 |
| * result => { { }, { 'a' }, { } } |
| * </pre> |
| * </li> |
| * </ol> |
| * |
| * @param divider the given divider |
| * @param array the given array |
| * @param start the given starting index |
| * @param end the given ending index |
| * @return a new array which is the split of the given array using the given divider |
| * @throws ArrayIndexOutOfBoundsException if start is lower than 0 or end is greater than the array length |
| */ |
| public static final char[][] splitOn( |
| char divider, |
| char[] array, |
| int start, |
| int end) { |
| int length = array == null ? 0 : array.length; |
| if (length == 0 || start > end) |
| return NO_CHAR_CHAR; |
| |
| int wordCount = 1; |
| for (int i = start; i < end; i++) |
| if (array[i] == divider) |
| wordCount++; |
| char[][] split = new char[wordCount][]; |
| int last = start, currentWord = 0; |
| for (int i = start; i < end; i++) { |
| if (array[i] == divider) { |
| split[currentWord] = new char[i - last]; |
| System.arraycopy( |
| array, |
| last, |
| split[currentWord++], |
| 0, |
| i - last); |
| last = i + 1; |
| } |
| } |
| split[currentWord] = new char[end - last]; |
| System.arraycopy(array, last, split[currentWord], 0, end - last); |
| return split; |
| } |
| |
| /** |
| * Return a new array which is the split of the given array using the given divider ignoring the |
| * text between (possibly nested) openEncl and closingEncl. If there are no openEncl in the code |
| * this is identical to {@link CharOperation#splitOn(char, char[], int, int)}. The given end |
| * is exclusive and the given start is inclusive. |
| * <br> |
| * <br> |
| * For example: |
| * <ol> |
| * <li><pre> |
| * divider = ',' |
| * array = { 'A' , '<', 'B', ',', 'C', '>', ',', 'D' } |
| * start = 0 |
| * end = 8 |
| * result => { { 'A' , '<', 'B', ',', 'C', '>'}, { 'D' }} |
| * </pre> |
| * </li> |
| * </ol> |
| * |
| * @param divider the given divider |
| * @param openEncl the opening enclosure |
| * @param closeEncl the closing enclosure |
| * @param array the given array |
| * @param start the given starting index |
| * @param end the given ending index |
| * @return a new array which is the split of the given array using the given divider |
| * @throws ArrayIndexOutOfBoundsException if start is lower than 0 or end is greater than the array length |
| * @since 3.12 |
| */ |
| public static final char[][] splitOnWithEnclosures( |
| char divider, |
| char openEncl, |
| char closeEncl, |
| char[] array, |
| int start, |
| int end) { |
| int length = array == null ? 0 : array.length; |
| if (length == 0 || start > end) |
| return NO_CHAR_CHAR; |
| |
| int wordCount = 1; |
| int enclCount = 0; |
| for (int i = start; i < end; i++) { |
| if (array[i] == openEncl) |
| enclCount++; |
| else if (array[i] == divider) |
| wordCount++; |
| } |
| if (enclCount == 0) |
| return CharOperation.splitOn(divider, array, start, end); |
| |
| int nesting = 0; |
| if (openEncl == divider || closeEncl == divider) // divider should be distinct |
| return CharOperation.NO_CHAR_CHAR; |
| |
| int[][] splitOffsets = new int[wordCount][2]; //maximum |
| int last = start, currentWord = 0, prevOffset = start; |
| for (int i = start; i < end; i++) { |
| if (array[i] == openEncl) { |
| ++nesting; |
| continue; |
| } |
| if (array[i] == closeEncl) { |
| if (nesting > 0) |
| --nesting; |
| continue; |
| } |
| if (array[i] == divider && nesting == 0) { |
| splitOffsets[currentWord][0] = prevOffset; |
| last = splitOffsets[currentWord++][1] = i; |
| prevOffset = last + 1; |
| } |
| } |
| if (last < end - 1) { |
| splitOffsets[currentWord][0] = prevOffset; |
| splitOffsets[currentWord++][1] = end; |
| } |
| char[][] split = new char[currentWord][]; |
| for (int i = 0; i < currentWord; ++i) { |
| int sStart = splitOffsets[i][0]; |
| int sEnd = splitOffsets[i][1]; |
| int size = sEnd - sStart; |
| split[i] = new char[size]; |
| System.arraycopy(array, sStart, split[i], 0, size); |
| } |
| return split; |
| } |
| |
| /** |
| * Answers a new array which is a copy of the given array starting at the given start and |
| * ending at the given end. The given start is inclusive and the given end is exclusive. |
| * Answers null if start is greater than end, if start is lower than 0 or if end is greater |
| * than the length of the given array. If end equals -1, it is converted to the array length. |
| * <br> |
| * <br> |
| * For example: |
| * <ol> |
| * <li><pre> |
| * array = { { 'a' } , { 'b' } } |
| * start = 0 |
| * end = 1 |
| * result => { { 'a' } } |
| * </pre> |
| * </li> |
| * <li><pre> |
| * array = { { 'a' } , { 'b' } } |
| * start = 0 |
| * end = -1 |
| * result => { { 'a' }, { 'b' } } |
| * </pre> |
| * </li> |
| * </ol> |
| * |
| * @param array the given array |
| * @param start the given starting index |
| * @param end the given ending index |
| * @return a new array which is a copy of the given array starting at the given start and |
| * ending at the given end |
| * @throws NullPointerException if the given array is null |
| */ |
| public static final char[][] subarray(char[][] array, int start, int end) { |
| if (end == -1) |
| end = array.length; |
| if (start > end) |
| return null; |
| if (start < 0) |
| return null; |
| if (end > array.length) |
| return null; |
| |
| char[][] result = new char[end - start][]; |
| System.arraycopy(array, start, result, 0, end - start); |
| return result; |
| } |
| |
| /** |
| * Answers a new array which is a copy of the given array starting at the given start and |
| * ending at the given end. The given start is inclusive and the given end is exclusive. |
| * Answers null if start is greater than end, if start is lower than 0 or if end is greater |
| * than the length of the given array. If end equals -1, it is converted to the array length. |
| * <br> |
| * <br> |
| * For example: |
| * <ol> |
| * <li><pre> |
| * array = { 'a' , 'b' } |
| * start = 0 |
| * end = 1 |
| * result => { 'a' } |
| * </pre> |
| * </li> |
| * <li><pre> |
| * array = { 'a', 'b' } |
| * start = 0 |
| * end = -1 |
| * result => { 'a' , 'b' } |
| * </pre> |
| * </li> |
| * </ol> |
| * |
| * @param array the given array |
| * @param start the given starting index |
| * @param end the given ending index |
| * @return a new array which is a copy of the given array starting at the given start and |
| * ending at the given end |
| * @throws NullPointerException if the given array is null |
| */ |
| public static final char[] subarray(char[] array, int start, int end) { |
| if (end == -1) |
| end = array.length; |
| if (start > end) |
| return null; |
| if (start < 0) |
| return null; |
| if (end > array.length) |
| return null; |
| |
| char[] result = new char[end - start]; |
| System.arraycopy(array, start, result, 0, end - start); |
| return result; |
| } |
| |
| /** |
| * Answers the result of a char[] conversion to lowercase. Answers null if the given chars array is null. |
| * <br> |
| * NOTE: If no conversion was necessary, then answers back the argument one. |
| * <br> |
| * <br> |
| * For example: |
| * <ol> |
| * <li><pre> |
| * chars = { 'a' , 'b' } |
| * result => { 'a' , 'b' } |
| * </pre> |
| * </li> |
| * <li><pre> |
| * array = { 'A', 'b' } |
| * result => { 'a' , 'b' } |
| * </pre> |
| * </li> |
| * </ol> |
| * |
| * @param chars the chars to convert |
| * @return the result of a char[] conversion to lowercase |
| */ |
| final static public char[] toLowerCase(char[] chars) { |
| if (chars == null) |
| return null; |
| int length = chars.length; |
| char[] lowerChars = null; |
| for (int i = 0; i < length; i++) { |
| char c = chars[i]; |
| char lc = ScannerHelper.toLowerCase(c); |
| if ((c != lc) || (lowerChars != null)) { |
| if (lowerChars == null) { |
| System.arraycopy( |
| chars, |
| 0, |
| lowerChars = new char[length], |
| 0, |
| i); |
| } |
| lowerChars[i] = lc; |
| } |
| } |
| return lowerChars == null ? chars : lowerChars; |
| } |
| |
| /** |
| * Answers the result of a char[] conversion to uppercase. Answers null if the given chars array is null. |
| * <br> |
| * NOTE: If no conversion was necessary, then answers back the argument one. |
| * <br> |
| * <br> |
| * For example: |
| * <ol> |
| * <li><pre> |
| * chars = { 'A' , 'B' } |
| * result => { 'A' , 'B' } |
| * </pre> |
| * </li> |
| * <li><pre> |
| * array = { 'a', 'B' } |
| * result => { 'A' , 'B' } |
| * </pre> |
| * </li> |
| * </ol> |
| * |
| * @param chars the chars to convert |
| * @return the result of a char[] conversion to uppercase |
| * |
| * @since 3.5 |
| */ |
| final static public char[] toUpperCase(char[] chars) { |
| if (chars == null) |
| return null; |
| int length = chars.length; |
| char[] upperChars = null; |
| for (int i = 0; i < length; i++) { |
| char c = chars[i]; |
| char lc = ScannerHelper.toUpperCase(c); |
| if ((c != lc) || (upperChars != null)) { |
| if (upperChars == null) { |
| System.arraycopy( |
| chars, |
| 0, |
| upperChars = new char[length], |
| 0, |
| i); |
| } |
| upperChars[i] = lc; |
| } |
| } |
| return upperChars == null ? chars : upperChars; |
| } |
| |
| /** |
| * Answers a new array removing leading and trailing spaces (' '). Answers the given array if there is no |
| * space characters to remove. |
| * <br> |
| * <br> |
| * For example: |
| * <ol> |
| * <li><pre> |
| * chars = { ' ', 'a' , 'b', ' ', ' ' } |
| * result => { 'a' , 'b' } |
| * </pre> |
| * </li> |
| * <li><pre> |
| * array = { 'A', 'b' } |
| * result => { 'A' , 'b' } |
| * </pre> |
| * </li> |
| * </ol> |
| * |
| * @param chars the given array |
| * @return a new array removing leading and trailing spaces (' ') |
| */ |
| final static public char[] trim(char[] chars) { |
| |
| if (chars == null) |
| return null; |
| |
| int start = 0, length = chars.length, end = length - 1; |
| while (start < length && chars[start] == ' ') { |
| start++; |
| } |
| while (end > start && chars[end] == ' ') { |
| end--; |
| } |
| if (start != 0 || end != length - 1) { |
| return subarray(chars, start, end + 1); |
| } |
| return chars; |
| } |
| |
| /** |
| * Answers a string which is the concatenation of the given array using the '.' as a separator. |
| * <br> |
| * <br> |
| * For example: |
| * <ol> |
| * <li><pre> |
| * array = { { 'a' } , { 'b' } } |
| * result => "a.b" |
| * </pre> |
| * </li> |
| * <li><pre> |
| * array = { { ' ', 'a' } , { 'b' } } |
| * result => " a.b" |
| * </pre> |
| * </li> |
| * </ol> |
| * |
| * @param array the given array |
| * @return a string which is the concatenation of the given array using the '.' as a separator |
| */ |
| final static public String toString(char[][] array) { |
| char[] result = concatWith(array, '.'); |
| return new String(result); |
| } |
| |
| /** |
| * Answers an array of strings from the given array of char array. |
| * |
| * @param array the given array |
| * @return an array of strings |
| * @since 3.0 |
| */ |
| final static public String[] toStrings(char[][] array) { |
| if (array == null) return NO_STRINGS; |
| int length = array.length; |
| if (length == 0) return NO_STRINGS; |
| String[] result = new String[length]; |
| for (int i = 0; i < length; i++) |
| result[i] = new String(array[i]); |
| return result; |
| } |
| } |