001    package railo.commons.lang;
002    
003    import java.io.UnsupportedEncodingException;
004    import java.util.HashMap;
005    import java.util.Iterator;
006    import java.util.Map;
007    
008    import railo.commons.io.SystemUtil;
009    import railo.runtime.exp.PageException;
010    import railo.runtime.op.Caster;
011    import railo.runtime.type.Collection;
012    import railo.runtime.type.util.ArrayUtil;
013    
014    
015    
016    
017    
018    /**
019     * Util to do some additional String Operations
020     */
021    public final class StringUtil {
022        
023            /**
024             * do first Letter Upper case
025             * @param str String to operate
026             * @return uppercase string
027             */
028            public static String ucFirst(String str) {
029                    if(str==null) return null;
030                    else if(str.length()<=1) return str.toUpperCase();
031                    else {
032                            return str.substring(0,1).toUpperCase()+str.substring(1);
033                    }
034            }
035    
036    
037        public static String capitalize( String input, char[] delims ) {
038    
039            if (isEmpty(input)) return input;
040    
041            if (ArrayUtil.isEmpty(delims))
042                delims = new char[]{ '.', '-', '(', ')' };
043    
044            StringBuilder sb = new StringBuilder( input.length() );
045    
046            boolean isLastDelim = true,isLastSpace = true;
047            int len=input.length();
048            for (int i=0; i<len; i++) {
049    
050                char c = input.charAt( i );
051    
052                if ( Character.isWhitespace(c) ) {
053    
054                    if ( !isLastSpace )
055                        sb.append( ' ' );
056    
057                    isLastSpace = true;
058                } 
059                else {
060    
061                    sb.append( ( isLastSpace || isLastDelim ) ? Character.toUpperCase( c ) : c );
062    
063                    isLastDelim = _contains(delims, c );
064                    isLastSpace = false;
065                }
066            }
067    
068            return sb.toString();
069        }
070    
071            
072            private static boolean _contains(char[] chars, char c) {
073                    for ( int i=0; i<chars.length; i++ ) {
074                            if(chars[i]==c) return true;
075                    }
076                    return false;
077            }
078    
079    
080            /**
081             * do first Letter Upper case
082             * @param str String to operate
083             * @return lower case String
084             */
085            public static String lcFirst(String str) {
086                    if(str==null) return null;
087                    else if(str.length()<=1) return str.toLowerCase();
088                    else {
089                            return str.substring(0,1).toLowerCase()+str.substring(1);
090                    }
091            }
092            
093            /**
094             * Unescapes HTML Tags
095             * @param html html code  to escape
096             * @return escaped html code
097             */
098            public static String unescapeHTML(String html) {
099                    return HTMLEntities.unescapeHTML(html);
100            }
101            
102            /**
103             * Escapes XML Tags
104             * @param html html code to unescape
105             * @return unescaped html code
106             */
107            public static String escapeHTML(String html) {
108                    return HTMLEntities.escapeHTML(html);
109            }
110            
111            /**
112             * escapes JS sensitive characters
113             * @param str String to escape
114             * @return escapes String
115             */
116            public static String escapeJS(String str) {
117                    char[] arr=str.toCharArray();
118                    StringBuilder rtn=new StringBuilder(arr.length);
119                    for(int i=0;i<arr.length;i++) {
120                            switch(arr[i]) {
121                                    case '\\': rtn.append("\\\\"); break;
122                                    case '\n': rtn.append("\\n"); break;
123                                    case '\r': rtn.append("\\r"); break;
124                                    case '\f': rtn.append("\\f"); break;
125                                    case '\b': rtn.append("\\b"); break;
126                                    case '\t': rtn.append("\\t"); break;
127                                    case '"' : rtn.append("\\\""); break;
128                                    case '\'': rtn.append("\\\'"); break;
129                                    default : rtn.append(arr[i]); break;
130                            }
131                    }
132                    return rtn.toString();
133            }
134    
135            /**
136             * reapeats a string
137             * @param str string to repeat
138             * @param count how many time string will be reapeted
139             * @return reapted string
140             */
141        public static String repeatString(String str,int count) {
142            if(count<=0) return "";
143            char[] chars = str.toCharArray();
144            char[] rtn=new char[chars.length*count];
145            int pos=0;
146            for(int i=0;i<count;i++) {
147                for(int y=0;y<chars.length;y++)rtn[pos++]=chars[y];
148                //rtn.append(str);
149            }
150            return new String(rtn);
151        }
152    
153            /**
154             * translate, like method toString, a object to a string, but when value is null value will be translated to a empty String (""). 
155             * @param o Object to convert
156             * @return converted String
157             */
158            public static String toStringEmptyIfNull(Object o) {
159                    if(o==null)return "";
160                    return o.toString();
161            }
162            
163            public static String emptyIfNull(String str) {
164                    if(str==null)return "";
165                    return str;
166            }
167            
168            public static String emptyIfNull(Collection.Key key) {
169                    if(key==null)return "";
170                    return key.getString();
171            }
172            
173            /**
174             * escape all special characters of the regular expresson language
175             * @param str String to escape
176             * @return escaped String
177             */
178            public static String reqExpEscape(String str) {
179                    char[] arr = str.toCharArray();
180                    StringBuilder sb=new StringBuilder(str.length()*2);
181                    
182                    for(int i=0;i<arr.length;i++) {
183                            sb.append('\\');
184                            sb.append(arr[i]);
185                    }
186                    
187                    return sb.toString();
188            }
189            
190            /**
191             * translate a string to a valid identity variable name
192             * @param varName variable name template to translate
193             * @return translated variable name
194             */
195            public static String toIdentityVariableName(String varName) {
196                    char[] chars=varName.toCharArray();
197                    long changes=0;
198    
199                    StringBuilder rtn=new StringBuilder(chars.length+2);
200                    rtn.append("CF");               
201                    
202                    for(int i=0;i<chars.length;i++) {
203                            char c=chars[i];
204                            if((c>='a' && c<='z') ||(c>='A' && c<='Z') ||(c>='0' && c<='9'))
205                                    rtn.append(c);
206                            else {  
207                                    rtn.append('_');
208                                    changes+=(c*(i+1));
209                            }
210                    }
211                    
212                    return rtn.append(changes).toString();
213            }
214            /**
215             * translate a string to a valid classname string
216             * @param str string to translate
217             * @return translated String
218             */
219            public static String toClassName(String str) {
220                    StringBuilder rtn=new StringBuilder();
221                    String[] arr=str.split("[\\\\|//]");
222                    for(int i=0;i<arr.length;i++) {
223                            if(arr[i].length()==0)continue;
224                            if(rtn.length()!=0)rtn.append('.');
225                            char[] chars=arr[i].toCharArray();
226                            long changes=0;
227                            for(int y=0;y<chars.length;y++) {
228                                    char c=chars[y];
229                                    if(y==0 && (c>='0' && c<='9'))rtn.append("_"+c);
230                                    else if((c>='a' && c<='z') ||(c>='A' && c<='Z') ||(c>='0' && c<='9'))
231                                            rtn.append(c);
232                                    else {  
233                                            rtn.append('_');
234                                            changes+=(c*(i+1));
235                                    }
236                            }
237                            if(changes>0)rtn.append(changes);
238                    }
239                    return rtn.toString();
240            }
241    
242            /**
243             * translate a string to a valid variable string
244             * @param str string to translate
245             * @return translated String
246             */
247    
248            public static String toVariableName(String str) {
249                    return toVariableName(str, true);
250            }
251            public static String toVariableName(String str, boolean addIdentityNumber) {
252                    
253                    StringBuilder rtn=new StringBuilder();
254                    char[] chars=str.toCharArray();
255                    long changes=0;
256                    boolean doCorrect=true;
257                    for(int i=0;i<chars.length;i++) {
258                            char c=chars[i];
259                            if(i==0 && (c>='0' && c<='9'))rtn.append("_"+c);
260                            else if((c>='a' && c<='z') ||(c>='A' && c<='Z') ||(c>='0' && c<='9') || c=='_' || c=='$')
261                                    rtn.append(c);
262                            else {  
263                                doCorrect=false;
264                                    rtn.append('_');
265                                    changes+=(c*(i+1));
266                            }
267                    }
268                    
269                    if(addIdentityNumber && changes>0)rtn.append(changes);
270                    //print.ln(" - "+rtn);
271                    
272                    if(doCorrect)return correctReservedWord(rtn.toString());
273                    return rtn.toString();
274            }
275            
276    
277            /**
278             * if given string is a keyword it will be replaced with none keyword
279             * @param str
280             * @return corrected word
281             */
282            private static String correctReservedWord(String str) {
283                    char first=str.charAt(0);
284                    
285                    switch(first) {
286                    case 'a':
287                            if(str.equals("abstract")) return "_"+str;
288                    break;
289                    case 'b':
290                            if(str.equals("boolean")) return "_"+str;
291                            else if(str.equals("break")) return "_"+str;
292                            else if(str.equals("byte")) return "_"+str;
293                    break;
294                    case 'c':
295                            if(str.equals("case")) return "_"+str;
296                            else if(str.equals("catch")) return "_"+str;
297                            else if(str.equals("char")) return "_"+str;
298                            else if(str.equals("const")) return "_"+str;
299                            else if(str.equals("class")) return "_"+str;
300                            else if(str.equals("continue")) return "_"+str;
301                    break;
302                    case 'd':
303                            if(str.equals("default")) return "_"+str;
304                            else if(str.equals("do")) return "_"+str;
305                            else if(str.equals("double")) return "_"+str;
306                    break;
307                    case 'e':
308                            if(str.equals("else")) return "_"+str;
309                            else if(str.equals("extends")) return "_"+str;
310                            else if(str.equals("enum")) return "_"+str;
311                    break;
312                    case 'f':
313                            if(str.equals("false")) return "_"+str;
314                            else if(str.equals("final")) return "_"+str;
315                            else if(str.equals("finally")) return "_"+str;
316                            else if(str.equals("float")) return "_"+str;
317                            else if(str.equals("for")) return "_"+str;
318                    break;
319                    case 'g':
320                            if(str.equals("goto")) return "_"+str;
321                    break;
322                    case 'i':
323                            if(str.equals("if")) return "_"+str;
324                            else if(str.equals("implements")) return "_"+str;
325                            else if(str.equals("import")) return "_"+str;
326                            else if(str.equals("instanceof")) return "_"+str;
327                            else if(str.equals("int")) return "_"+str;
328                            else if(str.equals("interface")) return "_"+str;
329                    break;
330                    case 'n':
331                            if(str.equals("native")) return "_"+str;
332                            else if(str.equals("new")) return "_"+str;
333                            else if(str.equals("null")) return "_"+str;
334                    break;
335                    case 'p':
336                            if(str.equals("package")) return "_"+str;
337                            else if(str.equals("private")) return "_"+str;
338                            else if(str.equals("protected")) return "_"+str;
339                            else if(str.equals("public")) return "_"+str;
340                    break;
341                    case 'r':
342                            if(str.equals("return")) return "_"+str;
343                    break;
344                    case 's':
345                            if(str.equals("short")) return "_"+str;
346                            else if(str.equals("static")) return "_"+str;
347                            else if(str.equals("strictfp")) return "_"+str;
348                            else if(str.equals("super")) return "_"+str;
349                            else if(str.equals("switch")) return "_"+str;
350                            else if(str.equals("synchronized")) return "_"+str;
351                    break;
352                    case 't':
353                            if(str.equals("this")) return "_"+str;
354                            else if(str.equals("throw")) return "_"+str;
355                            else if(str.equals("throws")) return "_"+str;
356                            else if(str.equals("transient")) return "_"+str;
357                            else if(str.equals("true")) return "_"+str;
358                            else if(str.equals("try")) return "_"+str;
359                    break;
360                    case 'v':
361                            if(str.equals("void")) return "_"+str;
362                            else if(str.equals("volatile")) return "_"+str;
363                    break;
364                    case 'w':
365                            if(str.equals("while")) return "_"+str;
366                    break;
367                    }
368                    return str;
369                    
370            }
371            
372            /** 
373         * This function returns a string with whitespace stripped from the beginning of str 
374         * @param str String to clean 
375         * @return cleaned String 
376         */ 
377        public static String ltrim(String str,String defaultValue) { 
378                    if(str==null) return defaultValue;
379                int len = str.length(); 
380                int st = 0; 
381    
382                while ((st < len) && (str.charAt(st) <= ' ')) { 
383                    st++; 
384                } 
385                return ((st > 0)) ? str.substring(st) : str; 
386        } 
387        
388        /** 
389         * This function returns a string with whitespace stripped from the end of str 
390         * @param str String to clean 
391         * @return cleaned String 
392         */ 
393        public static String rtrim(String str,String defaultValue) { 
394            if(str==null) return defaultValue;
395                int len = str.length(); 
396    
397                while ((0 < len) && (str.charAt(len-1) <= ' ')) { 
398                    len--; 
399                } 
400                return (len < str.length()) ? str.substring(0, len) : str; 
401        }   
402        
403    
404    
405        /**
406         * return if in a string are line feeds or not
407         * @param str string to check
408         * @return translated string
409         */
410        public static boolean hasLineFeed(String str) {
411            int len=str.length();
412            char c;
413            for(int i=0;i<len;i++) {
414                c=str.charAt(i);
415                if(c=='\n' || c=='\r') return true;
416            }
417            return false;
418        }
419    
420        /**
421         * remove all white spaces followd by whitespaces
422         * @param str strring to translate
423         * @return translated string
424         */
425        public static String suppressWhiteSpace(String str) {
426            int len=str.length();
427            StringBuilder sb=new StringBuilder(len);
428            //boolean wasWS=false;
429            
430            char c;
431            char buffer=0;
432            for(int i=0;i<len;i++) {
433                c=str.charAt(i);
434                if(c=='\n' || c=='\r')              buffer='\n';
435                else if(c==' ' || c=='\t')  {
436                    if(buffer==0)buffer=c;
437                }
438                else {
439                    if(buffer!=0){
440                            sb.append(buffer);
441                            buffer=0;
442                    }
443                    sb.append(c);
444                }
445                //sb.append(c);
446            }
447            if(buffer!=0)sb.append(buffer);
448            
449            return sb.toString();
450        }
451            
452    
453    
454        /**
455         * returns string, if given string is null or lengt 0 return default value
456         * @param value
457         * @param defaultValue
458         * @return value or default value
459         */
460        public static String toString(String value, String defaultValue) {
461            return value==null || value.length()==0?defaultValue:value;
462        }
463    
464        /**
465         * returns string, if given string is null or lengt 0 return default value
466         * @param value
467         * @param defaultValue
468         * @return value or default value
469         */
470        public static String toString(Object value, String defaultValue) {
471            if(value==null) return defaultValue;
472            return toString(value.toString(), defaultValue);
473        }
474    
475        /**
476         * cut string to max size if the string is greater, otherweise to nothing
477         * @param content
478         * @param max 
479         * @return cutted string
480         */
481        
482        public static String max(String content,int max) {
483            return max(content, max,"");
484        }
485        
486        public static String max(String content,int max, String dotDotDot) {
487            if(content==null) return null;
488            if(content.length()<=max) return content;
489            
490            return content.substring(0,max)+dotDotDot;
491        }
492        
493    
494        /**
495         * performs a replace operation on a string
496         *  
497         * @param input - the string input to work on 
498         * @param find - the substring to find
499         * @param repl - the substring to replace the matches with
500         * @param firstOnly - if true then only the first occurrence of {@code find} will be replaced
501         * @param ignoreCase - if true then matches will not be case sensitive
502         * @return
503         */
504            public static String replace( String input, String find, String repl, boolean firstOnly, boolean ignoreCase ) {
505            
506            String scan = input;        
507            int findLen = find.length();        
508            
509            if ( ignoreCase ) {
510                
511                scan = scan.toLowerCase();
512                find = find.toLowerCase();
513            } else if ( findLen == repl.length() ) {
514    
515                    if ( find.equals( repl ) )
516                            return input;
517                    
518                    if ( !firstOnly && findLen == 1 )
519                            return input.replace( find.charAt(0), repl.charAt(0) );
520            }
521            
522            StringBuilder sb = new StringBuilder( repl.length() > find.length() ? (int)Math.ceil( input.length() * 1.2 ) : input.length() );
523            
524            int start = 0;
525            int pos;        
526            
527            while ( (pos = scan.indexOf( find, start ) ) != -1 ) {
528                
529                sb.append( input.substring( start, pos ) );
530                sb.append( repl );
531                
532                start = pos + findLen;
533                
534                if ( firstOnly )
535                    break;
536            }
537                    
538            if ( input.length() > start )
539                    sb.append( input.substring( start ) );
540    
541            return sb.toString();
542        }
543        
544            
545            /**
546             * maintains the legacy signature of this method where matches are CaSe sensitive (sets the default of ignoreCase to false). 
547             * 
548             * @param input - the string input to work on 
549         * @param find - the substring to find
550         * @param repl - the substring to replace the matches with
551         * @param firstOnly - if true then only the first occurrence of {@code find} will be replaced
552         * @return - calls replace( input, find, repl, firstOnly, false )
553             */
554            public static String replace( String input, String find, String repl, boolean firstOnly ) {
555             
556                    return replace( input, find, repl, firstOnly, false );
557            }
558            
559    
560            /**
561             * performs a CaSe sensitive replace all
562             * 
563             * @param input - the string input to work on 
564         * @param find - the substring to find
565         * @param repl - the substring to replace the matches with
566         * @return - calls replace( input, find, repl, false, false )
567             */
568            public static String replace( String input, String find, String repl ) {
569                     
570                    return replace( input, find, repl, false, false );
571            }
572            
573        
574        /**
575         * adds zeros add the begin of a int example: addZeros(2,3) return "002"
576         * @param i number to add nulls
577         * @param size 
578         * @return min len of return value;
579         */
580        public static String addZeros(int i, int size) {
581            String rtn=Caster.toString(i);
582            if(rtn.length()<size) return repeatString("0",size-rtn.length())+rtn;
583            return rtn;
584        }
585    
586        
587        /**
588         * adds zeros add the begin of a int example: addZeros(2,3) return "002"
589         * @param i number to add nulls
590         * @param size 
591         * @return min len of return value;
592         */
593        public static String addZeros(long i, int size) {
594            String rtn=Caster.toString(i);
595            if(rtn.length()<size) return repeatString("0",size-rtn.length())+rtn;
596            return rtn;
597        }
598    
599            public static int indexOf(String haystack, String needle) {
600                    if(haystack==null) return -1;
601                    return haystack.indexOf(needle);
602            }
603            
604            public static int indexOfIgnoreCase(String haystack, String needle) {
605                    if(StringUtil.isEmpty(haystack) || StringUtil.isEmpty(needle)) return -1;
606                    needle=needle.toLowerCase();
607                    
608                    int lenHaystack=haystack.length();
609                    int lenNeedle=needle.length();
610                    
611                    char lastNeedle=needle.charAt(lenNeedle-1);
612                    char c;
613                    outer:for(int i=lenNeedle-1;i<lenHaystack;i++) {
614                            c=Character.toLowerCase(haystack.charAt(i));
615                            if(c==lastNeedle) {
616                                    for(int y=0;y<lenNeedle-1;y++) {
617                                            if(needle.charAt(y)!=Character.toLowerCase(haystack.charAt(i-(lenNeedle-1)+y)))
618                                                            continue outer;
619                                    }
620                                    return i-(lenNeedle-1);
621                            }
622                    }
623                    
624                    
625                    return -1;
626            }
627        
628        /**
629         * Tests if this string starts with the specified prefix.
630         * @param str string to check first char
631         * @param prefix the prefix.
632         * @return is first of given type
633         */
634        public static boolean startsWith(String str, char prefix) {
635            return str!=null && str.length()>0 && str.charAt(0)==prefix;
636        }
637        
638        /**
639         * Tests if this string ends with the specified suffix.
640         * @param str string to check first char
641         * @param suffix the suffix.
642         * @return is last of given type
643         */
644        public static boolean endsWith(String str, char suffix) {
645            return str!=null && str.length()>0 && str.charAt(str.length()-1)==suffix;
646        }
647    
648        /**
649         * Tests if this string ends with the specified suffix.
650         * @param str string to check first char
651         * @param suffix the suffix.
652         * @return is last of given type
653         */
654        /**
655         * Helper functions to query a strings start portion. The comparison is case insensitive.
656         *
657         * @param base  the base string.
658         * @param start  the starting text.
659         *
660         * @return true, if the string starts with the given starting text.
661         */
662        public static boolean startsWithIgnoreCase(final String base, final String start) {
663            if (base.length() < start.length()) {
664                return false;
665            }
666            return base.regionMatches(true, 0, start, 0, start.length());
667        }
668    
669        /**
670         * Helper functions to query a strings end portion. The comparison is case insensitive.
671         *
672         * @param base  the base string.
673         * @param end  the ending text.
674         *
675         * @return true, if the string ends with the given ending text.
676         */
677        public static boolean endsWithIgnoreCase(final String base, final String end) {
678            if (base.length() < end.length()) {
679                return false;
680            }
681            return base.regionMatches(true, base.length() - end.length(), end, 0, end.length());
682        }
683    
684        
685    
686        /**
687         * returns if byte arr is a BOM character Stream (UTF-8,UTF-16)
688        * @param barr
689        * @return is BOM or not
690        */
691       public static  boolean isBOM(byte[] barr) {
692            return barr.length>=3 && barr[0]==0xEF && barr[1]==0xBB && barr[2]==0xBF;
693        }
694    
695        /**
696         * return "" if value is null otherwise return same string
697         * @param str
698         * @return string (not null)
699         */
700        public static String valueOf(String str) {
701            if(str==null)return "";
702            return str;
703        }
704    
705        
706        /**
707         * cast a string a lower case String, is faster than the String.toLowerCase, if all Character are already Low Case
708         * @param str
709         * @return lower case value
710         */
711        public static String toLowerCase(String str) {
712            int len=str.length();
713            char c;
714            for(int i=0;i<len;i++) {
715                c=str.charAt(i);
716                if(!((c>='a' && c<='z') || (c>='0' && c<='9'))) {
717                    return str.toLowerCase();
718                }
719            }
720            
721            return str;
722        }
723        public static String toUpperCase(String str) {
724            int len=str.length();
725            char c;
726            for(int i=0;i<len;i++) {
727                c=str.charAt(i);
728                if(!((c>='A' && c<='Z') || (c>='0' && c<='9'))) {
729                    return str.toUpperCase();
730                }
731            }
732            
733            return str;
734        }
735    
736        /**
737         * soundex function
738         * @param str
739         * @return soundex from given string
740         */
741        public static String soundex(String str) {
742            return new org.apache.commons.codec.language.Soundex().soundex(str);
743        }
744    
745        /**
746         * return the last character of a string, if string ist empty return 0;
747         * @param str string to get last character
748         * @return last character
749         */
750        public static char lastChar(String str) {
751            if(str==null || str.length()==0) return 0;
752            return str.charAt(str.length()-1);
753        }
754        
755    
756        /**
757         * 
758         * @param str
759         * @return return if a String is "Empty", that means NULL or String with length 0 (whitespaces will not counted) 
760         */
761        public static boolean isEmpty(String str) {
762            return str==null || str.length()==0;
763        }
764        /**
765         * 
766         * @param str
767         * @return return if a String is "Empty", that means NULL or String with length 0 (whitespaces will not counted) 
768         */
769        public static boolean isEmpty(String str, boolean trim) {
770            if(!trim) return isEmpty(str);
771            return str==null || str.trim().length()==0;
772        }
773        
774        /**
775         * return the first character of a string, if string ist empty return 0;
776         * @param str string to get first character
777         * @return first character
778         */
779        public static char firstChar(String str) {
780            if(isEmpty(str)) return 0;
781            return str.charAt(0);
782        }
783    
784            /**
785             * change charset of string from system default to givenstr
786             * @param str
787             * @param charset
788             * @return
789             * @throws UnsupportedEncodingException 
790             */
791            public static String changeCharset(String str, String charset) throws UnsupportedEncodingException {
792                    if(str==null) return str;
793                    return new String(str.getBytes(charset),charset);
794            }
795    
796            /**
797             * change charset of string from system default to givenstr
798             * @param str
799             * @param charset
800             * @return
801             * @throws UnsupportedEncodingException 
802             */
803            public static String changeCharset(String str, String charset, String defaultValue) {
804                    if(str==null) return str;
805                    try {
806                            return new String(str.getBytes(SystemUtil.getCharset()),charset);
807                    } catch (UnsupportedEncodingException e) {
808                            return defaultValue;
809                    }
810            }
811    
812            public static boolean isWhiteSpace(char c) {
813                    return c<=' ';
814            }
815    
816            public static String removeWhiteSpace(String str) {
817                    if(isEmpty(str)) return str;
818                    StringBuilder sb=new StringBuilder();
819                    char[] carr = str.trim().toCharArray();
820                    for(int i=0;i<carr.length;i++) {
821                            if(!isWhiteSpace(carr[i]))sb.append(carr[i]);
822                    }
823                    return sb.toString();
824            }
825    
826            public static String replaceLast(String str, char from, char to) {
827                    int index = str.lastIndexOf(from);
828                    if(index==-1)return str;
829                    return str.substring(0,index)+to+str.substring(index+1);
830            }
831            public static String replaceLast(String str, String from, String to) {
832                    int index = str.lastIndexOf(from);
833                    if(index==-1)return str;
834                    return str.substring(0,index)+to+str.substring(index+from.length());
835            }
836    
837            /**
838             * removes quotes(",') that wraps the string
839             * @param string
840             * @return
841             */
842            public static String removeQuotes(String string,boolean trim) {
843                    if(trim)string=string.trim();
844                    if((StringUtil.startsWith(string, '"') && StringUtil.endsWith(string, '"')) || (StringUtil.startsWith(string, '\'') && StringUtil.endsWith(string, '\''))){
845                            string= string.substring(1,string.length()-1);
846                            if(trim)string=string.trim();
847                    }
848                    return string;
849            }
850    
851            public static boolean isEmpty(Object obj, boolean trim) {
852                    if(obj==null) return true;
853                    if(obj instanceof String)return isEmpty((String)obj,trim);
854                    if(obj instanceof StringBuffer)return isEmpty((StringBuffer)obj,trim);
855                    if(obj instanceof StringBuilder)return isEmpty((StringBuilder)obj,trim);
856                    if(obj instanceof Collection.Key)return isEmpty(((Collection.Key)obj).getString(),trim);
857                    return false;
858            }
859            
860            public static boolean isEmpty(Object obj) {
861                    if(obj==null) return true;
862                    if(obj instanceof String)return isEmpty((String)obj);
863                    if(obj instanceof Collection.Key)return isEmpty(((Collection.Key)obj).getString());
864                    if(obj instanceof StringBuffer)return isEmpty((StringBuffer)obj);
865                    if(obj instanceof StringBuilder)return isEmpty((StringBuilder)obj);
866                    return false;
867            }
868    
869            public static boolean isEmpty(StringBuffer sb,boolean trim) {
870                    if(trim) return sb==null || sb.toString().trim().length()==0;
871                    return sb==null || sb.length()==0;
872            }
873            public static boolean isEmpty(StringBuilder sb,boolean trim) {
874                    if(trim) return sb==null || sb.toString().trim().length()==0;
875                    return sb==null || sb.length()==0;
876            }
877    
878            public static boolean isEmpty(StringBuffer sb) {
879                    return sb==null || sb.length()==0;
880            }
881    
882            public static boolean isEmpty(StringBuilder sb) {
883                    return sb==null || sb.length()==0;
884            }
885    
886            public static String removeStarting(String str, String sub) {
887                    if(isEmpty(str) || isEmpty(sub) || !str.startsWith(sub)) return str;
888                    return str.substring(sub.length());
889            }
890    
891            public static String removeStartingIgnoreCase(String str, String sub) {
892                    if(isEmpty(sub) || !startsWithIgnoreCase(str, sub)) return str;
893                    return str.substring(sub.length());
894            }
895    
896            
897            public static String[] merge(String str, String[] arr) {
898                    String[] narr=new String[arr.length+1];
899            narr[0]=str;
900            for(int i=0;i<arr.length;i++) {
901                    narr[i+1]=arr[i];
902            }
903            return narr;
904            
905            }
906    
907            public static int length(String str) {
908                    if(str==null) return 0;
909                    return str.length();
910            }
911    
912            public static boolean hasUpperCase(String str) {
913                    if(isEmpty(str)) return false;
914                    return !str.equals(str.toLowerCase());
915            }
916    
917            /**
918             * trim given value, return defaultvalue when input is null
919             * @param str
920             * @param defaultValue
921             * @return trimmed string or defaultValue
922             */
923            public static String trim(String str,String defaultValue) {
924                    if(str==null) return defaultValue;
925                    return str.trim();
926            }
927    
928            public static boolean contains(String str, String substr) {
929                    if(str==null) return false;
930                    return str.indexOf(substr)!=-1;
931            }
932    
933            public static boolean containsIgnoreCase(String str, String substr) {
934                    return indexOfIgnoreCase(str,substr)!=-1;
935            }
936    
937            public static String substringEL(String str, int index,String defaultValue) {
938                    if(str==null || index<0 || index>str.length()) return defaultValue;
939                    return str.substring(index);
940            }
941    
942            /**
943             * translate a string in camel notation to a string in hypen notation
944             * example:
945             * helloWorld -> hello-world
946             * @param str
947             * @return
948             */
949            public static String camelToHypenNotation(String str) {
950                    if(isEmpty(str)) return str;
951                    
952                    StringBuilder sb=new StringBuilder();
953                    //int len=str.length();
954                    char c;
955                    
956                    sb.append(Character.toLowerCase(str.charAt(0)));
957                    for(int i=1;i<str.length();i++){
958                            c=str.charAt(i);
959                            if(Character.isUpperCase(c)){
960                                    sb.append('-');
961                                    sb.append(Character.toLowerCase(c));
962                            }
963                            else sb.append(c);
964                    }
965                    return sb.toString();
966            }
967    
968            /**
969             * translate a string in hypen notation to a string in camel notation
970             * example:
971             * hello-world -> helloWorld
972             * @param str
973             * @return
974             */
975            public static String hypenToCamelNotation(String str) {
976                    if(isEmpty(str)) return str;
977                    
978                    StringBuilder sb=new StringBuilder();
979                    int len=str.length();
980                    char c;
981                    
982                    for(int i=0;i<str.length();i++){
983                            c=str.charAt(i);
984                            if(c=='-'){
985                                    if(len>++i) sb.append(Character.toUpperCase(str.charAt(i)));
986                            }
987                            else sb.append(c);
988                    }
989                    return sb.toString();
990            }
991    
992            public static boolean isAscii(String str) {
993    
994                    for(int i=str.length()-1;i>=0;i--){
995    
996                            if( str.charAt(i) > 127 )
997                            return false;
998                    }
999                    return true;
1000            }
1001    
1002            public static boolean isWhiteSpace(String str) {
1003                    if(str==null) return false;
1004                    int len=str.length();
1005                    char c;
1006                    for(int i=0;i<len;i++){
1007                            c=str.charAt(i);
1008                            if(c!=' ' && c!='\t' && c!='\b' && c!='\r' && c!='\n') return false;
1009                    }
1010                    return true;
1011            }
1012    
1013            /**
1014             * this method works different from the regular substring method, the regular substring method takes startIndex and endIndex as second and third argument,
1015             * this method takes offset and length
1016             * @param str
1017             * @param off
1018             * @param len
1019             * @return
1020             */
1021            public static String substring(String str, int off, int len) {
1022                    return str.substring(off,off+len);
1023            }
1024            
1025            
1026            
1027            /**
1028             * this is the public entry point for the replaceMap() method
1029             * 
1030             * @param input - the string on which the replacements should be performed.
1031             * @param map - a java.util.Map with key/value pairs where the key is the substring to find and the value is the substring with which to replace the matched key 
1032             * @param ignoreCase - if true then matches will not be case sensitive
1033             * @return
1034             * @throws PageException 
1035             */
1036            public static String replaceMap( String input, Map map, boolean ignoreCase ) throws PageException {
1037                     
1038                    return replaceMap( input, map, ignoreCase, true );
1039            }
1040    
1041        
1042            /**
1043             * this is the core of the replaceMap() method.  
1044             * 
1045             * it is called once from the public entry point and then internally from resolveInternals()
1046             * 
1047             * when doResolveInternals is true -- this method calls resolveInternals.  therefore, calls from resolveInternals() 
1048             * must pass false to that param to avoid an infinite ping-pong loop 
1049             * 
1050             * @param input - the string on which the replacements should be performed.
1051             * @param map - a java.util.Map with key/value pairs where the key is the substring to find and the value is the substring with which to replace the matched key
1052             * @param ignoreCase - if true then matches will not be case sensitive
1053             * @param doResolveInternals - only the initial call (from the public entry point) should pass true
1054             * @return
1055             * @throws PageException 
1056             */
1057        private static String replaceMap( String input, Map map, boolean ignoreCase, boolean doResolveInternals ) throws PageException {
1058            if ( doResolveInternals )
1059                map = resolveInternals( map, ignoreCase, 0 );
1060            
1061            String result = input;
1062            Iterator<Map.Entry> it = map.entrySet().iterator();
1063            Map.Entry e;
1064            while ( it.hasNext() ) {
1065                e = it.next();
1066                result = replace( result, Caster.toString(e.getKey()), Caster.toString(e.getValue()), false, ignoreCase );
1067            }
1068            return result;
1069        }
1070            
1071            
1072        
1073        /**
1074         * resolves internal values within the map, so if the map has a key "{signature}" 
1075         * and its value is "Team {group}" and there's a key with the value {group} whose
1076         * value is "Railo", then {signature} will resolve to "Team Railo".
1077         * 
1078         *  {signature} = "Team {group}"
1079         *  {group}     = "Railo"
1080         * 
1081         * then signature will resolve to
1082         * 
1083         *  {signature} = "Team Railo"
1084         * 
1085         * @param map - key/value pairs for find key/replace with value
1086         * @param ignoreCase - if true then matches will not be case sensitive
1087         * @param count - used internally as safety valve to ensure that we don't go into infinite loop if two values reference each-other
1088         * @return 
1089         * @throws PageException 
1090         */
1091        private static Map resolveInternals( Map map, boolean ignoreCase, int count ) throws PageException {
1092            Map result = new HashMap();
1093            Iterator<Map.Entry> it = map.entrySet().iterator();
1094            boolean isModified = false;
1095            Map.Entry e;
1096            String v,r;
1097            while ( it.hasNext() ) {
1098                e = it.next();
1099                v = Caster.toString( e.getValue() );
1100                r = replaceMap( v, map, ignoreCase, false );                // pass false for last arg so that replaceMap() will not call this method in an infinite loop
1101                result.put( Caster.toString( e.getKey() ), r );
1102                if ( !v.equalsIgnoreCase( r ) )
1103                    isModified = true;
1104            }
1105                    
1106            if ( isModified && count++ < map.size() )
1107                result = resolveInternals( result, ignoreCase, count );     // recursive call
1108            
1109            return result;
1110        }
1111    }