001: /*
002: * Copyright 2004-2006 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.compass.core.util;
018:
019: import java.util.ArrayList;
020: import java.util.Arrays;
021: import java.util.Collection;
022: import java.util.Iterator;
023: import java.util.LinkedList;
024: import java.util.List;
025: import java.util.Locale;
026: import java.util.Properties;
027: import java.util.Set;
028: import java.util.StringTokenizer;
029: import java.util.TreeSet;
030:
031: /**
032: * Miscellaneous string utility methods. Mainly for internal use
033: * within the framework; consider Jakarta's Commons Lang for a more
034: * comprehensive suite of string utilities.
035: * <p/>
036: * <p>This class delivers some simple functionality that should really
037: * be provided by the core Java String and StringBuffer classes, such
038: * as the ability to replace all occurrences of a given substring in a
039: * target string. It also provides easy-to-use methods to convert between
040: * delimited strings, such as CSV strings, and collections and arrays.
041: *
042: * @author kimchy
043: */
044: public abstract class StringUtils {
045:
046: private static final String FOLDER_SEPARATOR = "/"; // folder separator
047:
048: private static final String WINDOWS_FOLDER_SEPARATOR = "\\"; // Windows folder separator
049:
050: private static final String TOP_PATH = ".."; // top folder
051:
052: private static final String CURRENT_PATH = "."; // current folder
053:
054: //---------------------------------------------------------------------
055: // General convenience methods for working with Strings
056: //---------------------------------------------------------------------
057:
058: /**
059: * Check if a String has length.
060: * <p><pre>
061: * StringUtils.hasLength(null) = false
062: * StringUtils.hasLength("") = false
063: * StringUtils.hasLength(" ") = true
064: * StringUtils.hasLength("Hello") = true
065: * </pre>
066: *
067: * @param str the String to check, may be <code>null</code>
068: * @return <code>true</code> if the String is not null and has length
069: */
070: public static boolean hasLength(String str) {
071: return (str != null && str.length() > 0);
072: }
073:
074: /**
075: * Check if a String has text. More specifically, returns <code>true</code>
076: * if the string not <code>null<code>, it's <code>length is > 0</code>, and
077: * it has at least one non-whitespace character.
078: * <p><pre>
079: * StringUtils.hasText(null) = false
080: * StringUtils.hasText("") = false
081: * StringUtils.hasText(" ") = false
082: * StringUtils.hasText("12345") = true
083: * StringUtils.hasText(" 12345 ") = true
084: * </pre>
085: *
086: * @param str the String to check, may be <code>null</code>
087: * @return <code>true</code> if the String is not null, length > 0,
088: * and not whitespace only
089: * @see Character#isWhitespace
090: */
091: public static boolean hasText(String str) {
092: int strLen;
093: if (str == null || (strLen = str.length()) == 0) {
094: return false;
095: }
096: for (int i = 0; i < strLen; i++) {
097: if (!Character.isWhitespace(str.charAt(i))) {
098: return true;
099: }
100: }
101: return false;
102: }
103:
104: /**
105: * Trim leading whitespace from the given String.
106: *
107: * @param str the String to check
108: * @return the trimmed String
109: * @see Character#isWhitespace
110: */
111: public static String trimLeadingWhitespace(String str) {
112: if (str.length() == 0) {
113: return str;
114: }
115: StringBuffer buf = new StringBuffer(str);
116: while (buf.length() > 0
117: && Character.isWhitespace(buf.charAt(0))) {
118: buf.deleteCharAt(0);
119: }
120: return buf.toString();
121: }
122:
123: /**
124: * Trim trailing whitespace from the given String.
125: *
126: * @param str the String to check
127: * @return the trimmed String
128: * @see Character#isWhitespace
129: */
130: public static String trimTrailingWhitespace(String str) {
131: if (str.length() == 0) {
132: return str;
133: }
134: StringBuffer buf = new StringBuffer(str);
135: while (buf.length() > 0
136: && Character.isWhitespace(buf.charAt(buf.length() - 1))) {
137: buf.deleteCharAt(buf.length() - 1);
138: }
139: return buf.toString();
140: }
141:
142: /**
143: * Test if the given String starts with the specified prefix,
144: * ignoring upper/lower case.
145: *
146: * @param str the String to check
147: * @param prefix the prefix to look for
148: * @see String#startsWith
149: */
150: public static boolean startsWithIgnoreCase(String str, String prefix) {
151: if (str == null || prefix == null) {
152: return false;
153: }
154: if (str.startsWith(prefix)) {
155: return true;
156: }
157: if (str.length() < prefix.length()) {
158: return false;
159: }
160: String lcStr = str.substring(0, prefix.length()).toLowerCase();
161: String lcPrefix = prefix.toLowerCase();
162: return lcStr.equals(lcPrefix);
163: }
164:
165: /**
166: * Test if the given String ends with the specified suffix,
167: * ignoring upper/lower case.
168: *
169: * @param str the String to check
170: * @param suffix the suffix to look for
171: * @see String#endsWith
172: */
173: public static boolean endsWithIgnoreCase(String str, String suffix) {
174: if (str == null || suffix == null) {
175: return false;
176: }
177: if (str.endsWith(suffix)) {
178: return true;
179: }
180: if (str.length() < suffix.length()) {
181: return false;
182: }
183:
184: String lcStr = str.substring(suffix.length()).toLowerCase();
185: String lcSuffix = suffix.toLowerCase();
186: return lcStr.equals(lcSuffix);
187: }
188:
189: /**
190: * Count the occurrences of the substring in string s.
191: *
192: * @param str string to search in. Return 0 if this is null.
193: * @param sub string to search for. Return 0 if this is null.
194: */
195: public static int countOccurrencesOf(String str, String sub) {
196: if (str == null || sub == null || str.length() == 0
197: || sub.length() == 0) {
198: return 0;
199: }
200: int count = 0, pos = 0, idx;
201: while ((idx = str.indexOf(sub, pos)) != -1) {
202: ++count;
203: pos = idx + sub.length();
204: }
205: return count;
206: }
207:
208: /**
209: * Replace all occurences of a substring within a string with
210: * another string.
211: *
212: * @param inString String to examine
213: * @param oldPattern String to replace
214: * @param newPattern String to insert
215: * @return a String with the replacements
216: */
217: public static String replace(String inString, String oldPattern,
218: String newPattern) {
219: if (inString == null) {
220: return null;
221: }
222: if (oldPattern == null || newPattern == null) {
223: return inString;
224: }
225:
226: StringBuffer sbuf = new StringBuffer();
227: // output StringBuffer we'll build up
228: int pos = 0; // our position in the old string
229: int index = inString.indexOf(oldPattern);
230: // the index of an occurrence we've found, or -1
231: int patLen = oldPattern.length();
232: while (index >= 0) {
233: sbuf.append(inString.substring(pos, index));
234: sbuf.append(newPattern);
235: pos = index + patLen;
236: index = inString.indexOf(oldPattern, pos);
237: }
238: sbuf.append(inString.substring(pos));
239:
240: // remember to append any characters to the right of a match
241: return sbuf.toString();
242: }
243:
244: /**
245: * Delete all occurrences of the given substring.
246: *
247: * @param pattern the pattern to delete all occurrences of
248: */
249: public static String delete(String inString, String pattern) {
250: return replace(inString, pattern, "");
251: }
252:
253: /**
254: * Delete any character in a given string.
255: *
256: * @param charsToDelete a set of characters to delete.
257: * E.g. "az\n" will delete 'a's, 'z's and new lines.
258: */
259: public static String deleteAny(String inString, String charsToDelete) {
260: if (inString == null || charsToDelete == null) {
261: return inString;
262: }
263: StringBuffer out = new StringBuffer();
264: for (int i = 0; i < inString.length(); i++) {
265: char c = inString.charAt(i);
266: if (charsToDelete.indexOf(c) == -1) {
267: out.append(c);
268: }
269: }
270: return out.toString();
271: }
272:
273: //---------------------------------------------------------------------
274: // Convenience methods for working with formatted Strings
275: //---------------------------------------------------------------------
276:
277: public static String qualify(String prefix, String name) {
278: if (name == null)
279: throw new NullPointerException();
280: return new StringBuffer(prefix.length() + name.length() + 1)
281: .append(prefix).append('.').append(name).toString();
282: }
283:
284: /**
285: * Unqualify a string qualified by a '.' dot character. For example,
286: * "this.name.is.qualified", returns "qualified".
287: *
288: * @param qualifiedName the qualified name
289: */
290: public static String unqualify(String qualifiedName) {
291: return unqualify(qualifiedName, '.');
292: }
293:
294: /**
295: * Unqualify a string qualified by a separator character. For example,
296: * "this:name:is:qualified" returns "qualified" if using a ':' separator.
297: *
298: * @param qualifiedName the qualified name
299: * @param separator the separator
300: */
301: public static String unqualify(String qualifiedName, char separator) {
302: return qualifiedName.substring(qualifiedName
303: .lastIndexOf(separator) + 1);
304: }
305:
306: /**
307: * Capitalize a <code>String</code>, changing the first letter to
308: * upper case as per {@link Character#toUpperCase(char)}.
309: * No other letters are changed.
310: *
311: * @param str the String to capitalize, may be <code>null</code>
312: * @return the capitalized String, <code>null</code> if null
313: */
314: public static String capitalize(String str) {
315: return changeFirstCharacterCase(str, true);
316: }
317:
318: /**
319: * Uncapitalize a <code>String</code>, changing the first letter to
320: * lower case as per {@link Character#toLowerCase(char)}.
321: * No other letters are changed.
322: *
323: * @param str the String to uncapitalize, may be <code>null</code>
324: * @return the uncapitalized String, <code>null</code> if null
325: */
326: public static String uncapitalize(String str) {
327: return changeFirstCharacterCase(str, false);
328: }
329:
330: private static String changeFirstCharacterCase(String str,
331: boolean capitalize) {
332: if (str == null || str.length() == 0) {
333: return str;
334: }
335: StringBuffer buf = new StringBuffer(str.length());
336: if (capitalize) {
337: buf.append(Character.toUpperCase(str.charAt(0)));
338: } else {
339: buf.append(Character.toLowerCase(str.charAt(0)));
340: }
341: buf.append(str.substring(1));
342: return buf.toString();
343: }
344:
345: /**
346: * Extract the filename from the given path,
347: * e.g. "mypath/myfile.txt" -> "myfile.txt".
348: *
349: * @param path the file path
350: * @return the extracted filename
351: */
352: public static String getFilename(String path) {
353: int separatorIndex = path.lastIndexOf(FOLDER_SEPARATOR);
354: return (separatorIndex != -1 ? path
355: .substring(separatorIndex + 1) : path);
356: }
357:
358: /**
359: * Apply the given relative path to the given path,
360: * assuming standard Java folder separation (i.e. "/" separators);
361: *
362: * @param path the path to start from (usually a full file path)
363: * @param relativePath the relative path to apply
364: * (relative to the full file path above)
365: * @return the full file path that results from applying the relative path
366: */
367: public static String applyRelativePath(String path,
368: String relativePath) {
369: int separatorIndex = path.lastIndexOf(FOLDER_SEPARATOR);
370: if (separatorIndex != -1) {
371: String newPath = path.substring(0, separatorIndex);
372: if (!relativePath.startsWith("/")) {
373: newPath += "/";
374: }
375: return newPath + relativePath;
376: } else {
377: return relativePath;
378: }
379: }
380:
381: /**
382: * Normalize the path by suppressing sequences like "path/.." and
383: * inner simple dots.
384: * <p>The result is convenient for path comparison. For other uses,
385: * notice that Windows separators ("\") are replaced by simple dashes.
386: *
387: * @param path the original path
388: * @return the normalized path
389: */
390: public static String cleanPath(String path) {
391: String pathToUse = replace(path, WINDOWS_FOLDER_SEPARATOR,
392: FOLDER_SEPARATOR);
393:
394: // Strip prefix from path to analyze, to not treat it as part of the
395: // first path element. This is necessary to correctly parse paths like
396: // "file:core/../core/io/Resource.class", where the ".." should just
397: // strip the first "core" directory while keeping the "file:" prefix.
398: int prefixIndex = pathToUse.indexOf(":");
399: String prefix = "";
400: if (prefixIndex != -1) {
401: prefix = pathToUse.substring(0, prefixIndex + 1);
402: pathToUse = pathToUse.substring(prefixIndex + 1);
403: }
404:
405: String[] pathArray = delimitedListToStringArray(pathToUse,
406: FOLDER_SEPARATOR);
407: List pathElements = new LinkedList();
408: int tops = 0;
409: for (int i = pathArray.length - 1; i >= 0; i--) {
410: if (CURRENT_PATH.equals(pathArray[i])) {
411: // do nothing
412: } else if (TOP_PATH.equals(pathArray[i])) {
413: tops++;
414: } else {
415: if (tops > 0) {
416: tops--;
417: } else {
418: pathElements.add(0, pathArray[i]);
419: }
420: }
421: }
422:
423: return prefix
424: + collectionToDelimitedString(pathElements,
425: FOLDER_SEPARATOR);
426: }
427:
428: /**
429: * Compare two paths after normalization of them.
430: *
431: * @param path1 First path for comparizon
432: * @param path2 Second path for comparizon
433: * @return True if the two paths are equivalent after normalization
434: */
435: public static boolean pathEquals(String path1, String path2) {
436: return cleanPath(path1).equals(cleanPath(path2));
437: }
438:
439: /**
440: * Parse the given locale string into a <code>java.util.Locale</code>.
441: * This is the inverse operation of Locale's <code>toString</code>.
442: *
443: * @param localeString the locale string, following
444: * <code>java.util.Locale</code>'s toString format ("en", "en_UK", etc).
445: * Also accepts spaces as separators, as alternative to underscores.
446: * @return a corresponding Locale instance
447: */
448: public static Locale parseLocaleString(String localeString) {
449: String[] parts = tokenizeToStringArray(localeString, "_ ",
450: false, false);
451: String language = parts.length > 0 ? parts[0] : "";
452: String country = parts.length > 1 ? parts[1] : "";
453: String variant = parts.length > 2 ? parts[2] : "";
454: return (language.length() > 0 ? new Locale(language, country,
455: variant) : null);
456: }
457:
458: //---------------------------------------------------------------------
459: // Convenience methods for working with String arrays
460: //---------------------------------------------------------------------
461:
462: /**
463: * Append the given String to the given String array, returning a new array
464: * consisting of the input array contents plus the given String.
465: *
466: * @param arr the array to append to
467: * @param str the String to append
468: * @return the new array
469: */
470: public static String[] addStringToArray(String[] arr, String str) {
471: String[] newArr = new String[arr.length + 1];
472: System.arraycopy(arr, 0, newArr, 0, arr.length);
473: newArr[arr.length] = str;
474: return newArr;
475: }
476:
477: /**
478: * Turn given source String array into sorted array.
479: *
480: * @param source the source array
481: * @return the sorted array (never <code>null</code>)
482: */
483: public static String[] sortStringArray(String[] source) {
484: if (source == null) {
485: return new String[0];
486: }
487: Arrays.sort(source);
488: return source;
489: }
490:
491: /**
492: * Split a String at the first occurrence of the delimiter.
493: * Does not include the delimiter in the result.
494: *
495: * @param toSplit the string to split
496: * @param delimiter to split the string up with
497: * @return a two element array with index 0 being before the delimiter, and
498: * index 1 being after the delimiter (neither element includes the delimiter);
499: * or <code>null</code> if the delimiter wasn't found in the given input String
500: */
501: public static String[] split(String toSplit, String delimiter) {
502: Assert
503: .hasLength(toSplit,
504: "Cannot split a null or empty string");
505: Assert
506: .hasLength(delimiter,
507: "Cannot use a null or empty delimiter to split a string");
508: int offset = toSplit.indexOf(delimiter);
509: if (offset < 0) {
510: return null;
511: }
512: String beforeDelimiter = toSplit.substring(0, offset);
513: String afterDelimiter = toSplit.substring(offset
514: + delimiter.length());
515: return new String[] { beforeDelimiter, afterDelimiter };
516: }
517:
518: /**
519: * Take an array Strings and split each element based on the given delimiter.
520: * A <code>Properties</code> instance is then generated, with the left of the
521: * delimiter providing the key, and the right of the delimiter providing the value.
522: * <p>Will trim both the key and value before adding them to the
523: * <code>Properties</code> instance.
524: *
525: * @param array the array to process
526: * @param delimiter to split each element using (typically the equals symbol)
527: * @return a <code>Properties</code> instance representing the array contents,
528: * or <code>null</code> if the array to process was null or empty
529: */
530: public static Properties splitArrayElementsIntoProperties(
531: String[] array, String delimiter) {
532: return splitArrayElementsIntoProperties(array, delimiter, null);
533: }
534:
535: /**
536: * Take an array Strings and split each element based on the given delimiter.
537: * A <code>Properties</code> instance is then generated, with the left of the
538: * delimiter providing the key, and the right of the delimiter providing the value.
539: * <p>Will trim both the key and value before adding them to the
540: * <code>Properties</code> instance.
541: *
542: * @param array the array to process
543: * @param delimiter to split each element using (typically the equals symbol)
544: * @param charsToDelete one or more characters to remove from each element
545: * prior to attempting the split operation (typically the quotation mark
546: * symbol), or <code>null</code> if no removal should occur
547: * @return a <code>Properties</code> instance representing the array contents,
548: * or <code>null</code> if the array to process was null or empty
549: */
550: public static Properties splitArrayElementsIntoProperties(
551: String[] array, String delimiter, String charsToDelete) {
552:
553: if (array == null || array.length == 0) {
554: return null;
555: }
556:
557: Properties result = new Properties();
558: for (int i = 0; i < array.length; i++) {
559: String element = array[i];
560: if (charsToDelete != null) {
561: element = deleteAny(array[i], charsToDelete);
562: }
563: String[] splittedElement = split(element, delimiter);
564: if (splittedElement == null) {
565: continue;
566: }
567: result.setProperty(splittedElement[0].trim(),
568: splittedElement[1].trim());
569: }
570: return result;
571: }
572:
573: /**
574: * Tokenize the given String into a String array via a StringTokenizer.
575: * Trims tokens and omits empty tokens.
576: * <p>The given delimiters string is supposed to consist of any number of
577: * delimiter characters. Each of those characters can be used to separate
578: * tokens. A delimiter is always a single character; for multi-character
579: * delimiters, consider using <code>delimitedListToStringArray</code>
580: *
581: * @param str the String to tokenize
582: * @param delimiters the delimiter characters, assembled as String
583: * (each of those characters is individually considered as delimiter).
584: * @return an array of the tokens
585: * @see java.util.StringTokenizer
586: * @see String#trim
587: * @see #delimitedListToStringArray
588: */
589: public static String[] tokenizeToStringArray(String str,
590: String delimiters) {
591: return tokenizeToStringArray(str, delimiters, true, true);
592: }
593:
594: /**
595: * Tokenize the given String into a String array via a StringTokenizer.
596: * <p>The given delimiters string is supposed to consist of any number of
597: * delimiter characters. Each of those characters can be used to separate
598: * tokens. A delimiter is always a single character; for multi-character
599: * delimiters, consider using <code>delimitedListToStringArray</code>
600: *
601: * @param str the String to tokenize
602: * @param delimiters the delimiter characters, assembled as String
603: * (each of those characters is individually considered as delimiter)
604: * @param trimTokens trim the tokens via String's <code>trim</code>
605: * @param ignoreEmptyTokens omit empty tokens from the result array
606: * (only applies to tokens that are empty after trimming; StringTokenizer
607: * will not consider subsequent delimiters as token in the first place).
608: * @return an array of the tokens
609: * @see java.util.StringTokenizer
610: * @see String#trim
611: * @see #delimitedListToStringArray
612: */
613: public static String[] tokenizeToStringArray(String str,
614: String delimiters, boolean trimTokens,
615: boolean ignoreEmptyTokens) {
616:
617: StringTokenizer st = new StringTokenizer(str, delimiters);
618: List tokens = new ArrayList();
619: while (st.hasMoreTokens()) {
620: String token = st.nextToken();
621: if (trimTokens) {
622: token = token.trim();
623: }
624: if (!ignoreEmptyTokens || token.length() > 0) {
625: tokens.add(token);
626: }
627: }
628: return (String[]) tokens.toArray(new String[tokens.size()]);
629: }
630:
631: /**
632: * Take a String which is a delimited list and convert it to a String array.
633: * <p>A single delimiter can consists of more than one character: It will still
634: * be considered as single delimiter string, rather than as bunch of potential
635: * delimiter characters - in contrast to <code>tokenizeToStringArray</code>.
636: *
637: * @param str the input String
638: * @param delimiter the delimiter between elements (this is a single delimiter,
639: * rather than a bunch individual delimiter characters)
640: * @return an array of the tokens in the list
641: * @see #tokenizeToStringArray
642: */
643: public static String[] delimitedListToStringArray(String str,
644: String delimiter) {
645: if (str == null) {
646: return new String[0];
647: }
648: if (delimiter == null) {
649: return new String[] { str };
650: }
651:
652: List result = new ArrayList();
653: int pos = 0;
654: int delPos;
655: while ((delPos = str.indexOf(delimiter, pos)) != -1) {
656: result.add(str.substring(pos, delPos));
657: pos = delPos + delimiter.length();
658: }
659: if (str.length() > 0 && pos <= str.length()) {
660: // Add rest of String, but not in case of empty input.
661: result.add(str.substring(pos));
662: }
663:
664: return (String[]) result.toArray(new String[result.size()]);
665: }
666:
667: /**
668: * Convert a CSV list into an array of Strings.
669: *
670: * @param str CSV list
671: * @return an array of Strings, or the empty array if s is null
672: */
673: public static String[] commaDelimitedListToStringArray(String str) {
674: return delimitedListToStringArray(str, ",");
675: }
676:
677: /**
678: * Convenience method to convert a CSV string list to a set.
679: * Note that this will suppress duplicates.
680: *
681: * @param str CSV String
682: * @return a Set of String entries in the list
683: */
684: public static Set commaDelimitedListToSet(String str) {
685: Set set = new TreeSet();
686: String[] tokens = commaDelimitedListToStringArray(str);
687: for (int i = 0; i < tokens.length; i++) {
688: set.add(tokens[i]);
689: }
690: return set;
691: }
692:
693: /**
694: * Convenience method to return a String array as a delimited (e.g. CSV)
695: * String. E.g. useful for toString() implementations.
696: *
697: * @param arr array to display. Elements may be of any type (toString
698: * will be called on each element).
699: * @param delim delimiter to use (probably a ",")
700: */
701: public static String arrayToDelimitedString(Object[] arr,
702: String delim) {
703: if (arr == null) {
704: return "";
705: }
706:
707: StringBuffer sb = new StringBuffer();
708: for (int i = 0; i < arr.length; i++) {
709: if (i > 0) {
710: sb.append(delim);
711: }
712: sb.append(arr[i]);
713: }
714: return sb.toString();
715: }
716:
717: /**
718: * Convenience method to return a String array as a delimited (e.g. CSV)
719: * String. E.g. useful for toString() implementations.
720: *
721: * @param arr array to display. Elements may be of any type (toString
722: * will be called on each element).
723: * @param delim delimiter to use (probably a ",")
724: */
725: public static String arrayToDelimitedString(Object[] arr, char delim) {
726: if (arr == null) {
727: return "";
728: }
729:
730: StringBuffer sb = new StringBuffer();
731: for (int i = 0; i < arr.length; i++) {
732: if (i > 0) {
733: sb.append(delim);
734: }
735: sb.append(arr[i]);
736: }
737: return sb.toString();
738: }
739:
740: /**
741: * Convenience method to return a Collection as a delimited (e.g. CSV)
742: * String. E.g. useful for toString() implementations.
743: *
744: * @param coll Collection to display
745: * @param delim delimiter to use (probably a ",")
746: * @param prefix string to start each element with
747: * @param suffix string to end each element with
748: */
749: public static String collectionToDelimitedString(Collection coll,
750: String delim, String prefix, String suffix) {
751: if (coll == null) {
752: return "";
753: }
754:
755: StringBuffer sb = new StringBuffer();
756: Iterator it = coll.iterator();
757: int i = 0;
758: while (it.hasNext()) {
759: if (i > 0) {
760: sb.append(delim);
761: }
762: sb.append(prefix).append(it.next()).append(suffix);
763: i++;
764: }
765: return sb.toString();
766: }
767:
768: /**
769: * Convenience method to return a Collection as a delimited (e.g. CSV)
770: * String. E.g. useful for toString() implementations.
771: *
772: * @param coll Collection to display
773: * @param delim delimiter to use (probably a ",")
774: */
775: public static String collectionToDelimitedString(Collection coll,
776: String delim) {
777: return collectionToDelimitedString(coll, delim, "", "");
778: }
779:
780: /**
781: * Convenience method to return a String array as a CSV String.
782: * E.g. useful for toString() implementations.
783: *
784: * @param arr array to display. Elements may be of any type (toString
785: * will be called on each element).
786: */
787: public static String arrayToCommaDelimitedString(Object[] arr) {
788: return arrayToDelimitedString(arr, ",");
789: }
790:
791: /**
792: * Convenience method to return a Collection as a CSV String.
793: * E.g. useful for toString() implementations.
794: *
795: * @param coll Collection to display
796: */
797: public static String collectionToCommaDelimitedString(
798: Collection coll) {
799: return collectionToDelimitedString(coll, ",");
800: }
801:
802: /**
803: * Reverses a string.
804: *
805: * @param str The String to reverse
806: * @return The reversed string
807: */
808: public static String reverse(String str) {
809: int length = str.length();
810: StringBuffer sb = new StringBuffer(length);
811: for (int i = length - 1; i > -1; i--) {
812: sb.append(str.charAt(i));
813: }
814: return sb.toString();
815: }
816: }
|