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}