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