001    package railo.runtime.interpreter.ref.func;
002    
003    import java.util.ArrayList;
004    import java.util.Iterator;
005    import java.util.List;
006    
007    import railo.commons.lang.CFTypes;
008    import railo.commons.lang.StringUtil;
009    import railo.runtime.PageContext;
010    import railo.runtime.exp.ExpressionException;
011    import railo.runtime.exp.PageException;
012    import railo.runtime.interpreter.ref.Ref;
013    import railo.runtime.interpreter.ref.RefSupport;
014    import railo.runtime.interpreter.ref.cast.Casting;
015    import railo.runtime.interpreter.ref.literal.LFunctionValue;
016    import railo.runtime.interpreter.ref.util.RefUtil;
017    import railo.runtime.op.Caster;
018    import railo.runtime.op.Constants;
019    import railo.runtime.reflection.Reflector;
020    import railo.runtime.type.FunctionValue;
021    import railo.runtime.type.FunctionValueImpl;
022    import railo.runtime.type.util.ArrayUtil;
023    import railo.transformer.cfml.expression.CFMLExprTransformer;
024    import railo.transformer.library.function.FunctionLibFunction;
025    import railo.transformer.library.function.FunctionLibFunctionArg;
026    
027    /**
028     * a built In Function call
029     *
030     *
031     */
032    public final class BIFCall extends RefSupport implements Ref {
033                    
034            private Ref[] refArgs;
035            private PageContext pc;
036        private FunctionLibFunction flf;
037    
038    
039            /**
040             * constructor of the class
041             * @param pc
042             * @param flf 
043             * @param refArgs 
044             */
045            public BIFCall(PageContext pc, FunctionLibFunction flf,Ref[] refArgs) {
046                    this.pc=pc;
047            this.flf=flf;
048                    this.refArgs=refArgs;
049            }
050            
051            /**
052             * @see railo.runtime.interpreter.ref.Ref#getValue()
053             */
054            public Object getValue() throws PageException {
055            
056            Object[] arguments = null;
057            
058            
059            if(isDynamic()){
060                    arguments = RefUtil.getValue(refArgs);
061                    if(flf.hasDefaultValues()){
062                            List tmp=new ArrayList();
063                            ArrayList args = flf.getArg();
064                            Iterator it = args.iterator();
065                            FunctionLibFunctionArg arg;
066                            while(it.hasNext()){
067                                    arg=(FunctionLibFunctionArg) it.next();
068                                    if(arg.getDefaultValue()!=null)
069                                            tmp.add(new FunctionValueImpl(arg.getName(),arg.getDefaultValue()));
070                            }
071                            for(int i=0;i<arguments.length;i++){
072                                    tmp.add(arguments[i]);
073                            }
074                            arguments=tmp.toArray();
075                    }
076                    arguments=new Object[]{pc,arguments};
077            }
078            else {
079                    if(isNamed(refArgs)){
080                            FunctionValue[] fvalues=getFunctionValues(refArgs);
081                            String[] names = getNames(fvalues);
082                            
083                            ArrayList<FunctionLibFunctionArg> list = flf.getArg();
084                                    Iterator<FunctionLibFunctionArg> it = list.iterator();
085                                    arguments=new Object[list.size()+1];
086                                    arguments[0]=pc;
087                    
088                                    
089                                    FunctionLibFunctionArg flfa;
090                                    int index=0;
091                                    VT vt;
092                                    while(it.hasNext()) {
093                                            flfa =it.next();
094                                            vt = getMatchingValueAndType(flfa,fvalues,names);
095                                            if(vt.index!=-1) 
096                                                    names[vt.index]=null;
097                                            arguments[++index]=new Casting(pc, vt.type, CFTypes.toShort(vt.type, CFTypes.TYPE_UNKNOW), vt.value).getValue();        
098                                    }
099                                    
100                                    for(int y=0;y<names.length;y++){
101                                            if(names[y]!=null) {
102                                                    ExpressionException ee = new ExpressionException("argument ["+names[y]+"] is not allowed for function ["+flf.getName()+"]");
103                                                    CFMLExprTransformer.addFunctionDoc(ee, flf);
104                                                    throw ee;
105                                            }
106                                    }
107                            
108                    }
109                    else {
110                            arguments = RefUtil.getValue(refArgs);
111                    Object[] newAttr = new Object[arguments.length+1];
112                    newAttr[0]=pc;
113                    for(int i=0;i<arguments.length;i++) {
114                        newAttr[i+1]=arguments[i];
115                    }
116                    arguments=newAttr;
117                    }
118            }
119            Class clazz=flf.getCazz();
120            if(clazz==null)throw new ExpressionException("class "+clazz+" not found");
121            return Caster.castTo(pc,flf.getReturnTypeAsString(),Reflector.callStaticMethod(clazz,"call",arguments),false);
122            }
123            
124    
125    
126        private VT getMatchingValueAndType(FunctionLibFunctionArg flfa, FunctionValue[] fvalues, String[] names) throws ExpressionException {
127            String flfan=flfa.getName();
128                    
129                    // first search if a argument match
130                    for(int i=0;i<names.length;i++){
131                            if(names[i]!=null && names[i].equalsIgnoreCase(flfan)) {
132                                    return new VT(fvalues[i].getValue(),flfa.getTypeAsString(),i);
133                            }
134                    }
135                    
136                    // then check if a alias match
137                    String alias=flfa.getAlias();
138                    if(!StringUtil.isEmpty(alias)) {
139                            for(int i=0;i<names.length;i++){
140                                    if(names[i]!=null && railo.runtime.type.List.listFindNoCase(alias, names[i])!=-1){
141                                            return new VT(fvalues[i].getValue(),flfa.getTypeAsString(),i);
142                                    }
143                            }
144                    }
145                    
146                    // if not required return the default value
147                    if(!flfa.getRequired()) {
148                            String defaultValue = flfa.getDefaultValue();
149                            String type=flfa.getTypeAsString().toLowerCase();
150                            
151                            if(defaultValue==null) {
152                                    if(type.equals("boolean") || type.equals("bool")) 
153                                            return new VT(Boolean.FALSE,type,-1);
154                                    if(type.equals("number") || type.equals("numeric") || type.equals("double")) 
155                                            return new VT(Constants.DOUBLE_ZERO,type,-1);
156                                    return new VT(null,type,-1);
157                            }
158                            else {
159                                    return new VT(defaultValue,type,-1);
160                            }
161                            
162                    }
163                    ExpressionException ee = new ExpressionException("missing required argument ["+flfan+"] for function ["+flfa.getFunction().getName()+"]");
164                    CFMLExprTransformer.addFunctionDoc(ee, flfa.getFunction());
165                    throw ee;
166            }
167    
168            private String[] getNames(FunctionValue[] fvalues) throws PageException {
169            String[] names=new String[fvalues.length];
170            for(int i=0;i<fvalues.length;i++){
171                            names[i]=fvalues[i].getName();
172                    }
173                    return names;
174            }
175    
176            private FunctionValue[] getFunctionValues(Ref[] refArgs) throws PageException {
177                    FunctionValue[] fvalues=new FunctionValue[refArgs.length];
178            for(int i=0;i<refArgs.length;i++){
179                            fvalues[i]=(FunctionValue) ((LFunctionValue) ((Casting)refArgs[i]).getRef()).getValue();
180                    }
181                    return fvalues;
182            }
183    
184            private boolean isNamed(Ref[] refArgs) throws PageException {
185            if(ArrayUtil.isEmpty(refArgs)) return false;
186                    Casting cast;
187                    int count=0;
188                    for(int i=0;i<refArgs.length;i++){
189                            if(refArgs[i] instanceof Casting){
190                                    cast=(Casting) refArgs[i];
191                                    if(cast.getRef() instanceof LFunctionValue && ((LFunctionValue)cast.getRef()).getValue() instanceof FunctionValue) {
192                                            count++;
193                                    }
194                            }
195                    }
196                    if(count!=0 && count!=refArgs.length){
197                            ExpressionException ee = new ExpressionException("invalid argument for function "+flf.getName()+", you can not mix named and unnamed arguments");
198                            CFMLExprTransformer.addFunctionDoc(ee, flf);
199                            throw ee;
200                    }
201                    return count!=0;
202            }
203    
204            private boolean isDynamic() {
205            return flf.getArgType()==FunctionLibFunction.ARG_DYNAMIC;
206        }
207        
208            /**
209             * @see railo.runtime.interpreter.ref.Ref#getTypeName()
210             */
211            public String getTypeName() {
212                    return "built in function";
213            }
214    
215    }
216    
217    class VT{
218    
219            Object value;
220            String type;
221            int index;
222    
223            public VT(Object value, String type, int index) {
224                    this.value=value;
225                    this.type=type;
226                    this.index=index;
227            }
228            
229    }