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.closure;
020
021import java.util.ArrayList;
022import java.util.Enumeration;
023import java.util.Iterator;
024import java.util.List;
025import java.util.ListIterator;
026import java.util.Map.Entry;
027import java.util.concurrent.ExecutorService;
028import java.util.concurrent.Executors;
029import java.util.concurrent.Future;
030
031import lucee.runtime.PageContext;
032import lucee.runtime.concurrency.Data;
033import lucee.runtime.concurrency.UDFCaller2;
034import lucee.runtime.exp.CasterException;
035import lucee.runtime.exp.FunctionException;
036import lucee.runtime.exp.PageException;
037import lucee.runtime.functions.BIF;
038import lucee.runtime.op.Caster;
039import lucee.runtime.type.Array;
040import lucee.runtime.type.ArrayImpl;
041import lucee.runtime.type.Collection;
042import lucee.runtime.type.Collection.Key;
043import lucee.runtime.type.Iteratorable;
044import lucee.runtime.type.KeyImpl;
045import lucee.runtime.type.Query;
046import lucee.runtime.type.QueryImpl;
047import lucee.runtime.type.Struct;
048import lucee.runtime.type.StructImpl;
049import lucee.runtime.type.UDF;
050import lucee.runtime.type.it.ForEachQueryIterator;
051import lucee.runtime.type.scope.ArgumentIntKey;
052import lucee.runtime.type.util.ListUtil;
053import lucee.runtime.type.util.StringListData;
054
055public class Map extends BIF {
056
057        private static final long serialVersionUID = -1435100019820996876L;
058
059
060        public static Object call(PageContext pc , Object obj, UDF udf) throws PageException {
061                return _call(pc, obj, udf, false,20);
062        }
063        public static Object call(PageContext pc , Object obj, UDF udf, boolean parallel) throws PageException {
064                return _call(pc, obj, udf, parallel, 20);
065        }
066        public static Object call(PageContext pc , Object obj, UDF udf, boolean parallel, double maxThreads) throws PageException {
067                return _call(pc, obj, udf, parallel, (int)maxThreads);
068        }
069        
070        public static Collection _call(PageContext pc , Object obj, UDF udf, boolean parallel, int maxThreads) throws PageException { 
071                
072                ExecutorService execute=null;
073                List<Future<Data<Object>>> futures=null;
074                if(parallel) {
075                        execute = Executors.newFixedThreadPool(maxThreads);
076                        futures=new ArrayList<Future<Data<Object>>>();
077                }
078                
079                Collection coll;
080                
081                // Array
082                if(obj instanceof Array) {
083                        coll=invoke(pc, (Array)obj, udf,execute,futures);
084                }
085                // Query
086                else if(obj instanceof Query) {
087                        coll=invoke(pc, (Query)obj, udf,execute,futures);
088                }
089                // Struct
090                else if(obj instanceof Struct) {
091                        coll=invoke(pc, (Struct)obj, udf,execute,futures);
092                }
093                // other Iteratorable
094                else if(obj instanceof Iteratorable) {
095                        coll=invoke(pc, (Iteratorable)obj, udf,execute,futures);
096                }
097                // Map
098                else if(obj instanceof java.util.Map) {
099                        coll=invoke(pc, (java.util.Map)obj, udf,execute,futures);
100                }
101                //List
102                else if(obj instanceof List) {
103                        coll=invoke(pc, (List)obj, udf,execute,futures);
104                }
105                // Iterator
106                else if(obj instanceof Iterator) {
107                        coll=invoke(pc, (Iterator)obj, udf,execute,futures);
108                }
109                // Enumeration
110                else if(obj instanceof Enumeration) {
111                        coll=invoke(pc, (Enumeration)obj, udf,execute,futures);
112                }
113                // String List
114                else if(obj instanceof StringListData) {
115                        coll=invoke(pc, (StringListData)obj, udf,execute,futures);
116                }
117                else
118                        throw new FunctionException(pc, "Map", 1, "data", "cannot iterate througth this type "+Caster.toTypeName(obj.getClass()));
119                
120                if(parallel) afterCall(pc,coll,futures,execute);
121                
122                return coll;
123        }
124        
125        private static Collection invoke(PageContext pc, StringListData sld, UDF udf, ExecutorService es, List<Future<Data<Object>>> futures) throws CasterException, PageException {
126                Array arr = ListUtil.listToArray(sld.list, sld.delimiter,sld.includeEmptyFieldsx,sld.multiCharacterDelimiter);
127                
128                Array rtn=new ArrayImpl();
129                Iterator<Entry<Key, Object>> it = arr.entryIterator();
130                Entry<Key, Object> e;
131                boolean async=es!=null;
132                Object res;
133                while(it.hasNext()){
134                        e = it.next();
135                        res=_inv(pc, udf, new Object[]{e.getValue(),Caster.toDoubleValue(e.getKey().getString()),sld.list,sld.delimiter},e.getKey(), es, futures);
136                        if(!async) rtn.set(e.getKey(),res);
137                }
138                return rtn;
139        }
140
141        private static Collection invoke(PageContext pc, Array arr, UDF udf, ExecutorService es, List<Future<Data<Object>>> futures) throws CasterException, PageException {
142                Array rtn=new ArrayImpl();
143                Iterator<Entry<Key, Object>> it = arr.entryIterator();
144                Entry<Key, Object> e;
145                boolean async=es!=null;
146                Object res;
147                while(it.hasNext()){
148                        e = it.next();
149                        res=_inv(pc, udf, new Object[]{e.getValue(),Caster.toDoubleValue(e.getKey().getString()),arr},e.getKey(), es, futures);
150                        if(!async) rtn.set(e.getKey(),res);
151                }
152                return rtn;
153        }
154        
155
156        private static Collection invoke(PageContext pc, List list, UDF udf, ExecutorService es, List<Future<Data<Object>>> futures) throws CasterException, PageException {
157                Array rtn=new ArrayImpl();
158                ListIterator it = list.listIterator();
159                boolean async=es!=null;
160                Object res,v;
161                int index;
162                ArgumentIntKey k;
163                while(it.hasNext()){
164                        index = it.nextIndex();
165                        k = ArgumentIntKey.init(index);
166            v = it.next();
167                        res=_inv(pc, udf, new Object[]{v,Caster.toDoubleValue(k.getString()),list},k, es, futures);
168                        if(!async) rtn.set(k,res);
169                }
170                return rtn;
171        }
172
173        private static Struct invoke(PageContext pc, Struct sct, UDF udf, ExecutorService es, List<Future<Data<Object>>> futures) throws PageException {
174                Struct rtn=sct instanceof StructImpl?new StructImpl(((StructImpl)sct).getType()):new StructImpl();
175                Iterator<Entry<Key, Object>> it = sct.entryIterator();
176                Entry<Key, Object> e;
177                boolean async=es!=null;
178                Object res;
179                while(it.hasNext()){
180                        e = it.next();
181                        res=_inv(pc, udf, new Object[]{e.getKey().getString(),e.getValue(),sct},e.getKey(), es, futures);
182                        if(!async) rtn.set(e.getKey(),res);
183                }
184                return rtn;
185        }
186
187        private static Query invoke(PageContext pc, Query qry, UDF udf, ExecutorService es, List<Future<Data<Object>>> futures) throws PageException {
188                Key[] colNames = qry.getColumnNames();
189                Query rtn=new QueryImpl(colNames,0,qry.getName());
190                final int pid=pc.getId();
191                ForEachQueryIterator it=new ForEachQueryIterator(qry, pid);
192                int rowNbr;
193                Object row,res;
194                
195                boolean async=es!=null;
196                while(it.hasNext()){
197                        row = it.next();
198                        rowNbr = qry.getCurrentrow(pid);
199                        
200                        res=_inv(pc, udf, new Object[]{row,rowNbr,qry},rowNbr, es, futures);
201                        if(!async) {
202                                addRow(Caster.toStruct(res),rtn);
203                        }
204                }
205                return rtn;
206        }
207        
208        private static void addRow(Struct data, Query qry) {
209                Iterator<Entry<Key, Object>> it = data.entryIterator();
210                Entry<Key, Object> e;
211                int rn = qry.addRow();
212                while(it.hasNext()){
213                        e=it.next();
214                        qry.setAtEL(e.getKey(), rn, e.getValue());
215                }
216        }
217        
218        private static Struct invoke(PageContext pc, java.util.Map map, UDF udf, ExecutorService es, List<Future<Data<Object>>> futures) throws PageException {
219                Struct rtn=new StructImpl();
220                Iterator<Entry> it = map.entrySet().iterator();
221                Entry e;
222                boolean async=es!=null;
223                Object res;
224                while(it.hasNext()){
225                        e = it.next();
226                        res=_inv(pc, udf, new Object[]{e.getKey(),e.getValue(),map},e.getKey(), es, futures);
227                        if(!async) {
228                                rtn.set(KeyImpl.toKey(e.getKey()),res);
229                        }
230                }
231                return rtn;
232        }
233        
234        private static Struct invoke(PageContext pc, Iteratorable i, UDF udf, ExecutorService es, List<Future<Data<Object>>> futures) throws PageException {
235                Iterator<Entry<Key, Object>> it = i.entryIterator();
236                
237                Struct rtn=new StructImpl();
238                Entry<Key, Object> e;
239                boolean async=es!=null;
240                Object res;
241                while(it.hasNext()){
242                        e = it.next();
243                        res=_inv(pc, udf, new Object[]{e.getKey().getString(),e.getValue()},e.getKey(), es, futures);
244                        if(!async) rtn.set(e.getKey(),res);
245                }
246                return rtn;
247        }
248        
249        private static Array invoke(PageContext pc, Iterator it, UDF udf, ExecutorService es, List<Future<Data<Object>>> futures) throws PageException {
250                
251                Array rtn=new ArrayImpl();
252                Object v;
253                boolean async=es!=null;
254                Object res;
255                int count=0;
256                ArgumentIntKey k;
257                while(it.hasNext()){
258                        v = it.next();
259                        k = ArgumentIntKey.init(++count);
260                        res=_inv(pc, udf, new Object[]{v},k, es, futures);
261                        if(!async) rtn.set(k,res);
262                }
263                return rtn;
264        }
265        
266        private static Array invoke(PageContext pc, Enumeration e, UDF udf, ExecutorService es, List<Future<Data<Object>>> futures) throws PageException {
267                
268                Array rtn=new ArrayImpl();
269                Object v;
270                boolean async=es!=null;
271                Object res;
272                int count=0;
273                ArgumentIntKey k;
274                while(e.hasMoreElements()){
275                        v = e.nextElement();
276                        k = ArgumentIntKey.init(++count);
277                        res=_inv(pc, udf, new Object[]{v},k, es, futures);
278                        if(!async) rtn.set(k,res);
279                }
280                return rtn;
281        }
282        
283        private static Object _inv(PageContext pc, UDF udf, Object[] args,Object key,ExecutorService es,List<Future<Data<Object>>> futures) throws PageException {
284                if(es==null) {
285                        return udf.call(pc, args, true);
286                }
287                futures.add(es.submit(new UDFCaller2<Object>(pc, udf, args, key,true)));
288                return null;
289        }
290        
291        public static void afterCall(PageContext pc, Collection coll, List<Future<Data<Object>>> futures, ExecutorService es) throws PageException {
292                boolean isQuery=coll instanceof Query;
293                try {
294                        Iterator<Future<Data<Object>>> it = futures.iterator();
295                        Data<Object> d;
296                        while(it.hasNext()){
297                                d = it.next().get();
298                                if(isQuery) addRow(Caster.toStruct(d.result),(Query)coll);
299                                else coll.set(KeyImpl.toKey(d.passed), d.result);
300                                pc.write(d.output);
301                        }
302                }
303                catch(Exception e){
304                        throw Caster.toPageException(e);
305                }
306                finally {
307                        es.shutdown();
308                }
309        }
310        
311        @Override
312        public Object invoke(PageContext pc, Object[] args) throws PageException {
313                if(args.length==2)
314                        return call(pc, (args[0]), Caster.toFunction(args[1]));
315                if(args.length==3)
316                        return call(pc, (args[0]), Caster.toFunction(args[1]), Caster.toBooleanValue(args[2]));
317                if(args.length==4)
318                        return call(pc, (args[0]), Caster.toFunction(args[1]), Caster.toBooleanValue(args[2]), Caster.toDoubleValue(args[3]));
319                
320                throw new FunctionException(pc, "Map", 2, 4, args.length);
321        }
322
323}