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.type.util; 020 021import java.util.ArrayList; 022import java.util.Iterator; 023import java.util.Map; 024import java.util.Map.Entry; 025 026import lucee.commons.lang.ExceptionUtil; 027import lucee.commons.lang.StringUtil; 028import lucee.runtime.Page; 029import lucee.runtime.PageContext; 030import lucee.runtime.PagePlus; 031import lucee.runtime.PageSource; 032import lucee.runtime.dump.DumpData; 033import lucee.runtime.dump.DumpProperties; 034import lucee.runtime.dump.DumpRow; 035import lucee.runtime.dump.DumpTable; 036import lucee.runtime.dump.SimpleDumpData; 037import lucee.runtime.exp.ExpressionException; 038import lucee.runtime.exp.PageException; 039import lucee.runtime.exp.PageExceptionImpl; 040import lucee.runtime.op.Caster; 041import lucee.runtime.type.Collection; 042import lucee.runtime.type.Collection.Key; 043import lucee.runtime.type.FunctionArgument; 044import lucee.runtime.type.KeyImpl; 045import lucee.runtime.type.Struct; 046import lucee.runtime.type.UDF; 047import lucee.runtime.type.UDFGSProperty; 048import lucee.runtime.type.UDFPlus; 049import lucee.runtime.type.scope.Argument; 050import lucee.runtime.type.scope.ArgumentIntKey; 051import lucee.transformer.library.function.FunctionLibFunction; 052import lucee.transformer.library.function.FunctionLibFunctionArg; 053 054public class UDFUtil { 055 056 public static final short TYPE_UDF=1; 057 public static final short TYPE_BIF=2; 058 public static final short TYPE_CLOSURE=4; 059 060 private static final char CACHE_DEL = ';'; 061 private static final char CACHE_DEL2 = ':'; 062 063 private static final FunctionArgument[] EMPTY = new FunctionArgument[0]; 064 065 /** 066 * add detailed function documentation to the exception 067 * @param pe 068 * @param flf 069 */ 070 public static void addFunctionDoc(PageExceptionImpl pe,FunctionLibFunction flf) { 071 ArrayList<FunctionLibFunctionArg> args=flf.getArg(); 072 Iterator<FunctionLibFunctionArg> it = args.iterator(); 073 074 // Pattern 075 StringBuilder pattern=new StringBuilder(flf.getName()); 076 StringBuilder end=new StringBuilder(); 077 pattern.append("("); 078 FunctionLibFunctionArg arg; 079 int c=0; 080 while(it.hasNext()){ 081 arg = it.next(); 082 if(!arg.isRequired()) { 083 pattern.append(" ["); 084 end.append("]"); 085 } 086 if(c++>0)pattern.append(", "); 087 pattern.append(arg.getName()); 088 pattern.append(":"); 089 pattern.append(arg.getTypeAsString()); 090 091 } 092 pattern.append(end); 093 pattern.append("):"); 094 pattern.append(flf.getReturnTypeAsString()); 095 096 pe.setAdditional(KeyConstants._Pattern, pattern); 097 098 // Documentation 099 StringBuilder doc=new StringBuilder(flf.getDescription()); 100 StringBuilder req=new StringBuilder(); 101 StringBuilder opt=new StringBuilder(); 102 StringBuilder tmp; 103 doc.append("\n"); 104 105 it = args.iterator(); 106 while(it.hasNext()){ 107 arg = it.next(); 108 tmp=arg.isRequired()?req:opt; 109 110 tmp.append("- "); 111 tmp.append(arg.getName()); 112 tmp.append(" ("); 113 tmp.append(arg.getTypeAsString()); 114 tmp.append("): "); 115 tmp.append(arg.getDescription()); 116 tmp.append("\n"); 117 } 118 119 if(req.length()>0)doc.append("\nRequired:\n").append(req); 120 if(opt.length()>0)doc.append("\nOptional:\n").append(opt); 121 122 123 pe.setAdditional(KeyImpl.init("Documentation"), doc); 124 125 } 126 127 public static Object getDefaultValue(PageContext pc, PageSource ps, int udfIndex, int index, Object defaultValue) throws PageException { 128 Page p=ComponentUtil.getPage(pc,ps); 129 if(p instanceof PagePlus) return ((PagePlus)p).udfDefaultValue(pc,udfIndex,index,defaultValue); 130 Object rtn = p.udfDefaultValue(pc,udfIndex,index); 131 if(rtn==null) return defaultValue;// in that case it can make no diff between null and not existing, but this only happens with data from old ra files 132 return rtn; 133 } 134 135 136 public static Object getDefaultValue(PageContext pc, UDFPlus udf, int index, Object defaultValue) throws PageException { 137 Page p=ComponentUtil.getPage(pc,udf.getPageSource()); 138 if(p instanceof PagePlus) return ((PagePlus)p).udfDefaultValue(pc,udf.getIndex(),index,defaultValue); 139 Object rtn = p.udfDefaultValue(pc,udf.getIndex(),index); 140 if(rtn==null) return defaultValue;// in that case it can make no diff between null and not existing, but this only happens with data from old ra files 141 return rtn; 142 } 143 144 public static void argumentCollection(Struct values) { 145 argumentCollection(values,EMPTY); 146 } 147 148 public static void argumentCollection(Struct values, FunctionArgument[] funcArgs) { 149 Object value=values.removeEL(KeyConstants._argumentCollection); 150 if(value !=null) { 151 value=Caster.unwrap(value,value); 152 153 if(value instanceof Argument) { 154 Argument argColl=(Argument) value; 155 Iterator<Key> it = argColl.keyIterator(); 156 Key k; 157 int i=-1; 158 while(it.hasNext()) { 159 i++; 160 k = it.next(); 161 if(funcArgs.length>i && k instanceof ArgumentIntKey) { 162 if(!values.containsKey(funcArgs[i].getName())) 163 values.setEL(funcArgs[i].getName(),argColl.get(k,Argument.NULL)); 164 else 165 values.setEL(k,argColl.get(k,Argument.NULL)); 166 } 167 else if(!values.containsKey(k)){ 168 values.setEL(k,argColl.get(k,Argument.NULL)); 169 } 170 } 171 } 172 else if(value instanceof Collection) { 173 Collection argColl=(Collection) value; 174 //Collection.Key[] keys = argColl.keys(); 175 Iterator<Key> it = argColl.keyIterator(); 176 Key k; 177 while(it.hasNext()) { 178 k = it.next(); 179 if(!values.containsKey(k)){ 180 values.setEL(k,argColl.get(k,Argument.NULL)); 181 } 182 } 183 } 184 else if(value instanceof Map) { 185 Map map=(Map) value; 186 Iterator it = map.entrySet().iterator(); 187 Map.Entry entry; 188 Key key; 189 while(it.hasNext()) { 190 entry=(Entry) it.next(); 191 key = Caster.toKey(entry.getKey(),null); 192 if(!values.containsKey(key)){ 193 values.setEL(key,entry.getValue()); 194 } 195 } 196 } 197 else if(value instanceof java.util.List) { 198 java.util.List list=(java.util.List) value; 199 Iterator it = list.iterator(); 200 Object v; 201 int index=0; 202 Key k; 203 while(it.hasNext()) { 204 v= it.next(); 205 k=ArgumentIntKey.init(++index); 206 if(!values.containsKey(k)){ 207 values.setEL(k,v); 208 } 209 } 210 } 211 else { 212 values.setEL(KeyConstants._argumentCollection,value); 213 } 214 } 215 } 216 217 public static String toReturnFormat(int returnFormat,String defaultValue) { 218 if(UDF.RETURN_FORMAT_WDDX==returnFormat) return "wddx"; 219 else if(UDF.RETURN_FORMAT_JSON==returnFormat) return "json"; 220 else if(UDF.RETURN_FORMAT_PLAIN==returnFormat) return "plain"; 221 else if(UDF.RETURN_FORMAT_SERIALIZE==returnFormat) return "cfml"; 222 else if(UDFPlus.RETURN_FORMAT_JAVA==returnFormat) return "java"; 223 // NO XML else if(UDFPlus.RETURN_FORMAT_XML==returnFormat) return "xml"; 224 else return defaultValue; 225 } 226 227 228 public static boolean isValidReturnFormat(int returnFormat) { 229 return toReturnFormat(returnFormat,null)!=null; 230 } 231 232 233 public static int toReturnFormat(String[] returnFormats, int defaultValue) { 234 if(ArrayUtil.isEmpty(returnFormats)) return defaultValue; 235 int rf; 236 for(int i=0;i<returnFormats.length;i++){ 237 rf=toReturnFormat(returnFormats[i].trim(), -1); 238 if(rf!=-1) return rf; 239 } 240 return defaultValue; 241 } 242 public static int toReturnFormat(String returnFormat, int defaultValue) { 243 if(StringUtil.isEmpty(returnFormat,true)) return defaultValue; 244 245 returnFormat=returnFormat.trim().toLowerCase(); 246 if("wddx".equals(returnFormat)) return UDF.RETURN_FORMAT_WDDX; 247 else if("json".equals(returnFormat)) return UDF.RETURN_FORMAT_JSON; 248 else if("plain".equals(returnFormat)) return UDF.RETURN_FORMAT_PLAIN; 249 else if("text".equals(returnFormat)) return UDF.RETURN_FORMAT_PLAIN; 250 else if("serialize".equals(returnFormat)) return UDF.RETURN_FORMAT_SERIALIZE; 251 else if("cfml".equals(returnFormat)) return UDF.RETURN_FORMAT_SERIALIZE; 252 else if("cfm".equals(returnFormat)) return UDF.RETURN_FORMAT_SERIALIZE; 253 else if("xml".equals(returnFormat)) return UDF.RETURN_FORMAT_XML; 254 else if("java".equals(returnFormat)) return UDFPlus.RETURN_FORMAT_JAVA; 255 return defaultValue; 256 } 257 258 259 public static int toReturnFormat(String returnFormat) throws ExpressionException { 260 int rf = toReturnFormat(returnFormat,-1); 261 if(rf!=-1) return rf; 262 throw new ExpressionException("invalid returnFormat definition ["+returnFormat+"], valid values are [wddx,plain,json,cfml]"); 263 } 264 265 public static String toReturnFormat(int returnFormat) throws ExpressionException { 266 if(UDF.RETURN_FORMAT_WDDX==returnFormat) return "wddx"; 267 else if(UDF.RETURN_FORMAT_JSON==returnFormat) return "json"; 268 else if(UDF.RETURN_FORMAT_PLAIN==returnFormat) return "plain"; 269 else if(UDF.RETURN_FORMAT_SERIALIZE==returnFormat) return "cfml"; 270 else if(UDFPlus.RETURN_FORMAT_JAVA==returnFormat) return "java"; 271 else throw new ExpressionException("invalid returnFormat definition, valid values are [wddx,plain,json,cfml]"); 272 } 273 274 275 public static DumpData toDumpData(PageContext pageContext, int maxlevel, DumpProperties dp,UDF udf, short type) { 276 277 if(!dp.getShowUDFs()) { 278 if(TYPE_UDF==type) return new SimpleDumpData("<UDF>"); 279 if(TYPE_BIF==type) return new SimpleDumpData("<BIF>"); 280 if(TYPE_CLOSURE==type) return new SimpleDumpData("<Closure>"); 281 } 282 // arguments 283 FunctionArgument[] args = udf.getFunctionArguments(); 284 285 DumpTable dtArgs; 286 if(TYPE_UDF==type) dtArgs = new DumpTable("udf","#cc66ff","#ffccff","#000000"); 287 else if(TYPE_CLOSURE==type) dtArgs = new DumpTable("udf","#ff00ff","#ffccff","#000000"); 288 else dtArgs = new DumpTable("udf","#ffff66","#ffffcc","#000000"); 289 290 dtArgs.appendRow(new DumpRow(63, new DumpData[]{new SimpleDumpData("label"), new SimpleDumpData("name"), new SimpleDumpData("required"), new SimpleDumpData("type"), new SimpleDumpData("default"), new SimpleDumpData("hint")})); 291 for(int i=0;i<args.length;i++) { 292 FunctionArgument arg=args[i]; 293 DumpData def; 294 try { 295 Object oa=null; 296 try { 297 oa = UDFUtil.getDefaultValue(pageContext, (UDFPlus)udf, i, null);//udf.getDefaultValue(pageContext,i,null); 298 } catch (Throwable t) { 299 ExceptionUtil.rethrowIfNecessary(t); 300 } 301 if(oa==null)oa="null"; 302 def=new SimpleDumpData(Caster.toString(oa)); 303 } catch (PageException e) { 304 def=new SimpleDumpData(""); 305 } 306 dtArgs.appendRow(new DumpRow(0, new DumpData[]{ 307 new SimpleDumpData(arg.getDisplayName()), 308 new SimpleDumpData(arg.getName().getString()), 309 new SimpleDumpData(arg.isRequired()), 310 new SimpleDumpData(arg.getTypeAsString()), 311 def, 312 new SimpleDumpData(arg.getHint())})); 313 //dtArgs.setRow(0,arg.getHint()); 314 315 } 316 DumpTable func; 317 if(TYPE_CLOSURE==type) { 318 func=new DumpTable("function", "#ff00ff", "#ffccff", "#000000"); 319 func.setTitle("Closure"); 320 } 321 else if(TYPE_UDF==type) { 322 func=new DumpTable("function", "#cc66ff", "#ffccff", "#000000"); 323 String f="Function "; 324 try { 325 f=StringUtil.ucFirst(ComponentUtil.toStringAccess(udf.getAccess()).toLowerCase())+" "+f; 326 } 327 catch (ExpressionException e) {} 328 f+=udf.getFunctionName(); 329 if(udf instanceof UDFGSProperty) f+=" (generated)"; 330 func.setTitle(f); 331 } 332 else { 333 String f="Build in Function "+udf.getFunctionName(); 334 335 336 func=new DumpTable("#ffff66","#ffffcc","#000000"); 337 func.setTitle(f); 338 } 339 340 if(udf instanceof UDFPlus) { 341 PageSource ps = ((UDFPlus)udf).getPageSource(); 342 if(ps!=null) 343 func.setComment("source:"+ps.getDisplayPath()); 344 } 345 346 if(!StringUtil.isEmpty(udf.getDescription()))func.setComment(udf.getDescription()); 347 348 func.appendRow(1,new SimpleDumpData("arguments"),dtArgs); 349 func.appendRow(1,new SimpleDumpData("return type"),new SimpleDumpData(udf.getReturnTypeAsString())); 350 351 boolean hasLabel=!StringUtil.isEmpty(udf.getDisplayName());//displayName!=null && !displayName.equals(""); 352 boolean hasHint=!StringUtil.isEmpty(udf.getHint());//hint!=null && !hint.equals(""); 353 354 if(hasLabel || hasHint) { 355 DumpTable box = new DumpTable("#ffffff","#cccccc","#000000"); 356 box.setTitle(hasLabel?udf.getDisplayName():udf.getFunctionName()); 357 if(hasHint)box.appendRow(0,new SimpleDumpData(udf.getHint())); 358 box.appendRow(0,func); 359 return box; 360 } 361 return func; 362 } 363 364 365}