001    package railo.runtime.type;
002    
003    import java.util.ArrayList;
004    import java.util.Date;
005    import java.util.Iterator;
006    import java.util.List;
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.DumpUtil;
015    import railo.runtime.engine.ThreadLocalPageContext;
016    import railo.runtime.exp.DatabaseException;
017    import railo.runtime.exp.DeprecatedException;
018    import railo.runtime.exp.ExpressionException;
019    import railo.runtime.exp.PageException;
020    import railo.runtime.exp.PageRuntimeException;
021    import railo.runtime.op.Caster;
022    import railo.runtime.op.Duplicator;
023    import railo.runtime.op.Operator;
024    import railo.runtime.op.ThreadLocalDuplication;
025    import railo.runtime.op.date.DateCaster;
026    import railo.runtime.reflection.Reflector;
027    import railo.runtime.reflection.pairs.MethodInstance;
028    import railo.runtime.type.dt.DateTime;
029    import railo.runtime.type.it.EntryIterator;
030    import railo.runtime.type.it.KeyIterator;
031    import railo.runtime.type.it.StringIterator;
032    import railo.runtime.type.scope.Undefined;
033    import railo.runtime.type.util.CollectionUtil;
034    import railo.runtime.type.util.QueryUtil;
035    import railo.runtime.util.ArrayIterator;
036    
037    /**
038     * implementation of the query column
039     */
040    public class QueryColumnImpl implements QueryColumnPro,Sizeable,Objects {
041    
042            private static final long serialVersionUID = -5544446523204021493L;
043            private static final int CAPACITY=32;
044        
045            protected int type;
046            protected int size;
047            protected Object[] data;
048        
049        protected boolean typeChecked=false;
050        protected QueryImpl query;
051        protected Collection.Key key;
052    
053            /**
054             * constructor with type
055             * @param query
056             * @param key 
057             * @param type
058             */
059        public QueryColumnImpl(QueryImpl query, Collection.Key key, int type) {
060                    this.data=new Object[CAPACITY];
061                    this.type=type;
062            this.key=key;
063            this.query=query;
064            }
065    
066            /**
067             * constructor with array
068             * @param query
069             * @param array
070             * @param type
071             */
072        public QueryColumnImpl(QueryImpl query, Collection.Key key, Array array,int type) {
073                    data=array.toArray();
074                size=array.size();
075                    this.type=type;
076                    this.query=query;
077                    this.key=key;
078            }
079    
080            /**
081             * @param query
082             * @param type type as (java.sql.Types.XYZ) int
083             * @param size 
084             */
085        public QueryColumnImpl(QueryImpl query, Collection.Key key, int type, int size) {
086                    this.data=new Object[size];
087                    this.type=type;
088                    this.size=size;
089                    this.query=query;
090                    this.key=key;
091            }
092            
093    
094            /**
095             * Constructor of the class
096             * for internal usage only
097             */
098            public QueryColumnImpl() {
099            }
100    
101            @Override
102            public int size() {
103                    return size;
104            }
105    
106            @Override
107            public Collection.Key[] keys() {
108                    Collection.Key[] k=new Collection.Key[size()];
109            int len=k.length;
110                    for(int i=1;i<=len;i++) {
111                            k[i-1]=KeyImpl.init(Caster.toString(i));
112                    }
113                    return k;
114            }
115    
116            @Override
117            public Object remove(Collection.Key key) throws PageException {
118                    resetType();
119                    return set(Caster.toIntValue(key.getString()),"");
120            }
121    
122            @Override
123            public Object remove(int row) throws DatabaseException {
124            // query.disconnectCache();
125            resetType();
126                    return set(row,"");
127            }
128            
129    
130            @Override
131            public Object removeEL(Collection.Key key) {
132            // query.disconnectCache();
133            resetType();
134                    return setEL(Caster.toIntValue(key.getString(),-1),"");
135            }
136    
137            @Override
138            public Object removeEL(int row) {
139            // query.disconnectCache();
140            resetType();
141                    return setEL(row,"");
142            }
143    
144            @Override
145            public synchronized void clear() {
146            // query.disconnectCache();
147            resetType();
148                    data=new Object[CAPACITY];
149                    size=0;
150            }
151            
152            @Override
153            public Object remove(PageContext pc) throws PageException {
154            return remove(query.getCurrentrow(pc.getId()));
155            }
156    
157            public Object removeEL(PageContext pc) {
158            return removeEL(query.getCurrentrow(pc.getId()));
159            }
160            
161            @Override
162            public Object get(String key) throws PageException {
163            return get(KeyImpl.init(key));
164            }
165            
166            @Override
167            public Object get(Key key) throws PageException {
168                    return get((PageContext)null,key);
169            }
170    
171            @Override
172            public Object get(PageContext pc, Collection.Key key) throws PageException {
173                    int row=Caster.toIntValue(key.getString(),Integer.MIN_VALUE);
174                    if(row==Integer.MIN_VALUE) {
175                            Object child=getChildElement(pc,key,NullSupportHelper.NULL());
176                    if(child!=NullSupportHelper.NULL()) return child;
177                throw new DatabaseException("key ["+key+"] not found",null,null,null);
178            }
179                return QueryUtil.getValue(this,row);
180            }
181    
182        private Object getChildElement(PageContext pc,Key key, Object defaultValue) {// pc maybe null
183            // column and query has same name
184            if(key.equals(this.key)) {
185                    return query.get(key,defaultValue);
186            }
187            // get it from undefined scope
188                    pc = ThreadLocalPageContext.get(pc);
189                    if(pc!=null){
190                            Undefined undefined = pc.undefinedScope();
191                            boolean old = undefined.setAllowImplicidQueryCall(false);
192                            Object sister = undefined.get(this.key,NullSupportHelper.NULL());
193                            undefined.setAllowImplicidQueryCall(old);
194                            if(sister!=NullSupportHelper.NULL()){
195                                    try {
196                                            return pc.get(sister, key);
197                                    } catch (PageException e) {
198                                            return defaultValue;
199                                    }
200                            }
201                    }
202            return defaultValue;
203            }
204    
205        /**
206         * touch the given line on the column at given row
207         * @param row
208         * @return new row or existing
209         * @throws DatabaseException
210         */
211        public Object touch(int row) {
212            if(row<1 || row>size) return NullSupportHelper.full()?null:"";
213            Object o=data[row-1];
214            if(o!=null) return o;
215            return setEL(row,new StructImpl());
216        }
217        
218        /**
219         * touch the given line on the column at given row
220         * @param row
221         * @return new row or existing
222         * @throws DatabaseException
223         */
224        public Object touchEL(int row) {
225            return touch(row);
226        }
227        
228            @Override
229            public Object get(Key key, Object defaultValue) {
230                    return get(null,key,defaultValue);
231            }
232    
233            @Override
234            public Object get(PageContext pc, Collection.Key key, Object defaultValue) {// pc maybe null
235                int row=Caster.toIntValue(key.getString(),Integer.MIN_VALUE);
236                if(row==Integer.MIN_VALUE) {
237                    return getChildElement(pc,key, defaultValue);
238                }
239                return get(row,defaultValue);
240            }
241    
242            @Override
243            public Object get(String key, Object defaultValue) {
244                return get(KeyImpl.init(key),defaultValue);
245            }
246    
247            @Override
248        public Object get(int row) throws DeprecatedException {
249                    throw new DeprecatedException("this method is no longer supported, use instead get(int,Object)");
250                    //return QueryUtil.getValue(this,row);
251        }
252        
253        public Object get(int row, Object defaultValue) {
254            if(row<1 || row>size) return defaultValue;
255                    return data[row-1];
256            }
257    
258        // FUTURE this method should replace the method above, but it needs adjustment with all callers
259            /*public Object get2(int row, Object defaultValue) {
260                    if(row<1 || row>size) return defaultValue;
261                return data[row-1];
262            }*/
263    
264            @Override
265            public Object set(String key, Object value) throws PageException {
266                int row=Caster.toIntValue(key,Integer.MIN_VALUE);
267                if(row==Integer.MIN_VALUE)return query.set(key,value);
268                return set(row,value);
269            }
270    
271            public Object set(Collection.Key key, Object value) throws PageException {
272                int row=Caster.toIntValue(key.getString(),Integer.MIN_VALUE);
273                if(row==Integer.MIN_VALUE)return query.set(key,value);
274                return set(row,value);
275            }
276    
277        @Override
278            public synchronized Object set(int row, Object value) throws DatabaseException {
279            // query.disconnectCache();
280            if(row<1) throw new DatabaseException("invalid row number ["+row+"]","valid row numbers a greater or equal to one",null,null,null);
281                if(row>size) {
282                    if(size==0)throw new DatabaseException("cannot set a value to a empty query, you first have to add a row",null,null,null,null);
283                    throw new DatabaseException("invalid row number ["+row+"]","valid row numbers goes from 1 to "+size,null,null,null);
284                }
285                
286                value=reDefineType(value);
287                data[row-1]=value;
288                return value;
289            }
290            @Override
291            public synchronized Object setEL(String key, Object value) {
292                int index=Caster.toIntValue(key,Integer.MIN_VALUE);
293                    if(index==Integer.MIN_VALUE) query.setEL(key,value);
294                return setEL(index, value);
295                    
296            }
297    
298            public Object setEL(Collection.Key key, Object value) {
299                int index=Caster.toIntValue(key.getString(),Integer.MIN_VALUE);
300                    if(index==Integer.MIN_VALUE) query.setEL(key,value);
301                return setEL(index, value);
302            }
303    
304            @Override
305            public synchronized Object setEL(int row, Object value) {
306            // query.disconnectCache();
307            if(row<1 || row>size) return value;
308                
309                value=reDefineType(value);
310                data[row-1]=value;
311                return value;
312            }
313    
314        @Override
315            public synchronized void add(Object value) {
316            // query.disconnectCache();
317            if(data.length<=size) growTo(size);
318                data[size++]=value;
319            }
320    
321        @Override
322        public synchronized void cutRowsTo(int maxrows) {
323            // query.disconnectCache();
324            if(maxrows>-1 && maxrows<size)size=maxrows;
325        }
326    
327            @Override
328            public synchronized void addRow(int count) {        
329            // query.disconnectCache();
330            if(data.length<(size+count)) growTo(size+count);
331                for(int i=0;i<count;i++)size++;
332            }
333    
334        public synchronized Object removeRow(int row) throws DatabaseException {
335            // query.disconnectCache();
336            if(row<1 || row>size) 
337                throw new DatabaseException("invalid row number ["+row+"]","valid rows goes from 1 to "+size,null,null,null);
338            Object o=data[row-1];
339            for(int i=row;i<size;i++) {
340                data[i-1]=data[i];
341            }
342            size--;
343            if(NullSupportHelper.full()) return o;
344            return o==null?"":o;
345        }
346    
347            @Override
348            public int getType() {
349                reOrganizeType();
350                    return type;
351            }
352            
353            
354        @Override
355            public String getTypeAsString() {
356                    return QueryImpl.getColumTypeName(getType());
357            }
358    
359    
360            @Override
361            public DumpData toDumpData(PageContext pageContext, int maxlevel, DumpProperties dp) {
362                    return DumpUtil.toDumpData(QueryUtil.getValue(this,query.getCurrentrow(pageContext.getId())), pageContext,maxlevel,dp);
363            }       
364            
365        private synchronized void growTo(int row) {
366            
367            int newSize=(data.length+1)*2;
368            while(newSize<=row) {
369                //print.ln(newSize+"<="+row);
370                newSize*=2;
371            }
372            
373            Object[] newData=new Object[newSize];
374            for(int i=0;i<data.length;i++) {
375                newData[i]=data[i];
376            }
377            data=newData;
378        }
379        
380        private Object reDefineType(Object value) {
381            return QueryColumnUtil.reDefineType(this,value);
382        }
383        
384        private synchronized void resetType() {
385            QueryColumnUtil.resetType(this);
386        }
387        
388        private synchronized void reOrganizeType() {
389            QueryColumnUtil.reOrganizeType(this);
390        }
391    
392        public Collection.Key getKey() {
393            return key;
394        }
395        public void setKey(Collection.Key key) {
396            this.key = key;
397        }
398    
399            @Override
400            public String getKeyAsString() throws PageException {
401                    return key.getLowerString();// TODO ist das OK?
402            }
403    
404        @Override
405        public Object get(PageContext pc) {
406            return QueryUtil.getValue(this,query.getCurrentrow(pc.getId()));
407        }
408        
409        @Override
410        public Object get(PageContext pc, Object defaultValue) {
411            return get(query.getCurrentrow(pc.getId()),defaultValue);
412        }
413    
414        @Override
415        public Object touch(PageContext pc) throws PageException {
416            return touch(query.getCurrentrow(pc.getId()));
417        }
418    
419        public Object touchEL(PageContext pc) {
420            return touchEL(query.getCurrentrow(pc.getId()));
421        }
422    
423        @Override
424        public Object set(PageContext pc, Object value) throws PageException {
425            return set(query.getCurrentrow(pc.getId()),value);
426        }
427    
428        @Override
429        public Object setEL(PageContext pc, Object value) {
430            return setEL(query.getCurrentrow(pc.getId()),value);
431        }
432    
433        @Override
434        public Object getParent() {
435            return query;
436        }
437    
438        @Override
439        public String castToString() throws PageException {
440            return Caster.toString(get(query.getCurrentrow(ThreadLocalPageContext.get().getId()),null));
441        }
442    
443            @Override
444            public String castToString(String defaultValue) {
445                    Object value = get(query.getCurrentrow(ThreadLocalPageContext.get().getId()),NullSupportHelper.NULL());
446                    if(value==NullSupportHelper.NULL()) return defaultValue;
447                    return Caster.toString(value,defaultValue);
448            }
449    
450        @Override
451        public boolean castToBooleanValue() throws PageException {
452            return Caster.toBooleanValue(get(query.getCurrentrow(ThreadLocalPageContext.get().getId()),null));
453        }
454        
455        @Override
456        public Boolean castToBoolean(Boolean defaultValue) {
457            Object value = get(query.getCurrentrow(ThreadLocalPageContext.get().getId()),NullSupportHelper.NULL());
458                    if(value==NullSupportHelper.NULL()) return defaultValue;
459                    return Caster.toBoolean(value,defaultValue);
460        }
461    
462        @Override
463        public double castToDoubleValue() throws PageException {
464            return Caster.toDoubleValue(get(query.getCurrentrow(ThreadLocalPageContext.get().getId()),null));
465        }
466        
467        @Override
468        public double castToDoubleValue(double defaultValue) {
469            Object value = get(query.getCurrentrow(ThreadLocalPageContext.get().getId()),NullSupportHelper.NULL());
470                    if(value==NullSupportHelper.NULL()) return defaultValue;
471                    return Caster.toDoubleValue(value,defaultValue);
472        }
473    
474        @Override
475        public DateTime castToDateTime() throws PageException {
476            return DateCaster.toDateAdvanced(get(query.getCurrentrow(ThreadLocalPageContext.get().getId()),null),null);
477        }
478        
479        @Override
480        public DateTime castToDateTime(DateTime defaultValue) {
481            Object value = get(query.getCurrentrow(ThreadLocalPageContext.get().getId()),NullSupportHelper.NULL());
482                    if(value==NullSupportHelper.NULL()) return defaultValue;
483                    return DateCaster.toDateAdvanced(value,true,null,defaultValue);
484        }
485    
486    
487            @Override
488            public int compareTo(boolean b) throws PageException {
489                    return Operator.compare(castToBooleanValue(), b);
490            }
491    
492            @Override
493            public int compareTo(DateTime dt) throws PageException {
494                    return Operator.compare((Date)castToDateTime(), (Date)dt);
495            }
496    
497            @Override
498            public int compareTo(double d) throws PageException {
499                    return Operator.compare(castToDoubleValue(), d);
500            }
501    
502            @Override
503            public int compareTo(String str) throws PageException {
504                    return Operator.compare(castToString(), str);
505            }
506    
507        @Override
508        public synchronized Object clone() {
509            return duplicate(true);
510        }
511    
512        public synchronized Collection duplicate(boolean deepCopy) {
513            return cloneColumn(query,deepCopy);
514        }
515        
516        public synchronized QueryColumnPro cloneColumn(Query query, boolean deepCopy) {
517            return cloneColumnImpl(deepCopy);
518        }
519        
520        public synchronized QueryColumnImpl cloneColumnImpl(boolean deepCopy) {
521            QueryColumnImpl clone=new QueryColumnImpl();
522            populate(this, clone, deepCopy);
523            return clone;
524        }
525        
526        protected static void populate(QueryColumnImpl src,QueryColumnImpl trg, boolean deepCopy) {
527            
528            ThreadLocalDuplication.set(src, trg);
529            try{
530                    trg.key=src.key;
531                    trg.query=src.query;
532                    trg.size=src.size;
533                    trg.type=src.type;
534                    trg.key=src.key;
535                    
536                    trg.data=new Object[src.data.length];
537                    for(int i=0;i<src.data.length;i++) {
538                        trg.data[i]=deepCopy?Duplicator.duplicate(src.data[i],true):src.data[i];
539                    }
540            }
541            finally {
542                    // ThreadLocalDuplication.remove(src); removed "remove" to catch sisters and brothers
543            }
544        }
545            
546    
547        @Override
548        public String toString() {
549            try {
550                return Caster.toString(get(query.getCurrentrow(ThreadLocalPageContext.get().getId()),null));
551            } catch (PageException e) {
552                return super.toString();
553            }
554        }
555    
556        @Override
557        public boolean containsKey(String key) {
558            return containsKey(KeyImpl.init(key));
559        }
560    
561            @Override
562            public boolean containsKey(Collection.Key key) {
563            return get(key,NullSupportHelper.NULL())!=NullSupportHelper.NULL();
564            }
565    
566            @Override
567            public long sizeOf() {
568                    return SizeOf.size(key)+SizeOf.size(data);
569            }
570            
571            public Iterator iterator() {
572                    return keyIterator();
573            }
574    
575            @Override
576            public Iterator<Collection.Key> keyIterator() {
577                    return new KeyIterator(keys());
578            }
579        
580            @Override
581            public Iterator<String> keysAsStringIterator() {
582            return new StringIterator(keys());
583        }
584            
585            @Override
586            public Iterator<Entry<Key, Object>> entryIterator() {
587                    return new EntryIterator(this, keys());
588            }
589    
590            @Override
591            public Iterator<Object> valueIterator() {
592                    return new ArrayIterator(data,0,size);
593            }
594            
595            @Override
596            public Object callWithNamedValues(PageContext pc, Key methodName,Struct args) throws PageException {
597                    
598            throw new ExpressionException("No matching Method/Function ["+methodName+"] for call with named arguments found");
599                    //return pc.getFunctionWithNamedValues(get(query.getCurrentrow()), methodName, Caster.toFunctionValues(args));
600            }
601    
602            @Override
603            public Object call(PageContext pc, Key methodName, Object[] arguments) throws PageException {
604                    MethodInstance mi = Reflector.getMethodInstanceEL(this.getClass(), methodName, arguments);
605                    if(mi!=null) {
606                            try {
607                                    return mi.invoke(this);
608                            } catch (Throwable t) {
609                                    try {
610                                            return pc.getFunction(QueryUtil.getValue(this,query.getCurrentrow(pc.getId())), methodName, arguments);
611                                    } catch (PageException pe) {
612                                            throw Caster.toPageException(t);
613                                    }
614                            }
615                    }
616                    return pc.getFunction(QueryUtil.getValue(this,query.getCurrentrow(pc.getId())), methodName, arguments);
617            }
618    
619            @Override
620            public Object set(PageContext pc, Key propertyName, Object value) throws PageException {
621                    return set(propertyName, value);
622            }
623    
624            @Override
625            public Object setEL(PageContext pc, Key propertyName, Object value) {
626                    return setEL(propertyName, value);
627            }
628    
629            public void add(int index, Object element) {
630                    throwNotAllowedToAlter();
631                    //setEL(index+1, element);
632            }
633    
634            private void throwNotAllowedToAlter() {
635                    throw new PageRuntimeException(new DatabaseException(
636                                    "Query columns do not support methods that would alter the structure of a query column" 
637                                    ,"you must use an analogous method on the query"
638                                    ,null
639                                    ,null
640                                    ,null));
641                    
642            }
643    
644            public boolean addAll(java.util.Collection<? extends Object> c) {
645                    throwNotAllowedToAlter();
646                    return false;
647                    /*Iterator<? extends Object> it = c.iterator();
648                    while(it.hasNext()){
649                            add(it.next());
650                    }
651                    return true;*/
652            }
653    
654            public boolean addAll(int index, java.util.Collection<? extends Object> c) {
655                    throwNotAllowedToAlter();
656                    return false;
657                    /*Iterator<? extends Object> it = c.iterator();
658                    while(it.hasNext()){
659                            setEL(++index,it.next());
660                    }
661                    return true;*/
662            }
663    
664            public boolean contains(Object o) {
665                    return indexOf(o)!=-1;
666            }
667    
668            public boolean containsAll(java.util.Collection<?> c) {
669                    Iterator<? extends Object> it = c.iterator();
670                    while(it.hasNext()){
671                            if(indexOf(it.next())==-1) return false;
672                    }
673                    return true;
674            }
675    
676            public int indexOf(Object o) {
677                    for(int i=0;i<size;i++){
678                            try {
679                                    if(Operator.compare(o, data[i])==0) return i;
680                            } 
681                            catch (PageException e) {}
682                    }
683                    return -1;
684            }
685    
686            public int lastIndexOf(Object o) {
687                    for(int i=size-1;i>=0;i--){
688                            try {
689                                    if(Operator.compare(o, data[i])==0) return i;
690                            } 
691                            catch (PageException e) {}
692                    }
693                    return -1;
694            }
695    
696            public boolean isEmpty() {
697                    return size()==0;
698            }
699    
700            public boolean removeAll(java.util.Collection<?> c) {
701                    throwNotAllowedToAlter();
702                    return false;
703                    /*boolean hasChanged=false;
704                    Iterator<? extends Object> it = c.iterator();
705                    while(it.hasNext()){
706                            if(remove(it.next())) {
707                                    hasChanged=true;
708                            }
709                    }
710                    return hasChanged;*/
711            }
712    
713            public boolean retainAll(java.util.Collection<?> c) {
714                    throwNotAllowedToAlter();
715                    return false;
716                    /*boolean hasChanged=false;
717                    Iterator it = valueIterator();
718                    while(it.hasNext()){
719                            if(!c.contains(it.next())){
720                                    hasChanged=true;
721                                    it.remove();
722                            }
723                    }
724                    return hasChanged;*/
725            }
726    
727            public List<Object> subList(int fromIndex, int toIndex) {
728                    ArrayList<Object> list=new ArrayList<Object>();
729                    for(int i=fromIndex;i<toIndex;i++){
730                            list.add(data[i]);
731                    }
732                    return list;
733            }
734    
735            public Object[] toArray() {
736                    return toArray(new Object[size()]);
737            }
738    
739            public  Object[] toArray(Object[] trg) {
740                    System.arraycopy(data, 0, trg, 0, data.length>trg.length?trg.length:data.length);
741                    return trg;
742            }
743    
744            @Override
745            public QueryColumnPro toDebugColumn() {
746                    return _toDebugColumn();
747            }
748            
749            public DebugQueryColumn _toDebugColumn() {
750                    return new DebugQueryColumn(data,key,query,size,type,typeChecked);
751            }
752            
753            @Override
754            public java.util.Iterator<String> getIterator() {
755            return keysAsStringIterator();
756        }
757            
758            @Override
759            public boolean equals(Object obj){
760                    if(!(obj instanceof Collection)) return false;
761                    return CollectionUtil.equals(this,(Collection)obj);
762            }
763            
764            @Override
765            public int hashCode() {
766                    return CollectionUtil.hashCode(this);
767            }
768    }