001    // Copyright (c) 2001 Hursh Jain (http://www.mollypages.org) 
002    // The Molly framework is freely distributable under the terms of an
003    // MIT-style license. For details, see the molly pages web site at:
004    // http://www.mollypages.org/. Use, modify, have fun !
005    
006    package fc.util;
007    
008    import java.util.*;
009    
010    /** 
011    Utility functions related to java.lang.String, i.e 
012    functions that are useful but not provided by that class.
013    
014    @author hursh jain
015    */
016    public final class StringUtil
017    {
018    /** 
019    Returns an empty string is the specified argument was null,
020    otherwise returns the argument itself. 
021    **/
022    public static String nullToEmpty(String val) 
023      {
024      if (val == null) {
025        return "";
026        }
027      return val;
028      }
029    
030    /** 
031    Returns an empty string is the specified argument was null,
032    otherwise returns the value of the toString() method invoked
033    on the specified object. 
034    **/
035    public static String nullToEmpty(Object val) 
036      {
037      if (val == null) {
038        return "";
039        }
040      return val.toString();
041      }
042    
043    /** 
044    Returns a String containing a string of the specified character concatenated
045    the specified number of times. Returns an empty string if length
046    is less than/equal to zero
047    
048    @param c    the character to be repeated
049    @param length the repeat length
050    */
051    public static String repeat(char c, int length)
052      {
053      StringBuffer b = new StringBuffer();
054      for (int n = 0; n < length; n++) {
055        b.append(c);
056        }
057      return b.toString();
058      }
059    
060    /** 
061    Returns a String containing the specified string concatenated
062    the specified number of times.
063    
064    @param str    the string to be repeated
065    @param length the repeat length
066    */
067    public static String repeat(String str, int length)
068      {
069      int len = str.length();
070      StringBuffer b = new StringBuffer(len*length);
071      for (int n = 0; n < length; n++) {
072        b.append(str);
073        }
074      return b.toString();
075      }
076    
077    /** 
078    Contatenates the given string so that the maximum length reached
079    is the specified length. If the specified string does not fit in
080    the length, then the string is truncated appropriately.
081    
082    @param  str   the string to repeat 
083    @param  length  the length of the returned string
084    **/
085    public static String repeatToWidth(String str, int length)
086      {
087      Argcheck.notnull(str, "specified string was null");
088        
089      int strlen = str.length();  
090      
091      //strlen.range:     [0,inf)
092      //repeat length range:  (-inf, inf)
093      
094      if (strlen == length)   
095        return str;
096    
097      if (strlen > length)
098        return str.substring(0, length);
099    
100    
101      //strlen.range now    [1, inf)  
102    
103      int multiple   = length / strlen; 
104      int fractional = length % strlen;
105    
106      String result = repeat(str, multiple);
107    
108      if (fractional != 0) { 
109        result = result.concat(str.substring(0, fractional));
110        }
111        
112      return result;
113      }
114    
115    
116    /** 
117    Converts the specified String into a fixed width string,
118    left padding (with a blank space) or truncating on the right as necessary. 
119    (i.e., the specified string is aligned left w.r.t to the specified width).
120    
121    @param  str   the target string
122    @param  width the fixed width
123    
124    @return the transformed string
125    **/
126    public static String fixedWidth(String str, int width) 
127      {   
128      return fixedWidth(str, width, HAlign.LEFT, ' ');
129      }
130      
131    /** 
132    Calls {@link fixedWidth(String, int, HAlign, char)} specifying
133    the padding character as a blank space.
134    **/
135    public static String fixedWidth(String str, int width, HAlign align) 
136      {
137      return fixedWidth(str, width, align, ' ');
138      }
139      
140    
141    /** 
142    Converts the specified String into a fixed width string. Strings
143    lesser in size than the fixed width are padded or truncated as
144    required by the specified alignment.
145    
146    @param  str     the target string
147    @param  width   the fixed width
148    @param  align     the alignment of the target string within the width
149    @param  paddingChar the character to pad the string (if necessary);
150    
151    @return the transformed string
152    **/
153    public static String fixedWidth(
154     String str, int width, HAlign align, char paddingChar) 
155      {
156      if (str == null)
157        str = "";
158        
159      int len = str.length();
160    
161      if (len < width) 
162        {
163        if (align == HAlign.LEFT) {
164          str = str + repeat(paddingChar, width-len);
165          }
166        else if (align == HAlign.RIGHT) {
167          str = repeat(paddingChar, width-len) + str;
168          }
169        else if (align == HAlign.CENTER) {
170          //arbitrary tie-break, if the diff is odd then the
171          //1 extra space is padded on the right side
172      
173          int diff = (width-len);   
174          String temp = repeat(paddingChar, diff/2);
175          str = temp + str + temp + repeat(paddingChar, diff%2);
176          }
177        else throw new IllegalArgumentException("Do not understand the specified alignment: " + align);
178        }
179    
180    
181      else if (len > width) {
182        str = str.substring(0, width);
183        }
184        
185      return str; 
186      }
187    
188    /**
189    Converts the specified String into a string with maxlen characters, using the specified
190    ellipsis as the suffix to denote the missing characters.
191    
192    @param  str     the target string
193    @param  ellipsis  the ellipsis suffix
194    @param  width   the max length, <b>including</b> the ellipsis
195    
196    @return the transformed string
197    */
198    public static String ellipsis(String str, String ellipsis, int maxlen)
199      {
200      if (str.length() > maxlen)
201        {
202          str = str.substring(0, maxlen - ellipsis.length()) + ellipsis;
203        }
204      
205      return str;
206      }
207    
208    /**
209    Converts the specified String into a string with maxlen characters, using <code>...<code>
210    as the suffix to denote the missing characters.
211    
212    @param  str     the target string
213    @param  width   the max length, <b>including</b> the ellipsis ("...")
214    
215    @return the transformed string
216    */
217    public static String ellipsis(String str, int maxlen)
218      {
219      return ellipsis(str, "...", maxlen);
220      }
221    
222    
223    /** 
224    Removes all occurences of specified characters from the specified 
225    string and returns the new resulting string.
226    @param  target  the string to remove characters from
227    @param  chars an array of characters, each of which is to be removed
228    */
229    public static String remove(String target, char[] chars)
230      {
231      if (target == null || chars == null) return target;
232      int strlen = target.length();
233      char[] newstr = new char[strlen];
234      char[] oldstr = new char[strlen];
235      target.getChars(0,strlen,oldstr,0);
236      int replacelen = chars.length;
237      
238      int oldindex = -1;
239      int newindex = -1;
240      char c = 0;
241      boolean found = false;
242    
243      while (++oldindex < strlen) 
244        {
245        c = oldstr[oldindex];
246        found = false;
247        for (int j = 0; j < replacelen; j++) 
248          {
249          if (c == chars[j]) {
250            found = true;
251            break;
252            }
253          } //~for
254        if (!found) 
255          {
256          newstr[++newindex] = c;
257          }
258        }   //~while
259      return new String(newstr, 0, newindex+1);
260      }     //~remove()
261    
262    
263    /**
264    Removes the last forward or backward slash from the specified
265    string (if any) and returns the resulting String. Returns <tt>null</tt>
266    if a null argument is specified. The trailing slash is a slash that
267    is the last character of the specified string.
268    **/
269    public static String removeTrailingSlash(String str)
270      {
271      String res = str;
272      if (str == null) return null; 
273      int len = str.length();
274      if (len == 0)
275        return str;  //str was ""
276      char c = str.charAt(len-1);
277      if (c == '/' || c == '\\') {
278        res = str.substring(0, len-1);  //len-1 will be >= 0
279        }
280      return res;     
281      }
282    
283    /**
284    Removes the starting (at the very beginning of the string) forward or 
285    backward slash from the specified string (if any) and returns the 
286    resulting String. Returns <tt>null</tt> if  null argument is specified.
287    **/
288    public static String removeBeginningSlash(String str)
289      {
290      if (str == null) return null; 
291      int len = str.length();
292      if (len == 0) 
293        return str;  //str was ""
294      char c = str.charAt(0);
295      if (c == '/' || c == '\\') 
296        {
297        if (len > 1) { 
298          return str.substring(1);
299          }
300        else { //str was "/"
301          return "";  
302          }
303        }
304      return str;
305      }
306    
307    /**
308    Removes the any file extension (.foo for example) from the specified name
309    and returns the resulting String. Returns <tt>null</tt> if the specified
310    path was null. This method takes a String as a parameter, as opposed to a
311    <tt>java.io.File</tt> because the caller knows best what portion of the
312    filename should be modified and returned by this method (full path name,
313    name without path information etc).
314    
315    @param  name the String denoting the file name to remove the extension from
316    **/
317    public static String removeSuffix(String name)
318      {
319      String result = null;
320      if (name == null)
321        return result;
322      result = name.replaceFirst("(.+)(\\..+)","$1");
323      //System.out.println("File " + name + " after removing extension =" + name);  
324      return result;
325      }
326    
327    /** 
328    Returns the path component of the specified filename. If no path exists
329    or the specified string is null, returns the empty string <tt>""</tt>.
330    <u>The path separator in the specified string should be <tt>/</tt></u>.
331    If on a platform where this is not the case, the specified string should
332    be modified to contain "/" before invoking this method. The returned
333    pathName <b>does</b>
334    contain the trailing "/". This ensures that 
335    <tt>dirName(somepath) + fileName(somepath)</tt> will always be equal to <tt>somepath</tt>.
336    <p>
337    <blockquote>
338    <pre>
339    The functionality of this method is different than java.io.File.getName()
340    and getParent(). Also unix dirname, basename are also compared below.
341    
342    //Using java.io.File (getPath() returns the entire name, identical to
343    //the input, so is not shown. Sample run on windows:
344    Name=''         ; getName()='';       getParent()='null'
345    Name='/'        ; getName()='';       getParent()='null'
346    Name='/a'       ; getName()='a';      getParent()='\'
347    Name='a/b'      ; getName()='b';      getParent()='a'
348    Name='a/b.txt'  ; getName()='b.txt';  getParent()='a'
349    Name='b.txt'    ; getName()='b.txt';  getParent()='null'
350    
351    Name='/a/'      ; getName()='a';      getParent()='\'
352    Name='/a/b/'    ; getName()='b';      getParent()='\a'
353    Name='a/b/'     ; getName()='b';      getParent()='a'
354    ----------------------------
355    //Using these methods:
356    Name=''         ; fileName()='';      dirName()=''
357    Name='/'        ; fileName()='';      dirName()='/' 
358    Name='/a'       ; fileName()='a';     dirName()='/' 
359    Name='a/b'      ; fileName()='b';     dirName()='a/' 
360    Name='a/b.txt'  ; fileName()='b.txt'; dirName()='a/' 
361    Name='b.txt'    ; fileName()='b.txt'; dirName()='' 
362    
363    Name='/a/'      ; fileName()='';      dirName()='/a/'
364    Name='/a/b/'    ; fileName()='';      dirName()='/a/b/'
365    Name='a/b/'     ; fileName()='';      dirName()='a/b/'
366    -----------------------------
367    //unix basename, dirname
368    Name=''         ; basename()='';    dirname()=''
369    Name='/'        ; basename()='/';   dirname()='/' 
370    Name='/a'       ; basename()='a';     dirname()='/' 
371    Name='a/b'      ; basename()='b';     dirname()='a/' 
372    Name='a/b.txt'  ; basename()='b.txt'; dirname()='a/' 
373    Name='b.txt'    ; basename()='b.txt'; dirname()='.' 
374    
375    Name='/a/'      ; basename()='a';     dirname()='/'
376    Name='a/b/'     ; basename()='b';     dirname()='a'
377    Name='/a/b/'  ; fileName()='b';     dirName()='a'
378    
379    -----------------------------
380    </pre>
381    Note, the main differences among the 3 approaches above are in the last 
382    2 statements in each section.
383    </blockquote>
384    **/
385    public static String dirName(String str) 
386      {
387      String res = "";
388      if (str == null)
389        return res;
390      int pos = str.lastIndexOf("/");
391      if (pos == -1)
392        return "";
393      return str.substring(0, (pos+1));
394      }
395    
396    /** 
397    Returns the file component of specified filename. If no filename exists
398    or the specified string is null, returns the empty string <tt>""</tt>.
399    <u>The path separator in the specified string is always assumed to be
400    <tt>/</tt></u>. If on a platform where this is not the case, the
401    specified string should be modified to contain "/" before invoking this
402    method. The returned file name does <b>not</b> contain the preceding "/".
403    **/
404    public static String fileName(String str) 
405      {
406      String res = "";
407      if (str == null)
408        return res;
409      int pos = str.lastIndexOf("/");
410      if (pos == -1)
411        return str;
412      
413      int strlen = str.length();
414      if (strlen == 1)
415        return res; //we return "" since the string has to be "/" 
416      else
417        pos++;    //skip "/" in other strings
418        
419      return str.substring(pos, strlen); //will return "" if str = "/"
420      }
421    
422    
423    /** 
424    Splits the string using the specified delimiters and
425    returns the splits parts in a List. Use {@link
426    java.lang.String#split} instead for greater options.
427    
428    @param  str   the string to be tokenized
429    @param  delim delimiter string, each character in the string will be used 
430            as a delimiter to use while tokenizing
431    */
432    public static List split(String str, String delim)
433      {   
434      int pos = 0;    //current position pointer in the string (str)
435      List result = new ArrayList();  
436      StringTokenizer st = new StringTokenizer(str,delim);
437        while (st.hasMoreTokens()) {
438                //tokens are stored as lowercase
439            result.add(st.nextToken().toLowerCase());  
440            }
441      return result;  
442      }
443    
444    /** 
445    Joins the elements of the specified list, delimited by
446    the specified delimiter.
447    
448    @param  list  containing the elements to be joined.
449    @param  delim delimits each element from the next
450    */
451    public static String join(List list, String delim)
452      { 
453      Argcheck.notnull(list, "specified list param was null");
454      Argcheck.notnull(delim, "specified delim param was null");
455    
456      int size = list.size();
457      int size_minus_one = size -1 ;
458      StringBuffer buf = new StringBuffer(size * 16);
459      
460      for (int n = 0; n < size; n++) {
461        buf.append(list.get(n).toString());
462        if ( n < (size_minus_one)) {
463          buf.append(delim);
464          }
465        }
466        
467      return buf.toString();  
468      } 
469    
470    
471    /* TO DO: LATER
472    Removes all whitespace in the specified string and returns all words 
473    with only a single space between them. Uses a Perl regular expression 
474    to do this.
475      public synchronized static void makeSingleSpaced(String target)
476      throws Exception
477      {
478      String regex1 = "s\\?([^\"]+)\\s*(target)?[^>]*>([^>]*)</a>";  
479      MatchResult result;
480      Perl5Pattern pattern =  (Perl5Pattern)StringUtil.compiler.compile(
481                    regex1,
482                    Perl5Compiler.CASE_INSENSITIVE_MASK | 
483                    Perl5Compiler.SINGLELINE_MASK); 
484      
485      }
486    */
487    
488    /**
489    Converts the specified String to start with a capital letter. Only
490    the first character is made uppercase, the rest of the specified
491    string is not affected.
492    **/
493    public static String capitalWord(String str) 
494      {
495      int strlen = str.length();
496      StringBuffer buf = new StringBuffer(strlen);
497      buf.append( str.substring(0,1).toUpperCase() + 
498            str.substring(1, strlen) ); 
499      return buf.toString(); 
500      }
501    
502    /**
503    Converts the specified String to be in sentence case, whereby
504    the first letter of each word in the sentence is uppercased
505    and all other letters are lowercased. The characters in the
506    delimiter string are used to delimit words in the sentence.
507    If the delimiter string is <tt>null</tt>, the original string
508    is returned as-is.
509    <br>
510    A runtime exception will be thrown if the specified string
511    was <tt>null</tt>.
512    **/
513    public static String sentenceCase(String str, String delimiters)
514      {
515      Argcheck.notnull(str, "specified string was null");
516      if (delimiters == null) 
517        return str;
518        
519      int strlen = str.length();
520      StringBuffer out = new StringBuffer(strlen);
521      StringBuffer temp = new StringBuffer(strlen);
522      for (int n = 0; n < strlen; n++)
523        {
524        //System.out.print("["+n+"] ");
525        char current_char = str.charAt(n);
526        if (delimiters.indexOf(current_char) >= 0) {
527          //System.out.println("->"+current_char);
528          if (temp.length() > 0 ) {
529            out.append( temp.substring(0, 1).toUpperCase() );
530            out.append( temp.substring(1, temp.length()).toLowerCase() );
531            }
532          out.append(current_char);
533          //System.out.println("temp="+temp);
534          temp = new StringBuffer(strlen);
535          continue;
536          }
537        temp.append(current_char);  
538        }
539      if (temp.length() > 0 ) {
540        out.append( temp.substring(0, 1).toUpperCase() );
541        out.append( temp.substring(1, temp.length()).toLowerCase() );
542        }
543      return out.toString();
544      }
545      
546    static final String[] VIEW_ASCII = {
547    /*0*/ "NUL", "[ascii(1)]", "[ascii(2)]", "[ascii(3)]", "[ascii(4)]",
548    /*5*/ "[ascii(5)]", "[ascii(6)]", "\\a", "\\b", "\\t",
549    /*10*/  "\\n", "\\v", "[ascii(12)]", "\\r", "[ascii(14)]",
550    /*15*/  "[ascii(15)]", "[ascii(16)]", "[ascii(17)", "[ascii(18)]", "[ascii(19)]",
551    /*20*/  "[ascii(20)]", "[ascii(21)]", "[ascii(22)]", "ascii(23)]", "[ascii(24)]",
552    /*25*/  "[ascii(25)]", "[ascii(26)]", "\\e", "[ascii(28)]", "[ascii(29)]",
553    /*30*/  "[ascii(30)]", "[ascii(31)]" 
554    };  
555      
556    /**
557    Converts non printable ascii characters in the specified String
558    to escaped or readable equivalents. For example, a newline is
559    converted to the sequence <tt>\n</tt> and say, ascii character 29 is 
560    converted to the sequence of chars: '<tt>ascii(29)</tt>'. 
561    <p>
562    If the specified String is <tt>null</tt>, this method returns 
563    <tt>null</tt>.
564    
565    @param  str   the String to convert
566    @return the converted String
567    **/     
568    public static String viewableAscii(String str) 
569      {
570      if (str == null)
571        return null;
572      
573      int strlen = str.length();
574      StringBuffer buf = new StringBuffer(strlen);
575      
576      //ignore all non ascii data, including UTF-16 surrogate bytes etc.
577      //by replacing such data with '?'   
578      for(int n = 0; n < strlen; n++) 
579        {
580        char c = str.charAt(n);
581        if ( c < 32) 
582          buf.append(VIEW_ASCII[c]);
583        else if ( c > 255) 
584          buf.append('?');
585        else
586          buf.append(c);
587        } 
588      return buf.toString();
589      } 
590    
591    /**
592    A version of {@link viewableAscii(String)} that takes a
593    single char as a parameter.
594    
595    @param  char the char to convert
596    @return the ascii readable char
597    **/
598    public static String viewableAscii(char c) 
599      {
600      if ( c < 32) 
601        return VIEW_ASCII[c];
602      else if ( c > 255) 
603        return "?";
604      else
605        return new String(new char[] {c});
606      }
607    
608    /**
609    Converts a character array into a viewable comma delimited
610    string. Non-printable/readable characters are shown as
611    readable equivalents.
612    */
613    public static String arrayToString(char[] array) {
614      if (array == null) {
615        return "null";
616        }
617      int arraylen = array.length;
618      StringBuffer buf = new StringBuffer(arraylen * 2);
619      buf.append("[");
620      int n = 0;
621      while (n < arraylen) {
622        buf.append(viewableAscii(array[n]));
623        n++;
624        if (n != arraylen)
625          buf.append(", ");
626        }
627      buf.append("]");
628      return buf.toString();
629      }
630    
631    
632    /**
633    Converts a list into a string, each item being seperated by the specified delimiter.
634    Each item in the list is converted by invokign <code>toString</code> on that item
635    so the specified delimiter only applies to the outermost level. 
636    <p>
637    The converted string does not start or end with any characters. To specify the start/end, use {@link listToString(List, String, String, String)} 
638    */
639    public static String listToString(List list, String delim) 
640      {
641      return listToString(list, delim, "", "");
642      }
643    
644    /**
645    Converts a list into a string, each item being seperated by the specified delimiter.
646    Each item in the list is converted by invokign <code>toString</code> on that item
647    so the specified delimiter only applies to the outermost level. 
648    <p>
649    The converted string start and ends with the specified chars as well. 
650    */
651    public static String listToString(List list, String delim, String start, String end) 
652      {
653      StringBuilder buf = new StringBuilder();
654      buf.append(start);  
655        
656      if (list != null) 
657        {
658        final int size = list.size();
659        for (int n = 0; n < size; n++) 
660          {
661          buf.append(list.get(n));
662          if (n + 1 < size) {
663            buf.append(delim);
664            }
665          }
666        }
667        
668      buf.append(end);
669      
670      return buf.toString();
671      }
672    
673    /**
674    Escapes all single quotes in the specified a string with a backslash
675    character. 
676    */
677    public static String escapeSingleQuotes(final String str)
678      {
679      return escapeSingleQuotes(str, "\\");
680      }
681    
682    /**
683    Escapes all single quotes in the specified a string with the specified
684    escape character. So, if the specified escape character is <tt>'</tt>, 
685    all occurrences of <tt>o'tis the ele'phant</tt> becomes 
686    <tt>o''tis the ele''phant</tt>
687    */
688    public static String escapeSingleQuotes(final String str, String escape)
689      {
690      if (str == null)
691        return null;
692      
693      final int len = str.length();
694      if (len == 0)
695        return str;
696        
697      final StringBuilder buf = new StringBuilder(len * 2);
698      for (int n = 0; n < len; n++) 
699        {
700        char c = str.charAt(n);
701        if (c == '\'') {
702          buf.append(escape);
703          buf.append('\'');
704          }
705        else{
706          buf.append(c);
707          }
708        }
709      return buf.toString();
710      }
711      
712    /**
713    Escapes all double quotes in the specified a string with a backslash
714    character. 
715    */
716    public static String escapeDoubleQuotes(final String str)
717      {
718      return escapeSingleQuotes(str, "\\");
719      }
720    
721    /**
722    Escapes all double quotes in the specified a string with the specified
723    escape character. So, if the specified escape character is <tt>\</tt>, 
724    all occurrences of <tt>o"tis the ele"phant</tt> becomes 
725    <tt>o\"tis the ele\"phant</tt>
726    */
727    public static String escapeDoubleQuotes(final String str, String escape)
728      {
729      if (str == null)
730        return null;
731      
732      final int len = str.length();
733      if (len == 0)
734        return str;
735        
736      final StringBuilder buf = new StringBuilder(len * 2);
737      for (int n = 0; n < len; n++) 
738        {
739        char c = str.charAt(n);
740        if (c == '"') {
741          buf.append(escape);
742          buf.append('"');
743          }
744        else{
745          buf.append(c);
746          }
747        }
748      return buf.toString();
749      } 
750    
751    //unit test
752    public static void main(String[] args)
753    {
754    String teststr = "hahaha, my name is ha";
755    char[] remove = new char[] {'h','m'};
756    System.out.println("testing StringUtil.remove(\"" + teststr + "\",'" + String.valueOf(remove) + "')");
757    String newstr = StringUtil.remove(teststr,remove);
758    System.out.println("result>>" + newstr + "<<");
759    System.out.println("Original String length: " + teststr.length() +" ; New String length: " + newstr.length());
760    System.out.println(":" + repeat(' ',20) + ":");
761    
762    System.out.println("sentenceCase(\"hello world\", \" \"): ["
763              + sentenceCase("hello world", " ") + "]");
764    System.out.println("sentenceCase(\"helloworld\", \" \"): ["
765              + sentenceCase("helloworld", " ") + "]");
766    System.out.println("sentenceCase(\"  hello world\", \" \"): ["
767              + sentenceCase("  hello world", " ") + "]");
768    System.out.println("sentenceCase(\"hello world  \", \" \"): ["
769              + sentenceCase("hello world  ", " ") + "]");
770    System.out.println("sentenceCase(\"hello_world  \", \"_\"): ["
771              + sentenceCase("hello_world  ", "_") + "]");
772    System.out.println("sentenceCase(\"__hello_world_ foo  \", \"_ \"): ["
773              + sentenceCase("__hello_world_ foo  ", "_ ") + "]");
774    System.out.println("sentenceCase(\"foXbAr\", \"X\"): ["
775              + sentenceCase("foXbAr", "X") + "]");
776          
777    
778    System.out.println("viewableAscii(abc[newline]foo[tab]\\u4234)="+viewableAscii("abc\nfoo\t\u4234"));
779    for(char c = 0; c < 255; c++) {
780      System.out.print(viewableAscii(c));   
781      }
782    System.out.println("");
783    
784    System.out.println("remove trailing slash on '' = " + removeTrailingSlash(""));
785    System.out.println("remove trailing slash on '/' = " + removeTrailingSlash(""));
786    System.out.println("remove trailing slash on 'foo/' = " + removeTrailingSlash("foo/"));
787    System.out.println("remove beginning slash on '' = " + removeBeginningSlash(""));
788    System.out.println("remove beginning slash on '/' = " + removeBeginningSlash("/"));
789    System.out.println("remove beginning slash on '/foo' = " + removeBeginningSlash("/foo"));
790    
791    System.out.println("====fixed width tests");
792    String fixedin = "hello";
793    int width = 15;
794    System.out.println("fixed width input string: " + fixedin);
795    System.out.println("fixed width = 15");
796    System.out.println("align default: [" + fixedWidth(fixedin, width) + "]");
797    System.out.println("align left: [" + fixedWidth(fixedin, width, HAlign.LEFT) + "]");
798    System.out.println("align right : [" + fixedWidth(fixedin, width, HAlign.RIGHT) + "]");
799    System.out.println("align center: [" + fixedWidth(fixedin, width, HAlign.CENTER) + "]");
800    
801    System.out.println("repeatToWidth('hello', 0)="+repeatToWidth("hello", 0));
802    System.out.println("repeatToWidth('hello', 1)="+repeatToWidth("hello", 1));
803    System.out.println("repeatToWidth('hello', 5)="+repeatToWidth("hello", 5));
804    System.out.println("repeatToWidth('hello', 9)="+repeatToWidth("hello", 9));
805    System.out.println("repeatToWidth('hello', 10)="+repeatToWidth("hello", 10));
806    System.out.println("repeatToWidth('hello', 11)="+repeatToWidth("hello", 11));
807    
808    System.out.println("repeatToWidth('X', 0)=["+repeatToWidth("X", 0) +"]");
809    
810    System.out.println("escapeSingleQuotes(\"'\")="+escapeSingleQuotes("'"));
811    System.out.println("escapeSingleQuotes(\"\")="+escapeSingleQuotes(""));
812    System.out.println("escapeSingleQuotes(\"'foo'bar'\")="+escapeSingleQuotes("'foo'bar'"));
813    System.out.println("escapeSingleQuotes(\"'''\")="+escapeSingleQuotes("'''"));
814    System.out.println("escapeSingleQuotes(\"'foo'bar'\", \"'\")="+escapeSingleQuotes("'foo'bar'", "'"));
815    
816    
817    System.out.println("listToString(null):" + listToString(null, "x"));
818    List list = new ArrayList();
819    list.add(1);
820    list.add(2);
821    System.out.println("listToString([1,2] / [ ]): " +listToString(list,"/","[","]"));
822    
823    System.out.println("ellipsis('hello world', 8): " + ellipsis("hello world", 8));
824    }
825    
826    }     //~class StringUtil