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 **/
019/**
020 * Implements the CFML Function arrayavg
021 */
022package lucee.runtime.functions.closure;
023
024import java.util.ArrayList;
025import java.util.Enumeration;
026import java.util.Iterator;
027import java.util.List;
028import java.util.ListIterator;
029import java.util.Map;
030import java.util.Map.Entry;
031import java.util.concurrent.ExecutorService;
032import java.util.concurrent.Executors;
033import java.util.concurrent.Future;
034
035import lucee.runtime.PageContext;
036import lucee.runtime.concurrency.Data;
037import lucee.runtime.concurrency.UDFCaller2;
038import lucee.runtime.exp.FunctionException;
039import lucee.runtime.exp.PageException;
040import lucee.runtime.functions.BIF;
041import lucee.runtime.op.Caster;
042import lucee.runtime.type.Array;
043import lucee.runtime.type.Collection.Key;
044import lucee.runtime.type.Iteratorable;
045import lucee.runtime.type.Query;
046import lucee.runtime.type.UDF;
047import lucee.runtime.type.it.ForEachQueryIterator;
048import lucee.runtime.type.util.ListUtil;
049import lucee.runtime.type.util.StringListData;
050
051
052public final class Each extends BIF {
053
054        private static final long serialVersionUID = 1955185705863596525L;
055
056        public static String call(PageContext pc , Object obj, UDF udf) throws PageException {
057                return _call(pc, obj, udf, false,20);
058        }
059        public static String call(PageContext pc , Object obj, UDF udf, boolean parallel) throws PageException {
060                return _call(pc, obj, udf, parallel, 20);
061        }
062        public static String call(PageContext pc , Object obj, UDF udf, boolean parallel, double maxThreads) throws PageException {
063                return _call(pc, obj, udf, parallel, (int)maxThreads);
064        }
065        
066        private static String _call(PageContext pc , Object obj, UDF udf, boolean parallel, int maxThreads) throws PageException {
067                ExecutorService execute=null;
068                List<Future<Data<Object>>> futures=null;
069                if(parallel) {
070                        execute = Executors.newFixedThreadPool(maxThreads);
071                        futures=new ArrayList<Future<Data<Object>>>();
072                }
073                
074                // Array
075                if(obj instanceof Array) {
076                        invoke(pc, (Array)obj, udf,execute,futures);
077                }
078
079                // Query
080                else if(obj instanceof Query) {
081                        invoke(pc, (Query)obj, udf,execute,futures);
082                }
083                
084
085                // other Iteratorable
086                else if(obj instanceof Iteratorable) {
087                        invoke(pc, (Iteratorable)obj, udf,execute,futures);
088                }
089                // Map
090                else if(obj instanceof Map) {
091                        Iterator it = ((Map)obj).entrySet().iterator();
092                        Entry e;
093                        while(it.hasNext()){
094                                e = (Entry) it.next();
095                                _call(pc,udf,new Object[]{e.getKey(),e.getValue(),obj},execute,futures);
096                                //udf.call(pc, new Object[]{e.getKey(),e.getValue()}, true);
097                        }
098                }
099                //List
100                else if(obj instanceof List) {
101                        ListIterator it = ((List)obj).listIterator();
102                        int index;
103                        while(it.hasNext()){
104                                index=it.nextIndex();
105                                _call(pc,udf,new Object[]{it.next(),new Double(index),obj},execute,futures);
106                                //udf.call(pc, new Object[]{it.next()}, true);
107                        }
108                }
109
110                // Iterator
111                else if(obj instanceof Iterator) {
112                        Iterator it = (Iterator)obj;
113                        while(it.hasNext()){
114                                _call(pc,udf, new Object[]{it.next()},execute,futures);
115                                //udf.call(pc, new Object[]{it.next()}, true);
116                        }
117                }
118                // Enumeration
119                else if(obj instanceof Enumeration) {
120                        Enumeration e = (Enumeration)obj;
121                        while(e.hasMoreElements()){
122                                _call(pc,udf,new Object[]{e.nextElement()},execute,futures);
123                                //udf.call(pc, new Object[]{e.nextElement()}, true);
124                        }
125                }
126                // StringListData
127                else if(obj instanceof StringListData) {
128                        invoke(pc, (StringListData)obj, udf, execute, futures);
129                }
130
131                else
132                        throw new FunctionException(pc, "Each", 1, "data", "cannot iterate througth this type "+Caster.toTypeName(obj.getClass()));
133                
134                if(parallel) afterCall(pc,futures,execute);
135                        
136                
137                return null;
138        }
139
140        public static void afterCall(PageContext pc, List<Future<Data<Object>>> futures, ExecutorService es) throws PageException {
141                try{
142                        Iterator<Future<Data<Object>>> it = futures.iterator();
143                        //Future<String> f;
144                        while(it.hasNext()){
145                                pc.write(it.next().get().output);
146                        }
147                }
148                catch(Exception e){
149                        throw Caster.toPageException(e);
150                }
151                finally {
152                        es.shutdown();
153                }
154        }
155        public static void invoke(PageContext pc , Array array, UDF udf,ExecutorService execute,List<Future<Data<Object>>> futures) throws PageException {
156                Iterator<Entry<Key, Object>> it = array.entryIterator();
157                Entry<Key, Object> e;
158                while(it.hasNext()){
159                        e=it.next();
160                        _call(pc,udf,new Object[]{e.getValue(),Caster.toDoubleValue(e.getKey().getString()),array},execute,futures);
161                        //udf.call(pc, new Object[]{it.next()}, true);
162                }
163        }
164
165        public static void invoke(PageContext pc ,Query qry, UDF udf,ExecutorService execute,List<Future<Data<Object>>> futures) throws PageException {
166                final int pid=pc.getId();
167                ForEachQueryIterator it=new ForEachQueryIterator(qry, pid);
168                try{
169                        Object row;
170                        //Entry<Key, Object> e;
171                        while(it.hasNext()){
172                                row=it.next();
173                                _call(pc,udf,new Object[]{row,Caster.toDoubleValue(qry.getCurrentrow(pid)),qry},execute,futures);
174                        }
175                }
176                finally {
177                        it.reset();
178                }
179        }
180
181        public static void invoke(PageContext pc , Iteratorable coll, UDF udf,ExecutorService execute,List<Future<Data<Object>>> futures) throws PageException {
182                Iterator<Entry<Key, Object>> it = coll.entryIterator();
183                Entry<Key, Object> e;
184                while(it.hasNext()){
185                        e = it.next();
186                        _call(pc,udf,new Object[]{e.getKey().getString(),e.getValue(),coll},execute,futures);
187                        //udf.call(pc, new Object[]{e.getKey().getString(),e.getValue()}, true);
188                }
189        }
190        
191        private static void invoke(PageContext pc, StringListData sld, UDF udf, ExecutorService execute, List<Future<Data<Object>>> futures) throws PageException {
192                Array arr = ListUtil.listToArray(sld.list, sld.delimiter,sld.includeEmptyFieldsx,sld.multiCharacterDelimiter);
193                        
194                
195                Iterator<Entry<Key, Object>> it = arr.entryIterator();
196                Entry<Key, Object> e;
197                while(it.hasNext()){
198                        e=it.next();
199                        _call(pc,udf,new Object[]{e.getValue(),Caster.toDoubleValue(e.getKey().getString()),sld.list,sld.delimiter},execute,futures);
200                }
201                
202        }
203        
204        private static void _call(PageContext pc, UDF udf, Object[] args,ExecutorService es,List<Future<Data<Object>>> futures) throws PageException {
205                if(es==null) {
206                        udf.call(pc, args, true);
207                        return;
208                }
209                futures.add(es.submit(new UDFCaller2<Object>(pc, udf, args,null, true)));
210        }
211        
212        @Override
213        public Object invoke(PageContext pc, Object[] args) throws PageException {
214                
215                if(args.length==2)
216                        return call(pc, args[0], Caster.toFunction(args[1]));
217                if(args.length==3)
218                        return call(pc, args[0], Caster.toFunction(args[1]), Caster.toBooleanValue(args[2]));
219                if(args.length==4)
220                        return call(pc, args[0], Caster.toFunction(args[1]), Caster.toBooleanValue(args[2]), Caster.toDoubleValue(args[3]));
221                
222                throw new FunctionException(pc, "Each", 2, 4, args.length);
223                
224                
225        }
226        
227}