package railo.runtime.interpreter.ref.func; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import railo.commons.lang.CFTypes; import railo.commons.lang.StringUtil; import railo.runtime.PageContext; import railo.runtime.exp.ExpressionException; import railo.runtime.exp.PageException; import railo.runtime.functions.BIF; import railo.runtime.interpreter.InterpreterException; import railo.runtime.interpreter.ref.Ref; import railo.runtime.interpreter.ref.RefSupport; import railo.runtime.interpreter.ref.cast.Casting; import railo.runtime.interpreter.ref.literal.LFunctionValue; import railo.runtime.interpreter.ref.util.RefUtil; import railo.runtime.op.Caster; import railo.runtime.op.Constants; import railo.runtime.type.FunctionValue; import railo.runtime.type.FunctionValueImpl; import railo.runtime.type.util.ArrayUtil; import railo.runtime.type.util.UDFUtil; import railo.transformer.library.function.FunctionLibFunction; import railo.transformer.library.function.FunctionLibFunctionArg; /** * a built In Function call * * */ public final class BIFCall extends RefSupport implements Ref { private Ref[] refArgs; private FunctionLibFunction flf; private Object obj; /** * constructor of the class * @param pc * @param flf * @param refArgs */ public BIFCall(FunctionLibFunction flf,Ref[] refArgs) { this.flf=flf; this.refArgs=refArgs; } public BIFCall(Object obj,FunctionLibFunction flf,Ref[] refArgs) { this.obj=obj; this.flf=flf; this.refArgs=refArgs; } @Override public Object getValue(PageContext pc) throws PageException { Object[] arguments = null; if(isDynamic()){ arguments = RefUtil.getValue(pc,refArgs); if(flf.hasDefaultValues()){ List<Object> tmp=new ArrayList<Object>(); ArrayList<FunctionLibFunctionArg> args = flf.getArg(); Iterator<FunctionLibFunctionArg> it = args.iterator(); FunctionLibFunctionArg arg; while(it.hasNext()){ arg=it.next(); if(arg.getDefaultValue()!=null) tmp.add(new FunctionValueImpl(arg.getName(),arg.getDefaultValue())); } for(int i=0;i<arguments.length;i++){ tmp.add(arguments[i]); } arguments=tmp.toArray(); } arguments=new Object[]{arguments}; } else { if(isNamed(pc,refArgs)){ FunctionValue[] fvalues=getFunctionValues(pc,refArgs); String[] names = getNames(fvalues); ArrayList<FunctionLibFunctionArg> list = flf.getArg(); Iterator<FunctionLibFunctionArg> it = list.iterator(); arguments=new Object[list.size()]; FunctionLibFunctionArg flfa; int index=0; VT vt; while(it.hasNext()) { flfa =it.next(); vt = getMatchingValueAndType(flfa,fvalues,names); if(vt.index!=-1) names[vt.index]=null; arguments[index++]=new Casting( vt.type, CFTypes.toShort(vt.type, false, CFTypes.TYPE_UNKNOW), vt.value).getValue(pc); } for(int y=0;y<names.length;y++){ if(names[y]!=null) { ExpressionException ee = new InterpreterException("argument ["+names[y]+"] is not allowed for function ["+flf.getName()+"]"); UDFUtil.addFunctionDoc(ee, flf); throw ee; } } } else { arguments = RefUtil.getValue(pc,refArgs); } } BIF bif=flf.getBIF(); if(flf.getMemberChaining() && obj!=null) { bif.invoke(pc, arguments); return obj; } return Caster.castTo(pc,flf.getReturnTypeAsString(),bif.invoke(pc, arguments),false); } private VT getMatchingValueAndType(FunctionLibFunctionArg flfa, FunctionValue[] fvalues, String[] names) throws ExpressionException { String flfan=flfa.getName(); // first search if a argument match for(int i=0;i<names.length;i++){ if(names[i]!=null && names[i].equalsIgnoreCase(flfan)) { return new VT(fvalues[i].getValue(),flfa.getTypeAsString(),i); } } // then check if a alias match String alias=flfa.getAlias(); if(!StringUtil.isEmpty(alias)) { for(int i=0;i<names.length;i++){ if(names[i]!=null && railo.runtime.type.util.ListUtil.listFindNoCase(alias, names[i])!=-1){ return new VT(fvalues[i].getValue(),flfa.getTypeAsString(),i); } } } // if not required return the default value if(!flfa.getRequired()) { String defaultValue = flfa.getDefaultValue(); String type=flfa.getTypeAsString().toLowerCase(); if(defaultValue==null) { if(type.equals("boolean") || type.equals("bool")) return new VT(Boolean.FALSE,type,-1); if(type.equals("number") || type.equals("numeric") || type.equals("double")) return new VT(Constants.DOUBLE_ZERO,type,-1); return new VT(null,type,-1); } return new VT(defaultValue,type,-1); } ExpressionException ee = new InterpreterException("missing required argument ["+flfan+"] for function ["+flfa.getFunction().getName()+"]"); UDFUtil.addFunctionDoc(ee, flfa.getFunction()); throw ee; } private String[] getNames(FunctionValue[] fvalues) { String[] names=new String[fvalues.length]; for(int i=0;i<fvalues.length;i++){ names[i]=fvalues[i].getNameAsString(); } return names; } private FunctionValue[] getFunctionValues(PageContext pc,Ref[] refArgs) throws PageException { FunctionValue[] fvalues=new FunctionValue[refArgs.length]; for(int i=0;i<refArgs.length;i++){ fvalues[i]=(FunctionValue) ((LFunctionValue) ((Casting)refArgs[i]).getRef()).getValue(pc); } return fvalues; } private boolean isNamed(PageContext pc,Ref[] refArgs) throws PageException { if(ArrayUtil.isEmpty(refArgs)) return false; Casting cast; int count=0; for(int i=0;i<refArgs.length;i++){ if(refArgs[i] instanceof Casting){ cast=(Casting) refArgs[i]; if(cast.getRef() instanceof LFunctionValue && ((LFunctionValue)cast.getRef()).getValue(pc) instanceof FunctionValue) { count++; } } } if(count!=0 && count!=refArgs.length){ ExpressionException ee = new InterpreterException("invalid argument for function "+flf.getName()+", you can not mix named and unnamed arguments"); UDFUtil.addFunctionDoc(ee, flf); throw ee; } return count!=0; } private boolean isDynamic() { return flf.getArgType()==FunctionLibFunction.ARG_DYNAMIC; } @Override public String getTypeName() { return "built in function"; } } class VT{ Object value; String type; int index; public VT(Object value, String type, int index) { this.value=value; this.type=type; this.index=index; } }