001 package com.saelist.util;
002
003 import org.apache.log4j.*;
004 import java.io.*;
005 import java.util.*;
006
007
008 /**
009 * Various text oriented utilites
010 *
011 * @author Kristmundsson
012 *
013 * @created 4. September 2002
014 */
015 public class Strings {
016
017 /** A logger for this class. */
018
019 public static Logger logger = Logger.getLogger("com.saelist.util.Strings");
020
021 /**
022 * Returns the stacktrace of the given exception as a string.
023 *
024 * @param e the exception to trace.
025 *
026 * @return the stacktrace of the exception as a string.
027 */
028 public static String asStackTrace(Throwable e) {
029 StringWriter writer = new StringWriter();
030 e.printStackTrace(new PrintWriter(writer));
031
032 return writer.toString();
033 }
034
035 /**
036 * Tests if the text contains any of the characters in chars .
037 *
038 * @param text the string to search.
039 * @param chars the chars to find.
040 *
041 * @return weather text conains any of the characters.
042 */
043 public static boolean containsAnyOf(String text, String chars) {
044 for(int i = 0; i < chars.length(); i++)
045 if(text.indexOf((char) chars.charAt(i)) >= 0)
046 return true;
047
048 return false;
049 }
050
051 /**
052 * Limits the size of text to maxSize charaters and appends the suffix.
053 *
054 * @param text the text to constrain.
055 * @param maxSize the desired maximum length of text.
056 * @param suffix will be appended to the result if it is actaully cut down.
057 *
058 * @return The cut-down string with the suffix appended if it exceeded
059 * length. otherwise the original text.
060 *
061 * @throws NullPointerException is text is null.
062 */
063 public static String cutDown(String text, int maxSize, String suffix) {
064 if(text!=null) {
065 if(text.length() > maxSize) {
066 if(suffix!=null)
067 return text.substring(0, maxSize) + suffix;
068 }
069 }
070 return text;
071 }
072
073 /**
074 * A conveniance interface to {@link #format(String, String[])} for a single
075 * argument.
076 */
077 public static String format(String text, String arg) {
078 return format(text, new String[] { arg });
079 }
080
081 /**
082 * A conveniance interface to {@link #format(String, String[])} for 2
083 * arguments.
084 */
085 public static String format(String text, String arg1, String arg2) {
086 return format(text, new String[] { arg1, arg2 });
087 }
088
089 /**
090 * A conveniance interface to {@link #format(String, String[])} for 3
091 * arguments.
092 */
093 public static String format(String text, String arg1, String arg2, String arg3) {
094 return format(text, new String[] { arg1, arg2, arg3 });
095 }
096
097 /**
098 * A conveniance interface to {@link #format(String, String[])} for 4
099 * arguments.
100 */
101 public static String format(String text, String arg1, String arg2, String arg3, String arg4) {
102 return format(text, new String[] { arg1, arg2, arg3, arg4 });
103 }
104
105 /**
106 * Replaces each occurence of "%" in the text with the eachth element of
107 * args. The process stops when it runs out of either.
108 *
109 * @param text the string to format.
110 * @param args the replacements for "%" in text.
111 *
112 * @return the formatted string
113 */
114 public static String format(String text, String[] args) {
115 int pos0 = 0;
116 int pos1 = text.indexOf("%");
117 StringBuffer result = new StringBuffer();
118
119 for(int i = 0; (i < args.length) && (pos1 >= 0); i++) {
120 result.append(text.substring(pos0, pos1)).append(args[i]);
121 pos0 = pos1 + 1;
122 pos1 = text.indexOf("%", pos0);
123 }
124
125 result.append(text.substring(pos0));
126
127 // System.out.println(text + " -> " + result.toString());
128 return result.toString();
129 }
130
131 /**
132 * Indents a string by n characters. Inserts n spaces at the front of the
133 * string and after all occurences of newline. Un-optimized.
134 *
135 * @param text the string to indent
136 * @param n the number of spaces to indent with.
137 *
138 * @return the indented string.
139 */
140 public static String indent(String text, int n) {
141 String spaces = repeat(" ", n);
142
143 return spaces + replace(text, "\n", "\n" + spaces);
144 }
145
146 /**
147 * Primitive variable substitution for strings. For any key x in the
148 * substitutions map the corresponding value will replace any subtrings of
149 * the form {x} int the text.
150 *
151 * @param text the string to be changed
152 * @param substitutions maps patterns to replacements.
153 *
154 * @return the changed string.
155 */
156 public static String interpret(String text, Map substitutions) {
157 int left = 0;
158 int right = 0;
159
160 if(text != null)
161 do {
162 left = text.indexOf('{', right);
163
164 if(left >= 0) {
165 right = text.indexOf('}', left + 1);
166
167 if(right >= 0)
168 text = text.substring(0, left) +
169 substitutions.get(text.substring(left + 1, right)) +
170 text.substring(Math.min(text.length(), right + 1));
171 }
172 } while((left > 0) && (right > 0));
173
174 return text;
175 }
176
177 /**
178 * Joins a List [a, b, c] into a string "a|b|c". Nulls are shown as empty
179 * strings ("a||c").
180 *
181 * @param list the list to join. Must not be null.
182 * @param separator will be inserted between the joined elements. If
183 * separator is null the string "null" will be inserted. To insert
184 * nothing provide the empty string.
185 *
186 * @return the joined string.
187 *
188 * @throws NullPointerException if list is null.
189 */
190 public static String join(List list, String separator) {
191 StringBuffer result = new StringBuffer();
192
193 for(int i = 0; i < (list.size() - 1); i++)
194 result.append(toNonNullString(list.get(i))).append(separator);
195
196 if(list.size() > 0)
197 result.append(toNonNullString(list.get(list.size() - 1)));
198
199 return result.toString();
200 }
201
202 /**
203 * See {@link #join(List, String)}.
204 */
205 public static String join(Object[] objects, String delimiter) {
206 return join(Arrays.asList(objects), delimiter);
207 }
208
209 /**
210 * Load the contents of a file as a string.
211 *
212 * @param filename the name of the file to load.
213 *
214 * @return the contents of the file as a string.
215 *
216 * @throws IOException if the file indicated cannot be accessed.
217 */
218 public static String loadFile(String filename) throws IOException {
219 return loadStream(new FileInputStream(new File(filename)));
220 }
221
222 public static void saveFile(String filename, String content) throws IOException {
223 logger.info("saveFile(): filename=" + filename);
224 OutputStream out = new FileOutputStream(filename);
225 out.write(content.getBytes());
226 out.flush();
227 out.close();
228 }
229
230 /**
231 * Loads the content of reader into a String.
232 *
233 * @param reader The reader to empty;
234 *
235 * @return the read string;
236 *
237 * @throws IOException if the file indicated cannot be accessed.
238 */
239 public static String loadReader(Reader reader) throws IOException {
240 BufferedReader bufferedReader;
241
242 if(!(reader instanceof BufferedReader))
243 bufferedReader = new BufferedReader(reader);
244 else
245 bufferedReader = (BufferedReader) reader;
246
247 StringBuffer buffer = new StringBuffer();
248 int i;
249
250 while((i = bufferedReader.read()) >= 0)
251 buffer.append((char) i);
252
253 return buffer.toString();
254 }
255
256 /**
257 * Load a resource (file) on the classpath as a string.
258 *
259 * @param name the fully qualified name of the file to load. E.g.
260 * /com/acme/stardust.xml, the name only if the file is resides in
261 * the same package as clazz.
262 * @param clazz the class in whose package the resource will be sought.
263 *
264 * @return the loaded resource as a string.
265 *
266 * @throws Exception if the resource is not found.
267 */
268 public static String loadResource(String name, Class clazz)
269 throws Exception {
270 java.io.InputStream in = clazz.getResourceAsStream(name);
271 verify(in != null, "Error loading resource " + name);
272
273 return loadStream(in);
274 }
275
276 /**
277 * Load a resource (file) on the classpath as a string.
278 *
279 * @param name the fully qualified name of the file to load. E.g.
280 * /com/acme/stardust.xml (or the resource must reside in the same
281 * package as this class.
282 *
283 * @return the loaded resource as a string.
284 *
285 * @throws Exception If the resource is not found or cannot be accessed.
286 */
287 public static String loadResource(String name) throws Exception {
288 return loadResource(name, String.class);
289 }
290
291 /**
292 * Load all content from the given stream as string.
293 *
294 * @param in the stream to load from.
295 *
296 * @return the content of the stream.
297 *
298 * @throws IOException thrown if the Stream throws it.
299 */
300 public static String loadStream(InputStream in) throws IOException {
301 BufferedReader reader = new BufferedReader(new InputStreamReader(in));
302 StringBuffer buffer = new StringBuffer();
303 int i;
304
305 while((i = reader.read()) >= 0)
306 buffer.append((char) i);
307
308 return buffer.toString();
309 }
310
311 /**
312 * Repeat the given string n times. precondition: n >= 0; Un-optimized.
313 *
314 * @param s the string to repeat
315 * @param n the number of times to repeat
316 *
317 * @return the repeated string.
318 */
319 public static String repeat(String s, int n) {
320 if(n <= 0)
321 return "";
322
323 return s + repeat(s, n - 1);
324 }
325
326 /**
327 * Substitutes all occurances of a pattern in a text replacementwith
328 * replacement.
329 *
330 * @param text the string to be changed
331 * @param pattern the substring of text to be replaced
332 * @param replacement the string to replace pattern in text.
333 *
334 * @return the affected string
335 */
336 public static String replace(String text, String pattern, String replacement) {
337 return replace(text, pattern, replacement, Integer.MAX_VALUE);
338 }
339
340 /**
341 * Substitutes the first count occurances of a pattern in a text with
342 * replacement. Note that the empty string matches anything so the
343 * replacement will be repeated count times
344 *
345 * @param text the string to be changed
346 * @param pattern the substring of text to be replaced must be a nonempty
347 * string.
348 * @param replacement the string to replace pattern in text. Must not be
349 * null.
350 * @param count how many of the first instances of pattern to replace.
351 *
352 * @return the resulting string
353 */
354 public static String replace(String text, String pattern, String replacement,
355 int count) {
356 // System.out.println(text + "\t" + pattern + "\t" + replacement);
357 int pos = text.indexOf(pattern);
358
359 if((pos < 0) || (count == 0))
360 return text;
361
362 return text.substring(0, pos) + replacement +
363 replace(text.substring(pos + pattern.length()), pattern, replacement,
364 count - 1);
365 }
366
367 /**
368 * Splits a string <b>"a|b|c"</b> into list with elements <b>["a", "b", "c"]
369 * </b>. Empty strings on either side of the separator are correctly
370 * identified and added to the list. An empty string without a separator
371 * returns an empty list as does null
372 *
373 * @param text the entities to put in the list separated by the separator.
374 * @param separator separates the entities to put in the list.
375 *
376 * @return A list. (never null).
377 */
378 public static List split(String text, String separator) {
379 List result = new ArrayList();
380
381 if((text == null) || text.equals(""))
382 return result;
383
384 StringTokenizer t = new StringTokenizer(text, separator, true);
385 String lastToken = separator;
386 String token;
387
388 while(t.hasMoreTokens()) {
389 token = t.nextToken();
390
391 if(separator.equals(token)) {
392 if(separator.equals(lastToken))
393 result.add("");
394 } else
395 result.add(token);
396
397 lastToken = token;
398 }
399
400 if(separator.equals(lastToken))
401 result.add("");
402
403 return result;
404 }
405
406 /**
407 * Tolerant substring. Text can be null and stop can be beyound the end and
408 * start can be beyond stop. Start can be negative.
409 *
410 * @param text the string to take the substring from. Can be null .
411 * @param start the position of the first character of the substring in text.
412 * Can be less than stop, in which case it is set to the value of
413 * stop .
414 * @param stop the position after the last character of the substring in
415 * text. Can be beyond the length of text, in which case it is set to
416 * the length text.
417 *
418 * @return the substring if any, otherwise "".
419 */
420 public static String substring(String text, int start, int stop) {
421 if(text == null)
422 return null;
423
424 if(start < 0)
425 start = 0;
426
427 if(stop > text.length())
428 stop = text.length();
429
430 if(start > stop)
431 start = stop;
432
433 return text.substring(start, stop);
434 }
435
436 /**
437 * Tolerant substring. Text can be null. Start can be negative.
438 *
439 * @param text the string to take the substring from. Can be null .
440 * @param start the position of the first character of the substring in text.
441 * Can be negative, in which case it is set to null. Can be beyond
442 * the end of the string in which case it is set to the length of the
443 * string.
444 *
445 * @return the part of text from start to the end of the string. Null if text
446 * is null.
447 */
448 public static String substring(String text, int start) {
449 if(text == null)
450 return null;
451
452 return substring(text, start, text.length());
453 }
454
455 /**
456 * En{@link java.util.List list}s an {@link java.util.Enumeration
457 * enumeration}.
458 *
459 * @param e the enumeration to enlist.
460 *
461 * @return the enlisted enumeration.
462 */
463 public static List toList(Enumeration e) {
464 List list = new ArrayList();
465
466 while(e.hasMoreElements())
467 list.add(e.nextElement());
468
469 return list;
470 }
471
472 /**
473 * Converts String array to a string with delimiter. Ignores elements that
474 * are null, "" or "-1".
475 *
476 * @param values to be concatenated.
477 * @param separator inserted between values in the returned string.
478 *
479 * @return the concatenated values if any or null otherwise.
480 */
481 public static String arrayToString(String[] values, char separator) {
482 StringBuffer buffer = new StringBuffer();
483
484 for(int i = 0; i < values.length; i++)
485 if((values[i] != "") && (values[i] != null) &&
486 !values[i].equalsIgnoreCase("-1"))
487 buffer.append(values[i] + separator);
488
489 if(buffer.length() == 0)
490 return null;
491 else
492
493 //Take off the last delimiter and turn it into a String
494 return buffer.substring(0, buffer.length() - 1);
495 }
496
497 /**
498 * Returns the empty string if value is null or "null".
499 *
500 * @param value to be converted.
501 *
502 * @return the possibly converted string.
503 */
504 public static String removeNull(String value) {
505 if((value == null) || value.equalsIgnoreCase("null"))
506 return "";
507
508 return value;
509 }
510
511 /**
512 * Converts null strings to "", all others objects to their {@link
513 * java.lang.Object#toString()}.
514 *
515 * @param object the object to stringify.
516 *
517 * @return Returns an empty string if the given string is null otherwise
518 * returns the givent string.
519 */
520 public static String toNonNullString(Object object) {
521 if(object == null)
522 return "";
523
524 return object.toString();
525 }
526
527 /**
528 * Converts a {@link java.util.List} of Objects into a array of strings.
529 *
530 * @param list the list to turn into an array. Null values become "null".
531 * Other values become their toString() results.
532 *
533 * @return the array of strings.
534 */
535 public static String[] toStringArray(List list) {
536 String[] result = new String[list.size()];
537
538 for(int i = 0; i < list.size(); i++)
539 result[i] = "" + list.get(i);
540
541 return result;
542 }
543
544 /**
545 * Converts a {@link java.util.List} into a array of strings.
546 *
547 * @param list the list to turn into an array. Null values become "null".
548 * Other values become their toString() results.
549 *
550 * @return the array of strings.
551 */
552 /* public static String[] toStringArray(List list, String elementname) {
553 //Fill products and accessories
554 String[] result = new String[list.size()];
555 Element element;
556
557 for(int i = 0; i < list.size(); i++) {
558 element = (Element) list.get(i);
559 result[i] = element.getChildText(elementname);
560 }
561
562 return result;
563 } */
564
565 /**
566 * Splits the text on the delimiters and returns an array of the strings. If
567 * returndelimiters is true, then the delimiters are are also returned as
568 * tokens.
569 *
570 * <p>
571 * Note: Since it uses {@link java.util.StringTokenizer}, empty strings are
572 * simply skipped and do not appear in the list. Use {@link #split split}
573 * for more consistent behaviour.
574 * </p>
575 *
576 * @param text the text to split
577 * @param delimiters on which to split
578 * @param returndelimiters true to get the delimiters also.
579 *
580 * @return an array of the split strings.
581 */
582 public static String[] tokenize(String text, String delimiters,
583 boolean returndelimiters) {
584 List list = new Vector();
585 StringTokenizer tokenizer = new StringTokenizer(text, delimiters,
586 returndelimiters);
587
588 while(tokenizer.hasMoreTokens())
589 list.add(tokenizer.nextToken());
590
591 return (String[]) list.toArray(new String[list.size()]);
592 }
593
594 /**
595 * Throws an exception with the given message if the condition is true.
596 *
597 * @param condition the condition to test.
598 * @param message The message to give the exception.
599 *
600 * @throws Exception if condition is true.
601 */
602 public static void verify(boolean condition, String message)
603 throws Exception {
604 if(!condition)
605 throw new Exception(message);
606 }
607
608 /**
609 * Indicates wether the pattern matches the text. The only wildcard allowed
610 * is the asterisk.
611 *
612 * @param text the text to check for a match.
613 * @param pattern the pattern to match to text. An asterisk stands for zero
614 * or more characters.
615 *
616 * @return true if the pattern matches the text .
617 */
618 public static boolean matches(String text, String pattern) {
619 StringTokenizer t = new StringTokenizer(pattern, "*", true);
620 List list = new ArrayList();
621
622 while(t.hasMoreTokens())
623 list.add(t.nextToken());
624
625 //System.out.println(list);
626 return matches(text, 0, (String[]) list.toArray(new String[0]), 0);
627 }
628
629 /**
630 * Implements what matches(text, pattern) promises.
631 *
632 * @param text the string to match to the pattern.
633 * @param pos how far into the text we are.
634 * @param tokens the tokenized pattern.
635 * @param tpos the tokenized pattern we are.
636 *
637 * @return true if the token list (from tpos) matched the text (from pos).
638 */
639 private static boolean matches(String text, int pos, String[] tokens, int tpos) {
640 // System.out.println("pos=" + pos + ", tops=" + tpos);
641 if(tpos < tokens.length) {
642 String token = tokens[tpos];
643
644 if(token.equals("*")) {
645 for(int i = 0; (pos + i) <= text.length(); i++)
646 if(matches(text, pos + i, tokens, tpos + 1))
647 return true;
648
649 return false;
650 } else {
651 if(pos == text.indexOf(token))
652 return matches(text, pos + token.length(), tokens, tpos + 1);
653
654 return false;
655 }
656 } else
657
658 return pos == text.length();
659 }
660
661 /**
662 * Returns the stack-trace of a {@link java.lang.Throwable} as String.
663 *
664 * @param t the {@link java.lang.Throwable} to trace:
665 *
666 * @return a string showing the stack when the exception occured.
667 */
668 public static String getStackTrace(Throwable t) {
669 StringWriter stringWriter = new StringWriter();
670 PrintWriter printWriter = new PrintWriter(stringWriter);
671 t.printStackTrace(printWriter);
672 printWriter.flush();
673
674 return stringWriter.toString();
675 }
676
677 /**
678 * Returns the part of text before sep. If sep is not part of text the whole
679 * text is returned.
680 *
681 * @param text The text to get the front off.
682 * @param sep separating the front from the rest.
683 *
684 * @return the part of text in front of sep.
685 */
686 public static String front(String text, String sep) {
687 int pos = text.indexOf(sep);
688
689 if(pos >= 0) {
690 return text.substring(0, pos);
691 }
692
693 return text;
694 }
695
696 /**
697 * Returns the part of text before the last sep. If sep is not part of text
698 * the whole text is returned.
699 *
700 * @param text The text to get the front off.
701 * @param sep separating the front from the rest.
702 *
703 * @return the part of text in front of sep.
704 */
705 public static String lastFront(String text, String sep) {
706 int pos = text.lastIndexOf(sep);
707
708 if(pos >= 0) {
709 return text.substring(0, pos);
710 }
711
712 return text;
713 }
714
715 /**
716 * Returns the rest of text after first sep. If sep is not part of test "" is
717 * returned.
718 *
719 * @param text The text to get the rest off.
720 * @param sep separating the rest from the front.
721 *
722 * @return the part of text after sep.
723 */
724 public static String rest(String text, String sep) {
725 int pos = text.indexOf(sep);
726
727 if((pos >= 0) && (pos < text.length())) {
728 return text.substring(pos + 1);
729 }
730
731 return "";
732 }
733
734 /**
735 * Returns the rest of text after last sep. If sep is not part of test "" is
736 * returned.
737 *
738 * @param text The text to get the rest off.
739 * @param sep separating the rest from the front.
740 *
741 * @return the part of text after sep.
742 */
743 public static String lastRest(String text, String sep) {
744 int pos = text.lastIndexOf(sep);
745
746 if((pos >= 0) && (pos < text.length())) {
747 return text.substring(pos + 1);
748 }
749
750 return "";
751 }
752
753 /**
754 * A convenience method to make an empty list.
755 */
756 public static List makeList() {
757 return new ArrayList(0);
758 }
759
760 /**
761 * A convenience method to make a one element list.
762 */
763 public static List makeList(Object arg1) {
764 List list = new ArrayList(1);
765 list.add(arg1);
766
767 return list;
768 }
769
770 /**
771 * A convenience method to make a two element list.
772 */
773 public static List makeList(Object arg1, Object arg2) {
774 List list = new ArrayList(2);
775 list.add(arg1);
776 list.add(arg2);
777
778 return list;
779 }
780
781 /**
782 * A convenience method to make a three element list.
783 */
784 public static List makeList(Object arg1, Object arg2, Object arg3) {
785 List list = new ArrayList(3);
786 list.add(arg1);
787 list.add(arg2);
788 list.add(arg3);
789
790 return list;
791 }
792
793 /**
794 * A convenience method to make a four element list.
795 */
796 public static List makeList(Object arg1, Object arg2, Object arg3, Object arg4) {
797 List list = new ArrayList(4);
798 list.add(arg1);
799 list.add(arg2);
800 list.add(arg3);
801 list.add(arg4);
802
803 return list;
804 }
805
806 /**
807 * A convenience method to enlist an array.
808 */
809 public static List makeList(Object[] args) {
810 List list = new ArrayList(args.length);
811
812 for(int i = 0; i < args.length; i++)
813 list.add(args[i]);
814
815 return list;
816 }
817
818 /**
819 * A convenience method to make a one entry map.
820 */
821 public static Map makeMap(Object key1, Object value1) {
822 Map result = new HashMap();
823 result.put(key1, value1);
824
825 return result;
826 }
827
828 /**
829 * A convenience method to make a two entry map.
830 */
831 public static Map makeMap(Object key1, Object value1, Object key2,
832 Object value2) {
833 Map result = new HashMap();
834 result.put(key1, value1);
835 result.put(key2, value2);
836
837 return result;
838 }
839
840 /**
841 * A convenience method to make a three entry map.
842 */
843 public static Map makeMap(Object key1, Object value1, Object key2,
844 Object value2, Object key3, Object value3) {
845 Map result = new HashMap();
846 result.put(key1, value1);
847 result.put(key2, value2);
848 result.put(key3, value3);
849
850 return result;
851 }
852
853 /**
854 * A convenience method to make a four entry map.
855 */
856 public static Map makeMap(Object key1, Object value1, Object key2,
857 Object value2, Object key3, Object value3, Object key4, Object value4) {
858 Map result = new HashMap();
859 result.put(key1, value1);
860 result.put(key2, value2);
861 result.put(key3, value3);
862 result.put(key4, value4);
863
864 return result;
865 }
866
867 /**
868 * @return a new list with each element of the given list wrapped between the
869 * prefix and postfix strings.
870 */
871 public static List wrap(String prefix, List list, String suffix) {
872 List result = new ArrayList(list.size());
873
874 for(Iterator it = list.iterator(); it.hasNext();)
875 result.add(prefix + it.next() + suffix);
876
877 return result;
878 }
879 }
880