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