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.commons.lang.Pair;
032import lucee.runtime.PageContext;
033import lucee.runtime.concurrency.Data;
034import lucee.runtime.concurrency.UDFCaller2;
035import lucee.runtime.exp.CasterException;
036import lucee.runtime.exp.FunctionException;
037import lucee.runtime.exp.PageException;
038import lucee.runtime.functions.BIF;
039import lucee.runtime.op.Caster;
040import lucee.runtime.type.Array;
041import lucee.runtime.type.ArrayImpl;
042import lucee.runtime.type.Collection;
043import lucee.runtime.type.Collection.Key;
044import lucee.runtime.type.Iteratorable;
045import lucee.runtime.type.KeyImpl;
046import lucee.runtime.type.Query;
047import lucee.runtime.type.QueryImpl;
048import lucee.runtime.type.Struct;
049import lucee.runtime.type.StructImpl;
050import lucee.runtime.type.UDF;
051import lucee.runtime.type.it.ForEachQueryIterator;
052import lucee.runtime.type.scope.ArgumentIntKey;
053import lucee.runtime.type.util.ListUtil;
054import lucee.runtime.type.util.StringListData;
055
056public class Filter extends BIF {
057
058        private static final long serialVersionUID = -5940580562772523622L;
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<Pair<Object, Object>>>> futures=null;
074                if(parallel) {
075                        execute = Executors.newFixedThreadPool(maxThreads);
076                        futures=new ArrayList<Future<Data<Pair<Object, 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, "Filter", 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, Array arr, UDF udf, ExecutorService es, List<Future<Data<Pair<Object, Object>>>> futures) throws CasterException, PageException {
126                Array rtn=new ArrayImpl();
127                Iterator<Entry<Key, Object>> it = arr.entryIterator();
128                Entry<Key, Object> e;
129                boolean async=es!=null;
130                Object res;
131                while(it.hasNext()){
132                        e = it.next();
133                        res=_inv(pc, udf, new Object[]{e.getValue(),Caster.toDoubleValue(e.getKey().getString()),arr},e.getKey(),e.getValue(), es, futures);
134                        if(!async && Caster.toBooleanValue(res)) {
135                                rtn.append(e.getValue());
136                        }
137                }
138                return rtn;
139        }
140        
141        private static Collection invoke(PageContext pc, StringListData sld, UDF udf, ExecutorService es, List<Future<Data<Pair<Object, Object>>>> futures) throws CasterException, PageException {
142                Array arr = ListUtil.listToArray(sld.list, sld.delimiter,sld.includeEmptyFieldsx,sld.multiCharacterDelimiter);
143                
144                Array rtn=new ArrayImpl();
145                Iterator<Entry<Key, Object>> it = arr.entryIterator();
146                Entry<Key, Object> e;
147                boolean async=es!=null;
148                Object res;
149                while(it.hasNext()){
150                        e = it.next();
151                        res=_inv(pc, udf, new Object[]{e.getValue(),Caster.toDoubleValue(e.getKey().getString()),sld.list,sld.delimiter},e.getKey(),e.getValue(), es, futures);
152                        if(!async && Caster.toBooleanValue(res)) {
153                                rtn.append(e.getValue());
154                        }
155                }
156                return rtn;
157        }
158
159        private static Collection invoke(PageContext pc, Query qry, UDF udf, ExecutorService es, List<Future<Data<Pair<Object, Object>>>> futures) throws CasterException, PageException {
160                Key[] colNames = qry.getColumnNames();
161                Query rtn=new QueryImpl(colNames,0,qry.getName());
162                final int pid=pc.getId();
163                ForEachQueryIterator it=new ForEachQueryIterator(qry, pid);
164                int rowNbr;
165                Object row;
166                boolean async=es!=null;
167                Object res;
168                while(it.hasNext()){
169                        row = it.next();
170                        rowNbr = qry.getCurrentrow(pid);
171                        
172                        res=_inv(pc, udf, new Object[]{row,Caster.toDoubleValue(rowNbr),qry},rowNbr,qry, es, futures);
173                        if(!async && Caster.toBooleanValue(res)) {
174                                addRow(qry,rtn,rowNbr);
175                        }
176                }
177                return rtn;
178        }
179        
180
181        private static void addRow(Query src, Query trg, int srcRow) {
182                Key[] colNames=src.getColumnNames();
183                int trgRow = trg.addRow();
184                for(int c=0;c<colNames.length;c++){
185                        trg.setAtEL(colNames[c], trgRow, src.getAt(colNames[c], srcRow,null));
186                }
187        }
188        private static Collection invoke(PageContext pc, List list, UDF udf, ExecutorService es, List<Future<Data<Pair<Object, Object>>>> futures) throws CasterException, PageException {
189                Array rtn=new ArrayImpl();
190                ListIterator it = list.listIterator();
191                boolean async=es!=null;
192                Object res,v;
193                int index;
194                ArgumentIntKey k;
195                while(it.hasNext()){
196                        index = it.nextIndex();
197                        k = ArgumentIntKey.init(index);
198            v = it.next();
199                        res=_inv(pc, udf, new Object[]{v,Caster.toDoubleValue(k.getString()),list},k,v, es, futures);
200                        if(!async && Caster.toBooleanValue(res)) rtn.append(v);
201                }
202                return rtn;
203        }
204
205        private static Struct invoke(PageContext pc, Struct sct, UDF udf, ExecutorService es, List<Future<Data<Pair<Object, Object>>>> futures) throws PageException {
206                Struct rtn=sct instanceof StructImpl?new StructImpl(((StructImpl)sct).getType()):new StructImpl();
207                Iterator<Entry<Key, Object>> it = sct.entryIterator();
208                Entry<Key, Object> e;
209                boolean async=es!=null;
210                Object res;
211                while(it.hasNext()){
212                        e = it.next();
213                        res=_inv(pc, udf, new Object[]{e.getKey().getString(),e.getValue(),sct},e.getKey(),e.getValue(), es, futures);
214                        if(!async && Caster.toBooleanValue(res)) rtn.set(e.getKey(),e.getValue());
215                }
216                return rtn;
217        }
218        
219        private static Struct invoke(PageContext pc, java.util.Map map, UDF udf, ExecutorService es, List<Future<Data<Pair<Object, Object>>>> futures) throws PageException {
220                Struct rtn=new StructImpl();
221                Iterator<Entry> it = map.entrySet().iterator();
222                Entry e;
223                boolean async=es!=null;
224                Object res;
225                while(it.hasNext()){
226                        e = it.next();
227                        res=_inv(pc, udf, new Object[]{e.getKey(),e.getValue(),map},e.getKey(),e.getValue(), es, futures);
228                        if(!async && Caster.toBooleanValue(res)) rtn.set(KeyImpl.toKey(e.getKey()),e.getValue());
229                }
230                return rtn;
231        }
232        
233        private static Struct invoke(PageContext pc, Iteratorable i, UDF udf, ExecutorService es, List<Future<Data<Pair<Object, Object>>>> futures) throws PageException {
234                Iterator<Entry<Key, Object>> it = i.entryIterator();
235                
236                Struct rtn=new StructImpl();
237                Entry<Key, Object> e;
238                boolean async=es!=null;
239                Object res;
240                while(it.hasNext()){
241                        e = it.next();
242                        res=_inv(pc, udf, new Object[]{e.getKey().getString(),e.getValue()},e.getKey(),e.getValue(), es, futures);
243                        if(!async && Caster.toBooleanValue(res)) rtn.set(e.getKey(),e.getValue());
244                }
245                return rtn;
246        }
247        
248        private static Array invoke(PageContext pc, Iterator it, UDF udf, ExecutorService es, List<Future<Data<Pair<Object, Object>>>> futures) throws PageException {
249                
250                Array rtn=new ArrayImpl();
251                Object v;
252                boolean async=es!=null;
253                Object res;
254                int count=0;
255                ArgumentIntKey k;
256                while(it.hasNext()){
257                        v = it.next();
258                        k = ArgumentIntKey.init(++count);
259                        res=_inv(pc, udf, new Object[]{v},k,v, es, futures);
260                        if(!async && Caster.toBooleanValue(res)) rtn.append(v);
261                }
262                return rtn;
263        }
264        
265        private static Array invoke(PageContext pc, Enumeration e, UDF udf, ExecutorService es, List<Future<Data<Pair<Object, Object>>>> futures) throws PageException {
266                
267                Array rtn=new ArrayImpl();
268                Object v;
269                boolean async=es!=null;
270                Object res;
271                int count=0;
272                ArgumentIntKey k;
273                while(e.hasMoreElements()){
274                        v = e.nextElement();
275                        k = ArgumentIntKey.init(++count);
276                        res=_inv(pc, udf, new Object[]{v},k,v, es, futures);
277                        if(!async && Caster.toBooleanValue(res)) rtn.append(v);
278                }
279                return rtn;
280        }
281        
282        private static Object _inv(PageContext pc, UDF udf, Object[] args,Object key,Object value,ExecutorService es,List<Future<Data<Pair<Object, Object>>>> futures) throws PageException {
283                if(es==null) {
284                        return udf.call(pc, args, true);
285                }
286                futures.add(es.submit(new UDFCaller2<Pair<Object, Object>>(pc, udf, args, new Pair<Object, Object>(key,value),true)));
287                return null;
288        }
289        
290        public static void afterCall(PageContext pc, Collection coll, List<Future<Data<Pair<Object, Object>>>> futures, ExecutorService es) throws PageException {
291                try{
292                        boolean isArray=false;
293                        boolean isQuery=false;
294                        if(coll instanceof Array) isArray=true;
295                        else if(coll instanceof Query) isQuery=true;
296                        
297                        
298                        Iterator<Future<Data<Pair<Object, Object>>>> it = futures.iterator();
299                        Data<Pair<Object, Object>> d;
300                        while(it.hasNext()){
301                                d = it.next().get();
302                                
303                                if(Caster.toBooleanValue(d.result)) { 
304                                        if(isArray) 
305                                                ((Array)coll).append(d.passed.getValue());
306                                        else if(isQuery) 
307                                                addRow(
308                                                                (Query)d.passed.getValue()
309                                                                ,(Query)coll
310                                                                ,Caster.toIntValue(d.passed.getName()));
311                                        else 
312                                                coll.set(KeyImpl.toKey(d.passed.getName()),d.passed.getValue());
313                                }
314                                
315                                pc.write(d.output);
316                        }
317                }
318                catch(Exception e){
319                        throw Caster.toPageException(e);
320                }
321                finally {
322                        es.shutdown();
323                }
324        }
325
326        @Override
327        public Object invoke(PageContext pc, Object[] args) throws PageException {
328                
329                if(args.length==2)
330                        return call(pc, (args[0]), Caster.toFunction(args[1]));
331                if(args.length==3)
332                        return call(pc, (args[0]), Caster.toFunction(args[1]), Caster.toBooleanValue(args[2]));
333                if(args.length==4)
334                        return call(pc, (args[0]), Caster.toFunction(args[1]), Caster.toBooleanValue(args[2]), Caster.toDoubleValue(args[3]));
335                
336                throw new FunctionException(pc, "Filter", 2, 4, args.length);
337        }
338}