/* * Created on Jan 29, 2005 */ package org.rubypeople.rdt.internal.core.util; import org.rubypeople.rdt.internal.compiler.parser.ScannerHelper; /** * @author Chris */ public class CharOperation { /** * Constant for an empty char array */ public static final char[] NO_CHAR = new char[0]; public static final String[] NO_STRINGS = new String[0]; /** * Constant for an empty char array with two dimensions. */ public static final char[][] NO_CHAR_CHAR = new char[0][]; /** * 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 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; i++) { int j = 0; while (j < replacedLength) { if (i + j == max) continue next; if (array[i + j] != toBeReplaced[j++]) continue next; } if (occurrenceCount == starts.length) { System.arraycopy(starts, 0, starts = new int[occurrenceCount * 2], 0, occurrenceCount); } starts[occurrenceCount++] = i; } } 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; } /** * 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 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 true if the a sub-pattern matches the subpart of the given name, * false otherwise. char[] pattern matching, accepting wild-cards '*' and * '?'. Can match only subset of name/pattern. end positions are * non-inclusive. The subpattern is defined by the patternStart and * pattternEnd positions. When not case sensitive, the pattern is assumed to * already be lowercased, the name will be lowercased character per * character as comparing. <br> * <br> * For example: * <ol> * <li> * * <pre> * * pattern = { '?', 'b', '*' } * patternStart = 1 * patternEnd = 3 * name = { 'a', 'b', 'c' , 'd' } * nameStart = 1 * nameEnd = 4 * isCaseSensitive = true * result => true * * </pre> * * </li> * <li> * * <pre> * * pattern = { '?', 'b', '*' } * patternStart = 1 * patternEnd = 2 * name = { 'a', 'b', 'c' , 'd' } * nameStart = 1 * nameEnd = 2 * isCaseSensitive = true * result => false * * </pre> * * </li> * </ol> * * @param pattern * the given pattern * @param patternStart * the given pattern start * @param patternEnd * the given pattern end * @param name * the given name * @param nameStart * the given name start * @param nameEnd * the given name end * @param isCaseSensitive * flag to know if the matching should be case sensitive * @return true if the a sub-pattern matches the subpart of the given name, * false otherwise */ public static final boolean match(char[] pattern, int patternStart, int patternEnd, char[] name, int nameStart, int nameEnd, boolean isCaseSensitive) { if (name == null) return false; // null name cannot match if (pattern == null) return true; // null pattern is equivalent to '*' int iPattern = patternStart; int iName = nameStart; if (patternEnd < 0) patternEnd = pattern.length; if (nameEnd < 0) nameEnd = name.length; /* check first segment */ char patternChar = 0; while ((iPattern < patternEnd) && (patternChar = pattern[iPattern]) != '*') { if (iName == nameEnd) return false; if (patternChar != (isCaseSensitive ? name[iName] : Character .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] : Character .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 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 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 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 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; } public static final boolean equals(String[] first, String[] 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].equals(second[i])) return false; return true; } /** * Answers a hashcode for the array * * @param array * the array for which a hashcode is required * @return the hashcode * @throws NullPointerException * if array is null */ public static final int hashCode(char[] array) { int length = array.length; int hash = length == 0 ? 31 : array[0]; if (length < 8) { for (int i = length; --i > 0;) hash = (hash * 31) + array[i]; } else { // 8 characters is enough to compute a decent hash code, don't waste // time examining every character for (int i = length - 1, last = i > 16 ? i - 16 : 0; i > last; i -= 2) hash = (hash * 31) + array[i]; } return hash & 0x7FFFFFFF; } /** * Answers 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; } 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; } /** * 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; } 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); } public static final boolean camelCaseMatch(char[] pattern, int patternStart, int patternEnd, char[] name, int nameStart, int nameEnd) { 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, so it's a match return true; } if (iName == nameEnd){ // We have exhausted name (and not 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) == 0) { return false; } } else if (Character.isJavaIdentifierPart(patternChar) && !Character.isUpperCase(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) { if ((ScannerHelper.OBVIOUS_IDENT_CHAR_NATURES[nameChar] & (ScannerHelper.C_LOWER_LETTER | ScannerHelper.C_SPECIAL | ScannerHelper.C_DIGIT)) != 0) { // nameChar is lowercase 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; } } else if (Character.isJavaIdentifierPart(nameChar) && !Character.isUpperCase(nameChar)) { // nameChar is lowercase 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; } } // At this point, either name has been exhausted, or it is at an uppercase letter. // Since pattern is also at an uppercase letter } } 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; } public static final boolean prefixEquals( char[] prefix, char[] name, boolean isCaseSensitive) { int max = prefix.length; if (name.length < max) return false; if (isCaseSensitive) { for (int i = max; --i >= 0; ) // assumes the prefix is not larger than the name if (prefix[i] != name[i]) return false; return true; } for (int i = max; --i >= 0; ) // assumes the prefix is not larger than the name if (ScannerHelper.toLowerCase(prefix[i]) != ScannerHelper.toLowerCase(name[i])) return false; return true; } 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 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 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 concatenation of the three arrays inserting the sep1 character between the * first two arrays and sep2 between the last two. * It answers null if the three arrays are null. * If the first array is null, then it answers the concatenation of second and third inserting * the sep2 character between them. * If the second array is null, then it answers the concatenation of first and third inserting * the sep1 character between them. * If the third array is null, then it answers the concatenation of first and second inserting * the sep1 character between them. * <br> * <br> * For example: * <ol> * <li><pre> * first = null * sep1 = '/' * second = { 'a' } * sep2 = ':' * third = { 'b' } * => result = { ' a' , ':', 'b' } * </pre> * </li> * <li><pre> * first = { 'a' } * sep1 = '/' * second = null * sep2 = ':' * third = { 'b' } * => result = { ' a' , '/', 'b' } * </pre> * </li> * <li><pre> * first = { 'a' } * sep1 = '/' * second = { 'b' } * sep2 = ':' * third = null * => result = { ' a' , '/', 'b' } * </pre> * </li> * <li><pre> * first = { 'a' } * sep1 = '/' * second = { 'b' } * sep2 = ':' * third = { 'c' } * => result = { ' a' , '/', 'b' , ':', 'c' } * </pre> * </li> * </ol> * * @param first the first array to concatenate * @param sep1 the character to insert * @param second the second array to concatenate * @param sep2 the character to insert * @param third the second array to concatenate * @return the concatenation of the three arrays inserting the sep1 character between the * two arrays and sep2 between the last two. */ public static final char[] concat( char[] first, char sep1, char[] second, char sep2, char[] third) { if (first == null) return concat(second, third, sep2); if (second == null) return concat(first, third, sep1); if (third == null) return concat(first, second, sep1); int length1 = first.length; int length2 = second.length; int length3 = third.length; char[] result = new char[length1 + length2 + length3 + 2]; System.arraycopy(first, 0, result, 0, length1); result[length1] = sep1; System.arraycopy(second, 0, result, length1 + 1, length2); result[length1 + length2 + 1] = sep2; System.arraycopy(third, 0, result, length1 + length2 + 2, length3); return result; } /** * Answers a new array with 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 concanated 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; } /** * 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; } public static char[][] splitOn(String divider, char[] key, int start, int last) { String newKey = new String(key); newKey = newKey.substring(start, last); String[] result = newKey.split(divider); char[][] resultEnd = new char[result.length][]; for (int i = 0; i < resultEnd.length; i++) { resultEnd[i] = result[i].toCharArray(); } return resultEnd; } public static char[][] splitOn(String divider, char[] key) { String newKey = new String(key); String[] result = newKey.split(divider); char[][] resultEnd = new char[result.length][]; for (int i = 0; i < resultEnd.length; i++) { resultEnd[i] = result[i].toCharArray(); } return resultEnd; } public static int occurencesOf(String toBeFound, char[] originalString) { String newKey = new String(originalString); int count = 0; int index = newKey.indexOf(toBeFound); while (index > -1) { count++; if (newKey.length() < index + toBeFound.length()) break; newKey = newKey.substring(index + toBeFound.length()); index = newKey.indexOf(toBeFound); } return count; } public static int lastIndexOf(String toBeFound, char[] typePart) { if (typePart == null || typePart.length == 0) return -1; return new String(typePart).lastIndexOf(toBeFound); } /** * 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; } public static char[] lastSegment(char[] typeName, String divider) { if (typeName == null) return NO_CHAR; char[][] result = splitOn(divider, typeName); return result[result.length - 1]; } /** * 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 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; } public static char[] concatWith(char[][] enclosingTypeNames, String string) { if (enclosingTypeNames == null) return NO_CHAR; StringBuffer buffer = new StringBuffer(); for (int i = 0; i < enclosingTypeNames.length; i++) { char[] name = enclosingTypeNames[i]; if (i > 0) buffer.append(string); buffer.append(name); } return buffer.toString().toCharArray(); } public static char[] concat(char[] one, char[] two, String separator) { StringBuffer buffer = new StringBuffer(); buffer.append(one); buffer.append(separator); buffer.append(two); return buffer.toString().toCharArray(); } }