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}