KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > jimm > util > StringUtils


1 package jimm.util;
2 import java.awt.FontMetrics JavaDoc;
3 import java.util.*;
4
5 /**
6  * Globally available utility classes, mostly for string manipulation.
7  *
8  * @author Jim Menard, <a HREF="mailto:jimm@io.com">jimm@io.com</a>
9  */

10 public class StringUtils {
11
12 protected static final int DEFAULT_MAX_MESSAGE_WIDTH = 78;
13
14 /**
15  * Returns a list of substrings created by splitting the given string at
16  * the given delimiter. The return value will be <code>null</code> if the
17  * string is <code>null</code>, else it will be a non-empty list of strings.
18  * If <var>delim</var> is <code>null</code> or is not found in the string,
19  * the list will contain one element: the original string.
20  * <p>
21  * This isn't the same thing as using a tokenizer. <var>delim</var> is
22  * a literal string, not a set of characters any of which may be a
23  * delimiter.
24  *
25  * @param str the string we're splitting
26  * @param delim the delimter string
27  */

28 public static List split(String JavaDoc str, String JavaDoc delim) {
29     if (str == null)
30     return null;
31
32     ArrayList list = new ArrayList();
33
34     if (delim == null) {
35     list.add(str);
36     return list;
37     }
38
39     int subStart, afterDelim = 0;
40     int delimLength = delim.length();
41     while ((subStart = str.indexOf(delim, afterDelim)) != -1) {
42     list.add(str.substring(afterDelim, subStart));
43     afterDelim = subStart + delimLength;
44     }
45     if (afterDelim <= str.length())
46     list.add(str.substring(afterDelim));
47
48     return list;
49 }
50
51 /**
52  * Returns a string consisting of all members of a collection separated
53  * by the specified string. The <code>toString</code> method of each
54  * collection member is called to convert it to a string.
55  *
56  * @param c a collection of objects
57  * @param joinWith the string that will separate each member of the collection
58  */

59 public static String JavaDoc join(Collection c, String JavaDoc joinWith) {
60     if (c == null)
61     return "";
62
63     StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
64     boolean first = true;
65     for (Iterator iter = c.iterator(); iter.hasNext(); ) {
66     if (first) first = false;
67     else if (joinWith != null) buf.append(joinWith);
68     buf.append(iter.next().toString());
69     }
70     return buf.toString();
71 }
72
73 /**
74  * Returns an array of strings, one for each line in the string. Lines end
75  * with any of cr, lf, or cr lf. A line ending at the end of the string
76  * will not output a further, empty string.
77  * <p>
78  * This code assumes <var>str</var> is not <code>null</code>.
79  *
80  * @param str the string to split
81  * @return a non-empty list of strings
82  */

83 public static List splitIntoLines(String JavaDoc str) {
84     ArrayList strings = new ArrayList();
85
86     int len = str.length();
87     if (len == 0) {
88     strings.add("");
89     return strings;
90     }
91
92     int lineStart = 0;
93
94     for (int i = 0; i < len; ++i) {
95     char c = str.charAt(i);
96     if (c == '\r') {
97         int newlineLength = 1;
98         if ((i + 1) < len && str.charAt(i + 1) == '\n')
99         newlineLength = 2;
100         strings.add(str.substring(lineStart, i));
101         lineStart = i + newlineLength;
102         if (newlineLength == 2) // skip \n next time through loop
103
++i;
104     }
105     else if (c == '\n') {
106         strings.add(str.substring(lineStart, i));
107         lineStart = i + 1;
108     }
109     }
110     if (lineStart < len)
111     strings.add(str.substring(lineStart));
112
113     return strings;
114 }
115
116 /**
117  * Appends a string to a string buffer, adding extra newlines so the message
118  * is not too wide. Max width is not guaranteed; if there is no space in a
119  * line before <code>DEFAULT_MAX_MESSAGE_WIDTH</code> then the next one after
120  * it will be used insetead. Each line will be trimmed before and after it's
121  * added, so some whitespace may be goofed up. This is used for error message
122  * wrapping, so it's not critical that whitespace be preserved.
123  * <p>
124  * TODO Looks for space, not all whitespace. This should probably change.
125  *
126  * @param buf the string buffer
127  * @param str the string
128  */

129 public static void splitUp(StringBuffer JavaDoc buf, String JavaDoc str) {
130     splitUp(buf, str, DEFAULT_MAX_MESSAGE_WIDTH);
131 }
132
133 /**
134  * Appends a string to a string buffer, adding extra newlines so the
135  * message is not too wide. Max width is not guaranteed; if there is no space
136  * in a line before <var>maxWidth</var> then the next one after it will be
137  * used instead. Each line will be trimmed before and after it's added,
138  * so some whitespace may be goofed up. This is used for error message
139  * wrapping, so it's not critical that whitespace be preserved.
140  * <p>
141  * TODO Looks for space, not all whitespace. This should probably change.
142  *
143  * @param buf the string buffer
144  * @param str the string
145  * @param maxWidth maximum number of chars in each line
146  */

147 public static void splitUp(StringBuffer JavaDoc buf, String JavaDoc str, int maxWidth) {
148     if (str == null)
149     return;
150
151     str = str.trim();
152     while (str.length() >= maxWidth) {
153     int pos = str.lastIndexOf(' ', maxWidth);
154     if (pos == -1) { // No spaces before; look for first one after
155
pos = str.indexOf(' ', maxWidth);
156         if (pos == -1)
157         break;
158     }
159     buf.append(str.substring(0, pos).trim());
160     buf.append("\n");
161     str = str.substring(pos + 1).trim();
162     }
163     buf.append(str);
164 }
165
166 /**
167  * Returns an array of strings, one for each line in the string after it
168  * has been wrapped to fit lines of <var>maxWidth</var>. Lines end
169  * with any of cr, lf, or cr lf. A line ending at the end of the string
170  * will not output a further, empty string.
171  * <p>
172  * This code assumes <var>str</var> is not <code>null</code>.
173  *
174  * @param str the string to split
175  * @param fm needed for string width calculations
176  * @param maxWidth the max line width, in points
177  * @return a non-empty list of strings
178  */

179 public static List wrap(String JavaDoc str, FontMetrics JavaDoc fm, int maxWidth) {
180     List lines = splitIntoLines(str);
181     if (lines.size() == 0)
182     return lines;
183
184     ArrayList strings = new ArrayList();
185     for (Iterator iter = lines.iterator(); iter.hasNext(); )
186     wrapLineInto((String JavaDoc)iter.next(), strings, fm, maxWidth);
187     return strings;
188 }
189
190 /**
191  * Given a line of text and font metrics information, wrap the line and
192  * add the new line(s) to <var>list</var>.
193  *
194  * @param line a line of text
195  * @param list an output list of strings
196  * @param fm font metrics
197  * @param maxWidth maximum width of the line(s)
198  */

199 public static void wrapLineInto(String JavaDoc line, List list, FontMetrics JavaDoc fm,
200                 int maxWidth)
201 {
202     int len = line.length();
203     int width;
204     while (len > 0 && (width = fm.stringWidth(line)) > maxWidth) {
205     // Guess where to split the line. Look for the next space before
206
// or after the guess.
207
int guess = len * maxWidth / width;
208     String JavaDoc before = line.substring(0, guess).trim();
209
210     width = fm.stringWidth(before);
211     int pos;
212     if (width > maxWidth) // Too long
213
pos = findBreakBefore(line, guess);
214     else { // Too short or possibly just right
215
pos = findBreakAfter(line, guess);
216         if (pos != -1) { // Make sure this doesn't make us too long
217
before = line.substring(0, pos).trim();
218         if (fm.stringWidth(before) > maxWidth)
219             pos = findBreakBefore(line, guess);
220         }
221     }
222     if (pos == -1) pos = guess; // Split in the middle of the word
223

224     list.add(line.substring(0, pos).trim());
225     line = line.substring(pos).trim();
226     len = line.length();
227     }
228     if (len > 0)
229     list.add(line);
230 }
231
232 /**
233  * Returns the index of the first whitespace character or '-' in
234  * <var>line</var> that is at or before <var>start</var>. Returns -1 if no
235  * such character is found.
236  *
237  * @param line a string
238  * @param start where to star looking
239  */

240 public static int findBreakBefore(String JavaDoc line, int start) {
241     for (int i = start; i >= 0; --i) {
242     char c = line.charAt(i);
243     if (Character.isWhitespace(c) || c == '-')
244         return i;
245     }
246     return -1;
247 }
248
249 /**
250  * Returns the index of the first whitespace character or '-' in
251  * <var>line</var> that is at or after <var>start</var>. Returns -1 if no
252  * such character is found.
253  *
254  * @param line a string
255  * @param start where to star looking
256  */

257 public static int findBreakAfter(String JavaDoc line, int start) {
258     int len = line.length();
259     for (int i = start; i < len; ++i) {
260     char c = line.charAt(i);
261     if (Character.isWhitespace(c) || c == '-')
262         return i;
263     }
264     return -1;
265 }
266
267 /**
268  * Returns a string with HTML special characters replaced by their entity
269  * equivalents.
270  *
271  * @param str the string to escape
272  * @return a new string without HTML special characters
273  */

274 public static String JavaDoc escapeHTML(String JavaDoc str) {
275     if (str == null || str.length() == 0)
276     return "";
277
278     StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
279     int len = str.length();
280     for (int i = 0; i < len; ++i) {
281     char c = str.charAt(i);
282     switch (c) {
283     case '&': buf.append("&amp;"); break;
284     case '<': buf.append("&lt;"); break;
285     case '>': buf.append("&gt;"); break;
286     case '"': buf.append("&quot;"); break;
287     case '\'': buf.append("&apos;"); break;
288     default: buf.append(c); break;
289     }
290     }
291     return buf.toString();
292 }
293
294 /**
295  * Returns a new string where all newlines (&quot;\n&quot;, &quot;\r&quot;,
296  * or &quot;\r\n&quot;) have been replaced by &quot;\n&quot; plus XHTML
297  * break tags (&quot;\n&lt;br /&gt;&quot;).
298  * <p>
299  * We don't call <code>splitIntoLines</code> because that method does not
300  * tell us if the string ended with a newline or not.
301  *
302  * @param str any string
303  * @return a new string with all newlines replaced by
304  * &quot;\n&lt;br /&gt;&quot;
305  */

306 public static String JavaDoc newlinesToXHTMLBreaks(String JavaDoc str) {
307     if (str == null || str.length() == 0)
308     return "";
309
310     StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
311     int len = str.length();
312     for (int i = 0; i < len; ++i) {
313     char c = str.charAt(i);
314     switch (c) {
315     case '\n': buf.append("\n<br />"); break;
316     case '\r':
317         if (i + 1 < len && str.charAt(i + 1) == '\n') // Look for '\n'
318
++i;
319         buf.append("\n<br />");
320         break;
321     default:
322         buf.append(c); break;
323     }
324     }
325     return buf.toString();
326 }
327
328 /**
329  * Returns a string with XML special characters replaced by their entity
330  * equivalents.
331  *
332  * @param str the string to escape
333  * @return a new string without XML special characters
334  */

335 public static String JavaDoc escapeXML(String JavaDoc str) {
336     return escapeHTML(str);
337 }
338
339 /**
340  * Returns a string with XML entities replaced by their normal characters.
341  *
342  * @param str the string to un-escape
343  * @return a new normal string
344  */

345 public static String JavaDoc unescapeXML(String JavaDoc str) {
346     if (str == null || str.length() == 0)
347     return "";
348
349     StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
350     int len = str.length();
351     for (int i = 0; i < len; ++i) {
352     char c = str.charAt(i);
353     if (c == '&') {
354         int pos = str.indexOf(";", i);
355         if (pos == -1) { // Really evil
356
buf.append('&');
357         }
358         else if (str.charAt(i+1) == '#') {
359         int val = Integer.parseInt(str.substring(i+2, pos), 16);
360         buf.append((char)val);
361         i = pos;
362         }
363         else {
364         String JavaDoc substr = str.substring(i, pos + 1);
365         if (substr.equals("&amp;"))
366             buf.append('&');
367         else if (substr.equals("&lt;"))
368             buf.append('<');
369         else if (substr.equals("&gt;"))
370             buf.append('>');
371         else if (substr.equals("&quot;"))
372             buf.append('"');
373         else if (substr.equals("&apos;"))
374             buf.append('\'');
375         else // ????
376
buf.append(substr);
377         i = pos;
378         }
379     }
380     else {
381         buf.append(c);
382     }
383     }
384     return buf.toString();
385 }
386
387 /**
388  * Returns a new string with all strings delimited by <var>start</var> and
389  * <var>end</var> replaced by whatever is generated by the
390  * <code>Replacer</code> <var>r</var>. The delimiters themselves are
391  * not part of the returned string.
392  * <p>
393  * If the <code>Replacer</code> ever returns <code>null</code>, we return
394  * <code>null</code>.
395  *
396  * @param start the delimiter start (for example, &quot;{&#64;&quot;)
397  * @param end the delimiter end (for example, &quot;}&quot;)
398  * @param r the replacer; takes the text between <var>start</var> and
399  * <var>end</var> and returns the replacement text
400  * @param s the string we're munging
401  * @return a new string munged by the replacer, or <code>null</code> if
402  * the replacer ever returns <code>null</code>
403  */

404 public static String JavaDoc replaceDelimited(String JavaDoc start, String JavaDoc end, Replacer r,
405                       String JavaDoc s)
406 {
407     return replaceDelimited(null, start, end, r, s);
408 }
409
410 /**
411  * Returns a new string with all strings delimited by <var>start</var> and
412  * <var>end</var> (but not immediately preceeded by <var>exceptAfter</var>)
413  * replaced by whatever is generated by the <code>Replacer</code>
414  * <var>r</var>. The delimiters themselves are not part of the returned
415  * string.
416  * <p>
417  * If the <code>Replacer</code> ever returns <code>null</code>, we return
418  * <code>null</code>.
419  *
420  * @param exceptAfter ignore <var>start</var> if it appears immediately
421  * after this string; may be <code>null</code>
422  * @param start the delimiter start (for example, &quot;{&#64;&quot;)
423  * @param end the delimiter end (for example, &quot;}&quot;)
424  * @param r the replacer; takes the text between <var>start</var> and
425  * <var>end</var> and returns the replacement text
426  * @param s the string we're munging
427  * @return a new string munged by the replacer, or <code>null</code> if
428  * the replacer ever returns <code>null</code>
429  */

430 public static String JavaDoc replaceDelimited(String JavaDoc exceptAfter, String JavaDoc start,
431                       String JavaDoc end, Replacer r, String JavaDoc s)
432 {
433     if (s == null)
434     return null;
435
436     int startLength, endLength;
437     if (start == null || end == null || (startLength = start.length()) == 0
438     || (endLength = end.length()) == 0)
439     return s;
440
441     int exceptAfterLength = exceptAfter == null ? 0 : exceptAfter.length();
442
443     String JavaDoc str = new String JavaDoc(s); // We're gonna munge the string, so copy it
444
int pos, pos2;
445     int searchFrom = 0;
446     while ((pos = str.indexOf(start, searchFrom)) != -1) {
447     // Skip this one if it is immediately preceeded by exceptAfter.
448
if (exceptAfterLength > 0) {
449         int lookFrom = pos - exceptAfterLength;
450         if (lookFrom >= 0
451         && str.indexOf(exceptAfter, lookFrom) == lookFrom)
452         {
453         searchFrom = pos + 1;
454         continue;
455         }
456     }
457
458     pos2 = str.indexOf(end, pos + startLength);
459     if (pos2 != -1) {
460         Object JavaDoc val = r.replace(str.substring(pos + startLength, pos2));
461         if (val == null)
462         return null;
463         String JavaDoc valAsString = val.toString();
464         str = str.substring(0, pos) + valAsString
465         + str.substring(pos2 + endLength);
466         searchFrom = pos + valAsString.length();
467     }
468     else // Didn't find end delimiter; stop right here
469
break;
470     }
471     return str;
472 }
473
474 /**
475  * Returns <var>str</var> with leading and trailing spaces trimmed or, if
476  * <var>str</var> is <code>null</code>, returns <code>null</code>.
477  *
478  * @return str trimmed or <code>null</code>
479  */

480 public static String JavaDoc nullOrTrimmed(String JavaDoc str) {
481     return str == null ? str : str.trim();
482 }
483
484 }
485
Popular Tags