1 package org.apache.velocity.util;
2
3 /*
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
19 * under the License.
20 */
21
22 import java.io.ByteArrayOutputStream;
23 import java.io.File;
24 import java.io.FileReader;
25 import java.io.PrintWriter;
26 import java.util.ArrayList;
27 import java.util.Hashtable;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.StringTokenizer;
31
32
33 /**
34 * This class provides some methods for dynamically
35 * invoking methods in objects, and some string
36 * manipulation methods used by torque. The string
37 * methods will soon be moved into the turbine
38 * string utilities class.
39 *
40 * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
41 * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a>
42 * @version $Id: StringUtils.java 741214 2009-02-05 18:13:47Z nbubna $
43 */
44 public class StringUtils
45 {
46 /**
47 * Line separator for the OS we are operating on.
48 */
49 private static final String EOL = System.getProperty("line.separator");
50
51 /**
52 * Concatenates a list of objects as a String.
53 *
54 * @param list The list of objects to concatenate.
55 * @return A text representation of the concatenated objects.
56 */
57 public String concat(List list)
58 {
59 StringBuffer sb = new StringBuffer();
60 int size = list.size();
61
62 for (int i = 0; i < size; i++)
63 {
64 sb.append(list.get(i).toString());
65 }
66 return sb.toString();
67 }
68
69 /**
70 * Return a package name as a relative path name
71 *
72 * @param pckge package name to convert to a directory.
73 * @return String directory path.
74 */
75 static public String getPackageAsPath(String pckge)
76 {
77 return pckge.replace( '.', File.separator.charAt(0) ) + File.separator;
78 }
79
80 /**
81 * <p>
82 * 'Camels Hump' replacement of underscores.
83 * </p>
84 *
85 * <p>
86 * Remove underscores from a string but leave the capitalization of the
87 * other letters unchanged.
88 * </p>
89 *
90 * <p>
91 * For example <code>foo_barBar</code> becomes <code>FooBarBar</code>.
92 * </p>
93 *
94 * @param data string to hump
95 * @return String
96 */
97 static public String removeAndHump (String data)
98 {
99 return removeAndHump(data,"_");
100 }
101
102 /**
103 * <p>
104 * 'Camels Hump' replacement.
105 * </p>
106 *
107 * <p>
108 * Remove one string from another string but leave the capitalization of the
109 * other letters unchanged.
110 * </p>
111 *
112 * <p>
113 * For example, removing "_" from <code>foo_barBar</code> becomes <code>FooBarBar</code>.
114 * </p>
115 *
116 * @param data string to hump
117 * @param replaceThis string to be replaced
118 * @return String
119 */
120 static public String removeAndHump (String data,String replaceThis)
121 {
122 String temp = null;
123 StringBuffer out = new StringBuffer();
124 temp = data;
125
126 StringTokenizer st = new StringTokenizer(temp, replaceThis);
127
128 while (st.hasMoreTokens())
129 {
130 String element = (String) st.nextElement();
131 out.append ( capitalizeFirstLetter(element));
132 }//while
133
134 return out.toString();
135 }
136
137 /**
138 * <p>
139 * Makes the first letter caps and the rest lowercase.
140 * </p>
141 *
142 * <p>
143 * For example <code>fooBar</code> becomes <code>Foobar</code>.
144 * </p>
145 *
146 * @param data capitalize this
147 * @return String
148 */
149 static public String firstLetterCaps ( String data )
150 {
151 String firstLetter = data.substring(0,1).toUpperCase();
152 String restLetters = data.substring(1).toLowerCase();
153 return firstLetter + restLetters;
154 }
155
156 /**
157 * <p>
158 * Capitalize the first letter but leave the rest as they are.
159 * </p>
160 *
161 * <p>
162 * For example <code>fooBar</code> becomes <code>FooBar</code>.
163 * </p>
164 *
165 * @param data capitalize this
166 * @return String
167 */
168 static public String capitalizeFirstLetter ( String data )
169 {
170 String firstLetter = data.substring(0,1).toUpperCase();
171 String restLetters = data.substring(1);
172 return firstLetter + restLetters;
173 }
174
175 /**
176 * Create a string array from a string separated by delim
177 *
178 * @param line the line to split
179 * @param delim the delimter to split by
180 * @return a string array of the split fields
181 */
182 public static String [] split(String line, String delim)
183 {
184 List list = new ArrayList();
185 StringTokenizer t = new StringTokenizer(line, delim);
186 while (t.hasMoreTokens())
187 {
188 list.add(t.nextToken());
189 }
190 return (String []) list.toArray(new String[list.size()]);
191 }
192
193 /**
194 * Chop i characters off the end of a string.
195 * This method assumes that any EOL characters in String s
196 * and the platform EOL will be the same.
197 * A 2 character EOL will count as 1 character.
198 *
199 * @param s String to chop.
200 * @param i Number of characters to chop.
201 * @return String with processed answer.
202 */
203 public static String chop(String s, int i)
204 {
205 return chop(s, i, EOL);
206 }
207
208 /**
209 * Chop i characters off the end of a string.
210 * A 2 character EOL will count as 1 character.
211 *
212 * @param s String to chop.
213 * @param i Number of characters to chop.
214 * @param eol A String representing the EOL (end of line).
215 * @return String with processed answer.
216 */
217 public static String chop(String s, int i, String eol)
218 {
219 if ( i == 0 || s == null || eol == null )
220 {
221 return s;
222 }
223
224 int length = s.length();
225
226 /*
227 * if it is a 2 char EOL and the string ends with
228 * it, nip it off. The EOL in this case is treated like 1 character
229 */
230 if ( eol.length() == 2 && s.endsWith(eol ))
231 {
232 length -= 2;
233 i -= 1;
234 }
235
236 if ( i > 0)
237 {
238 length -= i;
239 }
240
241 if ( length < 0)
242 {
243 length = 0;
244 }
245
246 return s.substring( 0, length);
247 }
248
249 /**
250 * @param argStr
251 * @param vars
252 * @return Substituted String.
253 */
254 public static StringBuffer stringSubstitution( String argStr,
255 Hashtable vars )
256 {
257 return stringSubstitution( argStr, (Map) vars );
258 }
259
260 /**
261 * Perform a series of substitutions. The substitions
262 * are performed by replacing $variable in the target
263 * string with the value of provided by the key "variable"
264 * in the provided hashtable.
265 *
266 * @param argStr target string
267 * @param vars name/value pairs used for substitution
268 * @return String target string with replacements.
269 */
270 public static StringBuffer stringSubstitution(String argStr,
271 Map vars)
272 {
273 StringBuffer argBuf = new StringBuffer();
274
275 for (int cIdx = 0 ; cIdx < argStr.length();)
276 {
277 char ch = argStr.charAt(cIdx);
278
279 switch (ch)
280 {
281 case '$':
282 StringBuffer nameBuf = new StringBuffer();
283 for (++cIdx ; cIdx < argStr.length(); ++cIdx)
284 {
285 ch = argStr.charAt(cIdx);
286 if (ch == '_' || Character.isLetterOrDigit(ch))
287 nameBuf.append(ch);
288 else
289 break;
290 }
291
292 if (nameBuf.length() > 0)
293 {
294 String value =
295 (String) vars.get(nameBuf.toString());
296
297 if (value != null)
298 {
299 argBuf.append(value);
300 }
301 }
302 break;
303
304 default:
305 argBuf.append(ch);
306 ++cIdx;
307 break;
308 }
309 }
310
311 return argBuf;
312 }
313
314 /**
315 * Read the contents of a file and place them in
316 * a string object.
317 *
318 * @param file path to file.
319 * @return String contents of the file.
320 */
321 public static String fileContentsToString(String file)
322 {
323 String contents = "";
324
325 File f = null;
326 try
327 {
328 f = new File(file);
329
330 if (f.exists())
331 {
332 FileReader fr = null;
333 try
334 {
335 fr = new FileReader(f);
336 char[] template = new char[(int) f.length()];
337 fr.read(template);
338 contents = new String(template);
339 }
340 catch (Exception e)
341 {
342 e.printStackTrace();
343 }
344 finally
345 {
346 if (fr != null)
347 {
348 fr.close();
349 }
350 }
351 }
352 }
353 catch (Exception e)
354 {
355 e.printStackTrace();
356 }
357 return contents;
358 }
359
360 /**
361 * Remove/collapse multiple newline characters.
362 *
363 * @param argStr string to collapse newlines in.
364 * @return String
365 */
366 public static String collapseNewlines(String argStr)
367 {
368 char last = argStr.charAt(0);
369 StringBuffer argBuf = new StringBuffer();
370
371 for (int cIdx = 0 ; cIdx < argStr.length(); cIdx++)
372 {
373 char ch = argStr.charAt(cIdx);
374 if (ch != '\n' || last != '\n')
375 {
376 argBuf.append(ch);
377 last = ch;
378 }
379 }
380
381 return argBuf.toString();
382 }
383
384 /**
385 * Remove/collapse multiple spaces.
386 *
387 * @param argStr string to remove multiple spaces from.
388 * @return String
389 */
390 public static String collapseSpaces(String argStr)
391 {
392 char last = argStr.charAt(0);
393 StringBuffer argBuf = new StringBuffer();
394
395 for (int cIdx = 0 ; cIdx < argStr.length(); cIdx++)
396 {
397 char ch = argStr.charAt(cIdx);
398 if (ch != ' ' || last != ' ')
399 {
400 argBuf.append(ch);
401 last = ch;
402 }
403 }
404
405 return argBuf.toString();
406 }
407
408 /**
409 * Replaces all instances of oldString with newString in line.
410 * Taken from the Jive forum package.
411 *
412 * @param line original string.
413 * @param oldString string in line to replace.
414 * @param newString replace oldString with this.
415 * @return String string with replacements.
416 */
417 public static final String sub(String line, String oldString,
418 String newString)
419 {
420 int i = 0;
421 if ((i = line.indexOf(oldString, i)) >= 0)
422 {
423 char [] line2 = line.toCharArray();
424 char [] newString2 = newString.toCharArray();
425 int oLength = oldString.length();
426 StringBuffer buf = new StringBuffer(line2.length);
427 buf.append(line2, 0, i).append(newString2);
428 i += oLength;
429 int j = i;
430 while ((i = line.indexOf(oldString, i)) > 0)
431 {
432 buf.append(line2, j, i - j).append(newString2);
433 i += oLength;
434 j = i;
435 }
436 buf.append(line2, j, line2.length - j);
437 return buf.toString();
438 }
439 return line;
440 }
441
442 /**
443 * Returns the output of printStackTrace as a String.
444 *
445 * @param e A Throwable.
446 * @return A String.
447 */
448 public static final String stackTrace(Throwable e)
449 {
450 String foo = null;
451 try
452 {
453 // And show the Error Screen.
454 ByteArrayOutputStream ostr = new ByteArrayOutputStream();
455 e.printStackTrace( new PrintWriter(ostr,true) );
456 foo = ostr.toString();
457 }
458 catch (Exception f)
459 {
460 // Do nothing.
461 }
462 return foo;
463 }
464
465 /**
466 * Return a context-relative path, beginning with a "/", that represents
467 * the canonical version of the specified path after ".." and "." elements
468 * are resolved out. If the specified path attempts to go outside the
469 * boundaries of the current context (i.e. too many ".." path elements
470 * are present), return <code>null</code> instead.
471 *
472 * @param path Path to be normalized
473 * @return String normalized path
474 */
475 public static final String normalizePath(String path)
476 {
477 // Normalize the slashes and add leading slash if necessary
478 String normalized = path;
479 if (normalized.indexOf('\\') >= 0)
480 {
481 normalized = normalized.replace('\\', '/');
482 }
483
484 if (!normalized.startsWith("/"))
485 {
486 normalized = "/" + normalized;
487 }
488
489 // Resolve occurrences of "//" in the normalized path
490 while (true)
491 {
492 int index = normalized.indexOf("//");
493 if (index < 0)
494 break;
495 normalized = normalized.substring(0, index) +
496 normalized.substring(index + 1);
497 }
498
499 // Resolve occurrences of "%20" in the normalized path
500 while (true)
501 {
502 int index = normalized.indexOf("%20");
503 if (index < 0)
504 break;
505 normalized = normalized.substring(0, index) + " " +
506 normalized.substring(index + 3);
507 }
508
509 // Resolve occurrences of "/./" in the normalized path
510 while (true)
511 {
512 int index = normalized.indexOf("/./");
513 if (index < 0)
514 break;
515 normalized = normalized.substring(0, index) +
516 normalized.substring(index + 2);
517 }
518
519 // Resolve occurrences of "/../" in the normalized path
520 while (true)
521 {
522 int index = normalized.indexOf("/../");
523 if (index < 0)
524 break;
525 if (index == 0)
526 return (null); // Trying to go outside our context
527 int index2 = normalized.lastIndexOf('/', index - 1);
528 normalized = normalized.substring(0, index2) +
529 normalized.substring(index + 3);
530 }
531
532 // Return the normalized path that we have completed
533 return (normalized);
534 }
535
536 /**
537 * If state is true then return the trueString, else
538 * return the falseString.
539 *
540 * @param state
541 * @param trueString
542 * @param falseString
543 * @return Selected result.
544 */
545 public String select(boolean state, String trueString, String falseString)
546 {
547 if (state)
548 {
549 return trueString;
550 }
551 else
552 {
553 return falseString;
554 }
555 }
556
557 /**
558 * Check to see if all the string objects passed
559 * in are empty.
560 *
561 * @param list A list of {@link java.lang.String} objects.
562 * @return Whether all strings are empty.
563 */
564 public boolean allEmpty(List list)
565 {
566 int size = list.size();
567
568 for (int i = 0; i < size; i++)
569 {
570 if (list.get(i) != null && list.get(i).toString().length() > 0)
571 {
572 return false;
573 }
574 }
575 return true;
576 }
577
578 /**
579 * Trim all strings in a List. Changes the strings in the existing list.
580 * @param list
581 * @return List of trimmed strings.
582 * @since 1.5
583 */
584 public static List trimStrings(List list)
585 {
586 if (list == null)
587 return null;
588
589 int sz = list.size();
590 for (int i = 0; i < sz; i++)
591 list.set(i,nullTrim((String) list.get(i)));
592 return list;
593 }
594
595 /**
596 * Trim the string, but pass a null through.
597 * @param s
598 * @return List of trimmed Strings.
599 * @since 1.5
600 */
601 public static String nullTrim(String s)
602 {
603 if (s == null)
604 {
605 return null;
606 }
607 else
608 {
609 return s.trim();
610 }
611 }
612 }