001    package railo.runtime.type;
002    
003    import java.util.ArrayList;
004    import java.util.Arrays;
005    import java.util.Comparator;
006    import java.util.Iterator;
007    import java.util.Map.Entry;
008    
009    import railo.commons.lang.SizeOf;
010    import railo.runtime.PageContext;
011    import railo.runtime.converter.LazyConverter;
012    import railo.runtime.dump.DumpData;
013    import railo.runtime.dump.DumpProperties;
014    import railo.runtime.dump.DumpTable;
015    import railo.runtime.dump.DumpUtil;
016    import railo.runtime.dump.SimpleDumpData;
017    import railo.runtime.exp.ExpressionException;
018    import railo.runtime.exp.PageException;
019    import railo.runtime.op.Caster;
020    import railo.runtime.op.Duplicator;
021    import railo.runtime.op.ThreadLocalDuplication;
022    import railo.runtime.type.it.EntryIterator;
023    import railo.runtime.type.it.KeyIterator;
024    import railo.runtime.type.it.StringIterator;
025    import railo.runtime.type.util.ArraySupport;
026    import railo.runtime.type.util.ArrayUtil;
027    
028    
029    
030    /**
031     * CFML array object
032     */
033    public final class ArrayImplNS extends ArraySupport implements Array,Sizeable {
034            
035            private Object[] arr;
036            private int dimension=1;
037            private final int cap=32;
038            private int size=0;
039            private int offset=0;
040            private int offCount=0;
041            
042            /**
043             * constructor with definiton of the dimension
044             * @param dimension dimension goes from one to 3
045             * @throws ExpressionException
046             */
047            public ArrayImplNS(int dimension) throws ExpressionException {
048                    if(dimension>3 || dimension<1)
049                            throw new ExpressionException("Array Dimension must be between 1 and 3");
050                    this.dimension=dimension;
051                    arr=new Object[offset+cap];
052            }
053            
054            /**
055             * constructor with default dimesnion (1)
056             */
057            public ArrayImplNS() {
058                    arr=new Object[offset+cap];
059            }
060            
061            /**
062             * constructor with to data to fill
063             * @param objects Objects array data to fill
064             */
065            public ArrayImplNS(Object[] objects) {
066                    arr=objects;
067                    size=arr.length;
068                    offset=0;
069            }
070            
071            /**
072             * return dimension of the array
073             * @return dimension of the array
074             */
075            public int getDimension() {
076                    return dimension;
077            }
078            
079            @Override
080            public Object get(String key) throws ExpressionException {
081            return getE(Caster.toIntValue(key));
082            }       
083    
084            @Override
085            public Object get(Collection.Key key) throws ExpressionException {
086            return getE(Caster.toIntValue(key.getString()));
087            }       
088            
089            @Override
090            public Object get(String key, Object defaultValue) {
091                    double index=Caster.toIntValue(key,Integer.MIN_VALUE);
092                    if(index==Integer.MIN_VALUE) return defaultValue;
093                return get((int)index,defaultValue);
094            }       
095            
096            @Override
097            public Object get(Collection.Key key, Object defaultValue) {
098                    double index=Caster.toIntValue(key.getString(),Integer.MIN_VALUE);
099                    if(index==Integer.MIN_VALUE) return defaultValue;
100                return get((int)index,defaultValue);
101            }               
102    
103            @Override
104            public Object get(int key, Object defaultValue) {
105                    if(key>size || key<1) {
106                            if(dimension>1) {
107                                    ArrayImplNS ai = new ArrayImplNS();
108                                    ai.dimension=dimension-1;
109                                    return setEL(key,ai);
110                            }
111                            return defaultValue;
112                    }
113                    Object o=arr[(offset+key)-1];
114                    
115                    if(o==null) {
116                            if(dimension>1) {
117                                    ArrayImplNS ai = new ArrayImplNS();
118                                    ai.dimension=dimension-1;
119                                    return setEL(key,ai);
120                            }
121                            return defaultValue;
122                    }
123                    return o;
124            }
125            
126            @Override
127            public Object getE(int key) throws ExpressionException {
128                    if(key<1) {
129                            throw invalidPosition(key);
130                    }
131                    else if(key>size) {
132                            if(dimension>1)return setE(key,new ArrayImplNS(dimension-1));
133                            throw invalidPosition(key);
134                    }
135                    
136                    Object o=arr[(offset+key)-1];
137                    
138                    if(o==null) {
139                            if(dimension>1) return setE(key,new ArrayImplNS(dimension-1));
140                            throw invalidPosition(key);
141                    }
142                    return o;
143            }
144            
145            /**
146             * Exception method if key doesn't exist at given position
147             * @param pos
148             * @return exception
149             */
150            private ExpressionException invalidPosition(int pos) {
151                    return new ExpressionException("Element at position ["+pos+"] doesn't exist in array");
152            }
153            
154            @Override
155            public Object setEL(String key, Object value) {
156                    try {
157                            return setEL(Caster.toIntValue(key), value);
158                    } catch (ExpressionException e) {
159                            return null;
160                    }
161            }
162            
163            @Override
164            public Object setEL(Collection.Key key, Object value) {
165                    try {
166                            return setEL(Caster.toIntValue(key.getString()), value);
167                    } catch (ExpressionException e) {
168                            return null;
169                    }
170            }
171            
172            @Override
173            public Object set(String key, Object value) throws ExpressionException {
174                    return setE(Caster.toIntValue(key),value);
175            }
176            
177            @Override
178            public Object set(Collection.Key key, Object value) throws ExpressionException {
179                    return setE(Caster.toIntValue(key.getString()),value);
180            }
181    
182            @Override
183            public Object setEL(int key, Object value) {
184                    if(offset+key>arr.length)enlargeCapacity(key);
185                    if(key>size)size=key;
186                    arr[(offset+key)-1]=checkValueEL(value);
187                    return value;
188            }
189            
190            /**
191             * set value at defined position
192             * @param key 
193             * @param value
194             * @return defined value
195             * @throws ExpressionException
196             */
197            public Object setE(int key, Object value) throws ExpressionException {
198                    if(offset+key>arr.length)enlargeCapacity(key);
199                    if(key>size)size=key;
200                    arr[(offset+key)-1]=checkValue(value);
201                    return value;           
202            }       
203            
204            /**
205         * !!! all methods that use this method must be sync
206             * enlarge the inner array to given size
207             * @param key min size of the array
208             */
209            private void enlargeCapacity(int key) {
210                    int diff=offCount-offset;
211                    
212                    int newSize=arr.length;
213                    if(newSize<1) newSize=1;
214                    while(newSize<key+offset+diff) {
215                            newSize*=2;
216                    }
217                    if(newSize>arr.length) {
218                            Object[] na=new Object[newSize];
219                            for(int i=offset;i<offset+size;i++) {
220                                    na[i+diff]=arr[i];
221                            }
222                            arr=na;
223                            offset+=diff;
224                    }
225            }
226            
227            /**
228             * !!! all methods that use this method must be sync
229         * enlarge the offset if 0
230             */
231            private void enlargeOffset() {
232                    if(offset==0) {
233                            offCount=offCount==0?1:offCount*2;
234                            offset=offCount;
235                            Object[] narr=new Object[arr.length+offset];
236                            for(int i=0;i<size;i++) {
237                                    narr[offset+i]=arr[i];
238                            }
239                            arr=narr;
240                    }
241            }
242    
243            /**
244             * !!! all methods that use this method must be sync
245         * check if value is valid to insert to array (to a multidimesnional array only array with one smaller dimension can be inserted)
246             * @param value value to check
247             * @return checked value
248             * @throws ExpressionException
249            */
250            private Object checkValue(Object value) throws ExpressionException {
251                    // is a 1 > Array
252                    if(dimension>1)      {
253                            if(value instanceof Array)      {
254                                    if(((Array)value).getDimension()!=dimension-1)
255                                            throw new ExpressionException("You can only Append an Array with "+(dimension-1)+" Dimension","array has wrong dimension, now is "+(((Array)value).getDimension())+ " but it must be "+(dimension-1));
256                            }
257                            else 
258                                    throw new ExpressionException("You can only Append an Array with "+(dimension-1)+" Dimension","now is a object of type "+Caster.toClassName(value));
259                    }
260                    return value;
261            }
262            
263            /**
264             * !!! all methods that use this method must be sync
265         * check if value is valid to insert to array (to a multidimesnional array only array with one smaller dimension can be inserted), if value is invalid return null;
266             * @param value value to check
267             * @return checked value
268            */
269            private Object checkValueEL(Object value) {
270                    // is a 1 > Array
271                    if(dimension>1)      {
272                            if(value instanceof Array)      {
273                                    if(((Array)value).getDimension()!=dimension-1)
274                                            return null;
275                            }
276                            else 
277                                    return null;
278                    }
279                    return value;
280            }
281    
282            @Override
283            public int size() {
284                    return size;
285            }
286            
287            @Override
288            public Collection.Key[] keys() {
289                    
290                    ArrayList<Collection.Key> lst=new ArrayList<Collection.Key>();
291                    int count=0;
292                    for(int i=offset;i<offset+size;i++) {
293                            Object o=arr[i];
294                            count++;
295                            if(o!=null) lst.add(KeyImpl.getInstance(count+""));
296                    }
297                    return  lst.toArray(new Collection.Key[lst.size()]);
298            }
299            
300            @Override
301            public int[] intKeys() {
302                    ArrayList<Integer> lst=new ArrayList<Integer>();            
303                    int count=0;
304                    for(int i=offset;i<offset+size;i++) {
305                            Object o=arr[i];
306                            count++;
307                            if(o!=null) lst.add(Integer.valueOf(count));
308                    }
309    
310                    int[] ints=new int[lst.size()];
311                    
312                    for(int i=0;i<ints.length;i++){
313                            ints[i]=lst.get(i).intValue();
314                    }
315                    return ints;
316            }
317            
318            @Override
319            public Object remove(Collection.Key key) throws ExpressionException {
320                    return removeE(Caster.toIntValue(key.getString()));
321            }
322    
323            public Object removeEL(Collection.Key key) {
324                    return removeEL(Caster.toIntValue(key.getString(),-1));
325            }
326            
327            @Override
328            public Object removeE(int key) throws ExpressionException {
329                    if(key>size || key<1) throw invalidPosition(key);
330                    Object obj=get(key,null);
331                    for(int i=(offset+key)-1;i<(offset+size)-1;i++) {
332                            arr[i]=arr[i+1];
333                    }
334                    size--;
335                    return obj;
336            }
337            
338            @Override
339            public Object removeEL(int key) {
340                if(key>size || key<1) return null;
341                    Object obj=get(key,null);
342                
343                    for(int i=(offset+key)-1;i<(offset+size)-1;i++) {
344                            arr[i]=arr[i+1];
345                    }
346                    size--;
347                    return obj;
348            }
349            
350            @Override
351            public void clear() {
352                if(size()>0) {
353                            arr=new Object[cap];
354                            size=0;
355                            offCount=1;
356                            offset=0;
357                }
358            }
359            
360            @Override
361            public boolean insert(int key, Object value) throws ExpressionException {
362                    if(key<1 || key>size+1) {
363                            throw new ExpressionException("can't insert value to array at position "+key+", array goes from 1 to "+size());
364                    }
365                    // Left
366                    if((size/2)>=key) {
367                            enlargeOffset();
368                            for(int i=offset;i<(offset+key)-1;i++) {
369                                    arr[i-1]=arr[i];
370                            }
371                            offset--;
372                            arr[(offset+key)-1]=checkValue(value);
373                            size++;
374                    }
375                    // Right
376                    else {
377                            if((offset+key)>arr.length || size+offset>=arr.length)enlargeCapacity(arr.length+2);
378                            for(int i=size+offset;i>=key+offset;i--) {
379                                    arr[i]=arr[i-1];
380                            }
381                            arr[(offset+key)-1]=checkValue(value);
382                            size++;
383                            
384                    }
385                    return true;
386            }
387    
388            @Override
389        public Object append(Object o) throws ExpressionException {
390            if(offset+size+1>arr.length)enlargeCapacity(size+1);
391            arr[offset+size]=checkValue(o);
392            size++;
393            return o;
394        }
395        
396            /**
397             * append a new value to the end of the array
398             * @param o value to insert
399             * @return inserted value
400             */
401        public Object appendEL(Object o) {
402            
403            if(offset+size+1>arr.length)enlargeCapacity(size+1);
404            arr[offset+size]=o;
405            size++;
406            return o;
407        }
408        
409        /**
410         * adds a value and return this array
411         * @param o
412         * @return this Array
413         */
414        public boolean add(Object o) {
415            if(offset+size+1>arr.length)enlargeCapacity(size+1);
416            arr[offset+size]=o;
417            size++;
418            return true;
419        }
420    
421            /**
422             * append a new value to the end of the array
423             * @param str value to insert
424             * @return inserted value
425             */
426            public String _append(String str) {
427                    if(offset+size+1>arr.length)enlargeCapacity(size+1);
428                    arr[offset+size]=str;
429                    size++;
430                    return str;
431            }
432            
433            /**
434             * add a new value to the begin of the array
435             * @param o value to insert
436             * @return inserted value
437             * @throws ExpressionException
438             */
439            public Object prepend(Object o) throws ExpressionException {
440                    insert(1,o);
441                    return o;
442            }
443            
444            /**
445             * resize array to defined size
446             * @param to new minimum size of the array
447             */
448            public void resize(int to) {
449                    if(to>size) {
450                            enlargeCapacity(to);
451                            size=to;
452                    }
453            }
454            
455            @Override
456            public void sort(String sortType, String sortOrder) throws PageException {
457                    sort(ArrayUtil.toComparator(null, sortType, sortOrder, false));
458            }
459    
460            @Override
461            public synchronized void sort(Comparator comp) throws PageException {
462                    if(getDimension()>1)
463                            throw new ExpressionException("only 1 dimensional arrays can be sorted");
464                    Arrays.sort(arr,offset,offset+size,comp);       
465            }
466            
467            /**
468             * @return return arra as native (Java) Object Array
469             */
470            public Object[] toArray() {
471                    Object[] rtn=new Object[size];
472                    int count=0;
473                    for(int i=offset;i<offset+size;i++) {
474                            rtn[count++]=arr[i];
475                    }
476                    
477                    return rtn;
478            }
479    
480            /**
481             * @return return array as ArrayList
482             */
483            public ArrayList toArrayList() {
484                    ArrayList al=new ArrayList();
485                    for(int i=offset;i<offset+size;i++) {
486                            al.add(arr[i]);
487                    }
488                    return al;
489            }
490            
491            @Override
492            public DumpData toDumpData(PageContext pageContext, int maxlevel, DumpProperties dp) {
493                    DumpTable table = new DumpTable("array","#ff9900","#ffcc00","#000000");
494                    table.setTitle("Array");
495                    
496                    int length=size();
497                    maxlevel--;
498                    for(int i=1;i<=length;i++) {
499                            Object o=null;
500                            try {
501                                    o = getE(i);
502                            } 
503                            catch (Exception e) {}
504                            table.appendRow(1,new SimpleDumpData(i),DumpUtil.toDumpData(o, pageContext,maxlevel,dp));
505                    }
506                    return table;
507            }
508            
509            /**
510             * return code print of the array as plain text
511             * @return content as string
512            */
513            public String toPlain() {
514                    
515                    StringBuffer sb=new StringBuffer();
516                    int length=size();
517                    for(int i=1;i<=length;i++) {
518                            sb.append(i);
519                            sb.append(": ");
520                            sb.append(get(i-1,null));
521                            sb.append("\n");
522                    }
523                    return sb.toString();
524            }
525            
526            @Override
527            public Object clone() {
528                    return duplicate(true);
529            }
530            
531            @Override
532            public Collection duplicate(boolean deepCopy) {
533                    ArrayImplNS arr=new ArrayImplNS();
534                    arr.dimension=dimension;
535                    Collection.Key[] keys=this.keys();
536                    
537                    ThreadLocalDuplication.set(this, arr);
538                    try {
539                            Collection.Key k;
540                            for(int i=0;i<keys.length;i++) {
541                                    k=keys[i];
542                                    arr.set(k,Duplicator.duplicate(this.get(k,null),deepCopy));
543                            }
544                    } 
545                    catch (ExpressionException e) {}
546                    finally{
547                            // ThreadLocalDuplication.remove(this); removed "remove" to catch sisters and brothers
548                    }
549                    
550                    return arr;
551            }
552            
553            @Override
554            public Iterator<Collection.Key> keyIterator() {
555                    return new KeyIterator(keys());
556            }
557        
558            @Override
559            public Iterator<String> keysAsStringIterator() {
560            return new StringIterator(keys());
561        }
562            
563            @Override
564            public Iterator<Entry<Key, Object>> entryIterator() {
565                    return new EntryIterator(this, keys());
566            }
567            
568            public Iterator iterator() {
569                    ArrayList lst=new ArrayList();
570                    //int count=0;
571                    for(int i=offset;i<offset+size;i++) {
572                            Object o=arr[i];
573                            //count++;
574                            if(o!=null) lst.add(o);
575                    }
576                    return lst.iterator();
577            }
578    
579    
580            @Override
581            public String toString() {
582                    return LazyConverter.serialize(this);
583            }
584    
585            @Override
586            public long sizeOf() {
587                    return SizeOf.size(arr)
588                    +SizeOf.size(dimension)
589                    +SizeOf.size(cap)
590                    +SizeOf.size(size)
591                    +SizeOf.size(offset)
592                    +SizeOf.size(offCount)
593                    +SizeOf.REF_SIZE;
594            }
595    }