Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
StringUtils |
|
| 4.842105263157895;4.842 |
1 | /* | |
2 | * Licensed to the Apache Software Foundation (ASF) under one | |
3 | * or more contributor license agreements. See the NOTICE file | |
4 | * distributed with this work for additional information | |
5 | * regarding copyright ownership. The ASF licenses this file | |
6 | * to you under the Apache License, Version 2.0 (the | |
7 | * "License"); you may not use this file except in compliance | |
8 | * with the License. You may obtain a copy of the License at | |
9 | * | |
10 | * http://www.apache.org/licenses/LICENSE-2.0 | |
11 | * | |
12 | * Unless required by applicable law or agreed to in writing, | |
13 | * software distributed under the License is distributed on an | |
14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
15 | * KIND, either express or implied. See the License for the | |
16 | * specific language governing permissions and limitations | |
17 | * under the License. | |
18 | */ | |
19 | package org.apache.shiro.util; | |
20 | ||
21 | import java.text.ParseException; | |
22 | import java.util.*; | |
23 | ||
24 | /** | |
25 | * <p>Simple utility class for String operations useful across the framework. | |
26 | * <p/> | |
27 | * <p>Some methods in this class were copied from the Spring Framework so we didn't have to re-invent the wheel, | |
28 | * and in these cases, we have retained all license, copyright and author information. | |
29 | * | |
30 | * @since 0.9 | |
31 | */ | |
32 | 0 | public class StringUtils { |
33 | ||
34 | //TODO - complete JavaDoc | |
35 | ||
36 | /** | |
37 | * Constant representing the empty string, equal to "" | |
38 | */ | |
39 | public static final String EMPTY_STRING = ""; | |
40 | ||
41 | /** | |
42 | * Constant representing the default delimiter character (comma), equal to <code>','</code> | |
43 | */ | |
44 | public static final char DEFAULT_DELIMITER_CHAR = ','; | |
45 | ||
46 | /** | |
47 | * Constant representing the default quote character (double quote), equal to '"'</code> | |
48 | */ | |
49 | public static final char DEFAULT_QUOTE_CHAR = '"'; | |
50 | ||
51 | /** | |
52 | * Check whether the given String has actual text. | |
53 | * More specifically, returns <code>true</code> if the string not <code>null</code>, | |
54 | * its length is greater than 0, and it contains at least one non-whitespace character. | |
55 | * <p/> | |
56 | * <code>StringUtils.hasText(null) == false<br/> | |
57 | * StringUtils.hasText("") == false<br/> | |
58 | * StringUtils.hasText(" ") == false<br/> | |
59 | * StringUtils.hasText("12345") == true<br/> | |
60 | * StringUtils.hasText(" 12345 ") == true</code> | |
61 | * <p/> | |
62 | * <p>Copied from the Spring Framework while retaining all license, copyright and author information. | |
63 | * | |
64 | * @param str the String to check (may be <code>null</code>) | |
65 | * @return <code>true</code> if the String is not <code>null</code>, its length is | |
66 | * greater than 0, and it does not contain whitespace only | |
67 | * @see java.lang.Character#isWhitespace | |
68 | */ | |
69 | public static boolean hasText(String str) { | |
70 | 296 | if (!hasLength(str)) { |
71 | 27 | return false; |
72 | } | |
73 | 269 | int strLen = str.length(); |
74 | 327 | for (int i = 0; i < strLen; i++) { |
75 | 326 | if (!Character.isWhitespace(str.charAt(i))) { |
76 | 268 | return true; |
77 | } | |
78 | } | |
79 | 1 | return false; |
80 | } | |
81 | ||
82 | /** | |
83 | * Check that the given String is neither <code>null</code> nor of length 0. | |
84 | * Note: Will return <code>true</code> for a String that purely consists of whitespace. | |
85 | * <p/> | |
86 | * <code>StringUtils.hasLength(null) == false<br/> | |
87 | * StringUtils.hasLength("") == false<br/> | |
88 | * StringUtils.hasLength(" ") == true<br/> | |
89 | * StringUtils.hasLength("Hello") == true</code> | |
90 | * <p/> | |
91 | * Copied from the Spring Framework while retaining all license, copyright and author information. | |
92 | * | |
93 | * @param str the String to check (may be <code>null</code>) | |
94 | * @return <code>true</code> if the String is not null and has length | |
95 | * @see #hasText(String) | |
96 | */ | |
97 | public static boolean hasLength(String str) { | |
98 | 308 | return (str != null && str.length() > 0); |
99 | } | |
100 | ||
101 | ||
102 | /** | |
103 | * Test if the given String starts with the specified prefix, | |
104 | * ignoring upper/lower case. | |
105 | * <p/> | |
106 | * <p>Copied from the Spring Framework while retaining all license, copyright and author information. | |
107 | * | |
108 | * @param str the String to check | |
109 | * @param prefix the prefix to look for | |
110 | * @return <code>true</code> starts with the specified prefix (ignoring case), <code>false</code> if it does not. | |
111 | * @see java.lang.String#startsWith | |
112 | */ | |
113 | public static boolean startsWithIgnoreCase(String str, String prefix) { | |
114 | 0 | if (str == null || prefix == null) { |
115 | 0 | return false; |
116 | } | |
117 | 0 | if (str.startsWith(prefix)) { |
118 | 0 | return true; |
119 | } | |
120 | 0 | if (str.length() < prefix.length()) { |
121 | 0 | return false; |
122 | } | |
123 | 0 | String lcStr = str.substring(0, prefix.length()).toLowerCase(); |
124 | 0 | String lcPrefix = prefix.toLowerCase(); |
125 | 0 | return lcStr.equals(lcPrefix); |
126 | } | |
127 | ||
128 | /** | |
129 | * Returns a 'cleaned' representation of the specified argument. 'Cleaned' is defined as the following: | |
130 | * <p/> | |
131 | * <ol> | |
132 | * <li>If the specified <code>String</code> is <code>null</code>, return <code>null</code></li> | |
133 | * <li>If not <code>null</code>, {@link String#trim() trim()} it.</li> | |
134 | * <li>If the trimmed string is equal to the empty String (i.e. ""), return <code>null</code></li> | |
135 | * <li>If the trimmed string is not the empty string, return the trimmed version</li>. | |
136 | * </ol> | |
137 | * <p/> | |
138 | * Therefore this method always ensures that any given string has trimmed text, and if it doesn't, <code>null</code> | |
139 | * is returned. | |
140 | * | |
141 | * @param in the input String to clean. | |
142 | * @return a populated-but-trimmed String or <code>null</code> otherwise | |
143 | */ | |
144 | public static String clean(String in) { | |
145 | 831 | String out = in; |
146 | ||
147 | 831 | if (in != null) { |
148 | 830 | out = in.trim(); |
149 | 830 | if (out.equals(EMPTY_STRING)) { |
150 | 26 | out = null; |
151 | } | |
152 | } | |
153 | ||
154 | 831 | return out; |
155 | } | |
156 | ||
157 | /** | |
158 | * Returns the specified array as a comma-delimited (',') string. | |
159 | * | |
160 | * @param array the array whose contents will be converted to a string. | |
161 | * @return the array's contents as a comma-delimited (',') string. | |
162 | * @since 1.0 | |
163 | */ | |
164 | public static String toString(Object[] array) { | |
165 | 34 | return toDelimitedString(array, ","); |
166 | } | |
167 | ||
168 | /** | |
169 | * Returns the array's contents as a string, with each element delimited by the specified | |
170 | * {@code delimiter} argument. Useful for {@code toString()} implementations and log messages. | |
171 | * | |
172 | * @param array the array whose contents will be converted to a string | |
173 | * @param delimiter the delimiter to use between each element | |
174 | * @return a single string, delimited by the specified {@code delimiter}. | |
175 | * @since 1.0 | |
176 | */ | |
177 | public static String toDelimitedString(Object[] array, String delimiter) { | |
178 | 34 | if (array == null || array.length == 0) { |
179 | 0 | return EMPTY_STRING; |
180 | } | |
181 | 34 | StringBuilder sb = new StringBuilder(); |
182 | 71 | for (int i = 0; i < array.length; i++) { |
183 | 37 | if (i > 0) { |
184 | 3 | sb.append(delimiter); |
185 | } | |
186 | 37 | sb.append(array[i]); |
187 | } | |
188 | 34 | return sb.toString(); |
189 | } | |
190 | ||
191 | /** | |
192 | * Returns the collection's contents as a string, with each element delimited by the specified | |
193 | * {@code delimiter} argument. Useful for {@code toString()} implementations and log messages. | |
194 | * | |
195 | * @param c the collection whose contents will be converted to a string | |
196 | * @param delimiter the delimiter to use between each element | |
197 | * @return a single string, delimited by the specified {@code delimiter}. | |
198 | * @since 1.2 | |
199 | */ | |
200 | public static String toDelimitedString(Collection c, String delimiter) { | |
201 | 0 | if (c == null || c.isEmpty()) { |
202 | 0 | return EMPTY_STRING; |
203 | } | |
204 | 0 | return join(c.iterator(), delimiter); |
205 | } | |
206 | ||
207 | /** | |
208 | * Tokenize the given String into a String array via a StringTokenizer. | |
209 | * Trims tokens and omits empty tokens. | |
210 | * <p>The given delimiters string is supposed to consist of any number of | |
211 | * delimiter characters. Each of those characters can be used to separate | |
212 | * tokens. A delimiter is always a single character; for multi-character | |
213 | * delimiters, consider using <code>delimitedListToStringArray</code> | |
214 | * <p/> | |
215 | * <p>Copied from the Spring Framework while retaining all license, copyright and author information. | |
216 | * | |
217 | * @param str the String to tokenize | |
218 | * @param delimiters the delimiter characters, assembled as String | |
219 | * (each of those characters is individually considered as delimiter). | |
220 | * @return an array of the tokens | |
221 | * @see java.util.StringTokenizer | |
222 | * @see java.lang.String#trim() | |
223 | */ | |
224 | public static String[] tokenizeToStringArray(String str, String delimiters) { | |
225 | 0 | return tokenizeToStringArray(str, delimiters, true, true); |
226 | } | |
227 | ||
228 | /** | |
229 | * Tokenize the given String into a String array via a StringTokenizer. | |
230 | * <p>The given delimiters string is supposed to consist of any number of | |
231 | * delimiter characters. Each of those characters can be used to separate | |
232 | * tokens. A delimiter is always a single character; for multi-character | |
233 | * delimiters, consider using <code>delimitedListToStringArray</code> | |
234 | * <p/> | |
235 | * <p>Copied from the Spring Framework while retaining all license, copyright and author information. | |
236 | * | |
237 | * @param str the String to tokenize | |
238 | * @param delimiters the delimiter characters, assembled as String | |
239 | * (each of those characters is individually considered as delimiter) | |
240 | * @param trimTokens trim the tokens via String's <code>trim</code> | |
241 | * @param ignoreEmptyTokens omit empty tokens from the result array | |
242 | * (only applies to tokens that are empty after trimming; StringTokenizer | |
243 | * will not consider subsequent delimiters as token in the first place). | |
244 | * @return an array of the tokens (<code>null</code> if the input String | |
245 | * was <code>null</code>) | |
246 | * @see java.util.StringTokenizer | |
247 | * @see java.lang.String#trim() | |
248 | */ | |
249 | @SuppressWarnings({"unchecked"}) | |
250 | public static String[] tokenizeToStringArray( | |
251 | String str, String delimiters, boolean trimTokens, boolean ignoreEmptyTokens) { | |
252 | ||
253 | 0 | if (str == null) { |
254 | 0 | return null; |
255 | } | |
256 | 0 | StringTokenizer st = new StringTokenizer(str, delimiters); |
257 | 0 | List tokens = new ArrayList(); |
258 | 0 | while (st.hasMoreTokens()) { |
259 | 0 | String token = st.nextToken(); |
260 | 0 | if (trimTokens) { |
261 | 0 | token = token.trim(); |
262 | } | |
263 | 0 | if (!ignoreEmptyTokens || token.length() > 0) { |
264 | 0 | tokens.add(token); |
265 | } | |
266 | 0 | } |
267 | 0 | return toStringArray(tokens); |
268 | } | |
269 | ||
270 | /** | |
271 | * Copy the given Collection into a String array. | |
272 | * The Collection must contain String elements only. | |
273 | * <p/> | |
274 | * <p>Copied from the Spring Framework while retaining all license, copyright and author information. | |
275 | * | |
276 | * @param collection the Collection to copy | |
277 | * @return the String array (<code>null</code> if the passed-in | |
278 | * Collection was <code>null</code>) | |
279 | */ | |
280 | @SuppressWarnings({"unchecked"}) | |
281 | public static String[] toStringArray(Collection collection) { | |
282 | 0 | if (collection == null) { |
283 | 0 | return null; |
284 | } | |
285 | 0 | return (String[]) collection.toArray(new String[collection.size()]); |
286 | } | |
287 | ||
288 | public static String[] splitKeyValue(String aLine) throws ParseException { | |
289 | 26 | String line = clean(aLine); |
290 | 26 | if (line == null) { |
291 | 0 | return null; |
292 | } | |
293 | 26 | String[] split = line.split(" ", 2); |
294 | 26 | if (split.length != 2) { |
295 | //fallback to checking for an equals sign | |
296 | 0 | split = line.split("=", 2); |
297 | 0 | if (split.length != 2) { |
298 | 0 | String msg = "Unable to determine Key/Value pair from line [" + line + "]. There is no space from " + |
299 | "which the split location could be determined."; | |
300 | 0 | throw new ParseException(msg, 0); |
301 | } | |
302 | ||
303 | } | |
304 | ||
305 | 26 | split[0] = clean(split[0]); |
306 | 26 | split[1] = clean(split[1]); |
307 | 26 | if (split[1].startsWith("=")) { |
308 | //they used spaces followed by an equals followed by zero or more spaces to split the key/value pair, so | |
309 | //remove the equals sign to result in only the key and values in the | |
310 | 26 | split[1] = clean(split[1].substring(1)); |
311 | } | |
312 | ||
313 | 26 | if (split[0] == null) { |
314 | 0 | String msg = "No valid key could be found in line [" + line + "] to form a key/value pair."; |
315 | 0 | throw new ParseException(msg, 0); |
316 | } | |
317 | 26 | if (split[1] == null) { |
318 | 0 | String msg = "No corresponding value could be found in line [" + line + "] for key [" + split[0] + "]"; |
319 | 0 | throw new ParseException(msg, 0); |
320 | } | |
321 | ||
322 | 26 | return split; |
323 | } | |
324 | ||
325 | public static String[] split(String line) { | |
326 | 68 | return split(line, DEFAULT_DELIMITER_CHAR); |
327 | } | |
328 | ||
329 | public static String[] split(String line, char delimiter) { | |
330 | 76 | return split(line, delimiter, DEFAULT_QUOTE_CHAR); |
331 | } | |
332 | ||
333 | public static String[] split(String line, char delimiter, char quoteChar) { | |
334 | 76 | return split(line, delimiter, quoteChar, quoteChar); |
335 | } | |
336 | ||
337 | public static String[] split(String line, char delimiter, char beginQuoteChar, char endQuoteChar) { | |
338 | 76 | return split(line, delimiter, beginQuoteChar, endQuoteChar, false, true); |
339 | } | |
340 | ||
341 | /** | |
342 | * Splits the specified delimited String into tokens, supporting quoted tokens so that quoted strings themselves | |
343 | * won't be tokenized. | |
344 | * <p/> | |
345 | * This method's implementation is very loosely based (with significant modifications) on | |
346 | * <a href="http://blogs.bytecode.com.au/glen">Glen Smith</a>'s open-source | |
347 | * <a href="http://opencsv.svn.sourceforge.net/viewvc/opencsv/trunk/src/au/com/bytecode/opencsv/CSVReader.java?&view=markup">CSVReader.java</a> | |
348 | * file. | |
349 | * <p/> | |
350 | * That file is Apache 2.0 licensed as well, making Glen's code a great starting point for us to modify to | |
351 | * our needs. | |
352 | * | |
353 | * @param aLine the String to parse | |
354 | * @param delimiter the delimiter by which the <tt>line</tt> argument is to be split | |
355 | * @param beginQuoteChar the character signifying the start of quoted text (so the quoted text will not be split) | |
356 | * @param endQuoteChar the character signifying the end of quoted text | |
357 | * @param retainQuotes if the quotes themselves should be retained when constructing the corresponding token | |
358 | * @param trimTokens if leading and trailing whitespace should be trimmed from discovered tokens. | |
359 | * @return the tokens discovered from parsing the given delimited <tt>line</tt>. | |
360 | */ | |
361 | public static String[] split(String aLine, char delimiter, char beginQuoteChar, char endQuoteChar, | |
362 | boolean retainQuotes, boolean trimTokens) { | |
363 | 80 | String line = clean(aLine); |
364 | 80 | if (line == null) { |
365 | 1 | return null; |
366 | } | |
367 | ||
368 | 79 | List<String> tokens = new ArrayList<String>(); |
369 | 79 | StringBuilder sb = new StringBuilder(); |
370 | 79 | boolean inQuotes = false; |
371 | ||
372 | 1611 | for (int i = 0; i < line.length(); i++) { |
373 | ||
374 | 1532 | char c = line.charAt(i); |
375 | 1532 | if (c == beginQuoteChar) { |
376 | // this gets complex... the quote may end a quoted block, or escape another quote. | |
377 | // do a 1-char lookahead: | |
378 | 15 | if (inQuotes // we are in quotes, therefore there can be escaped quotes in here. |
379 | && line.length() > (i + 1) // there is indeed another character to check. | |
380 | && line.charAt(i + 1) == beginQuoteChar) { // ..and that char. is a quote also. | |
381 | // we have two quote chars in a row == one quote char, so consume them both and | |
382 | // put one on the token. we do *not* exit the quoted text. | |
383 | 2 | sb.append(line.charAt(i + 1)); |
384 | 2 | i++; |
385 | } else { | |
386 | 13 | inQuotes = !inQuotes; |
387 | 13 | if (retainQuotes) { |
388 | 3 | sb.append(c); |
389 | } | |
390 | } | |
391 | 1517 | } else if (c == endQuoteChar) { |
392 | 6 | inQuotes = !inQuotes; |
393 | 6 | if (retainQuotes) { |
394 | 3 | sb.append(c); |
395 | } | |
396 | 1511 | } else if (c == delimiter && !inQuotes) { |
397 | 83 | String s = sb.toString(); |
398 | 83 | if (trimTokens) { |
399 | 83 | s = s.trim(); |
400 | } | |
401 | 83 | tokens.add(s); |
402 | 83 | sb = new StringBuilder(); // start work on next token |
403 | 83 | } else { |
404 | 1428 | sb.append(c); |
405 | } | |
406 | } | |
407 | 79 | String s = sb.toString(); |
408 | 79 | if (trimTokens) { |
409 | 79 | s = s.trim(); |
410 | } | |
411 | 79 | tokens.add(s); |
412 | 79 | return tokens.toArray(new String[tokens.size()]); |
413 | } | |
414 | ||
415 | /** | |
416 | * Joins the elements of the provided {@code Iterator} into | |
417 | * a single String containing the provided elements.</p> | |
418 | * <p/> | |
419 | * No delimiter is added before or after the list. | |
420 | * A {@code null} separator is the same as an empty String ("").</p> | |
421 | * <p/> | |
422 | * Copied from Commons Lang, version 3 (r1138702).</p> | |
423 | * | |
424 | * @param iterator the {@code Iterator} of values to join together, may be null | |
425 | * @param separator the separator character to use, null treated as "" | |
426 | * @return the joined String, {@code null} if null iterator input | |
427 | * @since 1.2 | |
428 | */ | |
429 | public static String join(Iterator<?> iterator, String separator) { | |
430 | 0 | final String empty = ""; |
431 | ||
432 | // handle null, zero and one elements before building a buffer | |
433 | 0 | if (iterator == null) { |
434 | 0 | return null; |
435 | } | |
436 | 0 | if (!iterator.hasNext()) { |
437 | 0 | return empty; |
438 | } | |
439 | 0 | Object first = iterator.next(); |
440 | 0 | if (!iterator.hasNext()) { |
441 | 0 | return first == null ? empty : first.toString(); |
442 | } | |
443 | ||
444 | // two or more elements | |
445 | 0 | StringBuilder buf = new StringBuilder(256); // Java default is 16, probably too small |
446 | 0 | if (first != null) { |
447 | 0 | buf.append(first); |
448 | } | |
449 | ||
450 | 0 | while (iterator.hasNext()) { |
451 | 0 | if (separator != null) { |
452 | 0 | buf.append(separator); |
453 | } | |
454 | 0 | Object obj = iterator.next(); |
455 | 0 | if (obj != null) { |
456 | 0 | buf.append(obj); |
457 | } | |
458 | 0 | } |
459 | 0 | return buf.toString(); |
460 | } | |
461 | ||
462 | /** | |
463 | * Splits the {@code delimited} string (delimited by the specified {@code separator} character) and returns the | |
464 | * delimited values as a {@code Set}. | |
465 | * <p/> | |
466 | * If either argument is {@code null}, this method returns {@code null}. | |
467 | * | |
468 | * @param delimited the string to split | |
469 | * @param separator the character that delineates individual tokens to split | |
470 | * @return the delimited values as a {@code Set}. | |
471 | * @since 1.2 | |
472 | */ | |
473 | public static Set<String> splitToSet(String delimited, String separator) { | |
474 | 6 | if (delimited == null || separator == null) { |
475 | 0 | return null; |
476 | } | |
477 | 6 | String[] split = split(delimited, separator.charAt(0)); |
478 | 6 | return CollectionUtils.asSet(split); |
479 | } | |
480 | ||
481 | /** | |
482 | * Returns the input argument, but ensures the first character is capitalized (if possible). | |
483 | * @param in the string to uppercase the first character. | |
484 | * @return the input argument, but with the first character capitalized (if possible). | |
485 | * @since 1.2 | |
486 | */ | |
487 | public static String uppercaseFirstChar(String in) { | |
488 | 5 | if (in == null || in.length() == 0) { |
489 | 0 | return in; |
490 | } | |
491 | 5 | int length = in.length(); |
492 | 5 | StringBuilder sb = new StringBuilder(length); |
493 | ||
494 | 5 | sb.append(Character.toUpperCase(in.charAt(0))); |
495 | 5 | if (length > 1) { |
496 | 5 | String remaining = in.substring(1); |
497 | 5 | sb.append(remaining); |
498 | } | |
499 | 5 | return sb.toString(); |
500 | } | |
501 | ||
502 | } |