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