001 package railo.runtime.interpreter.ref.func; 002 003 import java.util.ArrayList; 004 import java.util.Iterator; 005 import java.util.List; 006 007 import railo.commons.lang.CFTypes; 008 import railo.commons.lang.StringUtil; 009 import railo.runtime.PageContext; 010 import railo.runtime.exp.ExpressionException; 011 import railo.runtime.exp.PageException; 012 import railo.runtime.functions.BIF; 013 import railo.runtime.interpreter.ref.Ref; 014 import railo.runtime.interpreter.ref.RefSupport; 015 import railo.runtime.interpreter.ref.cast.Casting; 016 import railo.runtime.interpreter.ref.literal.LFunctionValue; 017 import railo.runtime.interpreter.ref.util.RefUtil; 018 import railo.runtime.op.Caster; 019 import railo.runtime.op.Constants; 020 import railo.runtime.reflection.Reflector; 021 import railo.runtime.type.FunctionValue; 022 import railo.runtime.type.FunctionValueImpl; 023 import railo.runtime.type.util.ArrayUtil; 024 import railo.runtime.type.util.UDFUtil; 025 import railo.transformer.library.function.FunctionLibFunction; 026 import railo.transformer.library.function.FunctionLibFunctionArg; 027 028 /** 029 * a built In Function call 030 * 031 * 032 */ 033 public final class BIFCall extends RefSupport implements Ref { 034 035 private Ref[] refArgs; 036 private FunctionLibFunction flf; 037 private Object obj; 038 039 040 /** 041 * constructor of the class 042 * @param pc 043 * @param flf 044 * @param refArgs 045 */ 046 public BIFCall(FunctionLibFunction flf,Ref[] refArgs) { 047 this.flf=flf; 048 this.refArgs=refArgs; 049 } 050 public BIFCall(Object obj,FunctionLibFunction flf,Ref[] refArgs) { 051 this.obj=obj; 052 this.flf=flf; 053 this.refArgs=refArgs; 054 } 055 056 @Override 057 public Object getValue(PageContext pc) throws PageException { 058 059 Object[] arguments = null; 060 061 062 if(isDynamic()){ 063 arguments = RefUtil.getValue(pc,refArgs); 064 if(flf.hasDefaultValues()){ 065 List<Object> tmp=new ArrayList<Object>(); 066 ArrayList<FunctionLibFunctionArg> args = flf.getArg(); 067 Iterator<FunctionLibFunctionArg> it = args.iterator(); 068 FunctionLibFunctionArg arg; 069 while(it.hasNext()){ 070 arg=it.next(); 071 if(arg.getDefaultValue()!=null) 072 tmp.add(new FunctionValueImpl(arg.getName(),arg.getDefaultValue())); 073 } 074 for(int i=0;i<arguments.length;i++){ 075 tmp.add(arguments[i]); 076 } 077 arguments=tmp.toArray(); 078 } 079 arguments=new Object[]{arguments}; 080 } 081 else { 082 if(isNamed(pc,refArgs)){ 083 FunctionValue[] fvalues=getFunctionValues(pc,refArgs); 084 String[] names = getNames(fvalues); 085 086 ArrayList<FunctionLibFunctionArg> list = flf.getArg(); 087 Iterator<FunctionLibFunctionArg> it = list.iterator(); 088 arguments=new Object[list.size()]; 089 090 091 FunctionLibFunctionArg flfa; 092 int index=0; 093 VT vt; 094 while(it.hasNext()) { 095 flfa =it.next(); 096 vt = getMatchingValueAndType(flfa,fvalues,names); 097 if(vt.index!=-1) 098 names[vt.index]=null; 099 arguments[index++]=new Casting( vt.type, CFTypes.toShort(vt.type, false, CFTypes.TYPE_UNKNOW), vt.value).getValue(pc); 100 } 101 102 for(int y=0;y<names.length;y++){ 103 if(names[y]!=null) { 104 ExpressionException ee = new ExpressionException("argument ["+names[y]+"] is not allowed for function ["+flf.getName()+"]"); 105 UDFUtil.addFunctionDoc(ee, flf); 106 throw ee; 107 } 108 } 109 110 } 111 else { 112 arguments = RefUtil.getValue(pc,refArgs); 113 } 114 } 115 BIF bif=flf.getBIF(); 116 117 if(flf.getMemberChaining() && obj!=null) { 118 bif.invoke(pc, arguments); 119 return obj; 120 } 121 return Caster.castTo(pc,flf.getReturnTypeAsString(),bif.invoke(pc, arguments),false); 122 123 124 /*Class clazz=flf.getClazz(); 125 if(clazz==null)throw new ExpressionException("class "+clazz+" not found"); 126 127 if(flf.getMemberChaining() && obj!=null) { 128 Reflector.callStaticMethod(clazz,"call",arguments); 129 return obj; 130 } 131 return Caster.castTo(pc,flf.getReturnTypeAsString(),Reflector.callStaticMethod(clazz,"call",arguments),false);*/ 132 } 133 134 135 136 private VT getMatchingValueAndType(FunctionLibFunctionArg flfa, FunctionValue[] fvalues, String[] names) throws ExpressionException { 137 String flfan=flfa.getName(); 138 139 // first search if a argument match 140 for(int i=0;i<names.length;i++){ 141 if(names[i]!=null && names[i].equalsIgnoreCase(flfan)) { 142 return new VT(fvalues[i].getValue(),flfa.getTypeAsString(),i); 143 } 144 } 145 146 // then check if a alias match 147 String alias=flfa.getAlias(); 148 if(!StringUtil.isEmpty(alias)) { 149 for(int i=0;i<names.length;i++){ 150 if(names[i]!=null && railo.runtime.type.util.ListUtil.listFindNoCase(alias, names[i])!=-1){ 151 return new VT(fvalues[i].getValue(),flfa.getTypeAsString(),i); 152 } 153 } 154 } 155 156 // if not required return the default value 157 if(!flfa.getRequired()) { 158 String defaultValue = flfa.getDefaultValue(); 159 String type=flfa.getTypeAsString().toLowerCase(); 160 161 if(defaultValue==null) { 162 if(type.equals("boolean") || type.equals("bool")) 163 return new VT(Boolean.FALSE,type,-1); 164 if(type.equals("number") || type.equals("numeric") || type.equals("double")) 165 return new VT(Constants.DOUBLE_ZERO,type,-1); 166 return new VT(null,type,-1); 167 } 168 return new VT(defaultValue,type,-1); 169 170 } 171 ExpressionException ee = new ExpressionException("missing required argument ["+flfan+"] for function ["+flfa.getFunction().getName()+"]"); 172 UDFUtil.addFunctionDoc(ee, flfa.getFunction()); 173 throw ee; 174 } 175 176 private String[] getNames(FunctionValue[] fvalues) { 177 String[] names=new String[fvalues.length]; 178 for(int i=0;i<fvalues.length;i++){ 179 names[i]=fvalues[i].getNameAsString(); 180 } 181 return names; 182 } 183 184 private FunctionValue[] getFunctionValues(PageContext pc,Ref[] refArgs) throws PageException { 185 FunctionValue[] fvalues=new FunctionValue[refArgs.length]; 186 for(int i=0;i<refArgs.length;i++){ 187 fvalues[i]=(FunctionValue) ((LFunctionValue) ((Casting)refArgs[i]).getRef()).getValue(pc); 188 } 189 return fvalues; 190 } 191 192 private boolean isNamed(PageContext pc,Ref[] refArgs) throws PageException { 193 if(ArrayUtil.isEmpty(refArgs)) return false; 194 Casting cast; 195 int count=0; 196 for(int i=0;i<refArgs.length;i++){ 197 if(refArgs[i] instanceof Casting){ 198 cast=(Casting) refArgs[i]; 199 if(cast.getRef() instanceof LFunctionValue && ((LFunctionValue)cast.getRef()).getValue(pc) instanceof FunctionValue) { 200 count++; 201 } 202 } 203 } 204 if(count!=0 && count!=refArgs.length){ 205 ExpressionException ee = new ExpressionException("invalid argument for function "+flf.getName()+", you can not mix named and unnamed arguments"); 206 UDFUtil.addFunctionDoc(ee, flf); 207 throw ee; 208 } 209 return count!=0; 210 } 211 212 private boolean isDynamic() { 213 return flf.getArgType()==FunctionLibFunction.ARG_DYNAMIC; 214 } 215 216 @Override 217 public String getTypeName() { 218 return "built in function"; 219 } 220 221 } 222 223 class VT{ 224 225 Object value; 226 String type; 227 int index; 228 229 public VT(Object value, String type, int index) { 230 this.value=value; 231 this.type=type; 232 this.index=index; 233 } 234 235 }