/**
*
* Copyright (c) 2014, the Railo Company Ltd. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
**/
package lucee.runtime.functions.system;
import java.io.File;
import lucee.commons.lang.ExceptionUtil;
import lucee.runtime.Mapping;
import lucee.runtime.Page;
import lucee.runtime.PageContext;
import lucee.runtime.config.ConfigWebImpl;
import lucee.runtime.exp.ExpressionException;
import lucee.runtime.exp.PageException;
import lucee.runtime.op.Caster;
import lucee.runtime.op.Duplicator;
import lucee.runtime.type.Collection;
import lucee.runtime.type.FunctionValue;
import lucee.runtime.type.KeyImpl;
import lucee.runtime.type.Struct;
import lucee.runtime.type.StructImpl;
import lucee.runtime.type.UDF;
import lucee.runtime.type.UDFImpl;
import lucee.runtime.type.scope.Variables;
import lucee.runtime.type.scope.VariablesImpl;
import lucee.runtime.type.util.ArrayUtil;
import lucee.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 callerScopes=(meta==null)?false:Caster.toBooleanValue(meta.get("callerScopes",Boolean.FALSE),false);
boolean caller=meta==null?false:Caster.toBooleanValue(meta.get(KeyConstants._caller,Boolean.FALSE),false);
Struct namedArguments=null,cs=null;
if(callerScopes) {
cs=new StructImpl();
if(pc.undefinedScope().getCheckArguments()) {
cs.set(KeyConstants._local, pc.localScope().duplicate(false));
cs.set(KeyConstants._arguments, pc.argumentsScope().duplicate(false));
}
}
Object[] arguments=null;
if(objArr.length<=3)arguments=ArrayUtil.OBJECT_EMPTY;
else if(objArr[3] instanceof FunctionValue){
FunctionValue fv;
namedArguments=new StructImpl(Struct.TYPE_LINKED);
if(callerScopes) namedArguments.setEL(KeyConstants._caller, cs);
else 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||callerScopes?2:3);
arguments=new Object[objArr.length-offset];
if(callerScopes) arguments[0]=cs;
else if(caller)arguments[0]=Duplicator.duplicate(pc.undefinedScope(),false);
for(int i=3;i<objArr.length;i++){
arguments[i-offset]=toObject(name,objArr[i]);
}
}
// execute UDF
if(namedArguments==null){
return ((UDFImpl)udf).call(pc,name, arguments, false);
}
return ((UDFImpl)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.getIdentification().getId():name.getString();
UDF udf=config.getFromFunctionCache(key);
if(udf!=null) return udf;
Mapping mapping=isweb?config.getFunctionMapping():config.getServerFunctionMapping();
Page p = mapping.getPageSource(filename).loadPage(pc,false);
// 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) {
ExceptionUtil.rethrowIfNecessary(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;
}
}