View Javadoc

1   /* ====================================================================
2    * The Apache Software License, Version 1.1
3    *
4    * Copyright (c) 2002-2003 The Apache Software Foundation.  All rights
5    * reserved.
6    *
7    * Redistribution and use in source and binary forms, with or without
8    * modification, are permitted provided that the following conditions
9    * are met:
10   *
11   * 1. Redistributions of source code must retain the above copyright
12   *    notice, this list of conditions and the following disclaimer.
13   *
14   * 2. Redistributions in binary form must reproduce the above copyright
15   *    notice, this list of conditions and the following disclaimer in
16   *    the documentation and/or other materials provided with the
17   *    distribution.
18   *
19   * 3. The end-user documentation included with the redistribution, if
20   *    any, must include the following acknowledgement:
21   *       "This product includes software developed by the
22   *        Apache Software Foundation (http://www.apache.org/)."
23   *    Alternately, this acknowledgement may appear in the software itself,
24   *    if and wherever such third-party acknowledgements normally appear.
25   *
26   * 4. The names "The Jakarta Project", "Commons", and "Apache Software
27   *    Foundation" must not be used to endorse or promote products derived
28   *    from this software without prior written permission. For written
29   *    permission, please contact apache@apache.org.
30   *
31   * 5. Products derived from this software may not be called "Apache"
32   *    nor may "Apache" appear in their names without prior written
33   *    permission of the Apache Software Foundation.
34   *
35   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
36   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
37   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
38   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
39   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
42   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
43   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
44   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
45   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
46   * SUCH DAMAGE.
47   * ====================================================================
48   *
49   * This software consists of voluntary contributions made by many
50   * individuals on behalf of the Apache Software Foundation.  For more
51   * information on the Apache Software Foundation, please see
52   * <http://www.apache.org/>.
53   */
54  package org.apache.commons.lang;
55  
56  /***
57   * <p>Operations on Strings that contain words.</p>
58   *
59   * <p>This class tries to handle <code>null</code> input gracefully.
60   * An exception will not be thrown for a <code>null</code> input.
61   * Each method documents its behaviour in more detail.</p>
62   *
63   * @author Apache Jakarta Velocity
64   * @author Henri Yandell
65   * @author Stephen Colebourne
66   * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
67   * @author Gary Gregory
68   * @since 2.0
69   * @version WordUtils.java,v 1.8 2003/08/23 10:39:20 scolebourne Exp
70   * @version $Id: WordUtils.java,v 1.1 2004/05/13 01:22:34 dquintela Exp $
71   */
72  public class WordUtils {
73  
74      /***
75       * <p><code>WordWrapUtils</code> instances should NOT be constructed in
76       * standard programming. Instead, the class should be used as
77       * <code>WordWrapUtils.wrap("foo bar", 20);</code>.</p>
78       *
79       * <p>This constructor is public to permit tools that require a JavaBean
80       * instance to operate.</p>
81       */
82      public WordUtils() {
83      }
84  
85      // Wrapping
86      //--------------------------------------------------------------------------
87  //    /***
88  //     * <p>Wraps a block of text to a specified line length using '\n' as
89  //     * a newline.</p>
90  //     *
91  //     * <p>This method takes a block of text, which might have long lines in it
92  //     * and wraps the long lines based on the supplied lineLength parameter.</p>
93  //     *
94  //     * <p>If a single word is longer than the line length (eg. a URL), it will
95  //     * not be broken, and will display beyond the expected width.</p>
96  //     *
97  //     * <p>If there are tabs in inString, you are going to get results that are
98  //     * a bit strange. Tabs are a single character but are displayed as 4 or 8
99  //     * spaces. Remove the tabs.</p>
100 //     *
101 //     * @param str  text which is in need of word-wrapping, may be null
102 //     * @param lineLength  the column to wrap the words at
103 //     * @return the text with all the long lines word-wrapped
104 //     *  <code>null</code> if null string input
105 //     */
106 //    public static String wrapText(String str, int lineLength) {
107 //        return wrap(str, null, lineLength);
108 //    }
109 
110 //    /***
111 //     * <p>Wraps a block of text to a specified line length.</p>
112 //     *
113 //     * <p>This method takes a block of text, which might have long lines in it
114 //     * and wraps the long lines based on the supplied lineLength parameter.</p>
115 //     *
116 //     * <p>If a single word is longer than the wrapColumn (eg. a URL), it will
117 //     * not be broken, and will display beyond the expected width.</p>
118 //     *
119 //     * <p>If there are tabs in inString, you are going to get results that are
120 //     * a bit strange. Tabs are a single character but are displayed as 4 or 8
121 //     * spaces. Remove the tabs.</p>
122 //     *
123 //     * @param str  text which is in need of word-wrapping, may be null
124 //     * @param newLineChars  the characters that define a newline, null treated as \n
125 //     * @param lineLength  the column to wrap the words at
126 //     * @return the text with all the long lines word-wrapped
127 //     *  <code>null</code> if null string input
128 //     */
129 //    public static String wrapText(String str, String newLineChars, int lineLength) {
130 //        if (str == null) {
131 //            return null;
132 //        }
133 //        if (newLineChars == null) {
134 //            newLineChars = "\n";
135 //        }
136 //        StringTokenizer lineTokenizer = new StringTokenizer(str, newLineChars, true);
137 //        StringBuffer stringBuffer = new StringBuffer();
138 //
139 //        while (lineTokenizer.hasMoreTokens()) {
140 //            try {
141 //                String nextLine = lineTokenizer.nextToken();
142 //
143 //                if (nextLine.length() > lineLength) {
144 //                    // This line is long enough to be wrapped.
145 //                    nextLine = wrapLine(nextLine, null, lineLength, false);
146 //                }
147 //
148 //                stringBuffer.append(nextLine);
149 //
150 //            } catch (NoSuchElementException nsee) {
151 //                // thrown by nextToken(), but I don't know why it would
152 //                break;
153 //            }
154 //        }
155 //
156 //        return (stringBuffer.toString());
157 //    }
158 
159     // Wrapping
160     //-----------------------------------------------------------------------
161     /***
162      * <p>Wraps a single line of text, identifying words by <code>' '</code>.</p>
163      *
164      * <p>New lines will be separated by the system property line separator.
165      * Very long words, such as URLs will <i>not</i> be wrapped.</p>
166      *
167      * <p>Leading spaces on a new line are stripped.
168      * Trailing spaces are not stripped.</p>
169      *
170      * <pre>
171      * WordUtils.wrap(null, *) = null
172      * WordUtils.wrap("", *) = ""
173      * </pre>
174      *
175      * @param str  the String to be word wrapped, may be null
176      * @param wrapLength  the column to wrap the words at, less than 1 is treated as 1
177      * @return a line with newlines inserted, <code>null</code> if null input
178      */
179     public static String wrap(String str, int wrapLength) {
180         return wrap(str, wrapLength, null, false);
181     }
182 
183     /***
184      * <p>Wraps a single line of text, identifying words by <code>' '</code>.</p>
185      *
186      * <p>Leading spaces on a new line are stripped.
187      * Trailing spaces are not stripped.</p>
188      *
189      * <pre>
190      * WordUtils.wrap(null, *, *, *) = null
191      * WordUtils.wrap("", *, *, *) = ""
192      * </pre>
193      *
194      * @param str  the String to be word wrapped, may be null
195      * @param wrapLength  the column to wrap the words at, less than 1 is treated as 1
196      * @param newLineStr  the string to insert for a new line,
197      *  <code>null</code> uses the system property line separator
198      * @param wrapLongWords  true if long words (such as URLs) should be wrapped
199      * @return a line with newlines inserted, <code>null</code> if null input
200      */
201     public static String wrap(String str, int wrapLen, String newLine, boolean wrapLongWords) {
202         if (str == null) {
203             return null;
204         }
205         String newLineStr = (newLine == null) ? SystemUtils.LINE_SEPARATOR : newLine;
206         int wrapLength = (wrapLen < 1) ? 1 : wrapLen;
207         int inputLineLength = str.length();
208         int offset = 0;
209         StringBuffer wrappedLine = new StringBuffer(inputLineLength + 32);
210 
211         while ((inputLineLength - offset) > wrapLength) {
212             if (str.charAt(offset) == ' ') {
213                 offset++;
214                 continue;
215             }
216             int spaceToWrapAt = str.lastIndexOf(' ', wrapLength + offset);
217 
218             if (spaceToWrapAt >= offset) {
219                 // normal case
220                 wrappedLine.append(str.substring(offset, spaceToWrapAt));
221                 wrappedLine.append(newLineStr);
222                 offset = spaceToWrapAt + 1;
223 
224             } else {
225                 // really long word or URL
226                 if (wrapLongWords) {
227                     // wrap really long word one line at a time
228                     wrappedLine.append(str.substring(offset, wrapLength + offset));
229                     wrappedLine.append(newLineStr);
230                     offset += wrapLength;
231                 } else {
232                     // do not wrap really long word, just extend beyond limit
233                     spaceToWrapAt = str.indexOf(' ', wrapLength + offset);
234                     if (spaceToWrapAt >= 0) {
235                         wrappedLine.append(str.substring(offset, spaceToWrapAt));
236                         wrappedLine.append(newLineStr);
237                         offset = spaceToWrapAt + 1;
238                     } else {
239                         wrappedLine.append(str.substring(offset));
240                         offset = inputLineLength;
241                     }
242                 }
243             }
244         }
245 
246         // Whatever is left in line is short enough to just pass through
247         wrappedLine.append(str.substring(offset));
248 
249         return wrappedLine.toString();
250     }
251 
252     // Capitalizing
253     //-----------------------------------------------------------------------
254     /***
255      * <p>Capitalizes all the whitespace separated words in a String.
256      * Only the first letter of each word is changed. To change all letters to
257      * the capitalized case, use {@link #capitalizeFully(String)}.</p>
258      *
259      * <p>Whitespace is defined by {@link Character#isWhitespace(char)}.
260      * A <code>null</code> input String returns <code>null</code>.
261      * Capitalization uses the unicode title case, normally equivalent to
262      * upper case.</p>
263      *
264      * <pre>
265      * WordUtils.capitalize(null)        = null
266      * WordUtils.capitalize("")          = ""
267      * WordUtils.capitalize("i am FINE") = "I Am FINE"
268      * </pre>
269      *
270      * @param str  the String to capitalize, may be null
271      * @return capitalized String, <code>null</code> if null String input
272      * @see #uncapitalize(String)
273      * @see #capitalizeFully(String)
274      */
275     public static String capitalize(String str) {
276         int strLen;
277         if (str == null || (strLen = str.length()) == 0) {
278             return str;
279         }
280         StringBuffer buffer = new StringBuffer(strLen);
281         boolean whitespace = true;
282         for (int i = 0; i < strLen; i++) {
283             char ch = str.charAt(i);
284             if (Character.isWhitespace(ch)) {
285                 buffer.append(ch);
286                 whitespace = true;
287             } else if (whitespace) {
288                 buffer.append(Character.toTitleCase(ch));
289                 whitespace = false;
290             } else {
291                 buffer.append(ch);
292             }
293         }
294         return buffer.toString();
295     }
296 
297     /***
298      * <p>Capitalizes all the whitespace separated words in a String.
299      * All letters are changed, so the resulting string will be fully changed.</p>
300      *
301      * <p>Whitespace is defined by {@link Character#isWhitespace(char)}.
302      * A <code>null</code> input String returns <code>null</code>.
303      * Capitalization uses the unicode title case, normally equivalent to
304      * upper case.</p>
305      *
306      * <pre>
307      * WordUtils.capitalize(null)        = null
308      * WordUtils.capitalize("")          = ""
309      * WordUtils.capitalize("i am FINE") = "I Am Fine"
310      * </pre>
311      *
312      * @param str  the String to capitalize, may be null
313      * @return capitalized String, <code>null</code> if null String input
314      */
315     public static String capitalizeFully(String str) {
316         int strLen;
317         if (str == null || (strLen = str.length()) == 0) {
318             return str;
319         }
320         StringBuffer buffer = new StringBuffer(strLen);
321         boolean whitespace = true;
322         for (int i = 0; i < strLen; i++) {
323             char ch = str.charAt(i);
324             if (Character.isWhitespace(ch)) {
325                 buffer.append(ch);
326                 whitespace = true;
327             } else if (whitespace) {
328                 buffer.append(Character.toTitleCase(ch));
329                 whitespace = false;
330             } else {
331                 buffer.append(Character.toLowerCase(ch));
332             }
333         }
334         return buffer.toString();
335     }
336 
337     /***
338      * <p>Uncapitalizes all the whitespace separated words in a String.
339      * Only the first letter of each word is changed.</p>
340      *
341      * <p>Whitespace is defined by {@link Character#isWhitespace(char)}.
342      * A <code>null</code> input String returns <code>null</code>.</p>
343      *
344      * <pre>
345      * WordUtils.uncapitalize(null)        = null
346      * WordUtils.uncapitalize("")          = ""
347      * WordUtils.uncapitalize("I Am FINE") = "i am fINE"
348      * </pre>
349      *
350      * @param str  the String to uncapitalize, may be null
351      * @return uncapitalized String, <code>null</code> if null String input
352      * @see #capitalize(String)
353      */
354     public static String uncapitalize(String str) {
355         int strLen;
356         if (str == null || (strLen = str.length()) == 0) {
357             return str;
358         }
359         StringBuffer buffer = new StringBuffer(strLen);
360         boolean whitespace = true;
361         for (int i = 0; i < strLen; i++) {
362             char ch = str.charAt(i);
363             if (Character.isWhitespace(ch)) {
364                 buffer.append(ch);
365                 whitespace = true;
366             } else if (whitespace) {
367                 buffer.append(Character.toLowerCase(ch));
368                 whitespace = false;
369             } else {
370                 buffer.append(ch);
371             }
372         }
373         return buffer.toString();
374     }
375 
376     /***
377      * <p>Swaps the case of a String using a word based algorithm.</p>
378      *
379      * <ul>
380      *  <li>Upper case character converts to Lower case</li>
381      *  <li>Title case character converts to Lower case</li>
382      *  <li>Lower case character after Whitespace or at start converts to Title case</li>
383      *  <li>Other Lower case character converts to Upper case</li>
384      * </ul>
385      *
386      * <p>Whitespace is defined by {@link Character#isWhitespace(char)}.
387      * A <code>null</code> input String returns <code>null</code>.</p>
388      *
389      * <pre>
390      * StringUtils.swapCase(null)                 = null
391      * StringUtils.swapCase("")                   = ""
392      * StringUtils.swapCase("The dog has a BONE") = "tHE DOG HAS A bone"
393      * </pre>
394      *
395      * @param str  the String to swap case, may be null
396      * @return the changed String, <code>null</code> if null String input
397      */
398     public static String swapCase(String str) {
399         int strLen;
400         if (str == null || (strLen = str.length()) == 0) {
401             return str;
402         }
403         StringBuffer buffer = new StringBuffer(strLen);
404 
405         boolean whitespace = true;
406         char ch = 0;
407         char tmp = 0;
408 
409         for (int i = 0; i < strLen; i++) {
410             ch = str.charAt(i);
411             if (Character.isUpperCase(ch)) {
412                 tmp = Character.toLowerCase(ch);
413             } else if (Character.isTitleCase(ch)) {
414                 tmp = Character.toLowerCase(ch);
415             } else if (Character.isLowerCase(ch)) {
416                 if (whitespace) {
417                     tmp = Character.toTitleCase(ch);
418                 } else {
419                     tmp = Character.toUpperCase(ch);
420                 }
421             } else {
422                 tmp = ch;
423             }
424             buffer.append(tmp);
425             whitespace = Character.isWhitespace(ch);
426         }
427         return buffer.toString();
428     }
429 
430 }