001: package org.apache.velocity.util;
002:
003: /*
004: * Licensed to the Apache Software Foundation (ASF) under one
005: * or more contributor license agreements. See the NOTICE file
006: * distributed with this work for additional information
007: * regarding copyright ownership. The ASF licenses this file
008: * to you under the Apache License, Version 2.0 (the
009: * "License"); you may not use this file except in compliance
010: * with the License. You may obtain a copy of the License at
011: *
012: * http://www.apache.org/licenses/LICENSE-2.0
013: *
014: * Unless required by applicable law or agreed to in writing,
015: * software distributed under the License is distributed on an
016: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017: * KIND, either express or implied. See the License for the
018: * specific language governing permissions and limitations
019: * under the License.
020: */
021:
022: import java.io.ByteArrayOutputStream;
023: import java.io.File;
024: import java.io.FileReader;
025: import java.io.PrintWriter;
026: import java.util.ArrayList;
027: import java.util.Hashtable;
028: import java.util.List;
029: import java.util.Map;
030: import java.util.StringTokenizer;
031:
032: /**
033: * This class provides some methods for dynamically
034: * invoking methods in objects, and some string
035: * manipulation methods used by torque. The string
036: * methods will soon be moved into the turbine
037: * string utilities class.
038: *
039: * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
040: * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a>
041: * @version $Id: StringUtils.java 463298 2006-10-12 16:10:32Z henning $
042: */
043: public class StringUtils {
044: /**
045: * Line separator for the OS we are operating on.
046: */
047: private static final String EOL = System
048: .getProperty("line.separator");
049:
050: /**
051: * Concatenates a list of objects as a String.
052: *
053: * @param list The list of objects to concatenate.
054: * @return A text representation of the concatenated objects.
055: */
056: public String concat(List list) {
057: StringBuffer sb = new StringBuffer();
058: int size = list.size();
059:
060: for (int i = 0; i < size; i++) {
061: sb.append(list.get(i).toString());
062: }
063: return sb.toString();
064: }
065:
066: /**
067: * Return a package name as a relative path name
068: *
069: * @param pckge package name to convert to a directory.
070: * @return String directory path.
071: */
072: static public String getPackageAsPath(String pckge) {
073: return pckge.replace('.', File.separator.charAt(0))
074: + File.separator;
075: }
076:
077: /**
078: * <p>
079: * Remove underscores from a string and replaces first
080: * letters with capitals. Other letters are changed to lower case.
081: * </p>
082: *
083: * <p>
084: * For example <code>foo_bar</code> becomes <code>FooBar</code>
085: * but <code>foo_barBar</code> becomes <code>FooBarbar</code>.
086: * </p>
087: *
088: * @param data string to remove underscores from.
089: * @return String
090: * @deprecated Use the org.apache.commons.util.StringUtils class
091: * instead. Using its firstLetterCaps() method in conjunction
092: * with a StringTokenizer will achieve the same result.
093: */
094: static public String removeUnderScores(String data) {
095: String temp = null;
096: StringBuffer out = new StringBuffer();
097: temp = data;
098:
099: StringTokenizer st = new StringTokenizer(temp, "_");
100:
101: while (st.hasMoreTokens()) {
102: String element = (String) st.nextElement();
103: out.append(firstLetterCaps(element));
104: }
105:
106: return out.toString();
107: }
108:
109: /**
110: * <p>
111: * 'Camels Hump' replacement of underscores.
112: * </p>
113: *
114: * <p>
115: * Remove underscores from a string but leave the capitalization of the
116: * other letters unchanged.
117: * </p>
118: *
119: * <p>
120: * For example <code>foo_barBar</code> becomes <code>FooBarBar</code>.
121: * </p>
122: *
123: * @param data string to hump
124: * @return String
125: */
126: static public String removeAndHump(String data) {
127: return removeAndHump(data, "_");
128: }
129:
130: /**
131: * <p>
132: * 'Camels Hump' replacement.
133: * </p>
134: *
135: * <p>
136: * Remove one string from another string but leave the capitalization of the
137: * other letters unchanged.
138: * </p>
139: *
140: * <p>
141: * For example, removing "_" from <code>foo_barBar</code> becomes <code>FooBarBar</code>.
142: * </p>
143: *
144: * @param data string to hump
145: * @param replaceThis string to be replaced
146: * @return String
147: */
148: static public String removeAndHump(String data, String replaceThis) {
149: String temp = null;
150: StringBuffer out = new StringBuffer();
151: temp = data;
152:
153: StringTokenizer st = new StringTokenizer(temp, replaceThis);
154:
155: while (st.hasMoreTokens()) {
156: String element = (String) st.nextElement();
157: out.append(capitalizeFirstLetter(element));
158: }//while
159:
160: return out.toString();
161: }
162:
163: /**
164: * <p>
165: * Makes the first letter caps and the rest lowercase.
166: * </p>
167: *
168: * <p>
169: * For example <code>fooBar</code> becomes <code>Foobar</code>.
170: * </p>
171: *
172: * @param data capitalize this
173: * @return String
174: */
175: static public String firstLetterCaps(String data) {
176: String firstLetter = data.substring(0, 1).toUpperCase();
177: String restLetters = data.substring(1).toLowerCase();
178: return firstLetter + restLetters;
179: }
180:
181: /**
182: * <p>
183: * Capitalize the first letter but leave the rest as they are.
184: * </p>
185: *
186: * <p>
187: * For example <code>fooBar</code> becomes <code>FooBar</code>.
188: * </p>
189: *
190: * @param data capitalize this
191: * @return String
192: */
193: static public String capitalizeFirstLetter(String data) {
194: String firstLetter = data.substring(0, 1).toUpperCase();
195: String restLetters = data.substring(1);
196: return firstLetter + restLetters;
197: }
198:
199: /**
200: * Create a string array from a string separated by delim
201: *
202: * @param line the line to split
203: * @param delim the delimter to split by
204: * @return a string array of the split fields
205: */
206: public static String[] split(String line, String delim) {
207: List list = new ArrayList();
208: StringTokenizer t = new StringTokenizer(line, delim);
209: while (t.hasMoreTokens()) {
210: list.add(t.nextToken());
211: }
212: return (String[]) list.toArray(new String[list.size()]);
213: }
214:
215: /**
216: * Chop i characters off the end of a string.
217: * This method assumes that any EOL characters in String s
218: * and the platform EOL will be the same.
219: * A 2 character EOL will count as 1 character.
220: *
221: * @param s String to chop.
222: * @param i Number of characters to chop.
223: * @return String with processed answer.
224: */
225: public static String chop(String s, int i) {
226: return chop(s, i, EOL);
227: }
228:
229: /**
230: * Chop i characters off the end of a string.
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: * @param eol A String representing the EOL (end of line).
236: * @return String with processed answer.
237: */
238: public static String chop(String s, int i, String eol) {
239: if (i == 0 || s == null || eol == null) {
240: return s;
241: }
242:
243: int length = s.length();
244:
245: /*
246: * if it is a 2 char EOL and the string ends with
247: * it, nip it off. The EOL in this case is treated like 1 character
248: */
249: if (eol.length() == 2 && s.endsWith(eol)) {
250: length -= 2;
251: i -= 1;
252: }
253:
254: if (i > 0) {
255: length -= i;
256: }
257:
258: if (length < 0) {
259: length = 0;
260: }
261:
262: return s.substring(0, length);
263: }
264:
265: /**
266: * @param argStr
267: * @param vars
268: * @return Substituted String.
269: */
270: public static StringBuffer stringSubstitution(String argStr,
271: Hashtable vars) {
272: return stringSubstitution(argStr, (Map) vars);
273: }
274:
275: /**
276: * Perform a series of substitutions. The substitions
277: * are performed by replacing $variable in the target
278: * string with the value of provided by the key "variable"
279: * in the provided hashtable.
280: *
281: * @param argStr target string
282: * @param vars name/value pairs used for substitution
283: * @return String target string with replacements.
284: */
285: public static StringBuffer stringSubstitution(String argStr,
286: Map vars) {
287: StringBuffer argBuf = new StringBuffer();
288:
289: for (int cIdx = 0; cIdx < argStr.length();) {
290: char ch = argStr.charAt(cIdx);
291:
292: switch (ch) {
293: case '$':
294: StringBuffer nameBuf = new StringBuffer();
295: for (++cIdx; cIdx < argStr.length(); ++cIdx) {
296: ch = argStr.charAt(cIdx);
297: if (ch == '_' || Character.isLetterOrDigit(ch))
298: nameBuf.append(ch);
299: else
300: break;
301: }
302:
303: if (nameBuf.length() > 0) {
304: String value = (String) vars
305: .get(nameBuf.toString());
306:
307: if (value != null) {
308: argBuf.append(value);
309: }
310: }
311: break;
312:
313: default:
314: argBuf.append(ch);
315: ++cIdx;
316: break;
317: }
318: }
319:
320: return argBuf;
321: }
322:
323: /**
324: * Read the contents of a file and place them in
325: * a string object.
326: *
327: * @param file path to file.
328: * @return String contents of the file.
329: */
330: public static String fileContentsToString(String file) {
331: String contents = "";
332:
333: File f = null;
334: try {
335: f = new File(file);
336:
337: if (f.exists()) {
338: FileReader fr = null;
339: try {
340: fr = new FileReader(f);
341: char[] template = new char[(int) f.length()];
342: fr.read(template);
343: contents = new String(template);
344: } catch (Exception e) {
345: e.printStackTrace();
346: } finally {
347: if (fr != null) {
348: fr.close();
349: }
350: }
351: }
352: } catch (Exception e) {
353: e.printStackTrace();
354: }
355: return contents;
356: }
357:
358: /**
359: * Remove/collapse multiple newline characters.
360: *
361: * @param argStr string to collapse newlines in.
362: * @return String
363: */
364: public static String collapseNewlines(String argStr) {
365: char last = argStr.charAt(0);
366: StringBuffer argBuf = new StringBuffer();
367:
368: for (int cIdx = 0; cIdx < argStr.length(); cIdx++) {
369: char ch = argStr.charAt(cIdx);
370: if (ch != '\n' || last != '\n') {
371: argBuf.append(ch);
372: last = ch;
373: }
374: }
375:
376: return argBuf.toString();
377: }
378:
379: /**
380: * Remove/collapse multiple spaces.
381: *
382: * @param argStr string to remove multiple spaces from.
383: * @return String
384: */
385: public static String collapseSpaces(String argStr) {
386: char last = argStr.charAt(0);
387: StringBuffer argBuf = new StringBuffer();
388:
389: for (int cIdx = 0; cIdx < argStr.length(); cIdx++) {
390: char ch = argStr.charAt(cIdx);
391: if (ch != ' ' || last != ' ') {
392: argBuf.append(ch);
393: last = ch;
394: }
395: }
396:
397: return argBuf.toString();
398: }
399:
400: /**
401: * Replaces all instances of oldString with newString in line.
402: * Taken from the Jive forum package.
403: *
404: * @param line original string.
405: * @param oldString string in line to replace.
406: * @param newString replace oldString with this.
407: * @return String string with replacements.
408: */
409: public static final String sub(String line, String oldString,
410: String newString) {
411: int i = 0;
412: if ((i = line.indexOf(oldString, i)) >= 0) {
413: char[] line2 = line.toCharArray();
414: char[] newString2 = newString.toCharArray();
415: int oLength = oldString.length();
416: StringBuffer buf = new StringBuffer(line2.length);
417: buf.append(line2, 0, i).append(newString2);
418: i += oLength;
419: int j = i;
420: while ((i = line.indexOf(oldString, i)) > 0) {
421: buf.append(line2, j, i - j).append(newString2);
422: i += oLength;
423: j = i;
424: }
425: buf.append(line2, j, line2.length - j);
426: return buf.toString();
427: }
428: return line;
429: }
430:
431: /**
432: * Returns the output of printStackTrace as a String.
433: *
434: * @param e A Throwable.
435: * @return A String.
436: */
437: public static final String stackTrace(Throwable e) {
438: String foo = null;
439: try {
440: // And show the Error Screen.
441: ByteArrayOutputStream ostr = new ByteArrayOutputStream();
442: e.printStackTrace(new PrintWriter(ostr, true));
443: foo = ostr.toString();
444: } catch (Exception f) {
445: // Do nothing.
446: }
447: return foo;
448: }
449:
450: /**
451: * Return a context-relative path, beginning with a "/", that represents
452: * the canonical version of the specified path after ".." and "." elements
453: * are resolved out. If the specified path attempts to go outside the
454: * boundaries of the current context (i.e. too many ".." path elements
455: * are present), return <code>null</code> instead.
456: *
457: * @param path Path to be normalized
458: * @return String normalized path
459: */
460: public static final String normalizePath(String path) {
461: // Normalize the slashes and add leading slash if necessary
462: String normalized = path;
463: if (normalized.indexOf('\\') >= 0) {
464: normalized = normalized.replace('\\', '/');
465: }
466:
467: if (!normalized.startsWith("/")) {
468: normalized = "/" + normalized;
469: }
470:
471: // Resolve occurrences of "//" in the normalized path
472: while (true) {
473: int index = normalized.indexOf("//");
474: if (index < 0)
475: break;
476: normalized = normalized.substring(0, index)
477: + normalized.substring(index + 1);
478: }
479:
480: // Resolve occurrences of "%20" in the normalized path
481: while (true) {
482: int index = normalized.indexOf("%20");
483: if (index < 0)
484: break;
485: normalized = normalized.substring(0, index) + " "
486: + normalized.substring(index + 3);
487: }
488:
489: // Resolve occurrences of "/./" in the normalized path
490: while (true) {
491: int index = normalized.indexOf("/./");
492: if (index < 0)
493: break;
494: normalized = normalized.substring(0, index)
495: + normalized.substring(index + 2);
496: }
497:
498: // Resolve occurrences of "/../" in the normalized path
499: while (true) {
500: int index = normalized.indexOf("/../");
501: if (index < 0)
502: break;
503: if (index == 0)
504: return (null); // Trying to go outside our context
505: int index2 = normalized.lastIndexOf('/', index - 1);
506: normalized = normalized.substring(0, index2)
507: + normalized.substring(index + 3);
508: }
509:
510: // Return the normalized path that we have completed
511: return (normalized);
512: }
513:
514: /**
515: * If state is true then return the trueString, else
516: * return the falseString.
517: *
518: * @param state
519: * @param trueString
520: * @param falseString
521: * @return Selected result.
522: */
523: public String select(boolean state, String trueString,
524: String falseString) {
525: if (state) {
526: return trueString;
527: } else {
528: return falseString;
529: }
530: }
531:
532: /**
533: * Check to see if all the string objects passed
534: * in are empty.
535: *
536: * @param list A list of {@link java.lang.String} objects.
537: * @return Whether all strings are empty.
538: */
539: public boolean allEmpty(List list) {
540: int size = list.size();
541:
542: for (int i = 0; i < size; i++) {
543: if (list.get(i) != null
544: && list.get(i).toString().length() > 0) {
545: return false;
546: }
547: }
548: return true;
549: }
550:
551: /**
552: * Trim all strings in a List. Changes the strings in the existing list.
553: * @param list
554: * @return List of trimmed strings.
555: */
556: public static List trimStrings(List list) {
557: if (list == null)
558: return null;
559:
560: int sz = list.size();
561: for (int i = 0; i < sz; i++)
562: list.set(i, nullTrim((String) list.get(i)));
563: return list;
564: }
565:
566: /**
567: * Trim the string, but pass a null through.
568: * @param s
569: * @return List of trimmed Strings.
570: */
571: public static String nullTrim(String s) {
572: if (s == null) {
573: return null;
574: } else {
575: return s.trim();
576: }
577: }
578: }
|