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