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.sql.Types;
022import java.util.ArrayList;
023import java.util.Arrays;
024import java.util.Comparator;
025import java.util.Iterator;
026import java.util.List;
027import java.util.ListIterator;
028import java.util.Map;
029import java.util.Set;
030
031import lucee.commons.lang.ArrayUtilException;
032import lucee.commons.lang.ComparatorUtil;
033import lucee.commons.lang.SizeOf;
034import lucee.commons.lang.StringUtil;
035import lucee.commons.math.MathUtil;
036import lucee.runtime.PageContext;
037import lucee.runtime.engine.ThreadLocalPageContext;
038import lucee.runtime.exp.CasterException;
039import lucee.runtime.exp.ExpressionException;
040import lucee.runtime.exp.PageException;
041import lucee.runtime.op.Caster;
042import lucee.runtime.op.Decision;
043import lucee.runtime.op.Operator;
044import lucee.runtime.type.Array;
045import lucee.runtime.type.QueryColumn;
046import lucee.runtime.type.comparator.SortRegister;
047
048/**
049 * Util for diffrent methods to manipulate arrays
050 */
051public final class ArrayUtil {
052    
053    public static final Object[] OBJECT_EMPTY = new Object[]{};
054        
055    /**
056     * trims all value of a String Array
057     * @param arr
058     * @return trimmed array
059     */
060    public static String[] trim(String[] arr) {
061        for(int i=0;i<arr.length;i++) {
062            arr[i]=arr[i].trim();
063        }
064        return arr;
065    }
066    
067    
068        /**
069         * @param list
070         * @return array
071         */
072        public static SortRegister[] toSortRegisterArray(ArrayList list) {
073                SortRegister[] arr=new SortRegister[list.size()];
074                for(int i=0;i<arr.length;i++) {
075                        arr[i]=new SortRegister(i,list.get(i));
076                }
077                return arr;
078        }
079
080        /**
081         * @param column
082         * @return array
083         */
084        public static SortRegister[] toSortRegisterArray(QueryColumn column) {
085                SortRegister[] arr=new SortRegister[column.size()];
086                int type = column.getType();
087                for(int i=0;i<arr.length;i++) {
088                        arr[i]=new SortRegister(i,toSortRegisterArray(column.get(i+1,null),type));
089                }
090                return arr;
091        }
092        
093        private static Object toSortRegisterArray(Object value, int type) {
094                
095                Object mod=null;
096            // Date
097            if(Types.TIMESTAMP==type) {
098                mod= Caster.toDate(value, true, null,null);
099            }
100            // Double
101            else if(Types.DOUBLE==type) {
102                mod= Caster.toDouble(value,null);
103            }
104            // Boolean
105            else if(Types.BOOLEAN==type) {
106                mod= Caster.toBoolean(value,null);
107            }
108            // Varchar
109            else if(Types.VARCHAR==type) {
110                mod= Caster.toString(value,null);
111            }
112            else return value;
113            
114            if(mod!=null) return mod;
115            return value;
116        }
117        
118        /**
119         * swap to values of the array
120         * @param array
121         * @param left left value to swap
122         * @param right right value to swap
123         * @throws ExpressionException
124         */
125        public static void swap(Array array, int left, int right) throws ExpressionException {
126                int len=array.size();
127                
128                if(len==0)
129                        throw new ExpressionException("array is empty");
130                if(left<1 || left>len)
131                        throw new ExpressionException("invalid index ["+left+"]","valid indexes are from 1 to "+len);
132                if(right<1 || right>len)
133                        throw new ExpressionException("invalid index ["+right+"]","valid indexes are from 1 to "+len);
134                
135                
136                try {
137                        Object leftValue=array.get(left,null);
138                        Object rightValue=array.get(right,null);
139                        
140                        array.setE(left,rightValue);
141                        array.setE(right,leftValue);
142                } catch (PageException e) {
143                        throw new ExpressionException("can't swap values of array",e.getMessage());
144                }
145                
146        }
147        
148        /**
149         * find a object in array
150         * @param array
151         * @param object object to find
152         * @return position in array or 0
153         */
154        public static int find(Array array, Object object) {
155                int len=array.size();
156                for(int i=1;i<=len;i++) {
157                        Object tmp=array.get(i,null);
158                        try {
159                                if(tmp !=null && Operator.compare(object,tmp)==0)
160                                        return i;
161                        } catch (PageException e) {}
162                }
163                return 0;
164        }
165        
166        /**
167         * average of all values of the array, only work when all values are numeric
168         * @param array
169         * @return average of all values
170         * @throws ExpressionException
171        */
172        public static double avg(Array array) throws ExpressionException {
173                if(array.size()==0)return 0;
174                return sum(array)/array.size();
175        }
176        
177        /**
178         * sum of all values of a array, only work when all values are numeric
179         * @param array Array 
180         * @return sum of all values
181         * @throws ExpressionException
182        */
183        public static double sum(Array array) throws ExpressionException {
184                if(array.getDimension()>1)
185                        throw new ExpressionException("can only get sum/avg from 1 dimensional arrays");
186                
187                double rtn=0;
188                int len=array.size();
189                //try {                 
190                        for(int i=1;i<=len;i++) {
191                                rtn+=_toDoubleValue(array,i);
192                        }
193                /*} 
194                catch (PageException e) {
195                        throw new ExpressionException("exception while execute array operation: "+e.getMessage());
196                }*/
197                return rtn;
198        }
199
200        /**
201         * median value of all items in the arrays, only works when all values are numeric
202         *
203         * @param array
204         * @return
205         * @throws ExpressionException
206         */
207        public static double median(Array array) throws ExpressionException {
208
209                int len = array.size();
210
211                if (len == 0)                   return 0;
212
213                if (array.getDimension() > 1)
214                        throw new ExpressionException("Median() can only be calculated for one dimensional arrays");
215
216                double[] arr = new double[len];
217
218                for (int i=0; i < len; i++)
219                        arr[i] = _toDoubleValue(array, i+1);
220
221                Arrays.sort(arr);
222
223                double result = arr[ len / 2 ];
224
225                if (len % 2 == 0) {
226
227                        return ( result + arr[ (len-2) / 2 ] ) / 2;
228                }
229
230                return result;
231        }
232
233        private static double _toDoubleValue(Array array, int i) throws ExpressionException {
234                Object obj = array.get(i,null);
235                if(obj==null)throw new ExpressionException("there is no element at position ["+i+"] or the element is null");
236                double tmp = Caster.toDoubleValue(obj,true,Double.NaN);
237                if(Double.isNaN(tmp))
238                        throw new CasterException(obj,Double.class);
239                return tmp;
240        }
241
242
243        /**
244         * the smallest value, of all values inside the array, only work when all values are numeric
245         * @param array
246         * @return the smallest value
247         * @throws PageException 
248        */
249        public static double min(Array array) throws PageException {
250                if(array.getDimension()>1)
251                        throw new ExpressionException("can only get max value from 1 dimensional arrays");
252                if(array.size()==0) return 0;
253                
254                double rtn=_toDoubleValue(array,1);
255                int len=array.size();
256                try {
257                        for(int i=2;i<=len;i++) {
258                                double v=_toDoubleValue(array,i);
259                                if(rtn>v)rtn=v;
260                                
261                        }
262                } catch (PageException e) {
263                        throw new ExpressionException("exception while execute array operation: "+e.getMessage());
264                }
265                return rtn;
266        }
267        
268        /**
269         * the greatest value, of all values inside the array, only work when all values are numeric
270         * @param array
271         * @return the greatest value
272         * @throws PageException 
273        */
274        public static double max(Array array) throws PageException {
275                if(array.getDimension()>1)
276                        throw new ExpressionException("can only get max value from 1 dimensional arrays");
277                if(array.size()==0) return 0;
278                
279                double rtn=_toDoubleValue(array,1);
280                int len=array.size();
281                try {
282                        for(int i=2;i<=len;i++) {
283                                double v=_toDoubleValue(array,i);
284                                if(rtn<v)rtn=v;
285                                
286                        }
287                } catch (PageException e) {
288                        throw new ExpressionException("exception while execute array operation: "+e.getMessage());
289                }
290                return rtn;
291        }
292        
293        /**
294         * return index of given value in Array or -1
295         * @param arr
296         * @param value
297         * @return index of position in array
298         */
299        public static int indexOf(String[] arr, String value) {
300            for(int i=0;i<arr.length;i++) {
301                if(arr[i].equals(value)) return i;
302            }
303            return -1;
304        }
305        
306        /**
307         * return index of given value in Array or -1
308         * @param arr
309         * @param value
310         * @return index of position in array
311         */
312        public static int indexOfIgnoreCase(String[] arr, String value) {
313            for(int i=0;i<arr.length;i++) {
314                if(arr[i].equalsIgnoreCase(value)) return i;
315            }
316            return -1;
317        }
318        
319        
320
321        /**
322         * convert a primitive array (value type) to Object Array (reference type).
323         * @param primArr value type Array 
324         * @return reference type Array
325         */
326        public static Boolean[] toReferenceType(boolean[] primArr) {
327                Boolean[] refArr=new Boolean[primArr.length];
328                for(int i=0;i<primArr.length;i++)refArr[i]=Caster.toBoolean(primArr[i]);
329                return refArr;
330        }
331        
332        /**
333         * convert a primitive array (value type) to Object Array (reference type).
334         * @param primArr value type Array 
335         * @return reference type Array
336         */
337        public static Byte[] toReferenceType(byte[] primArr) {
338                Byte[] refArr=new Byte[primArr.length];
339                for(int i=0;i<primArr.length;i++)refArr[i]=new Byte(primArr[i]);
340                return refArr;
341        }
342        
343        /**
344         * convert a primitive array (value type) to Object Array (reference type).
345         * @param primArr value type Array 
346         * @return reference type Array
347         */
348        public static Character[] toReferenceType(char[] primArr) {
349                Character[] refArr=new Character[primArr.length];
350                for(int i=0;i<primArr.length;i++)refArr[i]=new Character(primArr[i]);
351                return refArr;
352        }
353        
354        /**
355         * convert a primitive array (value type) to Object Array (reference type).
356         * @param primArr value type Array 
357         * @return reference type Array
358         */
359        public static Short[] toReferenceType(short[] primArr) {
360                Short[] refArr=new Short[primArr.length];
361                for(int i=0;i<primArr.length;i++)refArr[i]=Short.valueOf(primArr[i]);
362                return refArr;
363        }
364        
365        /**
366         * convert a primitive array (value type) to Object Array (reference type).
367         * @param primArr value type Array 
368         * @return reference type Array
369         */
370        public static Integer[] toReferenceType(int[] primArr) {
371                Integer[] refArr=new Integer[primArr.length];
372                for(int i=0;i<primArr.length;i++)refArr[i]=Integer.valueOf(primArr[i]);
373                return refArr;
374        }
375        
376        /**
377         * convert a primitive array (value type) to Object Array (reference type).
378         * @param primArr value type Array 
379         * @return reference type Array
380         */
381        public static Long[] toReferenceType(long[] primArr) {
382                Long[] refArr=new Long[primArr.length];
383                for(int i=0;i<primArr.length;i++)refArr[i]=Long.valueOf(primArr[i]);
384                return refArr;
385        }
386        
387        /**
388         * convert a primitive array (value type) to Object Array (reference type).
389         * @param primArr value type Array 
390         * @return reference type Array
391         */
392        public static Float[] toReferenceType(float[] primArr) {
393                Float[] refArr=new Float[primArr.length];
394                for(int i=0;i<primArr.length;i++)refArr[i]=new Float(primArr[i]);
395                return refArr;
396        }
397        
398        /**
399         * convert a primitive array (value type) to Object Array (reference type).
400         * @param primArr value type Array 
401         * @return reference type Array
402         */
403        public static Double[] toReferenceType(double[] primArr) {
404                Double[] refArr=new Double[primArr.length];
405                for(int i=0;i<primArr.length;i++)refArr[i]=new Double(primArr[i]);
406                return refArr;
407        }
408
409        /**
410         * gets a value of a array at defined index
411         * @param o
412         * @param index
413         * @return value at index position
414         * @throws ArrayUtilException
415         */
416        public static Object get(Object o,int index) throws ArrayUtilException {
417            o=get(o,index,null);
418                if(o!=null) return o;
419                throw new ArrayUtilException("Object is not a array, or index is invalid");
420        }
421        
422        /**
423         * gets a value of a array at defined index
424         * @param o
425         * @param index
426         * @return value of the variable
427         */
428        public static Object get(Object o,int index, Object defaultValue) {
429            if(index<0) return null;
430            if(o instanceof Object[])   {
431                    Object[] arr=((Object[])o);
432                    if(arr.length>index)return arr[index];
433                }
434            else if(o instanceof boolean[])     {
435                boolean[] arr=((boolean[])o);
436                    if(arr.length>index)return arr[index]?Boolean.TRUE:Boolean.FALSE;
437                }
438            else if(o instanceof byte[])        {
439                byte[] arr=((byte[])o);
440                    if(arr.length>index)return new Byte(arr[index]);
441                }
442            else if(o instanceof char[])        {
443                char[] arr=((char[])o);
444                    if(arr.length>index)return ""+(arr[index]);
445                }
446            else if(o instanceof short[])       {
447                short[] arr=((short[])o);
448                    if(arr.length>index)return Short.valueOf(arr[index]);
449                }
450            else if(o instanceof int[]) {
451                int[] arr=((int[])o);
452                    if(arr.length>index)return Integer.valueOf(arr[index]);
453                }
454            else if(o instanceof long[])        {
455                long[] arr=((long[])o);
456                    if(arr.length>index)return Long.valueOf(arr[index]);
457                }
458            else if(o instanceof float[])       {
459                float[] arr=((float[])o);
460                    if(arr.length>index)return new Float(arr[index]);
461                }
462            else if(o instanceof double[])      {
463                double[] arr=((double[])o);
464                    if(arr.length>index)return new Double(arr[index]);
465                }
466                return defaultValue;
467        }
468
469        /**
470         * sets a value to a array at defined index
471         * @param o
472         * @param index
473         * @param value
474         * @return value setted
475         * @throws ArrayUtilException
476         */
477        public static Object set(Object o,int index, Object value) throws ArrayUtilException {
478            if(index<0) 
479                    throw invalidIndex(index,0);
480            if(o instanceof Object[])   {
481                    Object[] arr=((Object[])o);
482                    if(arr.length>index)return arr[index]=value;
483                    throw invalidIndex(index,arr.length);
484                }
485            else if(o instanceof boolean[])     {
486                boolean[] arr=((boolean[])o);
487                if(arr.length>index) {
488                    arr[index]=Caster.toBooleanValue(value,false);
489                    return arr[index]?Boolean.TRUE:Boolean.FALSE;
490                }
491                    throw invalidIndex(index,arr.length);
492                }
493            else if(o instanceof byte[])        {
494                byte[] arr=((byte[])o);
495                if(arr.length>index) {
496                    double v=Caster.toDoubleValue(value,true,Double.NaN);
497                    if(Decision.isValid(v)) {
498                        return new Byte(arr[index]=(byte)v);
499                    }
500                }
501                    throw invalidIndex(index,arr.length);
502                }
503            else if(o instanceof short[])       {
504                short[] arr=((short[])o);
505                if(arr.length>index) {
506                    double v=Caster.toDoubleValue(value,true,Double.NaN);
507                    if(Decision.isValid(v)) {
508                        return Short.valueOf(arr[index]=(short)v);
509                    }
510                }
511                    throw invalidIndex(index,arr.length);
512                }
513            else if(o instanceof int[]) {
514                int[] arr=((int[])o);
515                if(arr.length>index) {
516                    double v=Caster.toDoubleValue(value,true,Double.NaN);
517                    if(Decision.isValid(v)) {
518                        return Integer.valueOf(arr[index]=(int)v);
519                    }
520                }
521                    throw invalidIndex(index,arr.length);
522                }
523            else if(o instanceof long[])        {
524                long[] arr=((long[])o);
525                if(arr.length>index) {
526                    double v=Caster.toDoubleValue(value,true,Double.NaN);
527                    if(Decision.isValid(v)) {
528                        return Long.valueOf(arr[index]=(long)v);
529                    }
530                }
531                    throw invalidIndex(index,arr.length);
532                }
533            else if(o instanceof float[])       {
534                float[] arr=((float[])o);
535                if(arr.length>index) {
536                    double v=Caster.toDoubleValue(value,true,Double.NaN);
537                    if(Decision.isValid(v)) {
538                        return new Float(arr[index]=(float)v);
539                    }
540                }
541                    throw invalidIndex(index,arr.length);
542                }
543            else if(o instanceof double[])      {
544                double[] arr=((double[])o);
545                if(arr.length>index) {
546                    double v=Caster.toDoubleValue(value,true,Double.NaN);
547                    if(Decision.isValid(v)) {
548                        return new Double(arr[index]=v);
549                    }
550                }
551                    throw invalidIndex(index,arr.length);
552                }
553            else if(o instanceof char[])        {
554                char[] arr=((char[])o);
555                if(arr.length>index) {
556                    String str=Caster.toString(value,null);
557                    if(str!=null && str.length()>0) {
558                        char c=str.charAt(0);
559                        arr[index]=c;
560                        return str;
561                    }
562                }
563                    throw invalidIndex(index,arr.length);
564                }
565                throw new ArrayUtilException("Object ["+Caster.toClassName(o)+"] is not a Array");
566        }
567
568        
569    private static ArrayUtilException invalidIndex(int index, int length) {
570        return new ArrayUtilException("Invalid index ["+index+"] for native Array call, Array has a Size of "+length);
571    }
572
573    /**
574         * sets a value to a array at defined index
575         * @param o
576         * @param index
577         * @param value
578         * @return value setted
579         */
580        public static Object setEL(Object o,int index, Object value) {
581            try {
582            return set(o,index,value);
583        } catch (ArrayUtilException e) {
584            return null;
585        }
586        }
587
588        public static boolean isEmpty(List list) {
589                return list==null || list.isEmpty();
590        }
591        
592        public static boolean isEmpty(Object[] array) {
593                return array==null || array.length==0;
594        }
595        public static boolean isEmpty(boolean[] array) {
596                return array==null || array.length==0;
597        }
598        public static boolean isEmpty(char[] array) {
599                return array==null || array.length==0;
600        }
601        public static boolean isEmpty(double[] array) {
602                return array==null || array.length==0;
603        }
604        public static boolean isEmpty(long[] array) {
605                return array==null || array.length==0;
606        }
607        public static boolean isEmpty(int[] array) {
608                return array==null || array.length==0;
609        }
610        public static boolean isEmpty(float[] array) {
611                return array==null || array.length==0;
612        }
613        public static boolean isEmpty(byte[] array) {
614                return array==null || array.length==0;
615        }
616
617        
618        
619        
620        
621        
622        
623
624
625
626        public static int size(Object[] array) {
627                if(array==null) return 0; 
628                return  array.length;
629        }
630        public static int size(boolean[] array) {
631                if(array==null) return 0; 
632                return  array.length;
633        }
634        public static int size(char[] array) {
635                if(array==null) return 0; 
636                return  array.length;
637        }
638        public static int size(double[] array) {
639                if(array==null) return 0; 
640                return  array.length;
641        }
642        public static int size(long[] array) {
643                if(array==null) return 0; 
644                return  array.length;
645        }
646        public static int size(int[] array) {
647                if(array==null) return 0; 
648                return  array.length;
649        }
650        public static int size(float[] array) {
651                if(array==null) return 0; 
652                return  array.length;
653        }
654        public static int size(byte[] array) {
655                if(array==null) return 0; 
656                return  array.length;
657        }
658
659
660        public static boolean[] toBooleanArray(Object obj) throws PageException {
661                if(obj instanceof boolean[]) return (boolean[]) obj;
662                
663                Array arr = Caster.toArray(obj);
664                boolean[] tarr=new boolean[arr.size()];
665                for(int i=0;i<tarr.length;i++) {
666                        tarr[i]=Caster.toBooleanValue(arr.getE(i+1));
667                }
668                return tarr;
669        }
670
671        public static byte[] toByteArray(Object obj) throws PageException {
672                if(obj instanceof byte[]) return (byte[]) obj;
673                
674                Array arr = Caster.toArray(obj);
675                byte[] tarr=new byte[arr.size()];
676                for(int i=0;i<tarr.length;i++) {
677                        tarr[i]=Caster.toByteValue(arr.getE(i+1));
678                }
679                return tarr;
680        }
681        
682        public static short[] toShortArray(Object obj) throws PageException {
683                if(obj instanceof short[]) return (short[]) obj;
684                
685                Array arr = Caster.toArray(obj);
686                short[] tarr=new short[arr.size()];
687                for(int i=0;i<tarr.length;i++) {
688                        tarr[i]=Caster.toShortValue(arr.getE(i+1));
689                }
690                return tarr;
691        }
692        
693        public static int[] toIntArray(Object obj) throws PageException {
694                if(obj instanceof int[]) return (int[]) obj;
695                
696                Array arr = Caster.toArray(obj);
697                int[] tarr=new int[arr.size()];
698                for(int i=0;i<tarr.length;i++) {
699                        tarr[i]=Caster.toIntValue(arr.getE(i+1));
700                }
701                return tarr;
702        }
703        
704        public static Object[] toNullArray(Object obj) throws PageException {
705                Array arr = Caster.toArray(obj);
706                Object[] tarr=new Object[arr.size()];
707                for(int i=0;i<tarr.length;i++) {
708                        tarr[i]=Caster.toNull(arr.getE(i+1));
709                }
710                return tarr;
711        }
712        
713        public static long[] toLongArray(Object obj) throws PageException {
714                if(obj instanceof long[]) return (long[]) obj;
715                
716                Array arr = Caster.toArray(obj);
717                long[] tarr=new long[arr.size()];
718                for(int i=0;i<tarr.length;i++) {
719                        tarr[i]=Caster.toLongValue(arr.getE(i+1));
720                }
721                return tarr;
722        }
723
724        public static float[] toFloatArray(Object obj) throws PageException {
725                if(obj instanceof float[]) return (float[]) obj;
726                
727                Array arr = Caster.toArray(obj);
728                float[] tarr=new float[arr.size()];
729                for(int i=0;i<tarr.length;i++) {
730                        tarr[i]=Caster.toFloatValue(arr.getE(i+1));
731                }
732                return tarr;
733        }
734        
735        public static double[] toDoubleArray(Object obj) throws PageException {
736                if(obj instanceof double[]) return (double[]) obj;
737                
738                Array arr = Caster.toArray(obj);
739                double[] tarr=new double[arr.size()];
740                for(int i=0;i<tarr.length;i++) {
741                        tarr[i]=Caster.toDoubleValue(arr.getE(i+1));
742                }
743                return tarr;
744        }
745        
746        public static char[] toCharArray(Object obj) throws PageException {
747                if(obj instanceof char[]) return (char[]) obj;
748                
749                Array arr = Caster.toArray(obj);
750                char[] tarr=new char[arr.size()];
751                for(int i=0;i<tarr.length;i++) {
752                        tarr[i]=Caster.toCharValue(arr.getE(i+1));
753                }
754                return tarr;
755        }
756
757
758        public static int arrayContainsIgnoreEmpty(Array arr, String value, boolean ignoreCase) {
759                int count=0;
760                int len=arr.size();
761
762                for(int i=1;i<=len;i++) {
763                        String item=Caster.toString(arr.get(i,""),"");
764                        if(ignoreCase) {
765                                if(StringUtil.indexOfIgnoreCase(item,value)!=-1) return count;
766                        }
767                        else {
768                                if(item.indexOf(value)!=-1) return count;
769                        }
770                        count++;
771                }
772                return -1;
773        }
774
775
776        public static Object[] toReferenceType(Object obj) throws CasterException {
777                Object[] ref = toReferenceType(obj,null);
778                if(ref!=null) return ref;
779                throw new CasterException(obj,Object[].class);
780                
781        }
782        public static Object[] toReferenceType(Object obj,Object[] defaultValue) {
783                if(obj instanceof Object[])                     return (Object[])obj;
784                else if(obj instanceof boolean[])               return toReferenceType((boolean[])obj);
785                else if(obj instanceof byte[])          return toReferenceType((byte[])obj);
786                else if(obj instanceof char[])          return toReferenceType((char[])obj);
787                else if(obj instanceof short[])         return toReferenceType((short[])obj);
788                else if(obj instanceof int[])                   return toReferenceType((int[])obj);
789                else if(obj instanceof long[])          return toReferenceType((long[])obj);
790                else if(obj instanceof float[])         return toReferenceType((float[])obj);
791                else if(obj instanceof double[])                return toReferenceType((double[])obj);
792                return defaultValue;
793        }
794
795
796        public static Object[] clone(Object[] src, Object[] trg) {
797                for(int i=0;i<src.length;i++){
798                        trg[i]=src[i];
799                }
800                return trg;
801        }
802
803
804        public static Object[] keys(Map map) {
805                if(map==null) return new Object[0];
806                Set set = map.keySet();
807                if(set==null) return new Object[0];
808                Object[] arr = set.toArray();
809                if(arr==null) return new Object[0];
810                return arr;
811        }
812        
813        public static Object[] values(Map map) {
814                if(map==null) return new Object[0];
815                return map.values().toArray();
816        }
817
818
819        public static long sizeOf(List list) {
820                ListIterator it = list.listIterator();
821                long size=0;
822                while(it.hasNext()){
823                        size+=SizeOf.size(it.next());
824                }
825                return size;
826        }
827        
828        public static long sizeOf(Array array) {
829                Iterator it = array.valueIterator();
830                long size=0;
831                while(it.hasNext()){
832                        size+=SizeOf.size(it.next());
833                }
834                return size;
835        }
836
837
838        /**
839         * creates a native array out of the input list, if all values are from the same type, this type is used for the array, otherwise object
840         * @param list
841         */
842        public static Object[] toArray(List<?> list) {
843                Iterator<?> it = list.iterator();
844                Class clazz=null;
845                while(it.hasNext()){
846                        Object v = it.next();
847                        if(v==null) continue;
848                        if(clazz==null) clazz=v.getClass();
849                        else if(clazz!=v.getClass()) return list.toArray();     
850                }
851                if(clazz==Object.class || clazz==null) 
852                        return list.toArray();
853                
854                Object arr = java.lang.reflect.Array.newInstance(clazz, list.size());
855                return list.toArray((Object[]) arr);    
856        }
857        
858
859        
860        public static Comparator toComparator(PageContext pc,String strSortType, String sortOrder, boolean localeSensitive) throws PageException {
861                
862                // check order
863                boolean isAsc=true;
864                if(sortOrder.equalsIgnoreCase("asc"))isAsc=true;
865                else if(sortOrder.equalsIgnoreCase("desc"))isAsc=false;
866                else throw new ExpressionException("invalid sort order type ["+sortOrder+"], sort order types are [asc and desc]");
867                
868                // check type
869                int sortType;
870                if(strSortType.equalsIgnoreCase("text")) sortType=ComparatorUtil.SORT_TYPE_TEXT;
871                else if(strSortType.equalsIgnoreCase("textnocase")) sortType=ComparatorUtil.SORT_TYPE_TEXT_NO_CASE;
872                else if(strSortType.equalsIgnoreCase("numeric")) sortType=ComparatorUtil.SORT_TYPE_NUMBER;
873                else 
874                        throw new ExpressionException("invalid sort type ["+strSortType+"], sort types are [text, textNoCase, numeric]");
875                
876                
877                return ComparatorUtil.toComparator(sortType, isAsc, localeSensitive?ThreadLocalPageContext.getLocale(pc):null, null);
878                        
879        }
880        
881        
882
883        public static <E> List<E> merge(E[] a1, E[] a2) {
884                List<E> list=new ArrayList<E>();
885                for(int i=0;i<a1.length;i++){
886                        list.add(a1[i]);
887                }
888                for(int i=0;i<a2.length;i++){
889                        list.add(a2[i]);
890                }
891                return list;
892        }
893
894
895        /**
896         * this method efficiently copy the contents of one native array into another by using System.arraycopy()
897         *
898         * @param dst - the array that will be modified
899         * @param src - the data to be copied
900         * @param dstPosition - pass -1 to append to the end of the dst array, or a valid position to add it elsewhere
901         * @param doPowerOf2 - if true, and the array needs to be resized, it will be resized to the next power of 2 size
902         * @return - either the original dst array if it had enough capacity, or a new array.
903         */
904        public static Object[] mergeNativeArrays(Object[] dst, Object[] src, int dstPosition, boolean doPowerOf2) {
905
906                if (dstPosition < 0)
907                        dstPosition = dst.length;
908
909                Object[] result = resizeIfNeeded(dst, dstPosition + src.length, doPowerOf2);
910
911                System.arraycopy(src, 0, result, dstPosition, src.length);
912
913                return result;
914        }
915
916
917        /**
918         * this method returns the original array if its length is equal or greater than the minSize, or create a new array
919         * and copies the data from the original array into the new one.
920         *
921         * @param arr - the array to check
922         * @param minSize - the required minimum size
923         * @param doPowerOf2 - if true, and a resize is required, the new size will be a power of 2
924         * @return - either the original arr array if it had enough capacity, or a new array.
925         */
926        public static Object[] resizeIfNeeded(Object[] arr, int minSize, boolean doPowerOf2) {
927
928                if (arr.length >= minSize)
929                        return arr;
930
931                if (doPowerOf2)
932                        minSize = MathUtil.nextPowerOf2(minSize);
933
934                Object[] result = new Object[minSize];
935                System.arraycopy(arr, 0, result, 0, arr.length);
936
937                return result;
938        }
939
940}