package railo.runtime.functions.system;
import java.io.File;
import railo.runtime.Mapping;
import railo.runtime.Page;
import railo.runtime.PageContext;
import railo.runtime.PageSourceImpl;
import railo.runtime.config.ConfigWebImpl;
import railo.runtime.exp.ExpressionException;
import railo.runtime.exp.PageException;
import railo.runtime.op.Caster;
import railo.runtime.op.Duplicator;
import railo.runtime.type.Collection;
import railo.runtime.type.FunctionValue;
import railo.runtime.type.KeyImpl;
import railo.runtime.type.Struct;
import railo.runtime.type.StructImpl;
import railo.runtime.type.UDF;
import railo.runtime.type.UDFPlus;
import railo.runtime.type.scope.Variables;
import railo.runtime.type.scope.VariablesImpl;
import railo.runtime.type.util.ArrayUtil;
import railo.runtime.type.util.KeyConstants;
public class CFFunction {
private static final Variables VAR = new VariablesImpl();
//private static Map udfs=new ReferenceMap();
public static Object call(PageContext pc , Object[] objArr) throws PageException {
if(objArr.length<3)
throw new ExpressionException("invalid call of a CFML Based built in function");
// translate arguments
String filename=Caster.toString((((FunctionValue) objArr[0]).getValue()));
Collection.Key name=KeyImpl.toKey((((FunctionValue) objArr[1]).getValue()));
boolean isweb=Caster.toBooleanValue((((FunctionValue) objArr[2]).getValue()));
UDF udf=loadUDF(pc, filename, name, isweb);
Struct meta = udf.getMetaData(pc);
boolean caller=meta==null?false:Caster.toBooleanValue(meta.get(KeyConstants._caller,Boolean.FALSE),false);
Struct namedArguments=null;
Object[] arguments=null;
if(objArr.length<=3)arguments=ArrayUtil.OBJECT_EMPTY;
else if(objArr[3] instanceof FunctionValue){
FunctionValue fv;
namedArguments=new StructImpl();
if(caller)namedArguments.setEL(KeyConstants._caller, Duplicator.duplicate(pc.undefinedScope(),false));
for(int i=3;i<objArr.length;i++){
fv=toFunctionValue(name,objArr[i]);
namedArguments.set(fv.getName(), fv.getValue());
}
}
else {
int offset=(caller?2:3);
arguments=new Object[objArr.length-offset];
if(caller)arguments[0]=Duplicator.duplicate(pc.undefinedScope(),false);
for(int i=3;i<objArr.length;i++){
arguments[i-offset]=toObject(name,objArr[i]);
}
}
// load UDF
// execute UDF
if(namedArguments==null){
return ((UDFPlus)udf).call(pc,name, arguments, false);
}
return ((UDFPlus)udf).callWithNamedValues(pc,name, namedArguments, false);
}
public static synchronized UDF loadUDF(PageContext pc, String filename,Collection.Key name,boolean isweb) throws PageException {
ConfigWebImpl config = (ConfigWebImpl) pc.getConfig();
String key=isweb?name.getString()+config.getId():name.getString();
UDF udf=config.getFromFunctionCache(key);
if(udf!=null) return udf;
Mapping mapping=isweb?config.getFunctionMapping():config.getServerFunctionMapping();
PageSourceImpl ps = (PageSourceImpl) mapping.getPageSource(filename);
Page p = ps.loadPage(pc);
// execute page
Variables old = pc.variablesScope();
pc.setVariablesScope(VAR);
boolean wasSilent = pc.setSilent();
try {
p.call(pc);
Object o= pc.variablesScope().get(name,null);
if(o instanceof UDF) {
udf= (UDF) o;
config.putToFunctionCache(key, udf);
return udf;
}
throw new ExpressionException("there is no Function defined with name ["+name+"] in template ["+mapping.getStrPhysical()+File.separator+filename+"]");
}
catch (Throwable t) {
throw Caster.toPageException(t);
}
finally{
pc.setVariablesScope(old);
if(!wasSilent)pc.unsetSilent();
}
}
private static FunctionValue toFunctionValue(Collection.Key name,Object obj) throws ExpressionException {
if(obj instanceof FunctionValue)
return (FunctionValue) obj;
throw new ExpressionException("invalid argument for function "+name+", you can not mix named and unnamed arguments");
}
private static Object toObject(Collection.Key name,Object obj) throws ExpressionException {
if(obj instanceof FunctionValue)
throw new ExpressionException("invalid argument for function "+name+", you can not mix named and unnamed arguments");
return obj;
}
}