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 685685 2008-08-13 21:43:27Z 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 * Remove underscores from a string and replaces first
83 * letters with capitals. Other letters are changed to lower case.
84 * </p>
85 *
86 * <p>
87 * For example <code>foo_bar</code> becomes <code>FooBar</code>
88 * but <code>foo_barBar</code> becomes <code>FooBarbar</code>.
89 * </p>
90 *
91 * @param data string to remove underscores from.
92 * @return String
93 * @deprecated Use the org.apache.commons.util.StringUtils class
94 * instead. Using its firstLetterCaps() method in conjunction
95 * with a StringTokenizer will achieve the same result.
96 */
97 static public String removeUnderScores (String data)
98 {
99 String temp = null;
100 StringBuffer out = new StringBuffer();
101 temp = data;
102
103 StringTokenizer st = new StringTokenizer(temp, "_");
104
105 while (st.hasMoreTokens())
106 {
107 String element = (String) st.nextElement();
108 out.append ( firstLetterCaps(element));
109 }
110
111 return out.toString();
112 }
113
114 /**
115 * <p>
116 * 'Camels Hump' replacement of underscores.
117 * </p>
118 *
119 * <p>
120 * Remove underscores from a string but leave the capitalization of the
121 * other letters unchanged.
122 * </p>
123 *
124 * <p>
125 * For example <code>foo_barBar</code> becomes <code>FooBarBar</code>.
126 * </p>
127 *
128 * @param data string to hump
129 * @return String
130 */
131 static public String removeAndHump (String data)
132 {
133 return removeAndHump(data,"_");
134 }
135
136 /**
137 * <p>
138 * 'Camels Hump' replacement.
139 * </p>
140 *
141 * <p>
142 * Remove one string from another string but leave the capitalization of the
143 * other letters unchanged.
144 * </p>
145 *
146 * <p>
147 * For example, removing "_" from <code>foo_barBar</code> becomes <code>FooBarBar</code>.
148 * </p>
149 *
150 * @param data string to hump
151 * @param replaceThis string to be replaced
152 * @return String
153 */
154 static public String removeAndHump (String data,String replaceThis)
155 {
156 String temp = null;
157 StringBuffer out = new StringBuffer();
158 temp = data;
159
160 StringTokenizer st = new StringTokenizer(temp, replaceThis);
161
162 while (st.hasMoreTokens())
163 {
164 String element = (String) st.nextElement();
165 out.append ( capitalizeFirstLetter(element));
166 }//while
167
168 return out.toString();
169 }
170
171 /**
172 * <p>
173 * Makes the first letter caps and the rest lowercase.
174 * </p>
175 *
176 * <p>
177 * For example <code>fooBar</code> becomes <code>Foobar</code>.
178 * </p>
179 *
180 * @param data capitalize this
181 * @return String
182 */
183 static public String firstLetterCaps ( String data )
184 {
185 String firstLetter = data.substring(0,1).toUpperCase();
186 String restLetters = data.substring(1).toLowerCase();
187 return firstLetter + restLetters;
188 }
189
190 /**
191 * <p>
192 * Capitalize the first letter but leave the rest as they are.
193 * </p>
194 *
195 * <p>
196 * For example <code>fooBar</code> becomes <code>FooBar</code>.
197 * </p>
198 *
199 * @param data capitalize this
200 * @return String
201 */
202 static public String capitalizeFirstLetter ( String data )
203 {
204 String firstLetter = data.substring(0,1).toUpperCase();
205 String restLetters = data.substring(1);
206 return firstLetter + restLetters;
207 }
208
209 /**
210 * Create a string array from a string separated by delim
211 *
212 * @param line the line to split
213 * @param delim the delimter to split by
214 * @return a string array of the split fields
215 */
216 public static String [] split(String line, String delim)
217 {
218 List list = new ArrayList();
219 StringTokenizer t = new StringTokenizer(line, delim);
220 while (t.hasMoreTokens())
221 {
222 list.add(t.nextToken());
223 }
224 return (String []) list.toArray(new String[list.size()]);
225 }
226
227 /**
228 * Chop i characters off the end of a string.
229 * This method assumes that any EOL characters in String s
230 * and the platform EOL will be the same.
231 * A 2 character EOL will count as 1 character.
232 *
233 * @param s String to chop.
234 * @param i Number of characters to chop.
235 * @return String with processed answer.
236 */
237 public static String chop(String s, int i)
238 {
239 return chop(s, i, EOL);
240 }
241
242 /**
243 * Chop i characters off the end of a string.
244 * A 2 character EOL will count as 1 character.
245 *
246 * @param s String to chop.
247 * @param i Number of characters to chop.
248 * @param eol A String representing the EOL (end of line).
249 * @return String with processed answer.
250 */
251 public static String chop(String s, int i, String eol)
252 {
253 if ( i == 0 || s == null || eol == null )
254 {
255 return s;
256 }
257
258 int length = s.length();
259
260 /*
261 * if it is a 2 char EOL and the string ends with
262 * it, nip it off. The EOL in this case is treated like 1 character
263 */
264 if ( eol.length() == 2 && s.endsWith(eol ))
265 {
266 length -= 2;
267 i -= 1;
268 }
269
270 if ( i > 0)
271 {
272 length -= i;
273 }
274
275 if ( length < 0)
276 {
277 length = 0;
278 }
279
280 return s.substring( 0, length);
281 }
282
283 /**
284 * @param argStr
285 * @param vars
286 * @return Substituted String.
287 */
288 public static StringBuffer stringSubstitution( String argStr,
289 Hashtable vars )
290 {
291 return stringSubstitution( argStr, (Map) vars );
292 }
293
294 /**
295 * Perform a series of substitutions. The substitions
296 * are performed by replacing $variable in the target
297 * string with the value of provided by the key "variable"
298 * in the provided hashtable.
299 *
300 * @param argStr target string
301 * @param vars name/value pairs used for substitution
302 * @return String target string with replacements.
303 */
304 public static StringBuffer stringSubstitution(String argStr,
305 Map vars)
306 {
307 StringBuffer argBuf = new StringBuffer();
308
309 for (int cIdx = 0 ; cIdx < argStr.length();)
310 {
311 char ch = argStr.charAt(cIdx);
312
313 switch (ch)
314 {
315 case '$':
316 StringBuffer nameBuf = new StringBuffer();
317 for (++cIdx ; cIdx < argStr.length(); ++cIdx)
318 {
319 ch = argStr.charAt(cIdx);
320 if (ch == '_' || Character.isLetterOrDigit(ch))
321 nameBuf.append(ch);
322 else
323 break;
324 }
325
326 if (nameBuf.length() > 0)
327 {
328 String value =
329 (String) vars.get(nameBuf.toString());
330
331 if (value != null)
332 {
333 argBuf.append(value);
334 }
335 }
336 break;
337
338 default:
339 argBuf.append(ch);
340 ++cIdx;
341 break;
342 }
343 }
344
345 return argBuf;
346 }
347
348 /**
349 * Read the contents of a file and place them in
350 * a string object.
351 *
352 * @param file path to file.
353 * @return String contents of the file.
354 */
355 public static String fileContentsToString(String file)
356 {
357 String contents = "";
358
359 File f = null;
360 try
361 {
362 f = new File(file);
363
364 if (f.exists())
365 {
366 FileReader fr = null;
367 try
368 {
369 fr = new FileReader(f);
370 char[] template = new char[(int) f.length()];
371 fr.read(template);
372 contents = new String(template);
373 }
374 catch (Exception e)
375 {
376 e.printStackTrace();
377 }
378 finally
379 {
380 if (fr != null)
381 {
382 fr.close();
383 }
384 }
385 }
386 }
387 catch (Exception e)
388 {
389 e.printStackTrace();
390 }
391 return contents;
392 }
393
394 /**
395 * Remove/collapse multiple newline characters.
396 *
397 * @param argStr string to collapse newlines in.
398 * @return String
399 */
400 public static String collapseNewlines(String argStr)
401 {
402 char last = argStr.charAt(0);
403 StringBuffer argBuf = new StringBuffer();
404
405 for (int cIdx = 0 ; cIdx < argStr.length(); cIdx++)
406 {
407 char ch = argStr.charAt(cIdx);
408 if (ch != '\n' || last != '\n')
409 {
410 argBuf.append(ch);
411 last = ch;
412 }
413 }
414
415 return argBuf.toString();
416 }
417
418 /**
419 * Remove/collapse multiple spaces.
420 *
421 * @param argStr string to remove multiple spaces from.
422 * @return String
423 */
424 public static String collapseSpaces(String argStr)
425 {
426 char last = argStr.charAt(0);
427 StringBuffer argBuf = new StringBuffer();
428
429 for (int cIdx = 0 ; cIdx < argStr.length(); cIdx++)
430 {
431 char ch = argStr.charAt(cIdx);
432 if (ch != ' ' || last != ' ')
433 {
434 argBuf.append(ch);
435 last = ch;
436 }
437 }
438
439 return argBuf.toString();
440 }
441
442 /**
443 * Replaces all instances of oldString with newString in line.
444 * Taken from the Jive forum package.
445 *
446 * @param line original string.
447 * @param oldString string in line to replace.
448 * @param newString replace oldString with this.
449 * @return String string with replacements.
450 */
451 public static final String sub(String line, String oldString,
452 String newString)
453 {
454 int i = 0;
455 if ((i = line.indexOf(oldString, i)) >= 0)
456 {
457 char [] line2 = line.toCharArray();
458 char [] newString2 = newString.toCharArray();
459 int oLength = oldString.length();
460 StringBuffer buf = new StringBuffer(line2.length);
461 buf.append(line2, 0, i).append(newString2);
462 i += oLength;
463 int j = i;
464 while ((i = line.indexOf(oldString, i)) > 0)
465 {
466 buf.append(line2, j, i - j).append(newString2);
467 i += oLength;
468 j = i;
469 }
470 buf.append(line2, j, line2.length - j);
471 return buf.toString();
472 }
473 return line;
474 }
475
476 /**
477 * Returns the output of printStackTrace as a String.
478 *
479 * @param e A Throwable.
480 * @return A String.
481 */
482 public static final String stackTrace(Throwable e)
483 {
484 String foo = null;
485 try
486 {
487 // And show the Error Screen.
488 ByteArrayOutputStream ostr = new ByteArrayOutputStream();
489 e.printStackTrace( new PrintWriter(ostr,true) );
490 foo = ostr.toString();
491 }
492 catch (Exception f)
493 {
494 // Do nothing.
495 }
496 return foo;
497 }
498
499 /**
500 * Return a context-relative path, beginning with a "/", that represents
501 * the canonical version of the specified path after ".." and "." elements
502 * are resolved out. If the specified path attempts to go outside the
503 * boundaries of the current context (i.e. too many ".." path elements
504 * are present), return <code>null</code> instead.
505 *
506 * @param path Path to be normalized
507 * @return String normalized path
508 */
509 public static final String normalizePath(String path)
510 {
511 // Normalize the slashes and add leading slash if necessary
512 String normalized = path;
513 if (normalized.indexOf('\\') >= 0)
514 {
515 normalized = normalized.replace('\\', '/');
516 }
517
518 if (!normalized.startsWith("/"))
519 {
520 normalized = "/" + normalized;
521 }
522
523 // Resolve occurrences of "//" in the normalized path
524 while (true)
525 {
526 int index = normalized.indexOf("//");
527 if (index < 0)
528 break;
529 normalized = normalized.substring(0, index) +
530 normalized.substring(index + 1);
531 }
532
533 // Resolve occurrences of "%20" in the normalized path
534 while (true)
535 {
536 int index = normalized.indexOf("%20");
537 if (index < 0)
538 break;
539 normalized = normalized.substring(0, index) + " " +
540 normalized.substring(index + 3);
541 }
542
543 // Resolve occurrences of "/./" in the normalized path
544 while (true)
545 {
546 int index = normalized.indexOf("/./");
547 if (index < 0)
548 break;
549 normalized = normalized.substring(0, index) +
550 normalized.substring(index + 2);
551 }
552
553 // Resolve occurrences of "/../" in the normalized path
554 while (true)
555 {
556 int index = normalized.indexOf("/../");
557 if (index < 0)
558 break;
559 if (index == 0)
560 return (null); // Trying to go outside our context
561 int index2 = normalized.lastIndexOf('/', index - 1);
562 normalized = normalized.substring(0, index2) +
563 normalized.substring(index + 3);
564 }
565
566 // Return the normalized path that we have completed
567 return (normalized);
568 }
569
570 /**
571 * If state is true then return the trueString, else
572 * return the falseString.
573 *
574 * @param state
575 * @param trueString
576 * @param falseString
577 * @return Selected result.
578 */
579 public String select(boolean state, String trueString, String falseString)
580 {
581 if (state)
582 {
583 return trueString;
584 }
585 else
586 {
587 return falseString;
588 }
589 }
590
591 /**
592 * Check to see if all the string objects passed
593 * in are empty.
594 *
595 * @param list A list of {@link java.lang.String} objects.
596 * @return Whether all strings are empty.
597 */
598 public boolean allEmpty(List list)
599 {
600 int size = list.size();
601
602 for (int i = 0; i < size; i++)
603 {
604 if (list.get(i) != null && list.get(i).toString().length() > 0)
605 {
606 return false;
607 }
608 }
609 return true;
610 }
611
612 /**
613 * Trim all strings in a List. Changes the strings in the existing list.
614 * @param list
615 * @return List of trimmed strings.
616 * @since 1.5
617 */
618 public static List trimStrings(List list)
619 {
620 if (list == null)
621 return null;
622
623 int sz = list.size();
624 for (int i = 0; i < sz; i++)
625 list.set(i,nullTrim((String) list.get(i)));
626 return list;
627 }
628
629 /**
630 * Trim the string, but pass a null through.
631 * @param s
632 * @return List of trimmed Strings.
633 * @since 1.5
634 */
635 public static String nullTrim(String s)
636 {
637 if (s == null)
638 {
639 return null;
640 }
641 else
642 {
643 return s.trim();
644 }
645 }
646 }