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.functions.query;
020
021import java.util.Iterator;
022import java.util.Map.Entry;
023
024import lucee.commons.lang.StringUtil;
025import lucee.runtime.PageContext;
026import lucee.runtime.exp.FunctionException;
027import lucee.runtime.exp.PageException;
028import lucee.runtime.functions.BIF;
029import lucee.runtime.op.Caster;
030import lucee.runtime.op.Decision;
031import lucee.runtime.type.Array;
032import lucee.runtime.type.ArrayImpl;
033import lucee.runtime.type.Collection.Key;
034import lucee.runtime.type.Query;
035import lucee.runtime.type.QueryImpl;
036import lucee.runtime.type.Struct;
037import lucee.runtime.type.util.ListUtil;
038import lucee.runtime.type.util.QueryUtil;
039
040/**
041 * Implements the CFML Function querynew
042 */
043public final class QueryNew extends BIF {
044
045        private static final long serialVersionUID = -4313766961671090938L;
046        
047        /** @deprecated used by old lucee archives */
048        public static lucee.runtime.type.Query call(PageContext pc , String columnNames) throws PageException {
049                return call(pc, (Object)columnNames);
050        }
051        
052        /** @deprecated used by old lucee archives */
053        public static lucee.runtime.type.Query call(PageContext pc , String columnNames, String columnTypes) throws PageException {
054                return call(pc, (Object)columnNames, (Object)columnTypes);
055        }
056        
057        /** @deprecated used by old lucee archives */
058        public static lucee.runtime.type.Query call(PageContext pc , String columnNames, String columnTypes, Object data) throws PageException {
059                return call(pc, (Object)columnNames, (Object)columnTypes,data);
060        }
061                
062        
063        
064        
065        public static lucee.runtime.type.Query call(PageContext pc , Object columnNames) throws PageException {
066                return new QueryImpl(toArray(pc,columnNames,1),0,"query");
067        }
068
069        public static lucee.runtime.type.Query call(PageContext pc , Object columnNames, Object columnTypes) throws PageException {
070                if(StringUtil.isEmpty(columnTypes)) return call(pc, columnNames);
071                return new QueryImpl(toArray(pc,columnNames,1),toArray(pc,columnTypes,2),0,"query");
072        }
073        
074        public static lucee.runtime.type.Query call(PageContext pc , Object columnNames, Object columnTypes, Object data) throws PageException {
075                
076                Array cn = toArray(pc, columnNames, 1);
077                lucee.runtime.type.Query qry;
078                if(StringUtil.isEmpty(columnTypes))
079                        qry= new QueryImpl(cn,0,"query");
080                else
081                        qry= new QueryImpl(cn,toArray(pc, columnTypes, 2),0,"query");
082                
083                if(data==null) return qry;
084                return populate(pc, qry, data); 
085        }
086        
087        @Override
088        public Object invoke(PageContext pc, Object[] args) throws PageException {
089                if(args.length==1)return call(pc,Caster.toString(args[0]));
090                if(args.length==2)return call(pc,Caster.toString(args[0]),Caster.toString(args[1]));
091                return call(pc,Caster.toString(args[0]),Caster.toString(args[1]),args[2]);
092        }
093        
094        public static Query populate(PageContext pc, Query qry,Object data) throws PageException {
095                if(Decision.isArray(data))
096                        return _populate(pc,qry,Caster.toArray(data));
097                else if(Decision.isStruct(data))
098                        return _populate(pc,qry,Caster.toStruct(data));
099                else 
100                        throw new FunctionException(pc, "QueryNew", 3, "data", "the date must be defined as array of structs , array of arrays or struct of arrays");
101        }
102        
103        private static Query _populate(PageContext pc, Query qry,Struct data) throws PageException {
104                Iterator<Entry<Key, Object>> it = data.entryIterator();
105                Entry<Key, Object> e;
106                Object v;
107                Array arr;
108                int rows = qry.getRecordcount();
109                while(it.hasNext()){
110                        e = it.next();
111                        if(qry.getColumn(e.getKey(),null)!=null) {
112                                v=e.getValue();
113                                arr = Caster.toArray(v,null);
114                                if(arr==null) arr=new ArrayImpl(new Object[]{v});
115                                populateColumn(qry,e.getKey(),arr,rows);
116                        }
117                }
118                return qry; 
119        }
120        
121        private static void populateColumn(Query qry, Key column, Array data,int rows) throws PageException {
122                Iterator<?> it = data.valueIterator();
123                int row=rows;
124                while(it.hasNext()){
125                        row++;
126                        if(row>qry.getRecordcount()) qry.addRow();
127                        qry.setAt(column, row, it.next());
128                }
129        }
130
131        private static Query _populate(PageContext pc, Query qry,Array data) throws PageException {
132                // check if the array only contains simple values or mixed
133                Iterator<?> it = data.valueIterator();
134                Object o;
135                boolean hasSimpleValues=false;
136                while(it.hasNext()){
137                        o=it.next();
138                        if(!Decision.isStruct(o) && !Decision.isArray(o)) hasSimpleValues=true;
139                }
140                
141                
142                if(hasSimpleValues) {
143                        qry.addRow();
144                        populateRow(qry, data);
145                }
146                else {
147                        it = data.valueIterator();
148                        while(it.hasNext()){
149                                o=it.next();
150                                qry.addRow();
151                                if(Decision.isStruct(o))populateRow(qry,Caster.toStruct(o));
152                                else if(Decision.isArray(o))populateRow(qry,Caster.toArray(o));
153                                else {
154                                        populateRow(qry,new ArrayImpl(new Object[]{o}));
155                                }
156                        }
157                }
158                return qry;
159        }
160
161        private static void populateRow(Query qry, Struct data) throws PageException {
162                Key[] columns = QueryUtil.getColumnNames(qry);
163                int row=qry.getRecordcount();
164                Object value;
165                for(int i=0;i<columns.length;i++){
166                        value=data.get(columns[i],null);
167                        if(value!=null) qry.setAt(columns[i], row, value);
168                }
169                
170        }
171
172        private static void populateRow(Query qry, Array data) throws PageException {
173                Iterator<?> it = data.valueIterator();
174                Key[] columns = QueryUtil.getColumnNames(qry);
175                int row=qry.getRecordcount();
176                int index=-1;
177                while(it.hasNext()){
178                        index++;
179                        if(index>=columns.length) break;
180                        qry.setAt(columns[index], row, it.next());
181                }
182        }
183        
184        private static Array toArray(PageContext pc,Object columnNames, int index) throws PageException {
185                if(Decision.isArray(columnNames))
186                        return Caster.toArray(columnNames);
187                String str=Caster.toString(columnNames,null);
188                if(str==null) throw new FunctionException(pc, "QueryNew", index, index==1?"columnNames":"columnTypes", "cannot cast to a array or a string list");
189                return ListUtil.listToArrayTrim(str,",");
190        }
191}