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