001    /*
002      Copyright (C) 2001-2003 Laurent Martelli <laurent@aopsys.com>
003    
004      This program is free software; you can redistribute it and/or modify
005      it under the terms of the GNU Lesser General Public License as
006      published by the Free Software Foundation; either version 2 of the
007      License, or (at your option) any later version.
008    
009      This program is distributed in the hope that it will be useful,
010      but WITHOUT ANY WARRANTY; without even the implied warranty of
011      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
012      GNU Lesser General Public License for more details.
013    
014      You should have received a copy of the GNU Lesser General Public License
015      along with this program; if not, write to the Free Software
016      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
017    
018    package org.objectweb.jac.util;
019    
020    import java.io.File;
021    import java.util.Collection;
022    import java.util.Iterator;
023    import java.util.List;
024    import java.util.Map;
025    import java.util.Vector;
026    
027    /**
028     * Various often used string functions
029     */
030    public class Strings
031    {
032        /**
033         * Replace slashed characters ("\t" -> '\t',"\r" -> '\t',
034         * "\n" -> '\n' ,"\f" -> '\f' , "\_" -> ' ') 
035         */
036        public static String unslashify (String str) {
037            StringBuffer ret = new StringBuffer(str.length());
038            int i=0;
039            while(i<str.length()) {
040                char c = str.charAt(i++);
041                if (c == '\\') {
042                    c = str.charAt(i++);
043                    switch (c) {
044                        case 't': c='\t'; break;
045                        case 'r': c='\r'; break;
046                        case 'n': c='\n'; break;
047                        case 'f': c='\f'; break;
048                        case '_': c=' '; break;
049                    }
050                } 
051                ret.append(c);
052            }
053            return ret.toString();
054        }
055    
056        /**
057         * The reverse of unslashify. slashify(unslashify(str)).equals(str).
058         */ 
059        public static String slashify(String str) {
060            StringBuffer ret = new StringBuffer((int)(str.length()*1.2));
061            for(int i=0; i<str.length(); i++) {
062                char c = str.charAt(i);
063                switch (c) {
064                    case '\\': ret.append("\\\\"); break;
065                    case '\t': ret.append("\\t"); break;
066                    case '\r': ret.append("\\r"); break;
067                    case '\n': ret.append("\\n"); break;
068                    case '\f': ret.append("\\f"); break;
069                    case ' ': ret.append("\\_"); break;
070                    default: ret.append(c); break;
071                }
072            }
073            return ret.toString();
074        }
075    
076        /**
077         * Split a string into an array
078         * @param source string to split
079         * @param separator the separator
080         * @return an array of strings
081         * @see #splitToList(String,String)
082         */
083        public static String[] split(String source, String separator) {
084            return (String[])splitToList(source,separator).toArray(ExtArrays.emptyStringArray);
085        }
086    
087        /**
088         * Split a string into a list of strings
089         * @param source string to split
090         * @param separator the separator
091         * @return a list of strings
092         * @see #split(String,String)
093         */
094        public static List splitToList(String source, String separator) {
095            Vector tmp = new Vector();
096            int startIndex =0;
097            int index = 0;
098            while ((index = source.indexOf(separator,startIndex))!=-1) {
099                tmp.add(source.substring(startIndex,index));
100                startIndex = index+separator.length();
101            }
102            if (source.length()>0 && startIndex<source.length())
103                tmp.add(source.substring(startIndex));
104            return tmp;
105        }
106    
107        /**
108         * Builds a string formed by the toString() of items from a
109         * collection separated by a separator string.
110         * @param items the collection. It must not contain null values
111         * @param separator the separator string
112         * @see #join(String[],String)
113         */
114        public static String join(Collection items, String separator) {
115            StringBuffer result = new StringBuffer();
116            Iterator it = items.iterator();
117            while (it.hasNext()) {
118                result.append(it.next().toString());
119                if (it.hasNext())
120                    result.append(separator);
121            }
122            return result.toString();
123        }
124    
125        /**
126         * Builds a string formed by the toString() of items from a
127         * collection separated by a separator string.
128         * @param items the collection. It may contain null values.
129         * @param separator the separator string
130         * @see #join(String[],String)
131         */
132        public static String safeJoin(Collection items, String separator) {
133            StringBuffer result = new StringBuffer();
134            Iterator it = items.iterator();
135            while (it.hasNext()) {
136                Object o = it.next();
137                result.append(o!=null?o.toString():"#NULL#");
138                if (it.hasNext())
139                    result.append(separator);
140            }
141            return result.toString();
142        }
143    
144        /**
145         * @see #join(Collection,String)
146         */
147        public static String join(String[] items, String separator) {
148            StringBuffer result = new StringBuffer();
149            for (int i=0; i<items.length; i++) {
150                if (i>0)
151                    result.append(separator);
152                result.append(items[i]);
153            }
154            return result.toString();
155        }
156    
157        /**
158         * Split a list of paths separated by path.separator
159         *
160         * @return an array of path
161         */
162        public static String[] splitPath(String paths) {
163            return Strings.split(paths,System.getProperty("path.separator"));
164        }
165    
166        /**
167         * Create a path string, using the appropriate path separator
168         * @param paths a collection of File
169         * @return the filenames of paths, separated by the appropriate path separator
170         */
171        public static String createPathString(Collection paths) {
172            String separator = System.getProperty("path.separator");
173            String pathString = null;
174            Iterator it = paths.iterator();
175            while (it.hasNext()) {
176                File path = (File)it.next();
177                if (pathString==null)
178                    pathString = path.toString();
179                else
180                    pathString += separator+path.toString();
181            }
182            return pathString;
183        }
184    
185        /**
186         * Build a String representation of an object of the form
187         * <classname>@<hashcode> 
188         * @param o the object to stringify
189         * @return a String representation of the object
190         */
191        public static String hex(Object o) {
192            return o==null?"null":o.getClass().getName()+"@"+Integer.toHexString(o.hashCode());
193        }
194    
195        public static String hash(Object o) {
196            return o==null?"null":"@"+Integer.toHexString(o.hashCode());
197        }
198       
199        /**
200         * Build a String representation of a vector in the way as
201         * Vector.toString(), but without brackets.  
202         * @param list the vector to stringify
203         * @return a String representation of the vector
204         */
205        public static String toString(Collection list) {
206            StringBuffer buffer = new StringBuffer();
207            buffer.append("[");
208            
209            Iterator i = list.iterator();
210            while (i.hasNext()) {
211                Object item = i.next();
212                buffer.append(item==null ? "null" : item.toString());
213                if (i.hasNext())
214                    buffer.append(", ");
215            }
216    
217            buffer.append("]");
218            return buffer.toString();
219        }
220    
221        public static String toString(Map map) {
222            StringBuffer buffer = new StringBuffer();
223            buffer.append("{");
224            
225            Iterator i = map.entrySet().iterator();
226            while (i.hasNext()) {
227                Map.Entry entry = (Map.Entry)i.next();
228                buffer.append(String.valueOf(entry.getKey()));
229                buffer.append("=");
230                buffer.append(String.valueOf(entry.getValue()));
231                if (i.hasNext())
232                    buffer.append(", ");
233            }
234    
235            buffer.append("}");
236            return buffer.toString();
237        }
238    
239        /**
240         * Build a string with a given length and all the characters
241         * equals.
242         * @param c the character to fill the string with
243         * @param length the length of the string
244         * @return a string with the required length where
245         * string.charAt(i)==c for all i between 0 and lenght-1.
246         */
247        public static String newString(char c, int length) {
248            char[] array = new char[length];
249            for (length--;length>=0;length--) {
250                array[length] = c;
251            }
252            return new String(array);
253        }
254    
255        /**
256         * A useful method that replaces all the occurences of a string.
257         *
258         * @param orgString the original string
259         * @param oldString the string to replace (if found) in the
260         * original string
261         * @param newString the string that replaces all the occurences of
262         * old string
263         * @return a new string with the occurences replaced 
264         */
265        public static String replace(String orgString, 
266                                     String oldString, String newString) {
267    
268            int pos = 0;
269            boolean end = false;
270    
271            StringBuffer result = new StringBuffer(orgString.length()*2);
272    
273            int oldLen = oldString.length();
274            while (!end) {
275                int newpos = orgString.indexOf(oldString,pos);
276                if (newpos != -1) {
277                    result.append(orgString.substring(pos, newpos));
278                    result.append(newString);
279                } else {
280                    result.append(orgString.substring(pos));
281                    end = true;
282                }
283                pos = newpos + oldLen;
284            }
285            return result.toString();
286        }
287    
288        /**
289         * Replaces all occurences of some characters by a character
290         * @param oldChars the characters that should be replaced
291         * @param newChar the character by which to replace
292         * @param s the string buffer whose's characters must be replaced
293         */
294        public static void replace(String oldChars, char newChar, StringBuffer s) {
295            for (int i=0; i<s.length(); i++) {
296                if (oldChars.indexOf(s.charAt(i))!=-1)
297                    s.setCharAt(i,newChar);
298            }
299        }
300    
301        /**
302         * Replaces all occurences of some characters by a character
303         * @param oldChars the characters that should be replaced
304         * @param newChar the character by which to replace
305         * @param s the string whose's characters must be replaced
306         */
307        public static String replace(String oldChars, char newChar, String s) {
308            StringBuffer result = new StringBuffer(s);
309            replace(oldChars,newChar,result);
310            return result.toString();
311        }
312    
313        /**
314         * Delete occurences of characters from a StringBuffer
315         * @param delChars the characters to delete
316         * @param s the StringBuffer to remlove the characters from
317         */
318        public static void deleteChars(String delChars, StringBuffer s) {
319            int length = s.length();
320            int current = 0;
321            for (int i=0; i<length; i++) {
322                char c = s.charAt(i);
323                if (delChars.indexOf(c)==-1) {
324                    s.setCharAt(current,c);
325                    current++;
326                }
327            }
328            s.setLength(current);
329        }
330    
331        public static String getShortClassName(Class cl) {
332            String type = cl.getName();
333            if (cl.isArray()) {
334                type = cl.getComponentType().getName();
335            }
336            type = type.substring(type.lastIndexOf( '.' )+1);
337            if (cl.isArray()) {
338                type = type + "[]";
339            }
340            return type;
341        }   
342    
343        public static String getShortClassName(String className) {
344            return className.substring(className.lastIndexOf( '.' )+1);
345        }
346    
347        public static String toUSAscii(String s) {
348            StringBuffer result = new StringBuffer(s);
349            toUSAscii(result);
350            return result.toString();
351        }
352    
353        /**
354         * Lowers all characters of a StringBuffer
355         */
356        public static void toLowerCase(StringBuffer s) {
357            for (int i=0; i<s.length(); i++) {
358                s.setCharAt(i,Character.toLowerCase(s.charAt(i)));
359            }
360        }
361    
362        /**
363         * Uppers all characters of a StringBuffer
364         */
365        public static void toUpperCase(StringBuffer s) {
366            for (int i=0; i<s.length(); i++) {
367                s.setCharAt(i,Character.toUpperCase(s.charAt(i)));
368            }
369        }
370    
371        /**
372         * Replace accented chars with their non-accented value. For
373         * instance, 'é' becomes 'e'.
374         * @param s string to convert
375         * @return converted string
376         */
377        public static void toUSAscii(StringBuffer s) {
378            for (int i=s.length()-1; i>=0; i--) {
379                switch (s.charAt(i)) {
380                    case 'é':
381                    case 'è':
382                    case 'ê':
383                    case 'ë':
384                        s.setCharAt(i, 'e');
385                        break;
386                    case 'É':
387                    case 'È':
388                    case 'Ê':
389                    case 'Ë':
390                        s.setCharAt(i, 'E');
391                        break;
392                    case 'ï':
393                    case 'î':
394                    case 'ì':
395                    case 'í':
396                        s.setCharAt(i, 'i');
397                        break;
398                    case 'Ã?':
399                    case 'ÃŽ':
400                    case 'Ì':
401                    case 'Ã?':
402                        s.setCharAt(i, 'I');
403                        break;
404                    case 'à':
405                    case 'â':
406                    case 'ä':
407                    case 'ã':
408                    case 'Ã¥':
409                        s.setCharAt(i, 'a');
410                        break;
411                    case 'À':
412                    case 'Â':
413                    case 'Ä':
414                    case 'Ã':
415                    case 'Ã…':
416                        s.setCharAt(i, 'A');
417                        break;
418                    case 'ù':
419                    case 'ú':
420                    case 'ü':
421                    case 'û':
422                        s.setCharAt(i, 'u');
423                        break;
424                    case 'Ù':
425                    case 'Ú':
426                    case 'Ü':
427                    case 'Û':
428                        s.setCharAt(i, 'U');
429                        break;
430                    case 'ö':
431                    case 'ô':
432                    case 'ó':
433                    case 'ò':
434                    case 'õ':
435                        s.setCharAt(i, 'o');
436                        break;
437                    case 'Ö':
438                    case 'Ô':
439                    case 'Ó':
440                    case 'Ã’':
441                    case 'Õ':
442                        s.setCharAt(i, 'O');
443                        break;
444                    case 'ç':
445                        s.setCharAt(i, 'c');
446                        break;
447                    case 'Ç':
448                        s.setCharAt(i, 'C');
449                        break;
450                    case 'ÿ':
451                    case 'ý':
452                        s.setCharAt(i, 'y');
453                        break;
454                    case 'Ã?':
455                        s.setCharAt(i, 'Y');
456                        break;
457                    case 'ñ':
458                        s.setCharAt(i, 'n');
459                        break;
460                    case 'Ñ':
461                        s.setCharAt(i, 'N');
462                        break;
463                    default:
464                }
465            }
466        }
467    
468        /**
469         * Compares the USAscii representation of two strings in a case insensitive manner. 
470         * @param a first string to compare
471         * @param b second string to compare
472         * @return true if a and b are equals
473         */
474        public static boolean equalsUSAsciiNoCase(String a, String b) {
475            if (a==null)
476                return b==null;
477            else if (b==null)
478                return a==null;
479            else 
480                return toUSAscii(a).toLowerCase().equals(toUSAscii(b).toLowerCase());
481        }
482    
483        /**
484         * Tells if a string is empty (is null, has a zero length, or
485         * contains only whitespaces)
486         * @param str string to test
487         * @return true if str is null, str.length()==0 or str.trim().length()==0
488         */
489        public static boolean isEmpty(String str) {
490            return str==null || str.length()==0 || str.trim().length()==0;
491        }
492    
493        /**
494         * Removes all whitespace and CR/LF characters at the beginning or
495         * at the end of a string.
496         * @param str the string to trim
497         */
498        public static String trimWSAndCRLF(String str) {
499            if (str==null || str.length()==0)
500                return str;
501    
502            // Trim at the beginning
503            int start = 0;
504            char c = str.charAt(start);
505            while (Character.isWhitespace(c) || c=='\n' || c=='\r') {
506                start++;
507                if (start>=str.length())
508                    break;
509                c = str.charAt(start);
510            }
511    
512            // Trim at the end
513            int end = str.length()-1;
514            c = str.charAt(end);
515            while (end>=start && (Character.isWhitespace(c) || c=='\n' || c=='\r')) {
516                end--;
517                c = str.charAt(end);
518            }
519            if (end<start)
520                return "";
521            else
522                return str.substring(start,end+1);
523        }
524    
525    
526        /**
527         * Convert a String to a string with onlu iso-8859-1
528         * characters. Non iso-8859-1 characters are encoded with \\u<char_value>
529         * @param s strng to encode
530         * @see #fromISO8859_1(String)
531         */
532        public static String toISO8599_1(String s) {
533            int len = s.length();
534            StringBuffer res = new StringBuffer(len*2);
535    
536            for(int i=0; i<len; i++) {
537                char c = s.charAt(i);
538                if (c=='\\') 
539                    res.append("\\\\");
540                else if ((c < 0x0020) || (c > 0x007e)) {
541                    switch (c) {
542                        case 'é': case 'è': case 'ê': case 'ë':
543                        case 'É': case 'È': case 'Ê': case 'Ë':
544                        case 'ï': case 'î': case 'ì': case 'í':
545                        case '�': case 'Î': case 'Ì': case '�':
546                        case 'à': case 'â': case 'ä': case 'ã': case 'å':
547                        case 'À': case 'Â': case 'Ä': case 'Ã': case 'Å':
548                        case 'ù': case 'ú': case 'ü': case 'û':
549                        case 'Ù': case 'Ú': case 'Ü': case 'Û':
550                        case 'ö': case 'ô': case 'ó': case 'ò': case 'õ':
551                        case 'Ö': case 'Ô': case 'Ó': case 'Ò': case 'Õ':
552                        case 'ç': case 'Ç':
553                        case 'ÿ': case 'ý': case '�':
554                        case 'ñ': case 'Ñ':
555                        case '\n': case '\t': case '\r':
556                            res.append(c);
557                            break;
558                        default:
559                            res.append("\\u");
560                            res.append(hexDigit[(c >> 12) & 0xF]);
561                            res.append(hexDigit[(c >> 8) & 0xF]);
562                            res.append(hexDigit[(c >> 4) & 0xF]);
563                            res.append(hexDigit[c & 0xF]);
564                    }
565                } else {
566                    res.append(c);
567                }
568            }
569            return res.toString();
570        }
571    
572        /**
573         * Convert a String to a string with onlu iso-8859-1
574         * characters. Non iso-8859-1 characters are encoded with \\u<char_value>
575         * @param s string to decode
576         * @see #fromISO8859_1(String)
577         */
578        public static String fromISO8859_1(String s) {
579            char c;
580            int len = s.length();
581            StringBuffer res = new StringBuffer(len);
582    
583            int i=0;
584            while (i<len) {
585                c = s.charAt(i++);
586                if (c == '\\') {
587                    c = s.charAt(i++);
588                    if (c == 'u') {
589                        // Read the xxxx
590                        int value=0;
591                        for (int j=0; j<4; j++) {
592                            c = s.charAt(i++);
593                            switch (c) {
594                                case '0': case '1': case '2': case '3': case '4':
595                                case '5': case '6': case '7': case '8': case '9':
596                                    value = (value << 4) + c - '0';
597                                    break;
598                                case 'a': case 'b': case 'c':
599                                case 'd': case 'e': case 'f':
600                                    value = (value << 4) + 10 + c - 'a';
601                                    break;
602                                case 'A': case 'B': case 'C':
603                                case 'D': case 'E': case 'F':
604                                    value = (value << 4) + 10 + c - 'A';
605                                    break;
606                                default:
607                                    throw new IllegalArgumentException(
608                                        "Malformed \\uxxxx encoding.");
609                            }
610                        }
611                        res.append((char)value);
612                    } else {
613                        res.append(c);
614                    }
615                } else {
616                    res.append(c);
617                }
618            }
619            return res.toString();
620        }
621    
622        private static final char[] hexDigit = {
623            '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'
624        };
625    
626    }