001    package railo.runtime.functions.system;
002    
003    import java.io.File;
004    
005    import railo.runtime.Mapping;
006    import railo.runtime.Page;
007    import railo.runtime.PageContext;
008    import railo.runtime.PageSourceImpl;
009    import railo.runtime.config.ConfigWebImpl;
010    import railo.runtime.exp.ExpressionException;
011    import railo.runtime.exp.PageException;
012    import railo.runtime.op.Caster;
013    import railo.runtime.op.Duplicator;
014    import railo.runtime.type.Collection;
015    import railo.runtime.type.FunctionValue;
016    import railo.runtime.type.KeyImpl;
017    import railo.runtime.type.Struct;
018    import railo.runtime.type.StructImpl;
019    import railo.runtime.type.UDF;
020    import railo.runtime.type.scope.Variables;
021    import railo.runtime.type.scope.VariablesImpl;
022    import railo.runtime.type.util.ArrayUtil;
023    import railo.runtime.type.util.KeyConstants;
024    
025    public class CFFunction {
026            
027            
028            private static final Variables VAR = new VariablesImpl();
029            //private static Map udfs=new ReferenceMap();
030            
031            public static Object call(PageContext pc , Object[] objArr) throws PageException {
032                    if(objArr.length<3)
033                            throw new ExpressionException("invalid call of a CFML Based built in function");
034                    
035                    // translate arguments
036                    String filename=Caster.toString((((FunctionValue) objArr[0]).getValue()));
037                    Collection.Key name=KeyImpl.toKey((((FunctionValue) objArr[1]).getValue()));
038                    boolean isweb=Caster.toBooleanValue((((FunctionValue) objArr[2]).getValue()));
039                    
040                    
041                    UDF udf=loadUDF(pc, filename, name, isweb);
042                    Struct meta = udf.getMetaData(pc);
043                    boolean caller=meta==null?false:Caster.toBooleanValue(meta.get(KeyConstants._caller,Boolean.FALSE),false);
044                    
045                    Struct namedArguments=null;
046                    Object[] arguments=null;
047                    if(objArr.length<=3)arguments=ArrayUtil.OBJECT_EMPTY;
048                    else if(objArr[3] instanceof FunctionValue){
049                            FunctionValue fv;
050                            namedArguments=new StructImpl();
051                            if(caller)namedArguments.setEL(KeyConstants._caller, Duplicator.duplicate(pc.undefinedScope(),false));
052                            for(int i=3;i<objArr.length;i++){
053                                    fv=toFunctionValue(name,objArr[i]);
054                                    namedArguments.set(fv.getName(), fv.getValue());
055                            }
056                    }
057                    else {
058                            int offset=(caller?2:3);
059                            arguments=new Object[objArr.length-offset];
060                            if(caller)arguments[0]=Duplicator.duplicate(pc.undefinedScope(),false);
061                            for(int i=3;i<objArr.length;i++){
062                                    arguments[i-offset]=toObject(name,objArr[i]);
063                            }
064                    }
065                    
066                    
067                    // load UDF
068                    
069                    
070                    // execute UDF
071                    if(namedArguments==null){
072                            return udf.call(pc, arguments, false);
073                    }
074                    
075                    
076                    return udf.callWithNamedValues(pc, namedArguments, false);
077            }
078    
079            public static synchronized UDF loadUDF(PageContext pc, String filename,Collection.Key name,boolean isweb) throws PageException {
080                    ConfigWebImpl config = (ConfigWebImpl) pc.getConfig();
081                    String key=isweb?name.getString()+config.getId():name.getString();
082            UDF udf=config.getFromFunctionCache(key);
083                    if(udf!=null) return udf;
084                    
085                    Mapping mapping=isweb?config.getFunctionMapping():config.getServerFunctionMapping();
086            PageSourceImpl ps = (PageSourceImpl) mapping.getPageSource(filename);
087            Page p = ps.loadPage(pc);       
088                    
089            
090            // execute page
091            Variables old = pc.variablesScope();
092            pc.setVariablesScope(VAR);
093            boolean wasSilent = pc.setSilent();
094            try {
095                            p.call(pc);
096                            Object o= pc.variablesScope().get(name,null);
097                            if(o instanceof UDF) {
098                                    udf= (UDF) o;
099                                    config.putToFunctionCache(key, udf);
100                                    return udf;
101                            }
102                            throw new ExpressionException("there is no Function defined with name ["+name+"] in template ["+mapping.getStrPhysical()+File.separator+filename+"]");
103                    } 
104            catch (Throwable t) {
105                            throw Caster.toPageException(t);
106                    }
107                    finally{
108                            pc.setVariablesScope(old);
109                            if(!wasSilent)pc.unsetSilent();
110                    }
111                    
112                    
113            }
114    
115            private static FunctionValue toFunctionValue(Collection.Key name,Object obj) throws ExpressionException {
116                    if(obj instanceof FunctionValue)
117                            return (FunctionValue) obj;
118                    throw new ExpressionException("invalid argument for function "+name+", you can not mix named and unnamed arguments");
119            }
120    
121            private static Object toObject(Collection.Key name,Object obj) throws ExpressionException {
122                    if(obj instanceof FunctionValue)
123                            throw new ExpressionException("invalid argument for function "+name+", you can not mix named and unnamed arguments");
124                    return obj;
125            }
126    }