001: /*
002: * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate copyright
003: * notice. All rights reserved.
004: */
005: package com.tc.util;
006:
007: import java.util.Arrays;
008:
009: /**
010: * String utility methods.
011: */
012: public class StringUtil {
013:
014: /** A space character */
015: public static final char SPACE = ' ';
016:
017: /** A space string */
018: public static final String SPACE_STRING = " ";
019:
020: /** The empty string */
021: public static final String EMPTY = "";
022:
023: /** A string representing a null value: "<null>" */
024: public static final String NULL_STRING = "<null>";
025:
026: /**
027: * Normal toString(), but convert null to the {@link #NULL_STRING}.
028: * @return Always a string, never null
029: */
030: public static final String safeToString(Object object) {
031: return object != null ? object.toString() : NULL_STRING;
032: }
033:
034: /**
035: * Indent lines using a single tab by inserting them into source
036: * after line breaks and returning a new String.
037: * @param source Source string, may NOT be null
038: * @return New string, updated with indents
039: * @throws NullPointerException If source is null
040: */
041: public static String indentLines(String source) {
042: return indentLines(source, 1);
043: }
044:
045: /**
046: * Indent lines using tabs by inserting them into source
047: * after line breaks and returning a new String.
048: * @param source Source string, may NOT be null
049: * @param indentLevel Number of tabs to insert, must be >= 0
050: * @return Original buffer, updated
051: * @throws IllegalArgumentException If indentLevel < 0
052: * @throws NullPointerException If source is null
053: */
054: public static String indentLines(String source, int indentLevel) {
055: return indentLines(new StringBuffer(source), indentLevel)
056: .toString();
057: }
058:
059: /**
060: * Indent lines using tabs by inserting them into source and returning source.
061: * @param source Source buffer, may be null
062: * @param indentLevel Number of tabs to insert, must be >= 0
063: * @return Original buffer, updated
064: * @throws IllegalArgumentException If indentLevel < 0
065: */
066: public static StringBuffer indentLines(StringBuffer source,
067: int indentLevel) {
068: return indentLines(source, indentLevel, '\t');
069: }
070:
071: /**
072: * Indent lines in the StringBuffer (line breaks found at \n) with indentChar repeated
073: * indentLevel times.
074: * @param source Source buffer, may be null
075: * @param indentLevel Number of chars to indent, must be >= 0
076: * @param indentChar Indent character (usually ' ' or '\t')
077: * @return Original buffer, updated
078: * @throws IllegalArgumentException If indentLevel < 0
079: */
080: public static StringBuffer indentLines(StringBuffer source,
081: int indentLevel, char indentChar) {
082: if ((source == null) || (indentLevel == 0)) {
083: return source;
084: }
085:
086: final String indentStr;
087:
088: if (indentLevel <= 0) {
089: // I suppose one could write something to remove indentation, but I don't feel like it now
090: throw new IllegalArgumentException(
091: "Negative indentation not supported");
092: } else if (indentLevel > 1) {
093: char[] chars = new char[indentLevel];
094: Arrays.fill(chars, indentChar);
095: indentStr = new String(chars);
096: } else {
097: // errr....the call below is jdk1.4 specific
098: // indentStr = Character.toString(indentChar);
099:
100: indentStr = new String(new char[] { indentChar });
101: }
102:
103: source.insert(0, indentStr);
104:
105: int index = 0;
106: while ((index = indexOfStringBuffer(source, "\n", index)) != -1) {
107: index++;
108: if (index == source.length()) {
109: break;
110: }
111: source.insert(index, indentStr);
112: }
113:
114: return source;
115: }
116:
117: /**
118: * Find index of "search" in "source", starting at "start" index.
119: * @param source Source buffer, must be non-null
120: * @param search Search string, must be non-null
121: * @param start Start index, should be 0<=start<source.length(), will return -1 if out of range
122: * @return Index of found string or -1 if not found
123: * @throws NullPointerException If source or search is null
124: */
125: public static int indexOfStringBuffer(StringBuffer source,
126: String search, int start) {
127: return source.toString().indexOf(search, start);
128: }
129:
130: /**
131: * Creates a String representation of an array of objects by calling <code>toString</code> on each one. Formatting
132: * is controlled by the parameters.
133: *
134: * @param objs (required) the array of objects to display
135: * @param separator (optional) a string to place between each object
136: * @param prefix (optional) a string to prefix each object with
137: * @param postfix (optional) a string to append to each object
138: * @return a String representation of the array, never returns null
139: */
140: public static final String toString(Object[] objs,
141: String separator, String prefix, String postfix) {
142: final String sep = getNonNull(separator);
143: final String pre = getNonNull(prefix);
144: final String post = getNonNull(postfix);
145: StringBuffer rv = new StringBuffer();
146: if (objs != null) {
147: for (int pos = 0; pos < objs.length; ++pos) {
148: if (rv.length() > 0) {
149: rv.append(sep);
150: }
151: rv.append(pre);
152: rv.append(objs[pos] != null ? objs[pos].toString()
153: : "null");
154: rv.append(post);
155: }
156: } else {
157: rv.append(NULL_STRING);
158: }
159: return rv.toString();
160: }
161:
162: /**
163: * Helper method to convert object array [a, b, c] to comma-separated string "a, b, c".
164: * @param objs Array of objects, can be null
165: * @return String, never null
166: */
167: public static final String toString(Object[] objs) {
168: return toString(objs, ", ", null, null);
169: }
170:
171: /**
172: * Format value to string using radix, then prepend with 0's out to paddedWidth.
173: * If the formatted value is > paddedWidth, then the value is returned.
174: *
175: * @param value Long value, must be >= 0
176: * @param radix The radix to use when representing the value
177: * @param paddedWidth The width to pad to by prepending 0
178: * @return Padded formatted string value for the long value, never null
179: */
180: public static final String toPaddedString(long value, int radix,
181: int paddedWidth) {
182: StringBuffer result = new StringBuffer();
183: String strValue = Long.toString(value, radix);
184: for (int pos = 0; pos < paddedWidth - strValue.length(); ++pos) {
185: result.append("0");
186: }
187: result.append(strValue);
188: return result.toString();
189: }
190:
191: /**
192: * Simple search/replace for non-pattern strings. optionally skipping over quoted strings.
193: *
194: * @param source the original string to perform the search/replace on, a modified version of this is returned, if null
195: * then null will be returned immediately
196: * @param search the string to search for in <code>source</code>, if null then null is returned immediately
197: * @param replace the string to replace <code>search</code> occurrences in <code>source</code>, if null then the
198: * search string is simply removed from source and not replaced with anything
199: * @param skipQuotedStrings if true then quoted strings will be skipped over
200: * @return a modified version of <code>source</code>, or null if <code>source</code> or <code>search</code> are
201: * null
202: */
203: public static final String replaceAll(String source, String search,
204: String replace, boolean skipQuotedStrings) {
205: if (source == null || search == null) {
206: return null;
207: }
208: StringBuffer result = new StringBuffer();
209: int beginQuoteIdx = 0;
210: for (int pos = 0; pos < source.length(); ++pos) {
211: if (skipQuotedStrings && source.startsWith("'", pos)) {
212: // Skip the single-quoted string
213: beginQuoteIdx = pos;
214: for (++pos; pos < source.length()
215: && !source.startsWith("'", pos); ++pos) {
216: // skip
217: }
218:
219: result.append(source.substring(beginQuoteIdx, pos + 1));
220: } else if (skipQuotedStrings
221: && source.startsWith("\"", pos)) {
222: // Skip the double-quoted string
223: beginQuoteIdx = pos;
224: for (++pos; pos < source.length()
225: && !source.startsWith("\"", pos); ++pos) {
226: // skip
227: }
228: result.append(source.substring(beginQuoteIdx, pos + 1));
229: } else if (source.startsWith(search, pos)) {
230: if (replace != null) {
231: result.append(replace);
232: }
233: pos += search.length() - 1;
234: } else {
235: result.append(source.charAt(pos));
236: }
237: }
238: return result.toString();
239: }
240:
241: /**
242: * Reduces the size that a string occupies to the minimal possible by
243: * ensuring that the back-end char array contains exactly the characters that
244: * are needed, and no more.
245: *
246: * Note that this method doesn't modify the original string as they are
247: * immutable, a new string is returned instead.
248: *
249: * @param source the string that needs to be reduced
250: * @return the reduced string, null if source is null
251: */
252: public static final String reduce(String source) {
253: if (null == source)
254: return null;
255:
256: char[] chars = new char[source.length()];
257: source.getChars(0, source.length(), chars, 0);
258: return new String(chars);
259: }
260:
261: /**
262: * For a string s, if non-null return s, else return nullToken.
263: * @param s The starting string
264: * @param nullToken The null token
265: * @return s or nullToken depending on s
266: */
267: public static final String getNonNull(String s, String nullToken) {
268: return (s == null) ? nullToken : s;
269: }
270:
271: /**
272: * Get a non-null version of the String.
273: * @param s The string
274: * @return Either s or the empty string if s was null
275: */
276: public static final String getNonNull(String s) {
277: return getNonNull(s, EMPTY);
278: }
279:
280: }
|