1 /*******************************************************************************
2 * Copyright (c) 2000, 2001, 2002 International Business Machines Corp. and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Common Public License v0.5
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/cpl-v05.html
9 * IBM Corporation - initial API and implementation
10 ******************************************************************************/
11 package net.sourceforge.phpdt.core.compiler;
14 * This class is a collection of helper methods to manipulate char arrays.
18 public final class CharOperation {
21 * Constant for an empty char array
23 public static final char[] NO_CHAR = new char[0];
26 * Constant for an empty char array with two dimensions.
28 public static final char[][] NO_CHAR_CHAR = new char[0][];
31 * Answers a new array with appending the suffix character at the end of the array.
37 * array = { 'a', 'b' }
39 * => result = { 'a', 'b' , 'c' }
49 * @param array the array that is concanated with the suffix character
50 * @param suffix the suffix character
51 * @return the new array
53 public static final char[] append(char[] array, char suffix) {
55 return new char[] { suffix };
56 int length = array.length;
57 System.arraycopy(array, 0, array = new char[length + 1], 0, length);
58 array[length] = suffix;
62 * Append the given subarray to append to the target array starting at the given index in the target array.
63 * The start of the subarray is inclusive, the end is exclusive.
64 * Answers a new target array if it needs to grow, otherwise answers the same target array.
69 * target = { 'a', 'b', -1 }
71 * array = { 'c', 'd' }
74 * => result = { 'a', 'b' , 'c' }
78 * target = { 'a', 'b' }
80 * array = { 'c', 'd' }
83 * => result = new { 'a', 'b' , 'c', -1 }
86 * target = { 'a', 'b', 'c' }
88 * array = { 'c', 'd', 'e', 'f' }
91 * => result = new { 'a', 'd' , 'e', 'f', -1, -1 }
95 public static final char[] append(char[] target, int index, char[] array, int start, int end) {
96 int targetLength = target.length;
97 int subLength = end-start;
98 int newTargetLength = subLength+index;
99 if (newTargetLength > targetLength) {
100 System.arraycopy(target, 0, target = new char[newTargetLength*2], 0, index);
102 System.arraycopy(array, start, target, index, subLength);
107 * Answers the concatenation of the two arrays. It answers null if the two arrays are null.
108 * If the first array is null, then the second array is returned.
109 * If the second array is null, then the first array is returned.
121 * first = { { ' a' } }
123 * => result = { { ' a' } }
128 * second = { { ' a' } }
129 * => result = { { ' a' } }
133 * first = { { ' b' } }
134 * second = { { ' a' } }
135 * => result = { { ' b' }, { ' a' } }
140 * @param first the first array to concatenate
141 * @param second the second array to concatenate
142 * @return the concatenation of the two arrays, or null if the two arrays are null.
144 public static final char[][] arrayConcat(char[][] first, char[][] second) {
150 int length1 = first.length;
151 int length2 = second.length;
152 char[][] result = new char[length1 + length2][];
153 System.arraycopy(first, 0, result, 0, length1);
154 System.arraycopy(second, 0, result, length1, length2);
159 * Answers a new array adding the second array at the end of first array.
160 * It answers null if the first and second are null.
161 * If the first array is null, then a new array char[][] is created with second.
162 * If the second array is null, then the first array is returned.
170 * => result = { { ' a' } }
173 * first = { { ' a' } }
175 * => result = { { ' a' } }
179 * first = { { ' a' } }
181 * => result = { { ' a' } , { ' b' } }
186 * @param first the first array to concatenate
187 * @param second the array to add at the end of the first array
188 * @return a new array adding the second array at the end of first array, or null if the two arrays are null.
190 public static final char[][] arrayConcat(char[][] first, char[] second) {
194 return new char[][] { second };
196 int length = first.length;
197 char[][] result = new char[length + 1][];
198 System.arraycopy(first, 0, result, 0, length);
199 result[length] = second;
204 * Answers the concatenation of the two arrays. It answers null if the two arrays are null.
205 * If the first array is null, then the second array is returned.
206 * If the second array is null, then the first array is returned.
214 * => result = { ' a' }
220 * => result = { ' a' }
226 * => result = { ' a' , ' b' }
231 * @param first the first array to concatenate
232 * @param second the second array to concatenate
233 * @return the concatenation of the two arrays, or null if the two arrays are null.
235 public static final char[] concat(char[] first, char[] second) {
241 int length1 = first.length;
242 int length2 = second.length;
243 char[] result = new char[length1 + length2];
244 System.arraycopy(first, 0, result, 0, length1);
245 System.arraycopy(second, 0, result, length1, length2);
250 * Answers the concatenation of the three arrays. It answers null if the three arrays are null.
251 * If first is null, it answers the concatenation of second and third.
252 * If second is null, it answers the concatenation of first and third.
253 * If third is null, it answers the concatenation of first and second.
262 * => result = { ' a', 'b' }
269 * => result = { ' a', 'b' }
276 * => result = { ' a', 'b' }
290 * => result = { 'a', 'b', 'c' }
295 * @param first the first array to concatenate
296 * @param second the second array to concatenate
297 * @param third the third array to concatenate
299 * @return the concatenation of the three arrays, or null if the three arrays are null.
301 public static final char[] concat(
306 return concat(second, third);
308 return concat(first, third);
310 return concat(first, second);
312 int length1 = first.length;
313 int length2 = second.length;
314 int length3 = third.length;
315 char[] result = new char[length1 + length2 + length3];
316 System.arraycopy(first, 0, result, 0, length1);
317 System.arraycopy(second, 0, result, length1, length2);
318 System.arraycopy(third, 0, result, length1 + length2, length3);
323 * Answers the concatenation of the two arrays inserting the separator character between the two arrays.
324 * It answers null if the two arrays are null.
325 * If the first array is null, then the second array is returned.
326 * If the second array is null, then the first array is returned.
335 * => result = { ' a' }
342 * => result = { ' a' }
349 * => result = { ' a' , '/', 'b' }
354 * @param first the first array to concatenate
355 * @param second the second array to concatenate
356 * @param separator the character to insert
357 * @return the concatenation of the two arrays inserting the separator character
358 * between the two arrays , or null if the two arrays are null.
360 public static final char[] concat(
369 int length1 = first.length;
372 int length2 = second.length;
376 char[] result = new char[length1 + length2 + 1];
377 System.arraycopy(first, 0, result, 0, length1);
378 result[length1] = separator;
379 System.arraycopy(second, 0, result, length1 + 1, length2);
384 * Answers the concatenation of the three arrays inserting the sep1 character between the
385 * two arrays and sep2 between the last two.
386 * It answers null if the three arrays are null.
387 * If the first array is null, then it answers the concatenation of second and third inserting
388 * the sep2 character between them.
389 * If the second array is null, then the first array is returned.
398 * => result = { ' a' }
405 * => result = { ' a' }
412 * => result = { ' a' , '/', 'b' }
417 * @param first the first array to concatenate
418 * @param second the second array to concatenate
419 * @param separator the character to insert
420 * @return the concatenation of the two arrays inserting the separator character
421 * between the two arrays , or null if the two arrays are null.
423 public static final char[] concat(
430 return concat(second, third, sep2);
432 return concat(first, third, sep1);
434 return concat(first, second, sep1);
436 int length1 = first.length;
437 int length2 = second.length;
438 int length3 = third.length;
439 char[] result = new char[length1 + length2 + length3 + 2];
440 System.arraycopy(first, 0, result, 0, length1);
441 result[length1] = sep1;
442 System.arraycopy(second, 0, result, length1 + 1, length2);
443 result[length1 + length2 + 1] = sep2;
444 System.arraycopy(third, 0, result, length1 + length2 + 2, length3);
449 * Answers a new array with prepending the prefix character and appending the suffix
450 * character at the end of the array. If array is null, it answers a new array containing the
451 * prefix and the suffix characters.
460 * => result = { 'a', 'b' , 'c' }
467 * => result = { 'a', 'c' }
471 * @param prefix the prefix character
472 * @param array the array that is concanated with the prefix and suffix characters
473 * @param suffix the suffix character
474 * @return the new array
476 public static final char[] concat(char prefix, char[] array, char suffix) {
478 return new char[] { prefix, suffix };
480 int length = array.length;
481 char[] result = new char[length + 2];
483 System.arraycopy(array, 0, result, 1, length);
484 result[length + 1] = suffix;
489 * Answers the concatenation of the given array parts using the given separator between each
490 * part and appending the given name at the end.
497 * array = { { 'a' }, { 'b' } }
499 * => result = { 'a', '.', 'b' , '.', 'c' }
504 * array = { { 'a' }, { 'b' } }
506 * => result = { 'a', '.', 'b' }
512 * => result = { 'c' }
516 * @param name the given name
517 * @param array the given array
518 * @param separator the given separator
519 * @return the concatenation of the given array parts using the given separator between each
520 * part and appending the given name at the end
522 public static final char[] concatWith(
526 int nameLength = name == null ? 0 : name.length;
528 return concatWith(array, separator);
530 int length = array == null ? 0 : array.length;
534 int size = nameLength;
537 if (array[index].length > 0)
538 size += array[index].length + 1;
539 char[] result = new char[size];
541 for (int i = length - 1; i >= 0; i--) {
542 int subLength = array[i].length;
545 System.arraycopy(array[i], 0, result, index, subLength);
546 result[--index] = separator;
549 System.arraycopy(name, 0, result, 0, nameLength);
554 * Answers the concatenation of the given array parts using the given separator between each
555 * part and appending the given name at the end.
562 * array = { { 'a' }, { 'b' } }
564 * => result = { 'a', '.', 'b' , '.', 'c' }
569 * array = { { 'a' }, { 'b' } }
571 * => result = { 'a', '.', 'b' }
577 * => result = { 'c' }
581 * @param array the given array
582 * @param name the given name
583 * @param separator the given separator
584 * @return the concatenation of the given array parts using the given separator between each
585 * part and appending the given name at the end
587 public static final char[] concatWith(
591 int nameLength = name == null ? 0 : name.length;
593 return concatWith(array, separator);
595 int length = array == null ? 0 : array.length;
599 int size = nameLength;
602 if (array[index].length > 0)
603 size += array[index].length + 1;
604 char[] result = new char[size];
606 for (int i = 0; i < length; i++) {
607 int subLength = array[i].length;
609 System.arraycopy(array[i], 0, result, index, subLength);
611 result[index++] = separator;
614 System.arraycopy(name, 0, result, index, nameLength);
619 * Answers the concatenation of the given array parts using the given separator between each part.
625 * array = { { 'a' }, { 'b' } }
627 * => result = { 'a', '.', 'b' }
637 * @param array the given array
638 * @param separator the given separator
639 * @return the concatenation of the given array parts using the given separator between each part
641 public static final char[] concatWith(char[][] array, char separator) {
642 int length = array == null ? 0 : array.length;
644 return CharOperation.NO_CHAR;
646 int size = length - 1;
648 while (--index >= 0) {
649 if (array[index].length == 0)
652 size += array[index].length;
655 return CharOperation.NO_CHAR;
656 char[] result = new char[size];
658 while (--index >= 0) {
659 length = array[index].length;
668 result[size] = separator;
675 * Answers true if the array contains an occurrence of character, false otherwise.
683 * array = { { ' a' }, { ' b' } }
689 * array = { { ' a' }, { ' b' } }
695 * @param character the character to search
696 * @param array the array in which the search is done
697 * @exception NullPointerException if array is null.
699 * @return true if the array contains an occurrence of character, false otherwise.
701 public static final boolean contains(char character, char[][] array) {
702 for (int i = array.length; --i >= 0;) {
703 char[] subarray = array[i];
704 for (int j = subarray.length; --j >= 0;)
705 if (subarray[j] == character)
712 * Answers true if the array contains an occurrence of character, false otherwise.
726 * array = { ' a' , ' b' }
732 * @param character the character to search
733 * @param array the array in which the search is done
734 * @exception NullPointerException if array is null.
736 * @return true if the array contains an occurrence of character, false otherwise.
738 public static final boolean contains(char character, char[] array) {
739 for (int i = array.length; --i >= 0;)
740 if (array[i] == character)
746 * Answers a deep copy of the toCopy array.
748 * @param toCopy the array to copy
749 * @return a deep copy of the toCopy array.
751 public static final char[][] deepCopy(char[][] toCopy) {
752 int toCopyLength = toCopy.length;
753 char[][] result = new char[toCopyLength][];
754 for (int i = 0; i < toCopyLength; i++) {
755 char[] toElement = toCopy[i];
756 int toElementLength = toElement.length;
757 char[] resultElement = new char[toElementLength];
758 System.arraycopy(toElement, 0, resultElement, 0, toElementLength);
759 result[i] = resultElement;
765 * Return true if array ends with the sequence of characters contained in toBeFound,
772 * array = { 'a', 'b', 'c', 'd' }
773 * toBeFound = { 'b', 'c' }
778 * array = { 'a', 'b', 'c' }
779 * toBeFound = { 'b', 'c' }
785 * @param array the array to check
786 * @param toBeFound the array to find
787 * @exception NullPointerException if array is null or toBeFound is null
788 * @return true if array ends with the sequence of characters contained in toBeFound,
791 public static final boolean endsWith(char[] array, char[] toBeFound) {
792 int i = toBeFound.length;
793 int j = array.length - i;
798 if (toBeFound[i] != array[i + j])
804 * Answers true if the two arrays are identical character by character, otherwise false.
805 * The equality is case sensitive.
823 * first = { { 'a' } }
824 * second = { { 'a' } }
829 * first = { { 'A' } }
830 * second = { { 'a' } }
835 * @param first the first array
836 * @param second the second array
837 * @return true if the two arrays are identical character by character, otherwise false
839 public static final boolean equals(char[][] first, char[][] second) {
842 if (first == null || second == null)
844 if (first.length != second.length)
847 for (int i = first.length; --i >= 0;)
848 if (!equals(first[i], second[i]))
854 * If isCaseSensite is true, answers true if the two arrays are identical character
855 * by character, otherwise false.
856 * If it is false, answers true if the two arrays are identical character by
857 * character without checking the case, otherwise false.
865 * isCaseSensitive = true
872 * isCaseSensitive = true
877 * first = { { 'A' } }
878 * second = { { 'a' } }
879 * isCaseSensitive = true
884 * first = { { 'A' } }
885 * second = { { 'a' } }
886 * isCaseSensitive = false
892 * @param first the first array
893 * @param second the second array
894 * @param isCaseSensitive check whether or not the equality should be case sensitive
895 * @return true if the two arrays are identical character by character according to the value
896 * of isCaseSensitive, otherwise false
898 public static final boolean equals(
901 boolean isCaseSensitive) {
903 if (isCaseSensitive) {
904 return equals(first, second);
908 if (first == null || second == null)
910 if (first.length != second.length)
913 for (int i = first.length; --i >= 0;)
914 if (!equals(first[i], second[i], false))
920 * Answers true if the two arrays are identical character by character, otherwise false.
921 * The equality is case sensitive.
951 * @param first the first array
952 * @param second the second array
953 * @return true if the two arrays are identical character by character, otherwise false
955 public static final boolean equals(char[] first, char[] second) {
958 if (first == null || second == null)
960 if (first.length != second.length)
963 for (int i = first.length; --i >= 0;)
964 if (first[i] != second[i])
970 * If isCaseSensite is true, answers true if the two arrays are identical character
971 * by character, otherwise false.
972 * If it is false, answers true if the two arrays are identical character by
973 * character without checking the case, otherwise false.
981 * isCaseSensitive = true
988 * isCaseSensitive = true
995 * isCaseSensitive = true
1002 * isCaseSensitive = false
1008 * @param first the first array
1009 * @param second the second array
1010 * @param isCaseSensitive check whether or not the equality should be case sensitive
1011 * @return true if the two arrays are identical character by character according to the value
1012 * of isCaseSensitive, otherwise false
1014 public static final boolean equals(
1017 boolean isCaseSensitive) {
1019 if (isCaseSensitive) {
1020 return equals(first, second);
1022 if (first == second)
1024 if (first == null || second == null)
1026 if (first.length != second.length)
1029 for (int i = first.length; --i >= 0;)
1030 if (Character.toLowerCase(first[i])
1031 != Character.toLowerCase(second[i]))
1036 * If isCaseSensite is true, the equality is case sensitive, otherwise it is case insensitive.
1038 * Answers true if the name contains the fragment at the starting index startIndex, otherwise false.
1044 * fragment = { 'b', 'c' , 'd' }
1045 * name = { 'a', 'b', 'c' , 'd' }
1047 * isCaseSensitive = true
1052 * fragment = { 'b', 'c' , 'd' }
1053 * name = { 'a', 'b', 'C' , 'd' }
1055 * isCaseSensitive = true
1060 * fragment = { 'b', 'c' , 'd' }
1061 * name = { 'a', 'b', 'C' , 'd' }
1063 * isCaseSensitive = false
1068 * fragment = { 'b', 'c' , 'd' }
1069 * name = { 'a', 'b'}
1071 * isCaseSensitive = true
1077 * @param fragment the fragment to check
1078 * @param second the array to check
1079 * @param startIndex the starting index
1080 * @param isCaseSensitive check whether or not the equality should be case sensitive
1081 * @return true if the name contains the fragment at the starting index startIndex according to the
1082 * value of isCaseSensitive, otherwise false.
1083 * @exception NullPointerException if fragment or name is null.
1085 public static final boolean fragmentEquals(
1089 boolean isCaseSensitive) {
1091 int max = fragment.length;
1092 if (name.length < max + startIndex)
1094 if (isCaseSensitive) {
1097 ) // assumes the prefix is not larger than the name
1098 if (fragment[i] != name[i + startIndex])
1104 ) // assumes the prefix is not larger than the name
1105 if (Character.toLowerCase(fragment[i])
1106 != Character.toLowerCase(name[i + startIndex]))
1112 * Answers a hashcode for the array
1114 * @param array the array for which a hashcode is required
1115 * @return the hashcode
1116 * @exception NullPointerException if array is null
1118 public static final int hashCode(char[] array) {
1121 int length = array.length;
1123 for (int i = length; i > 0; i--)
1124 hash = (hash * 37) + array[offset++];
1126 // only sample some characters
1127 int skip = length / 8;
1128 for (int i = length; i > 0; i -= skip, offset += skip)
1129 hash = (hash * 39) + array[offset];
1131 return hash & 0x7FFFFFFF;
1134 * Answers true if c is a whitespace according to the JLS (\u000a, \u000c, \u000d, \u0009), otherwise false.
1151 * @param c the character to check
1152 * @return true if c is a whitespace according to the JLS, otherwise false.
1154 public static boolean isWhitespace(char c) {
1156 case 10 : /* \ u000a: LINE FEED */
1157 case 12 : /* \ u000c: FORM FEED */
1158 case 13 : /* \ u000d: CARRIAGE RETURN */
1159 case 32 : /* \ u0020: SPACE */
1160 case 9 : /* \ u0009: HORIZONTAL TABULATION */
1168 * Answers the first index in the array for which the corresponding character is
1169 * equal to toBeFound. Answers -1 if no occurrence of this character is found.
1176 * array = { ' a', 'b', 'c', 'd' }
1182 * array = { ' a', 'b', 'c', 'd' }
1188 * @param toBeFound the character to search
1189 * @param array the array to be searched
1190 * @return the first index in the array for which the corresponding character is
1191 * equal to toBeFound, -1 otherwise
1192 * @exception NullPointerException if array is null
1194 public static final int indexOf(char toBeFound, char[] array) {
1195 for (int i = 0; i < array.length; i++)
1196 if (toBeFound == array[i])
1202 * Answers the first index in the array for which the corresponding character is
1203 * equal to toBeFound starting the search at index start.
1204 * Answers -1 if no occurrence of this character is found.
1211 * array = { ' a', 'b', 'c', 'd' }
1218 * array = { ' a', 'b', 'c', 'd' }
1225 * array = { ' a', 'b', 'c', 'd' }
1232 * @param toBeFound the character to search
1233 * @param array the array to be searched
1234 * @param start the starting index
1235 * @return the first index in the array for which the corresponding character is
1236 * equal to toBeFound, -1 otherwise
1237 * @exception NullPointerException if array is null
1238 * @exception ArrayIndexOutOfBoundsException if start is lower than 0
1240 public static final int indexOf(char toBeFound, char[] array, int start) {
1241 for (int i = start; i < array.length; i++)
1242 if (toBeFound == array[i])
1248 * Answers the last index in the array for which the corresponding character is
1249 * equal to toBeFound starting from the end of the array.
1250 * Answers -1 if no occurrence of this character is found.
1257 * array = { ' a', 'b', 'c', 'd' , 'c', 'e' }
1263 * array = { ' a', 'b', 'c', 'd' }
1269 * @param toBeFound the character to search
1270 * @param array the array to be searched
1271 * @return the last index in the array for which the corresponding character is
1272 * equal to toBeFound starting from the end of the array, -1 otherwise
1273 * @exception NullPointerException if array is null
1275 public static final int lastIndexOf(char toBeFound, char[] array) {
1276 for (int i = array.length; --i >= 0;)
1277 if (toBeFound == array[i])
1283 * Answers the last index in the array for which the corresponding character is
1284 * equal to toBeFound stopping at the index startIndex.
1285 * Answers -1 if no occurrence of this character is found.
1292 * array = { ' a', 'b', 'c', 'd' }
1299 * array = { ' a', 'b', 'c', 'd', 'e' }
1306 * array = { ' a', 'b', 'c', 'd' }
1313 * @param toBeFound the character to search
1314 * @param array the array to be searched
1315 * @param startIndex the stopping index
1316 * @return the last index in the array for which the corresponding character is
1317 * equal to toBeFound stopping at the index startIndex, -1 otherwise
1318 * @exception NullPointerException if array is null
1319 * @exception ArrayIndexOutOfBoundsException if startIndex is lower than 0
1321 public static final int lastIndexOf(
1325 for (int i = array.length; --i >= startIndex;)
1326 if (toBeFound == array[i])
1332 * Answers the last index in the array for which the corresponding character is
1333 * equal to toBeFound starting from endIndex to startIndex.
1334 * Answers -1 if no occurrence of this character is found.
1341 * array = { ' a', 'b', 'c', 'd' }
1349 * array = { ' a', 'b', 'c', 'd', 'e' }
1357 * array = { ' a', 'b', 'c', 'd' }
1365 * @param toBeFound the character to search
1366 * @param array the array to be searched
1367 * @param startIndex the stopping index
1368 * @param endIndex the starting index
1369 * @return the last index in the array for which the corresponding character is
1370 * equal to toBeFound starting from endIndex to startIndex, -1 otherwise
1371 * @exception NullPointerException if array is null
1372 * @exception ArrayIndexOutOfBoundsException if endIndex is greater or equals to array length or starting is lower than 0
1374 public static final int lastIndexOf(
1379 for (int i = endIndex; --i >= startIndex;)
1380 if (toBeFound == array[i])
1386 * Answers the last portion of a name given a separator.
1391 * lastSegment("java.lang.Object".toCharArray(),'.') --> Object
1394 * @param array the array
1395 * @param separator the given separator
1396 * @return the last portion of a name given a separator
1397 * @exception NullPointerException if array is null
1399 final static public char[] lastSegment(char[] array, char separator) {
1400 int pos = lastIndexOf(separator, array);
1403 return subarray(array, pos + 1, array.length);
1407 * Answers true if the pattern matches the given name, false otherwise. This char[] pattern matching
1408 * accepts wild-cards '*' and '?'.
1410 * When not case sensitive, the pattern is assumed to already be lowercased, the
1411 * name will be lowercased character per character as comparing.
1412 * If name is null, the answer is false.
1413 * If pattern is null, the answer is true if name is not null.
1419 * pattern = { '?', 'b', '*' }
1420 * name = { 'a', 'b', 'c' , 'd' }
1421 * isCaseSensitive = true
1426 * pattern = { '?', 'b', '?' }
1427 * name = { 'a', 'b', 'c' , 'd' }
1428 * isCaseSensitive = true
1433 * pattern = { 'b', '*' }
1434 * name = { 'a', 'b', 'c' , 'd' }
1435 * isCaseSensitive = true
1441 * @param pattern the given pattern
1442 * @param name the given name
1443 * @param isCaseSensitive flag to know whether or not the matching should be case sensitive
1444 * @return true if the pattern matches the given name, false otherwise
1446 public static final boolean match(
1449 boolean isCaseSensitive) {
1452 return false; // null name cannot match
1453 if (pattern == null)
1454 return true; // null pattern is equivalent to '*'
1467 * Answers true if the a sub-pattern matches the subpart of the given name, false otherwise.
1468 * char[] pattern matching, accepting wild-cards '*' and '?'. Can match only subset of name/pattern.
1469 * end positions are non-inclusive.
1470 * The subpattern is defined by the patternStart and pattternEnd positions.
1471 * When not case sensitive, the pattern is assumed to already be lowercased, the
1472 * name will be lowercased character per character as comparing.
1478 * pattern = { '?', 'b', '*' }
1481 * name = { 'a', 'b', 'c' , 'd' }
1484 * isCaseSensitive = true
1489 * pattern = { '?', 'b', '*' }
1492 * name = { 'a', 'b', 'c' , 'd' }
1495 * isCaseSensitive = true
1501 * @param pattern the given pattern
1502 * @param patternStart the given pattern start
1503 * @param patternEnd the given pattern end
1504 * @param name the given name
1505 * @param nameStart the given name start
1506 * @param nameEnd the given name end
1507 * @param isCaseSensitive flag to know if the matching should be case sensitive
1508 * @return true if the a sub-pattern matches the subpart of the given name, false otherwise
1510 public static final boolean match(
1517 boolean isCaseSensitive) {
1520 return false; // null name cannot match
1521 if (pattern == null)
1522 return true; // null pattern is equivalent to '*'
1523 int iPattern = patternStart;
1524 int iName = nameStart;
1527 patternEnd = pattern.length;
1529 nameEnd = name.length;
1531 /* check first segment */
1532 char patternChar = 0;
1533 while ((iPattern < patternEnd)
1534 && (patternChar = pattern[iPattern]) != '*') {
1535 if (iName == nameEnd)
1540 : Character.toLowerCase(name[iName]))
1541 && patternChar != '?') {
1547 /* check sequence of star+segment */
1549 if (patternChar == '*') {
1550 segmentStart = ++iPattern; // skip star
1552 segmentStart = 0; // force iName check
1554 int prefixStart = iName;
1555 checkSegment : while (iName < nameEnd) {
1556 if (iPattern == patternEnd) {
1557 iPattern = segmentStart; // mismatch - restart current segment
1558 iName = ++prefixStart;
1559 continue checkSegment;
1561 /* segment is ending */
1562 if ((patternChar = pattern[iPattern]) == '*') {
1563 segmentStart = ++iPattern; // skip start
1564 if (segmentStart == patternEnd) {
1567 prefixStart = iName;
1568 continue checkSegment;
1570 /* check current name character */
1571 if ((isCaseSensitive ? name[iName] : Character.toLowerCase(name[iName]))
1573 && patternChar != '?') {
1574 iPattern = segmentStart; // mismatch - restart current segment
1575 iName = ++prefixStart;
1576 continue checkSegment;
1582 return (segmentStart == patternEnd)
1583 || (iName == nameEnd && iPattern == patternEnd)
1584 || (iPattern == patternEnd - 1 && pattern[iPattern] == '*');
1588 * Answers true if the pattern matches the filepath using the pathSepatator, false otherwise.
1590 * Path char[] pattern matching, accepting wild-cards '**', '*' and '?' (using Ant directory tasks
1591 * conventions, also see "http://jakarta.apache.org/ant/manual/dirtasks.html#defaultexcludes").
1592 * Path pattern matching is enhancing regular pattern matching in supporting extra rule where '**' represent
1593 * any folder combination.
1595 * - foo\ is equivalent to foo\**
1596 * - *.php is equivalent to **\*.php
1597 * When not case sensitive, the pattern is assumed to already be lowercased, the
1598 * name will be lowercased character per character as comparing.
1600 * @param pattern the given pattern
1601 * @param filepath the given path
1602 * @param isCaseSensitive to find out whether or not the matching should be case sensitive
1603 * @param pathSeparator the given path separator
1604 * @return true if the pattern matches the filepath using the pathSepatator, false otherwise
1606 public static final boolean pathMatch(
1609 boolean isCaseSensitive,
1610 char pathSeparator) {
1612 if (filepath == null)
1613 return false; // null name cannot match
1614 if (pattern == null)
1615 return true; // null pattern is equivalent to '*'
1617 // special case: pattern foo is equivalent to **\foo (not absolute)
1618 boolean freeLeadingDoubleStar;
1620 // offsets inside pattern
1621 int pSegmentStart, pLength = pattern.length;
1623 if (freeLeadingDoubleStar = pattern[0] != pathSeparator){
1628 int pSegmentEnd = CharOperation.indexOf(pathSeparator, pattern, pSegmentStart+1);
1629 if (pSegmentEnd < 0) pSegmentEnd = pLength;
1631 // special case: pattern foo\ is equivalent to foo\**
1632 boolean freeTrailingDoubleStar = pattern[pLength - 1] == pathSeparator;
1634 // offsets inside filepath
1635 int fSegmentStart, fLength = filepath.length;
1636 if (filepath[0] != pathSeparator){
1641 if (fSegmentStart != pSegmentStart) {
1642 return false; // both must start with a separator or none.
1644 int fSegmentEnd = CharOperation.indexOf(pathSeparator, filepath, fSegmentStart+1);
1645 if (fSegmentEnd < 0) fSegmentEnd = fLength;
1648 while (pSegmentStart < pLength
1649 && !freeLeadingDoubleStar
1650 && !(pSegmentEnd == pLength && freeTrailingDoubleStar
1651 || (pSegmentEnd == pSegmentStart + 2
1652 && pattern[pSegmentStart] == '*'
1653 && pattern[pSegmentStart + 1] == '*'))) {
1655 if (fSegmentStart >= fLength)
1669 // jump to next segment
1671 CharOperation.indexOf(
1674 pSegmentStart = pSegmentEnd + 1);
1676 if (pSegmentEnd < 0)
1677 pSegmentEnd = pLength;
1680 CharOperation.indexOf(
1683 fSegmentStart = fSegmentEnd + 1);
1685 if (fSegmentEnd < 0) fSegmentEnd = fLength;
1688 /* check sequence of doubleStar+segment */
1689 int pSegmentRestart;
1690 if ((pSegmentStart >= pLength && freeTrailingDoubleStar)
1691 || (pSegmentEnd == pSegmentStart + 2
1692 && pattern[pSegmentStart] == '*'
1693 && pattern[pSegmentStart + 1] == '*')) {
1695 CharOperation.indexOf(
1698 pSegmentStart = pSegmentEnd + 1);
1700 if (pSegmentEnd < 0) pSegmentEnd = pLength;
1701 pSegmentRestart = pSegmentStart;
1703 pSegmentRestart = 0; // force fSegmentStart check
1705 int fSegmentRestart = fSegmentStart;
1706 checkSegment : while (fSegmentStart < fLength) {
1708 if (pSegmentStart >= pLength) {
1709 if (freeTrailingDoubleStar) return true;
1710 // mismatch - restart current path segment
1712 CharOperation.indexOf(pathSeparator, pattern, pSegmentStart = pSegmentRestart);
1713 if (pSegmentEnd < 0) pSegmentEnd = pLength;
1716 CharOperation.indexOf(pathSeparator, filepath, fSegmentRestart + 1);
1718 if (fSegmentRestart < 0) {
1719 fSegmentRestart = fLength;
1724 CharOperation.indexOf(pathSeparator, filepath, fSegmentStart = fSegmentRestart);
1725 if (fSegmentEnd < 0) fSegmentEnd = fLength;
1726 continue checkSegment;
1729 /* path segment is ending */
1730 if (pSegmentEnd == pSegmentStart + 2
1731 && pattern[pSegmentStart] == '*'
1732 && pattern[pSegmentStart + 1] == '*') {
1734 CharOperation.indexOf(pathSeparator, pattern, pSegmentStart = pSegmentEnd + 1);
1736 if (pSegmentEnd < 0) pSegmentEnd = pLength;
1737 pSegmentRestart = pSegmentStart;
1738 fSegmentRestart = fSegmentStart;
1739 if (pSegmentStart >= pLength) return true;
1740 continue checkSegment;
1742 /* chech current path segment */
1743 if (!CharOperation.match(
1751 // mismatch - restart current path segment
1753 CharOperation.indexOf(pathSeparator, pattern, pSegmentStart = pSegmentRestart);
1754 if (pSegmentEnd < 0) pSegmentEnd = pLength;
1757 CharOperation.indexOf(pathSeparator, filepath, fSegmentRestart + 1);
1759 if (fSegmentRestart < 0) {
1760 fSegmentRestart = fLength;
1765 CharOperation.indexOf(pathSeparator, filepath, fSegmentStart = fSegmentRestart);
1766 if (fSegmentEnd < 0) fSegmentEnd = fLength;
1767 continue checkSegment;
1769 // jump to next segment
1771 CharOperation.indexOf(
1774 pSegmentStart = pSegmentEnd + 1);
1776 if (pSegmentEnd < 0)
1777 pSegmentEnd = pLength;
1780 CharOperation.indexOf(
1783 fSegmentStart = fSegmentEnd + 1);
1785 if (fSegmentEnd < 0)
1786 fSegmentEnd = fLength;
1789 return (pSegmentRestart >= pSegmentEnd)
1790 || (fSegmentStart >= fLength && pSegmentStart >= pLength)
1791 || (pSegmentStart == pLength - 2
1792 && pattern[pSegmentStart] == '*'
1793 && pattern[pSegmentStart + 1] == '*')
1794 || (pSegmentStart == pLength && freeTrailingDoubleStar);
1798 * Answers the number of occurrences of the given character in the given array, 0 if any.
1806 * array = { 'a' , 'b', 'b', 'a', 'b', 'a' }
1812 * array = { 'a' , 'b', 'b', 'a', 'b', 'a' }
1818 * @param toBeFound the given character
1819 * @param array the given array
1820 * @return the number of occurrences of the given character in the given array, 0 if any
1821 * @exception NullPointerException if array is null
1823 public static final int occurencesOf(char toBeFound, char[] array) {
1825 for (int i = 0; i < array.length; i++)
1826 if (toBeFound == array[i])
1832 * Answers the number of occurrences of the given character in the given array starting
1833 * at the given index, 0 if any.
1841 * array = { 'a' , 'b', 'b', 'a', 'b', 'a' }
1848 * array = { 'a' , 'b', 'b', 'a', 'b', 'a' }
1855 * @param toBeFound the given character
1856 * @param array the given array
1857 * @return the number of occurrences of the given character in the given array, 0 if any
1858 * @exception NullPointerException if array is null
1859 * @exception ArrayIndexOutOfBoundsException if start is lower than 0
1861 public static final int occurencesOf(
1866 for (int i = start; i < array.length; i++)
1867 if (toBeFound == array[i])
1873 * Answers true if the given name starts with the given prefix, false otherwise.
1874 * The comparison is case sensitive.
1880 * prefix = { 'a' , 'b' }
1881 * name = { 'a' , 'b', 'b', 'a', 'b', 'a' }
1886 * prefix = { 'a' , 'c' }
1887 * name = { 'a' , 'b', 'b', 'a', 'b', 'a' }
1893 * @param prefix the given prefix
1894 * @param name the given name
1895 * @return true if the given name starts with the given prefix, false otherwise
1896 * @exception NullPointerException if the given name is null or if the given prefix is null
1898 public static final boolean prefixEquals(char[] prefix, char[] name) {
1900 int max = prefix.length;
1901 if (name.length < max)
1905 ) // assumes the prefix is not larger than the name
1906 if (prefix[i] != name[i])
1912 * Answers true if the given name starts with the given prefix, false otherwise.
1913 * isCaseSensitive is used to find out whether or not the comparison should be case sensitive.
1919 * prefix = { 'a' , 'B' }
1920 * name = { 'a' , 'b', 'b', 'a', 'b', 'a' }
1921 * isCaseSensitive = false
1926 * prefix = { 'a' , 'B' }
1927 * name = { 'a' , 'b', 'b', 'a', 'b', 'a' }
1928 * isCaseSensitive = true
1934 * @param prefix the given prefix
1935 * @param name the given name
1936 * @param isCaseSensitive to find out whether or not the comparison should be case sensitive
1937 * @return true if the given name starts with the given prefix, false otherwise
1938 * @exception NullPointerException if the given name is null or if the given prefix is null
1940 public static final boolean prefixEquals(
1943 boolean isCaseSensitive) {
1945 int max = prefix.length;
1946 if (name.length < max)
1948 if (isCaseSensitive) {
1951 ) // assumes the prefix is not larger than the name
1952 if (prefix[i] != name[i])
1959 ) // assumes the prefix is not larger than the name
1960 if (Character.toLowerCase(prefix[i])
1961 != Character.toLowerCase(name[i]))
1967 * Replace all occurrence of the character to be replaced with the remplacement character in the
1974 * array = { 'a' , 'b', 'b', 'a', 'b', 'a' }
1975 * toBeReplaced = 'b'
1976 * replacementChar = 'a'
1977 * result => No returned value, but array is now equals to { 'a' , 'a', 'a', 'a', 'a', 'a' }
1981 * array = { 'a' , 'b', 'b', 'a', 'b', 'a' }
1982 * toBeReplaced = 'c'
1983 * replacementChar = 'a'
1984 * result => No returned value, but array is now equals to { 'a' , 'b', 'b', 'a', 'b', 'a' }
1989 * @param array the given array
1990 * @param toBeReplaced the character to be replaced
1991 * @param replacementChar the replacement character
1992 * @exception NullPointerException if the given array is null
1994 public static final void replace(
1997 char replacementChar) {
1998 if (toBeReplaced != replacementChar) {
1999 for (int i = 0, max = array.length; i < max; i++) {
2000 if (array[i] == toBeReplaced)
2001 array[i] = replacementChar;
2007 * Answers a new array of characters with substitutions. No side-effect is operated on the original
2008 * array, in case no substitution happened, then the result is the same as the
2015 * array = { 'a' , 'b', 'b', 'a', 'b', 'a' }
2016 * toBeReplaced = { 'b' }
2017 * replacementChar = { 'a', 'a' }
2018 * result => { 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a' }
2022 * array = { 'a' , 'b', 'b', 'a', 'b', 'a' }
2023 * toBeReplaced = { 'c' }
2024 * replacementChar = { 'a' }
2025 * result => { 'a' , 'b', 'b', 'a', 'b', 'a' }
2030 * @param the given array
2031 * @param toBeReplaced characters to be replaced
2032 * @param the replacement characters
2033 * @return a new array of characters with substitutions or the given array if none
2034 * @exception NullPointerException if the given array is null
2036 public static final char[] replace(
2038 char[] toBeReplaced,
2039 char[] replacementChars) {
2041 int max = array.length;
2042 int replacedLength = toBeReplaced.length;
2043 int replacementLength = replacementChars.length;
2045 int[] starts = new int[5];
2046 int occurrenceCount = 0;
2048 if (!equals(toBeReplaced, replacementChars)) {
2050 next : for (int i = 0; i < max; i++) {
2052 while (j < replacedLength) {
2055 if (array[i + j] != toBeReplaced[j++])
2058 if (occurrenceCount == starts.length) {
2062 starts = new int[occurrenceCount * 2],
2066 starts[occurrenceCount++] = i;
2069 if (occurrenceCount == 0)
2073 + occurrenceCount * (replacementLength - replacedLength)];
2074 int inStart = 0, outStart = 0;
2075 for (int i = 0; i < occurrenceCount; i++) {
2076 int offset = starts[i] - inStart;
2077 System.arraycopy(array, inStart, result, outStart, offset);
2086 inStart += replacedLength;
2087 outStart += replacementLength;
2089 System.arraycopy(array, inStart, result, outStart, max - inStart);
2094 * Return a new array which is the split of the given array using the given divider and triming each subarray to remove
2095 * whitespaces equals to ' '.
2102 * array = { 'a' , 'b', 'b', 'a', 'b', 'a' }
2103 * result => { { 'a' }, { }, { 'a' }, { 'a' } }
2108 * array = { 'a' , 'b', 'b', 'a', 'b', 'a' }
2109 * result => { { 'a', 'b', 'b', 'a', 'b', 'a' } }
2114 * array = { 'a' , ' ', 'b', 'b', 'a', 'b', 'a' }
2115 * result => { { 'a' }, { }, { 'a' }, { 'a' } }
2120 * array = { ' ', ' ', 'a' , 'b', 'b', 'a', 'b', 'a', ' ' }
2121 * result => { { 'a', 'b', 'b', 'a', 'b', 'a' } }
2126 * @param divider the given divider
2127 * @param array the given array
2128 * @return a new array which is the split of the given array using the given divider and triming each subarray to remove
2129 * whitespaces equals to ' '
2131 public static final char[][] splitAndTrimOn(char divider, char[] array) {
2132 int length = array == null ? 0 : array.length;
2134 return NO_CHAR_CHAR;
2137 for (int i = 0; i < length; i++)
2138 if (array[i] == divider)
2140 char[][] split = new char[wordCount][];
2141 int last = 0, currentWord = 0;
2142 for (int i = 0; i < length; i++) {
2143 if (array[i] == divider) {
2144 int start = last, end = i - 1;
2145 while (start < i && array[start] == ' ')
2147 while (end > start && array[end] == ' ')
2149 split[currentWord] = new char[end - start + 1];
2153 split[currentWord++],
2159 int start = last, end = length - 1;
2160 while (start < length && array[start] == ' ')
2162 while (end > start && array[end] == ' ')
2164 split[currentWord] = new char[end - start + 1];
2168 split[currentWord++],
2175 * Return a new array which is the split of the given array using the given divider.
2182 * array = { 'a' , 'b', 'b', 'a', 'b', 'a' }
2183 * result => { { 'a' }, { }, { 'a' }, { 'a' } }
2188 * array = { 'a' , 'b', 'b', 'a', 'b', 'a' }
2189 * result => { { 'a', 'b', 'b', 'a', 'b', 'a' } }
2194 * array = { ' ', ' ', 'a' , 'b', 'b', 'a', 'b', 'a', ' ' }
2195 * result => { { ' ', 'a', 'b', 'b', 'a', 'b', 'a', ' ' } }
2200 * @param divider the given divider
2201 * @param array the given array
2202 * @return a new array which is the split of the given array using the given divider
2204 public static final char[][] splitOn(char divider, char[] array) {
2205 int length = array == null ? 0 : array.length;
2207 return NO_CHAR_CHAR;
2210 for (int i = 0; i < length; i++)
2211 if (array[i] == divider)
2213 char[][] split = new char[wordCount][];
2214 int last = 0, currentWord = 0;
2215 for (int i = 0; i < length; i++) {
2216 if (array[i] == divider) {
2217 split[currentWord] = new char[i - last];
2221 split[currentWord++],
2227 split[currentWord] = new char[length - last];
2228 System.arraycopy(array, last, split[currentWord], 0, length - last);
2233 * Return a new array which is the split of the given array using the given divider. The given end
2234 * is exclusive and the given start is inclusive.
2241 * array = { 'a' , 'b', 'b', 'a', 'b', 'a' }
2244 * result => { { }, { }, { 'a' } }
2249 * @param divider the given divider
2250 * @param array the given array
2251 * @param start the given starting index
2252 * @param end the given ending index
2253 * @return a new array which is the split of the given array using the given divider
2254 * @exception ArrayIndexOutOfBoundsException if start is lower than 0 or end is greater than the array length
2256 public static final char[][] splitOn(
2261 int length = array == null ? 0 : array.length;
2262 if (length == 0 || start > end)
2263 return NO_CHAR_CHAR;
2266 for (int i = start; i < end; i++)
2267 if (array[i] == divider)
2269 char[][] split = new char[wordCount][];
2270 int last = start, currentWord = 0;
2271 for (int i = start; i < end; i++) {
2272 if (array[i] == divider) {
2273 split[currentWord] = new char[i - last];
2277 split[currentWord++],
2283 split[currentWord] = new char[end - last];
2284 System.arraycopy(array, last, split[currentWord], 0, end - last);
2289 * Answers true if the given array starts with the given characters, false otherwise.
2290 * The comparison is case sensitive.
2296 * toBeFound = { 'a' , 'b' }
2297 * array = { 'a' , 'b', 'b', 'a', 'b', 'a' }
2302 * toBeFound = { 'a' , 'c' }
2303 * array = { 'a' , 'b', 'b', 'a', 'b', 'a' }
2309 * @param array the given array
2310 * @param toBeFound the given character to search
2311 * @return true if the given array starts with the given characters, false otherwise
2312 * @exception NullPointerException if the given array is null or if the given characters array to be found is null
2314 public static final boolean startsWith(char[] array, char[] toBeFound) {
2315 int i = toBeFound.length;
2316 if (i > array.length)
2319 if (toBeFound[i] != array[i])
2325 * Answers a new array which is a copy of the given array starting at the given start and
2326 * ending at the given end. The given start is inclusive and the given end is exclusive.
2327 * Answers null if start is greater than end, if start is lower than 0 or if end is greater
2328 * than the length of the given array. If end equals -1, it is converted to the array length.
2334 * array = { { 'a' } , { 'b' } }
2337 * result => { { 'a' } }
2341 * array = { { 'a' } , { 'b' } }
2344 * result => { { 'a' }, { 'b' } }
2349 * @param array the given array
2350 * @param start the given starting index
2351 * @param end the given ending index
2352 * @return a new array which is a copy of the given array starting at the given start and
2353 * ending at the given end
2354 * @exception NullPointerException if the given array is null
2356 public static final char[][] subarray(char[][] array, int start, int end) {
2363 if (end > array.length)
2366 char[][] result = new char[end - start][];
2367 System.arraycopy(array, start, result, 0, end - start);
2372 * Answers a new array which is a copy of the given array starting at the given start and
2373 * ending at the given end. The given start is inclusive and the given end is exclusive.
2374 * Answers null if start is greater than end, if start is lower than 0 or if end is greater
2375 * than the length of the given array. If end equals -1, it is converted to the array length.
2381 * array = { 'a' , 'b' }
2388 * array = { 'a', 'b' }
2391 * result => { 'a' , 'b' }
2396 * @param array the given array
2397 * @param start the given starting index
2398 * @param end the given ending index
2399 * @return a new array which is a copy of the given array starting at the given start and
2400 * ending at the given end
2401 * @exception NullPointerException if the given array is null
2403 public static final char[] subarray(char[] array, int start, int end) {
2410 if (end > array.length)
2413 char[] result = new char[end - start];
2414 System.arraycopy(array, start, result, 0, end - start);
2418 * Answers the result of a char[] conversion to lowercase. Answers null if the given chars array is null.
2420 * NOTE: if no conversion was necessary, then answers back the argument one.
2426 * chars = { 'a' , 'b' }
2427 * result => { 'a' , 'b' }
2431 * array = { 'A', 'b' }
2432 * result => { 'a' , 'b' }
2437 * @param chars the chars to convert
2438 * @return the result of a char[] conversion to lowercase
2440 final static public char[] toLowerCase(char[] chars) {
2443 int length = chars.length;
2444 char[] lowerChars = null;
2445 for (int i = 0; i < length; i++) {
2447 char lc = Character.toLowerCase(c);
2448 if ((c != lc) || (lowerChars != null)) {
2449 if (lowerChars == null) {
2453 lowerChars = new char[length],
2460 return lowerChars == null ? chars : lowerChars;
2464 * Answers a new array removing leading and trailing spaces (' '). Answers the given array if there is no
2465 * space characters to remove.
2471 * chars = { ' ', 'a' , 'b', ' ', ' ' }
2472 * result => { 'a' , 'b' }
2476 * array = { 'A', 'b' }
2477 * result => { 'A' , 'b' }
2482 * @param chars the given array
2483 * @return a new array removing leading and trailing spaces (' ')
2485 final static public char[] trim(char[] chars) {
2490 int start = 0, length = chars.length, end = length - 1;
2491 while (start < length && chars[start] == ' ') {
2494 while (end > start && chars[end] == ' ') {
2497 if (start != 0 || end != length - 1) {
2498 return subarray(chars, start, end + 1);
2504 * Answers a string which is the concatenation of the given array using the '.' as a separator.
2510 * array = { { 'a' } , { 'b' } }
2515 * array = { { ' ', 'a' } , { 'b' } }
2521 * @param chars the given array
2522 * @return a string which is the concatenation of the given array using the '.' as a separator
2524 final static public String toString(char[][] array) {
2525 char[] result = concatWith(array, '.');
2526 return new String(result);