001/**
002 *
003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved.
004 *
005 * This library is free software; you can redistribute it and/or
006 * modify it under the terms of the GNU Lesser General Public
007 * License as published by the Free Software Foundation; either 
008 * version 2.1 of the License, or (at your option) any later version.
009 * 
010 * This library is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013 * Lesser General Public License for more details.
014 * 
015 * You should have received a copy of the GNU Lesser General Public 
016 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
017 * 
018 **/
019package lucee.runtime.type.util;
020
021import java.util.ArrayList;
022import java.util.Arrays;
023import java.util.HashSet;
024import java.util.Iterator;
025import java.util.List;
026import java.util.Set;
027
028import lucee.commons.lang.StringList;
029import lucee.commons.lang.StringUtil;
030import lucee.runtime.exp.ExpressionException;
031import lucee.runtime.exp.PageException;
032import lucee.runtime.op.Caster;
033import lucee.runtime.type.Array;
034import lucee.runtime.type.ArrayImpl;
035import lucee.runtime.type.Collection;
036import lucee.runtime.type.Collection.Key;
037
038/**
039 * List is not a type, only some static method to manipulate String lists
040 */
041public final class ListUtil {
042
043        
044        /**
045         * casts a list to Array object, the list can be have quoted (",') arguments and delimter in this arguments are ignored. quotes are not removed
046         * example:
047         *  listWithQuotesToArray("aab,a'a,b',a\"a,b\"",",","\"'") will be translated to ["aab","a'a,b'","a\"a,b\""]
048         *
049         * @param list list to cast
050         * @param delimiter delimter of the list
051         * @param quotes quotes of the list
052         * @return Array Object
053         */
054        public static Array listWithQuotesToArray(String list, String delimiter,String quotes) {
055                if(list.length()==0) return new ArrayImpl();
056                
057                int len=list.length();
058                int last=0;
059                char[] del=delimiter.toCharArray();
060                char[] quo=quotes.toCharArray();
061                char c;
062                char inside=0;
063                
064                ArrayImpl array=new ArrayImpl();
065                try{
066                        for(int i=0;i<len;i++) {
067                            c=list.charAt(i);
068                            for(int y=0;y<quo.length;y++){
069                                if(c==quo[y]) {
070                                                if(c==inside)inside=0;
071                                                else if(inside==0)inside=c;
072                                                continue;
073                                        }
074                            }
075                            
076                            for(int y=0;y<del.length;y++) {
077                                        if(inside==0 && c==del[y]) {
078                                                array._append(list.substring(last,i));
079                                                last=i+1;
080                                                break;
081                                        }
082                            }
083                        }
084                        if(last<=len)array.append(list.substring(last));
085                }
086                catch(ExpressionException e){}
087                return array;
088        }
089        
090        /**
091         * casts a list to Array object
092         * @param list list to cast
093         * @param delimiter delimter of the list
094         * @return Array Object
095         */
096        public static Array listToArray(String list, String delimiter) {
097            if(delimiter.length()==1)return listToArray(list, delimiter.charAt(0));
098            if(list.length()==0) return new ArrayImpl();
099            if(delimiter.length()==0) {
100                int len = list.length();
101                ArrayImpl array=new ArrayImpl();
102                array.appendEL("");// ACF compatibility
103                        for(int i=0;i<len;i++){
104                        array.appendEL(list.charAt(i));
105                }
106                        array.appendEL("");// ACF compatibility
107                        return array;
108            }
109                
110            int len=list.length();
111                int last=0;
112                char[] del=delimiter.toCharArray();
113                char c;
114                
115                ArrayImpl array=new ArrayImpl();
116                try{
117                        for(int i=0;i<len;i++) {
118                            c=list.charAt(i);
119                            for(int y=0;y<del.length;y++) {
120                                        if(c==del[y]) {
121                                                array.appendEL(list.substring(last,i));
122                                                last=i+1;
123                                                break;
124                                        }
125                            }
126                        }
127                        if(last<=len)array.append(list.substring(last));
128                }
129                catch(ExpressionException e){}
130                return array;
131        }
132        
133        public static Array listToArray(String list, String delimiter, boolean includeEmptyFields, boolean multiCharDelim) {
134                if(includeEmptyFields)return listToArray(list, delimiter, multiCharDelim);
135                return listToArrayRemoveEmpty(list, delimiter,multiCharDelim);
136                
137        }
138        private static Array listToArray(String list, String delimiter, boolean multiCharDelim) {
139                if(!multiCharDelim || delimiter.length()==0) return listToArray(list, delimiter);
140                if(delimiter.length()==1)return listToArray(list, delimiter.charAt(0));
141                int len=list.length();
142                if(len==0) return new ArrayImpl();
143                 
144                Array array=new ArrayImpl();
145                int from=0;
146                int index;
147                int dl=delimiter.length();
148                while((index=list.indexOf(delimiter,from))!=-1){
149                        array.appendEL(list.substring(from,index));
150                        from=index+dl;
151                }
152                array.appendEL(list.substring(from,len));
153                
154                return array;
155        }
156
157        /**
158         * casts a list to Array object
159         * @param list list to cast
160         * @param delimiter delimter of the list
161         * @return Array Object
162         */
163        public static Array listToArray(String list, char delimiter) {
164                if(list.length()==0) return new ArrayImpl();
165                int len=list.length();
166                int last=0;
167                
168                Array array=new ArrayImpl();
169                try{
170                        for(int i=0;i<len;i++) {
171                                if(list.charAt(i)==delimiter) {
172                                        array.append(list.substring(last,i));
173                                        last=i+1;
174                                }
175                        }
176                        if(last<=len)array.append(list.substring(last));
177                }
178                catch(PageException e){}
179                return array;
180        }
181        
182        /**
183         * casts a list to Array object remove Empty Elements
184         * @param list list to cast
185         * @param delimiter delimter of the list
186         * @return Array Object
187         */
188        private static Array listToArrayRemoveEmpty(String list, String delimiter, boolean multiCharDelim) {
189                if(!multiCharDelim || delimiter.length()==0) return listToArrayRemoveEmpty(list, delimiter);
190                
191            if(delimiter.length()==1)return listToArrayRemoveEmpty(list, delimiter.charAt(0));
192                
193            int len=list.length();
194                if(len==0)  return new ArrayImpl();
195                
196                
197                Array array=new ArrayImpl();
198                int from=0;
199                int index;
200                int dl=delimiter.length();
201                while((index=list.indexOf(delimiter,from))!=-1){
202                        if(from<index)array.appendEL(list.substring(from,index));
203                        from=index+dl;
204                }
205                if(from<len)array.appendEL(list.substring(from,len));
206                return array;
207                
208        }
209
210        public static Array listToArrayRemoveEmpty(String list, String delimiter) {
211            if(delimiter.length()==1)return listToArrayRemoveEmpty(list, delimiter.charAt(0));
212                int len=list.length();
213                ArrayImpl array=new ArrayImpl();
214                if(len==0) return array;
215                if(delimiter.length()==0) {
216                for(int i=0;i<len;i++){
217                        array.appendEL(list.charAt(i));
218                }
219                        return array;
220            }
221                int last=0;
222                
223                char[] del = delimiter.toCharArray();
224                char c;
225                for(int i=0;i<len;i++) {
226                    c=list.charAt(i);
227                    for(int y=0;y<del.length;y++) {
228                                if(c==del[y]) {
229                                        if(last<i)array._append(list.substring(last,i));
230                                        last=i+1;
231                                        break;
232                                }
233                    }
234                }
235                if(last<len)array._append(list.substring(last));
236                return array;
237        }
238        
239    /**
240     * casts a list to Array object remove Empty Elements
241     * @param list list to cast
242     * @param delimiter delimter of the list
243     * @return Array Object
244     */
245    public static Array listToArrayRemoveEmpty(String list, char delimiter) {
246        int len=list.length();
247        ArrayImpl array=new ArrayImpl();
248        if(len==0) return array;
249        int last=0;
250        
251        for(int i=0;i<len;i++) {
252            if(list.charAt(i)==delimiter) {
253                if(last<i)array._append(list.substring(last,i));
254                last=i+1;
255            }
256        }
257        if(last<len)array._append(list.substring(last));
258
259        return array;
260    }
261    
262    public static List<String> toListRemoveEmpty(String list, char delimiter) {
263        int len=list.length();
264        List<String> array=new ArrayList<String>();
265        if(len==0) return array;
266        int last=0;
267        
268        for(int i=0;i<len;i++) {
269            if(list.charAt(i)==delimiter) {
270                if(last<i)array.add(list.substring(last,i));
271                last=i+1;
272            }
273        }
274        if(last<len)array.add(list.substring(last));
275
276        return array;
277    }
278
279        
280        private static String ltrim(String list,char[] del) {
281                int len=list.length();
282                char c;
283                //               remove at start
284                outer:while(len>0) {
285                    c=list.charAt(0);
286                    for(int i=0;i<del.length;i++) {
287                        if(c==del[i]) {
288                            list=list.substring(1);
289                            len=list.length();
290                            continue outer;
291                        }
292                    }
293                    break;
294                }
295                return list;
296        }
297    
298    /**
299     * casts a list to Array object remove Empty Elements
300     * @param list list to cast
301     * @param delimiter delimter of the list
302     * @return Array Object
303     */
304    public static StringList listToStringListRemoveEmpty(String list, char delimiter) {
305        int len=list.length();
306        StringList rtn=new StringList();
307        if(len==0) return rtn.reset();
308        int last=0;
309        
310        for(int i=0;i<len;i++) {
311            if(list.charAt(i)==delimiter) {
312                if(last<i)rtn.add(list.substring(last,i));
313                last=i+1;
314            }
315        }
316        if(last<len)rtn.add(list.substring(last));
317
318        return rtn.reset();
319    }
320        
321        /**
322         * casts a list to Array object, remove all empty items at start and end of the list
323         * @param list list to cast
324         * @param delimiter delimter of the list
325         * @return Array Object
326         */
327        public static Array listToArrayTrim(String list, String delimiter) {
328            if(delimiter.length()==1)return listToArrayTrim(list, delimiter.charAt(0));
329                if(list.length()==0) return new ArrayImpl();
330                char[] del = delimiter.toCharArray();
331                char c;
332                
333                // remove at start
334                outer:while(list.length()>0) {
335                    c=list.charAt(0);
336                    for(int i=0;i<del.length;i++) {
337                        if(c==del[i]) {
338                            list=list.substring(1);
339                            continue outer;
340                        }
341                    }
342                    break;
343                }
344                
345                int len;
346                outer:while(list.length()>0) {
347                    c=list.charAt(list.length()-1);
348                    for(int i=0;i<del.length;i++) {
349                        if(c==del[i]) {
350                            len=list.length();
351                            list=list.substring(0,len-1<0?0:len-1);
352                            continue outer;
353                        }
354                    }
355                    break;
356                }
357                return listToArray(list, delimiter);
358        }
359        
360        /**
361         * casts a list to Array object, remove all empty items at start and end of the list and store count to info
362         * @param list list to cast
363         * @param delimiter delimter of the list
364         * @param info
365         * @return Array Object
366         */
367        public static Array listToArrayTrim(String list, String delimiter, int[] info) {
368            if(delimiter.length()==1)return listToArrayTrim(list, delimiter.charAt(0),info);
369                if(list.length()==0) return new ArrayImpl();
370                char[] del = delimiter.toCharArray();
371                char c;
372                
373                // remove at start
374                outer:while(list.length()>0) {
375                    c=list.charAt(0);
376                    for(int i=0;i<del.length;i++) {
377                        if(c==del[i]) {
378                                        info[0]++;
379                            list=list.substring(1);
380                            continue outer;
381                        }
382                    }
383                    break;
384                }
385                
386                int len;
387                outer:while(list.length()>0) {
388                    c=list.charAt(list.length()-1);
389                    for(int i=0;i<del.length;i++) {
390                        if(c==del[i]) {
391                                        info[1]++;
392                            len=list.length();
393                            list=list.substring(0,len-1<0?0:len-1);
394                            continue outer;
395                        }
396                    }
397                    break;
398                }
399                return listToArray(list, delimiter);
400        }
401    
402    /**
403     * casts a list to Array object, remove all empty items at start and end of the list and store count to info
404     * @param list list to cast
405     * @param pos
406     * @param delimiter delimter of the list
407     * @return Array Object
408     * @throws ExpressionException 
409     */
410    public static String listInsertAt(String list, int pos, String value, String delimiter, boolean ignoreEmpty) throws ExpressionException {
411        if(pos<1)
412            throw new ExpressionException("invalid string list index ["+(pos)+"]");
413   
414        char[] del = delimiter.toCharArray();
415        char c;
416        StringBuilder result=new StringBuilder();
417        String end="";
418        int len;
419        
420        // remove at start
421        if(ignoreEmpty){
422                outer:while(list.length()>0) {
423                    c=list.charAt(0);
424                    for(int i=0;i<del.length;i++) {
425                        if(c==del[i]) {
426                            list=list.substring(1);
427                            result.append(c);
428                            continue outer;
429                        }
430                    }
431                    break;
432                }
433        }
434        
435        // remove at end
436        if(ignoreEmpty){
437                outer:while(list.length()>0) {
438                    c=list.charAt(list.length()-1);
439                    for(int i=0;i<del.length;i++) {
440                        if(c==del[i]) {
441                            len=list.length();
442                            list=list.substring(0,len-1<0?0:len-1);
443                            end=c+end;
444                            continue outer;
445                        }
446                 
447                    }
448                    break;
449                }
450        }
451        
452        len=list.length();
453        int last=0;
454        
455        int count=0;
456        outer:for(int i=0;i<len;i++) {
457            c=list.charAt(i);
458            for(int y=0;y<del.length;y++) {
459                if(c==del[y]) {
460                    
461                        if(!ignoreEmpty || last<i) {
462                                if(pos==++count){
463                            result.append(value);
464                            result.append(del[0]);
465                        }
466                                        }
467                    result.append(list.substring(last,i));
468                    result.append(c);
469                    last=i+1;
470                    continue outer;
471                }
472            }
473        }
474        count++;
475        if(last<=len){
476            if(pos==count) {
477                result.append(value);
478                result.append(del[0]);
479            }
480            
481            result.append(list.substring(last));
482        }
483        if(pos>count) {
484            throw new ExpressionException("invalid string list index ["+(pos)+"], indexes go from 1 to "+(count));
485            
486        }
487        
488        return result+end;
489    }
490        
491        /**
492         * casts a list to Array object, remove all empty items at start and end of the list
493         * @param list list to cast
494         * @param delimiter delimter of the list
495         * @return Array Object
496         */
497    public static Array listToArrayTrim(String list, char delimiter) {
498        if(list.length()==0) return new ArrayImpl();
499        // remove at start
500        while(list.indexOf(delimiter)==0) {
501            list=list.substring(1);
502        }
503        int len=list.length();
504        if(len==0) return new ArrayImpl();
505        while(list.lastIndexOf(delimiter)==len-1) {
506            list=list.substring(0,len-1<0?0:len-1);
507            len=list.length();
508        }
509        return listToArray(list, delimiter);
510    }
511
512    /**
513     * @param list
514     * @param delimiter
515     * @return trimmed list
516     */
517    public static StringList toListTrim(String list, char delimiter) {
518        if(list.length()==0) return new StringList();
519        // remove at start
520        while(list.indexOf(delimiter)==0) {
521            list=list.substring(1);
522        }
523        int len=list.length();
524        if(len==0) return new StringList();
525        while(list.lastIndexOf(delimiter)==len-1) {
526            list=list.substring(0,len-1<0?0:len-1);
527            len=list.length();
528        }
529        
530        return toList(list, delimiter);
531    }
532    
533    /**
534     * @param list
535     * @param delimiter
536     * @return list
537     */
538    public static StringList toList(String list, char delimiter) {
539        if(list.length()==0) return new StringList();
540        int len=list.length();
541        int last=0;
542        
543        StringList rtn=new StringList();
544       
545        for(int i=0;i<len;i++) {
546            if(list.charAt(i)==delimiter) {
547                rtn.add(list.substring(last,i));
548                last=i+1;
549            }
550        }
551        if(last<=len)rtn.add(list.substring(last));
552        rtn.reset();
553        return rtn;
554    }
555    
556    public static StringList toWordList(String list) {
557        if(list.length()==0) return new StringList();
558        int len=list.length();
559        int last=0;
560        char c,l=0;
561        StringList rtn=new StringList();
562       
563        for(int i=0;i<len;i++) {
564            if(StringUtil.isWhiteSpace(c=list.charAt(i))) {
565                rtn.add(list.substring(last,i),l);
566                l=c;
567                last=i+1;
568            }
569        }
570        if(last<=len)rtn.add(list.substring(last),l);
571        rtn.reset();
572        return rtn;
573    }
574    
575        /**
576         * casts a list to Array object, remove all empty items at start and end of the list
577         * @param list list to cast
578         * @param delimiter delimter of the list
579         * @param info
580         * @return Array Object
581         */
582        public static Array listToArrayTrim(String list, char delimiter, int[] info) {
583                if(list.length()==0) return new ArrayImpl();
584                // remove at start
585                while(list.indexOf(delimiter)==0) {
586                        info[0]++;
587                        list=list.substring(1);
588                }
589                int len=list.length();
590                if(len==0) return new ArrayImpl();
591                while(list.lastIndexOf(delimiter)==len-1) {
592                        info[1]++;
593                        list=list.substring(0,len-1<0?0:len-1);
594                        len=list.length();
595                }
596                return listToArray(list, delimiter);
597        }
598    
599
600        /* *
601         * finds a value inside a list, ignore case
602         * @param list list to search
603         * @param value value to find
604         * @return position in list (0-n) or -1
605         *
606        private static int listFindNoCase(String list, String value) {
607                return listFindNoCase(list, value, ",", true);
608        }*/     
609
610        /**
611         * finds a value inside a list, do not ignore case
612         * @param list list to search
613         * @param value value to find
614         * @param delimiter delimiter of the list
615         * @return position in list (0-n) or -1
616         */
617        public static int listFindNoCase(String list, String value, String delimiter) {
618                return listFindNoCase(list, value, delimiter, true);
619        }       
620
621        
622        /**
623         * finds a value inside a list, do not ignore case
624         * @param list list to search
625         * @param value value to find
626         * @param delimiter delimiter of the list
627         * @param trim trim the list or not
628         * @return position in list (0-n) or -1
629         */
630        public static int listFindNoCase(String list, String value, String delimiter,boolean trim) {
631                Array arr = trim?listToArrayTrim(list,delimiter):listToArray(list,delimiter);
632                int len=arr.size();
633                for(int i=1;i<=len;i++) {
634                        if(((String)arr.get(i,"")).equalsIgnoreCase(value)) return i-1;
635                }
636                return -1;
637        }
638
639        public static int listFindForSwitch(String list, String value, String delimiter) {
640                if(list.indexOf(delimiter)==-1 && list.equalsIgnoreCase(value)) return 1;
641                
642                Array arr = listToArray(list,delimiter);
643                int len=arr.size();
644                for(int i=1;i<=len;i++) {
645                        if(((String)arr.get(i,"")).equalsIgnoreCase(value)) return i;
646                }
647                return -1;
648        }
649        
650
651        /**
652         * finds a value inside a list, ignore case, ignore empty items
653         * @param list list to search
654         * @param value value to find
655         * @param delimiter delimiter of the list
656         * @return position in list or 0
657         */
658        public static int listFindNoCaseIgnoreEmpty(String list, String value, String delimiter) {
659            if(delimiter.length()==1)return listFindNoCaseIgnoreEmpty(list, value, delimiter.charAt(0));
660            if(list==null) return -1;
661                int len=list.length();
662                if(len==0) return -1;
663                int last=0;
664                int count=0;
665                char[] del = delimiter.toCharArray();
666                char c;
667                
668                for(int i=0;i<len;i++) {
669                        c=list.charAt(i);
670                        for(int y=0;y<del.length;y++) {
671                                if(c==del[y]) {
672                                        if(last<i) {
673                                                if(list.substring(last,i).equalsIgnoreCase(value)) return count;
674                                                count++;
675                                        }
676                                        last=i+1;
677                                        break;
678                                }
679                    }
680                }
681                if(last<len) {
682                        if(list.substring(last).equalsIgnoreCase(value)) return count;
683                }
684                return -1;
685        }
686        
687        /**
688         * finds a value inside a list, ignore case, ignore empty items
689         * @param list list to search
690         * @param value value to find
691         * @param delimiter delimiter of the list
692         * @return position in list or 0
693         */
694        public static int listFindNoCaseIgnoreEmpty(String list, String value, char delimiter) {
695                if(list==null) return -1;
696                int len=list.length();
697                if(len==0) return -1;
698                int last=0;
699                int count=0;
700                
701                for(int i=0;i<len;i++) {
702                        if(list.charAt(i)==delimiter) {
703                                if(last<i) {
704                                        if(list.substring(last,i).equalsIgnoreCase(value)) return count;
705                                        count++;
706                                }
707                                last=i+1;
708                        }
709                }
710                if(last<len) {
711                        if(list.substring(last).equalsIgnoreCase(value)) return count;
712                }
713                return -1;
714        }
715        
716
717        
718        
719        /**
720         * finds a value inside a list, case sensitive
721         * @param list list to search
722         * @param value value to find
723         * @return position in list or 0
724         */
725        public static int listFind(String list, String value) {
726                return listFind(list, value, ",");
727        }
728        
729        /**
730         * finds a value inside a list, do not case sensitive
731         * @param list list to search
732         * @param value value to find
733         * @param delimiter delimiter of the list
734         * @return position in list or 0
735         */
736        public static int listFind(String list, String value, String delimiter) {
737                Array arr = 
738                                listToArrayTrim(list,delimiter);
739                int len=arr.size();
740                for(int i=1;i<=len;i++) {
741                        if(arr.get(i,"").equals(value)) return i-1;
742                }
743
744                return -1;
745        }
746
747        /**
748         * finds a value inside a list, case sensitive, ignore empty items
749         * @param list list to search
750         * @param value value to find
751         * @param delimiter delimiter of the list
752         * @return position in list or 0
753         */
754        public static int listFindIgnoreEmpty(String list, String value, String delimiter) {
755            if(delimiter.length()==1)return listFindIgnoreEmpty(list, value, delimiter.charAt(0));
756                if(list==null) return -1;
757                int len=list.length();
758                if(len==0) return -1;
759                int last=0;
760                int count=0;
761                char[] del = delimiter.toCharArray();
762                char c;
763                
764                for(int i=0;i<len;i++) {
765                        c=list.charAt(i);
766                        for(int y=0;y<del.length;y++) {
767                                if(c==del[y]) {
768                                        if(last<i) {
769                                                if(list.substring(last,i).equals(value)) return count;
770                                                count++;
771                                        }
772                                        last=i+1;
773                                        break;
774                                }
775                    }
776                }
777                if(last<len) {
778                        if(list.substring(last).equals(value)) return count;
779                }
780                return -1;
781        }
782
783        /**
784         * finds a value inside a list, case sensitive, ignore empty items
785         * @param list list to search
786         * @param value value to find
787         * @param delimiter delimiter of the list
788         * @return position in list or 0
789         */
790        public static int listFindIgnoreEmpty(String list, String value, char delimiter) {
791                if(list==null) return -1;
792                int len=list.length();
793                if(len==0) return -1;
794                int last=0;
795                int count=0;
796                
797                for(int i=0;i<len;i++) {
798                        if(list.charAt(i)==delimiter) {
799                                if(last<i) {
800                                        if(list.substring(last,i).equals(value)) return count;
801                                        count++;
802                                }
803                                last=i+1;
804                        }
805                }
806                if(last<len) {
807                        if(list.substring(last).equals(value)) return count;
808                }
809                return -1;
810        }
811        
812        /**
813         * returns if a value of the list contains given value, ignore case
814         * @param list list to search in
815         * @param value value to serach
816         * @param delimiter delimiter of the list
817         * @return position in list or 0
818         */
819        public static int listContainsNoCase(String list, String value, String delimiter, boolean includeEmptyFields, boolean multiCharacterDelimiter) {
820                if(StringUtil.isEmpty(value)) return -1;
821                
822                Array arr=listToArray(list,delimiter,includeEmptyFields,multiCharacterDelimiter);
823                int len=arr.size();
824                
825                for(int i=1;i<=len;i++) {
826                        if(StringUtil.indexOfIgnoreCase(arr.get(i,"").toString(), value)!=-1) return i-1;
827                }
828                return -1;
829        }
830
831        /* *
832         * returns if a value of the list contains given value, ignore case, ignore empty values
833         * @param list list to search in
834         * @param value value to serach
835         * @param delimiter delimiter of the list
836         * @return position in list or 0
837        
838        public static int listContainsIgnoreEmptyNoCase(String list, String value, String delimiter) {
839                if(StringUtil.isEmpty(value)) return -1;
840                Array arr=listToArrayRemoveEmpty(list,delimiter);
841                int count=0;
842                int len=arr.size();
843                
844                for(int i=1;i<=len;i++) {
845                        String item=arr.get(i,"").toString();
846                        if(StringUtil.indexOfIgnoreCase(item, value)!=-1) return count;
847                        count++;
848                }
849                return -1;
850        } */
851        
852
853        /**
854         * returns if a value of the list contains given value, case sensitive
855         * @param list list to search in
856         * @param value value to serach
857         * @param delimiter delimiter of the list
858         * @return position in list or 0
859         */
860        public static int listContains(String list, String value, String delimiter, boolean includeEmptyFields, boolean multiCharacterDelimiter) {
861                if(StringUtil.isEmpty(value)) return -1;
862                
863                Array arr=listToArray(list,delimiter,includeEmptyFields,multiCharacterDelimiter);
864                int len=arr.size();
865                for(int i=1;i<=len;i++) {
866                        if(arr.get(i,"").toString().indexOf(value)!=-1) return i-1;
867                }
868                return -1;
869        }
870
871        /* *
872         * returns if a value of the list contains given value, case sensitive, ignore empty positions
873         * @param list list to search in
874         * @param value value to serach
875         * @param delimiter delimiter of the list
876         * @return position in list or 0
877        
878        private static int listContainsIgnoreEmpty(String list, String value, String delimiter, boolean multiCharacterDelimiter) {
879                if(StringUtil.isEmpty(value)) return -1;
880                Array arr=listToArrayRemoveEmpty(list,delimiter);
881                int count=0;
882                int len=arr.size();
883                
884                String item;
885                for(int i=1;i<=len;i++) {
886                        item=arr.get(i,"").toString();
887                        if(item.indexOf(value)!=-1) return count;
888                        count++;
889                }
890                return -1;
891        } */
892        
893
894        /**
895         * convert a string array to string list, removes empty values at begin and end of the list
896         * @param array array to convert
897         * @param delimiter delimiter for the new list
898         * @return list generated from string array
899         */
900        public static String arrayToListTrim(String[] array, String delimiter) {
901                return trim(arrayToList(array,delimiter),delimiter,false);
902        }
903        
904        /**
905         * convert a string array to string list
906         * @param array array to convert
907         * @param delimiter delimiter for the new list
908         * @return list generated from string array
909         */
910        public static String arrayToList(String[] array, String delimiter) {
911                if(ArrayUtil.isEmpty(array)) return "";
912                StringBuilder sb=new StringBuilder(array[0]);
913                
914                if(delimiter.length()==1) {
915                        char c=delimiter.charAt(0);
916                        for(int i=1;i<array.length;i++) {
917                                sb.append(c);
918                                sb.append(array[i]);
919                        }
920                }
921                else {
922                        for(int i=1;i<array.length;i++) {
923                                sb.append(delimiter);
924                                sb.append(array[i]);
925                        }
926                }
927                
928
929                return sb.toString();
930        }
931        
932        public static String arrayToList(Collection.Key[] array, String delimiter) {
933                if(array.length==0) return "";
934                StringBuilder sb=new StringBuilder(array[0].getString());
935                
936                if(delimiter.length()==1) {
937                        char c=delimiter.charAt(0);
938                        for(int i=1;i<array.length;i++) {
939                                sb.append(c);
940                                sb.append(array[i].getString());
941                        }
942                }
943                else {
944                        for(int i=1;i<array.length;i++) {
945                                sb.append(delimiter);
946                                sb.append(array[i].getString());
947                        }
948                }
949                
950
951                return sb.toString();
952        }
953        
954        /**
955         * convert Array Object to string list
956         * @param array array to convert
957         * @param delimiter delimiter for the new list
958         * @return list generated from string array
959         * @throws PageException
960         */
961        public static String arrayToList(Array array, String delimiter) throws PageException {
962                if(array.size()==0) return "";
963                StringBuilder sb=new StringBuilder(Caster.toString(array.get(1,"")));
964                int len=array.size();
965                
966                for(int i=2;i<=len;i++) {
967                        sb.append(delimiter);
968                        sb.append(array.get(i,""));
969                }
970                return sb.toString();
971        }
972        
973        public static String listToList(java.util.List<?> list, String delimiter) throws PageException {
974                if(list.size()==0) return "";
975                StringBuilder sb=new StringBuilder();
976                Iterator<?> it = list.iterator();
977                
978                if(it.hasNext()) sb.append(Caster.toString(it.next()));
979                        
980                while(it.hasNext()) {
981                        sb.append(delimiter);
982                        sb.append(Caster.toString(it.next()));
983                }
984                return sb.toString();
985        }
986        
987        public static String listToList2(java.util.List<String> list, String delimiter) {
988                if(list.size()==0) return "";
989                StringBuilder sb=new StringBuilder();
990                Iterator<String> it = list.iterator();
991                
992                if(it.hasNext()) sb.append(it.next());
993                        
994                while(it.hasNext()) {
995                        sb.append(delimiter);
996                        sb.append(it.next());
997                }
998                return sb.toString();
999        }
1000        
1001        
1002        /**
1003         * trims a string array, removes all empty array positions at the start and the end of the array
1004         * @param array array to remove elements
1005         * @return cleared array
1006         */
1007        public static String[] trim(String[] array) {
1008                int from=0;
1009                int to=0;
1010
1011                // test start
1012                for(int i=0;i<array.length;i++) {
1013                        from=i;
1014                        if(array[i].length()!=0)break;
1015                }
1016                
1017                // test end
1018                for(int i=array.length-1;i>=0;i--) {
1019                        to=i;
1020                        if(array[i].length()!=0)break;
1021                }
1022                
1023                int newLen=to-from+1;
1024                
1025                if(newLen<array.length) {
1026                        String[] rtn=new String[newLen];
1027                        System.arraycopy(array,from,rtn,0,newLen);
1028                        return rtn;
1029                }
1030                return array;
1031        }
1032
1033        /**
1034         * trims a string list, remove all empty delimiter at start and the end
1035         * @param list list to trim
1036         * @param delimiter delimiter of the list
1037         * @return trimed list
1038         */
1039        public static String trim(String list, String delimiter) {
1040                return trim(list,delimiter,new int[2],false);
1041        }
1042        
1043        
1044        public static String trim(String list, String delimiter, boolean multiCharacterDelimiter) {
1045                return trim(list,delimiter,new int[2],multiCharacterDelimiter);
1046        }
1047        
1048        /**
1049         * trims a string list, remove all empty delimiter at start and the end
1050         * @param list list to trim
1051         * @param delimiter delimiter of the list
1052         * @param removeInfo int array contain count of removed values (removeInfo[0]=at the begin;removeInfo[1]=at the end)
1053         * @return trimed list
1054         */
1055        public static String trim(String list, String delimiter,int[] removeInfo, boolean multiCharacterDelimiter) {
1056                if(list.length()==0)return "";
1057                
1058                if(multiCharacterDelimiter && delimiter.length()>1) {
1059                        int from=0;
1060                        
1061                        // remove at start
1062                        while(list.length()>=from+delimiter.length()) {
1063                                if(list.indexOf(delimiter,from)==from) {
1064                                from+=delimiter.length();
1065                                removeInfo[0]++;
1066                                continue;
1067                            }
1068                            break;
1069                        }
1070
1071                        if(from>0) list= list.substring(from);
1072                        
1073                        
1074                        // remove at end
1075                        while(list.length()>=delimiter.length()) {
1076                                if(list.lastIndexOf(delimiter)==list.length()-delimiter.length()) {
1077                                removeInfo[1]++;
1078                                        list=list.substring(0,list.length()-delimiter.length());
1079                                        continue;
1080                            }
1081                            break;
1082                        }
1083                        return list;
1084                }
1085                
1086                
1087                
1088
1089                if(list.length()==0)return "";
1090                int from=0;
1091                int to=list.length();
1092                //int len=delimiter.length();
1093                char[] del=delimiter.toCharArray();
1094                char c;
1095                
1096                // remove at start
1097                outer:while(list.length()>from) {
1098                    c=list.charAt(from);
1099                    for(int i=0;i<del.length;i++) {
1100                        if(c==del[i]) {
1101                            from++;
1102                            removeInfo[0]++;
1103                            //list=list.substring(from);
1104                            continue outer;
1105                        }
1106                    }
1107                    break;
1108                }
1109                
1110                //int len;
1111                outer:while(to>from) {
1112                    c=list.charAt(to-1);
1113                    for(int i=0;i<del.length;i++) {
1114                        if(c==del[i]) {
1115                            to--;
1116                                        removeInfo[1]++;
1117                            continue outer;
1118                        }
1119                    }
1120                    break;
1121                }
1122                int newLen=to-from;
1123                
1124                if(newLen<list.length()) {
1125                        return list.substring(from,to);
1126                }
1127                return list;
1128                
1129        }
1130        
1131        /**
1132         * sorts a string list
1133         * @param list list to sort
1134         * @param sortType sort type (numeric,text,textnocase)
1135         * @param sortOrder sort order (asc,desc)
1136         * @param delimiter list delimiter
1137         * @return sorted list
1138         * @throws PageException
1139         */
1140        public static String sortIgnoreEmpty(String list, String sortType, String sortOrder, String delimiter) throws PageException {
1141                return _sort(toStringArray(listToArrayRemoveEmpty(list,delimiter)),sortType, sortOrder, delimiter);
1142        }
1143
1144        /**
1145         * sorts a string list
1146         * @param list list to sort
1147         * @param sortType sort type (numeric,text,textnocase)
1148         * @param sortOrder sort order (asc,desc)
1149         * @param delimiter list delimiter
1150         * @return sorted list
1151         * @throws PageException
1152         */
1153        public static String sort(String list, String sortType, String sortOrder, String delimiter) throws PageException {
1154                return _sort(toStringArray(listToArray(list,delimiter)),sortType, sortOrder, delimiter);
1155        }
1156        private static String _sort(Object[] arr, String sortType, String sortOrder, String delimiter) throws PageException {
1157
1158                Arrays.sort(arr,ArrayUtil.toComparator(null, sortType, sortOrder, false));
1159                
1160                StringBuilder sb=new StringBuilder();
1161                for(int i=0;i<arr.length;i++) {
1162                        if(i!=0)sb.append(delimiter);
1163                        sb.append(arr[i]);
1164                }
1165                return sb.toString();
1166        }
1167
1168
1169        /**
1170         * cast a Object Array to a String Array
1171         * @param array
1172         * @return String Array
1173         */
1174        public  static String[] toStringArrayEL(Array array) {
1175                String[] arr=new String[array.size()];
1176                for(int i=0;i<arr.length;i++) {
1177                        arr[i]=Caster.toString(array.get(i+1,null),null);
1178                }
1179                
1180                return arr;
1181        }
1182        
1183        /**
1184         * cast a Object Array to a String Array
1185         * @param array
1186         * @return String Array
1187         * @throws PageException
1188         */
1189    public static String[] toStringArray(Array array) throws PageException {
1190        String[] arr=new String[array.size()];
1191        for(int i=0;i<arr.length;i++) {
1192            arr[i]=Caster.toString(array.get(i+1,null));
1193        }
1194        return arr;
1195    }
1196    
1197    public static String[] toStringArray(Set<String> set) {
1198        return set.toArray(new String[set.size()]);
1199    }
1200    
1201    public static String[] toStringArray(List<String> list) {
1202        return list.toArray(new String[list.size()]);
1203    }
1204    
1205    /**
1206     * cast a Object Array to a String Array
1207     * @param array
1208     * @param defaultValue 
1209     * @return String Array
1210     */
1211    public  static String[] toStringArray(Array array,String defaultValue) {
1212        String[] arr=new String[array.size()];
1213        for(int i=0;i<arr.length;i++) {
1214            arr[i]=Caster.toString(array.get(i+1,defaultValue),defaultValue);
1215        }
1216        
1217        return arr;
1218    }
1219        
1220        /**
1221         * cast a Object Array to a String Array and trim all values
1222         * @param array
1223         * @return String Array
1224         * @throws PageException
1225         */
1226    public  static String[] toStringArrayTrim(Array array) throws PageException {
1227                String[] arr=new String[array.size()];
1228                for(int i=0;i<arr.length;i++) {
1229                        arr[i]=Caster.toString(array.get(i+1,"")).trim();
1230                }
1231                
1232                return arr;
1233        }
1234
1235        /**
1236         * return first element of the list
1237         * @param list
1238         * @param delimiter
1239         * @return returns the first element of the list
1240         * @deprecated use instead  first(String list, String delimiter, boolean ignoreEmpty)
1241         */
1242        public static String first(String list, String delimiter) {
1243                return first(list, delimiter,true, 1);
1244        }
1245
1246        /**
1247         * return last element of the list
1248         * @param list
1249         * @param delimiter
1250         * @return returns the last Element of a list
1251         * @deprecated use instead last(String list, String delimiter, boolean ignoreEmpty)
1252         */
1253        public static String last(String list, String delimiter) {
1254                return last(list, delimiter, true);
1255        }
1256        
1257        /**
1258         * return last element of the list
1259         * @param list
1260         * @param delimiter
1261         * @param ignoreEmpty
1262         * @return returns the last Element of a list
1263         */
1264        public static String last(String list, String delimiter, boolean ignoreEmpty) {
1265
1266                if(StringUtil.isEmpty(list)) return "";
1267                int len=list.length();
1268                
1269                char[] del;
1270                if(StringUtil.isEmpty(delimiter)) {
1271                    del=new char[]{','};
1272                }
1273                else del=delimiter.toCharArray();
1274                
1275                int index;
1276                int x;
1277                while(true) {
1278                    index=-1;
1279                    
1280                    for(int i=0;i<del.length;i++) {
1281                        x=list.lastIndexOf(del[i]);
1282                        if(x>index)index=x;
1283                    }
1284
1285                        if(index==-1) {
1286                                return list;
1287                        }
1288                        
1289                        else if(index+1==len) {
1290                                if(!ignoreEmpty) return"";
1291                                list=list.substring(0,len-1);
1292                                len--;
1293                        }
1294                        else {
1295                                return list.substring(index+1);
1296                        }
1297                }
1298        }
1299        
1300    /**
1301     * return last element of the list
1302     * @param list
1303     * @param delimiter
1304     * @return returns the last Element of a list
1305     */
1306        
1307        
1308    public static String last(String list, char delimiter) {
1309
1310        int len=list.length();
1311        if(len==0) return "";
1312        int index=0;
1313        
1314        while(true) {
1315            index=list.lastIndexOf(delimiter);
1316            if(index==-1) {
1317                return list;
1318            }
1319            else if(index+1==len) {
1320                list=list.substring(0,len-1);
1321                len--;
1322            }
1323            else {
1324                return list.substring(index+1);
1325            }
1326        }
1327    }
1328
1329        /**
1330         * returns count of items in the list
1331         * @param list
1332         * @param delimiter
1333         * @return list len
1334         */
1335        public static int len(String list, char delimiter,boolean ignoreEmpty) {
1336                int len=StringUtil.length(list);
1337                if(len==0) return 0;
1338
1339                int count=0;
1340                int last=0;
1341                
1342                for(int i=0;i<len;i++) {
1343                        if(list.charAt(i)==delimiter) {
1344                                if(!ignoreEmpty || last<i)count++;
1345                                last=i+1;
1346                        }
1347                }
1348                if(!ignoreEmpty || last<len)count++;
1349                return count;
1350        }
1351        
1352        /**
1353         * returns count of items in the list
1354         * @param list
1355         * @param delimiter
1356         * @return list len
1357         */
1358        public static int len(String list, String delimiter, boolean ignoreEmpty) {
1359            if(delimiter.length()==1)return len(list, delimiter.charAt(0),ignoreEmpty);
1360                char[] del=delimiter.toCharArray();
1361            int len=StringUtil.length(list);
1362                if(len==0) return 0;
1363                
1364                int count=0;
1365                int last=0;
1366                char c;
1367                
1368                for(int i=0;i<len;i++) {
1369                    c=list.charAt(i);
1370                    for(int y=0;y<del.length;y++) {
1371                                if(c==del[y]) {
1372                                    if(!ignoreEmpty || last<i)count++;
1373                                        last=i+1;
1374                                        break;
1375                                }
1376                    }
1377                }
1378                if(!ignoreEmpty || last<len)count++;
1379                return count;
1380        }
1381        
1382        /* *
1383         * cast a int into a char
1384         * @param i int to cast
1385         * @return int as char
1386         * /
1387        private char c(int i) {
1388            return (char)i;
1389        }*/
1390        
1391        /**
1392         * gets a value from list
1393         * @param list list to cast
1394         * @param delimiter delimter of the list
1395         * @param position
1396         * @return Array Object
1397         */
1398        public static String getAt(String list, String delimiter, int position, boolean ignoreEmpty, String defaultValue) {
1399            if(delimiter.length()==1)return getAt(list, delimiter.charAt(0), position,ignoreEmpty,defaultValue);
1400                int len=list.length();
1401                
1402                if(len==0) return defaultValue;
1403                int last=-1;
1404                int count=-1;
1405                
1406                
1407                char[] del = delimiter.toCharArray();
1408                char c;
1409                for(int i=0;i<len;i++) {
1410                        c=list.charAt(i);
1411                    for(int y=0;y<del.length;y++) {
1412                                if(c==del[y]) {
1413                                        if(ignoreEmpty && (last+1)==i) {
1414                                                last=i;
1415                                                break; 
1416                                        }
1417                                        
1418                                        count++;
1419                                        if(count==position) {
1420                                                return list.substring(last+1,i);
1421                                        }
1422                                        last=i;
1423                                        break;
1424                                }
1425                    }
1426                }
1427
1428                if(position==count+1){
1429                        if(!ignoreEmpty || last+1<len) return list.substring(last+1);
1430                }
1431                return defaultValue;
1432        }
1433        
1434        /**
1435         * get a elemnt at a specified position in list
1436         * @param list list to cast
1437         * @param delimiter delimter of the list
1438         * @param position
1439         * @return Array Object
1440         */
1441        public static String getAt(String list, char delimiter, int position, boolean ignoreEmpty, String defaultValue) {
1442                int len=list.length();
1443                if(len==0) return defaultValue;
1444                int last=-1;
1445                int count=-1;
1446                
1447                for(int i=0;i<len;i++) {
1448                        // char == delimiter
1449                        if(list.charAt(i)==delimiter) {
1450                                if(ignoreEmpty && (last+1)==i) {
1451                                        last=i;
1452                                        continue; 
1453                                }
1454                                
1455                                count++;
1456                                if(count==position) {
1457                                        return list.substring(last+1,i);
1458                                }
1459                                last=i;
1460                        }
1461                }
1462
1463                if(position==count+1){
1464                        if(!ignoreEmpty || last+1<len) return list.substring(last+1);
1465                }
1466                return defaultValue;
1467        }
1468
1469        public static String[] listToStringArray(String list, char delimiter) {
1470                Array array = ListUtil.listToArrayRemoveEmpty(list,delimiter);
1471                String[] arr=new String[array.size()];
1472        for(int i=0;i<arr.length;i++) {
1473            arr[i]=Caster.toString(array.get(i+1,""),"");
1474        }
1475        return arr;
1476        }
1477
1478        /**
1479         * trim every single item of the array
1480         * @param arr
1481         * @return
1482         */
1483        public static String[] trimItems(String[] arr) {
1484                for(int i=0;i<arr.length;i++) {
1485                        arr[i]=arr[i].trim();
1486                }
1487                return arr;
1488        }
1489
1490        /**
1491         * trim every single item of the array
1492         * @param arr
1493         * @return
1494         * @throws PageException 
1495         */
1496        public static Array trimItems(Array arr) throws PageException {
1497                Key[] keys = CollectionUtil.keys(arr);
1498                
1499                for(int i=0;i<keys.length;i++) {
1500                        arr.setEL(keys[i], Caster.toString(arr.get(keys[i],null)).trim());
1501                }
1502                return arr;
1503        }
1504        
1505        public static Set<String> listToSet(String list, String delimiter,boolean trim) {
1506            if(list.length()==0) return new HashSet<String>();
1507                int len=list.length();
1508                int last=0;
1509                char[] del=delimiter.toCharArray();
1510                char c;
1511                
1512                HashSet<String> set=new HashSet<String>();
1513                for(int i=0;i<len;i++) {
1514                    c=list.charAt(i);
1515                    for(int y=0;y<del.length;y++) {
1516                                if(c==del[y]) {
1517                                        set.add(trim?list.substring(last,i).trim():list.substring(last,i));
1518                                        last=i+1;
1519                                        break;
1520                                }
1521                    }
1522                }
1523                if(last<=len)set.add(list.substring(last));
1524                return set;
1525        }
1526
1527        public static Set<String> listToSet(String list, char delimiter,boolean trim) {
1528            if(list.length()==0) return new HashSet<String>();
1529                int len=list.length();
1530                int last=0;
1531                char c;
1532                
1533                HashSet<String> set=new HashSet<String>();
1534                for(int i=0;i<len;i++) {
1535                    c=list.charAt(i);
1536                                if(c==delimiter) {
1537                                        set.add(trim?list.substring(last,i).trim():list.substring(last,i));
1538                                        last=i+1;
1539                                }
1540                }
1541                if(last<=len)set.add(list.substring(last));
1542                return set;
1543        }
1544
1545        
1546        public static Set<String> toSet(String[] arr) {
1547                Set<String> set=new HashSet<String>();
1548                
1549                for(int i=0;i<arr.length;i++){
1550                        set.add(arr[i]);
1551                }
1552                return set;
1553        }
1554
1555
1556        public static String first(String list, String delimiters, boolean ignoreEmpty, int count) {
1557
1558                if (count < 1)
1559                        return "";
1560
1561                char[] delims = StringUtil.isEmpty(delimiters) ? new char[]{','} : delimiters.toCharArray();
1562
1563                int ix = getDelimIndex(list, count, delims, ignoreEmpty);
1564
1565                if (ix == -1)
1566                        return list;
1567
1568                String result = list.substring(0, ix);
1569
1570                if (ignoreEmpty)
1571                        result = ltrim(result, delims);
1572
1573                return result;
1574        }
1575
1576        public static String first(String list, String delimiters, boolean ignoreEmpty) {
1577
1578                return first(list, delimiters, ignoreEmpty, 1);
1579        }
1580
1581
1582        public static String rest(String list, String delimiters, boolean ignoreEmpty, int offset) {
1583
1584                if (offset < 1)
1585                        return list;
1586
1587                char[] delims = StringUtil.isEmpty(delimiters) ? new char[]{','} : delimiters.toCharArray();
1588
1589                int ix = getDelimIndex(list, offset, delims, ignoreEmpty);
1590
1591                if (ix == -1 || ix >= list.length() - 1)
1592                        return "";
1593
1594                String result = list.substring(ix + 1);
1595
1596                if (ignoreEmpty)
1597                        result = ltrim(result, delims);
1598
1599                return result;
1600        }
1601
1602        public static String rest(String list, String delimiters, boolean ignoreEmpty) {
1603
1604                return rest(list, delimiters, ignoreEmpty, 1);
1605        }
1606
1607        /**
1608         * returns the 0-based delimiter position for the specified item
1609         *
1610         * @param list
1611         * @param itemPos
1612         * @param ignoreEmpty
1613         * @return
1614         */
1615        public static int getDelimIndex(String list, int itemPos, char[] delims, boolean ignoreEmpty) {
1616
1617                if (StringUtil.isEmpty(list))                   return -1;
1618
1619                int last = -1, curr = -1, listIndex = 0;
1620                int len  = list.length();
1621
1622                for (int i=0; i<len; i++) {
1623
1624                        if (contains(delims, list.charAt(i))) {
1625
1626                                curr = i;
1627
1628                                if (ignoreEmpty) {
1629
1630                                        if (curr == last + 1) {
1631                                                last = curr;
1632                                                continue;
1633                                        }
1634
1635                                        last = curr;
1636                                }
1637
1638                                if (++listIndex == itemPos)
1639                                        break;
1640                        }
1641                }
1642
1643                if (listIndex < itemPos)
1644                        return len;
1645
1646                return curr;
1647        }
1648
1649        private static boolean contains(char[] carr, char c) {
1650
1651                for (char ca : carr) {
1652                        if (ca == c)
1653                                return true;
1654                }
1655
1656                return false;
1657        }
1658
1659}