001/**
002 *
003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved.
004 *
005 * This library is free software; you can redistribute it and/or
006 * modify it under the terms of the GNU Lesser General Public
007 * License as published by the Free Software Foundation; either 
008 * version 2.1 of the License, or (at your option) any later version.
009 * 
010 * This library is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013 * Lesser General Public License for more details.
014 * 
015 * You should have received a copy of the GNU Lesser General Public 
016 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
017 * 
018 **/
019package lucee.runtime.interpreter.ref.func;
020
021import java.util.ArrayList;
022import java.util.Iterator;
023import java.util.List;
024
025import lucee.commons.lang.CFTypes;
026import lucee.commons.lang.StringUtil;
027import lucee.runtime.PageContext;
028import lucee.runtime.exp.ExpressionException;
029import lucee.runtime.exp.FunctionException;
030import lucee.runtime.exp.PageException;
031import lucee.runtime.functions.BIF;
032import lucee.runtime.interpreter.InterpreterException;
033import lucee.runtime.interpreter.ref.Ref;
034import lucee.runtime.interpreter.ref.RefSupport;
035import lucee.runtime.interpreter.ref.cast.Casting;
036import lucee.runtime.interpreter.ref.literal.LFunctionValue;
037import lucee.runtime.interpreter.ref.util.RefUtil;
038import lucee.runtime.op.Caster;
039import lucee.runtime.op.Constants;
040import lucee.runtime.type.FunctionValue;
041import lucee.runtime.type.FunctionValueImpl;
042import lucee.runtime.type.util.ArrayUtil;
043import lucee.runtime.type.util.UDFUtil;
044import lucee.transformer.library.function.FunctionLibFunction;
045import lucee.transformer.library.function.FunctionLibFunctionArg;
046
047/**
048 * a built In Function call
049 *
050 *
051 */
052public final class BIFCall extends RefSupport implements Ref {
053                
054        private Ref[] refArgs;
055    private FunctionLibFunction flf;
056        private Object obj;
057
058
059        /**
060         * constructor of the class
061         * @param pc
062         * @param flf 
063         * @param refArgs 
064         */
065        public BIFCall(FunctionLibFunction flf,Ref[] refArgs) {
066                this.flf=flf;
067                this.refArgs=refArgs;
068        }
069        public BIFCall(Object obj,FunctionLibFunction flf,Ref[] refArgs) {
070                this.obj=obj;
071                this.flf=flf;
072                this.refArgs=refArgs;
073        }
074        
075        @Override
076    public Object getValue(PageContext pc) throws PageException {
077        
078        Object[] arguments = null;
079        
080        
081        if(isDynamic()){
082                arguments = RefUtil.getValue(pc,refArgs);
083                if(flf.hasDefaultValues()){
084                        List<Object> tmp=new ArrayList<Object>();
085                        ArrayList<FunctionLibFunctionArg> args = flf.getArg();
086                        Iterator<FunctionLibFunctionArg> it = args.iterator();
087                        FunctionLibFunctionArg arg;
088                        while(it.hasNext()){
089                                arg=it.next();
090                                if(arg.getDefaultValue()!=null)
091                                        tmp.add(new FunctionValueImpl(arg.getName(),arg.getDefaultValue()));
092                        }
093                        for(int i=0;i<arguments.length;i++){
094                                tmp.add(arguments[i]);
095                        }
096                        arguments=tmp.toArray();
097                }
098                arguments=new Object[]{arguments};
099        }
100        else {
101                if(isNamed(pc,refArgs)){
102                        FunctionValue[] fvalues=getFunctionValues(pc,refArgs);
103                        String[] names = getNames(fvalues);
104                        
105                        ArrayList<FunctionLibFunctionArg> list = flf.getArg();
106                                Iterator<FunctionLibFunctionArg> it = list.iterator();
107                                arguments=new Object[list.size()];
108                                
109                                
110                                FunctionLibFunctionArg flfa;
111                                int index=0;
112                                VT vt;
113                                while(it.hasNext()) {
114                                        flfa =it.next();
115                                        vt = getMatchingValueAndType(flfa,fvalues,names);
116                                        if(vt.index!=-1) 
117                                                names[vt.index]=null;
118                                        arguments[index++]=new Casting( vt.type, CFTypes.toShort(vt.type, false, CFTypes.TYPE_UNKNOW), vt.value).getValue(pc);  
119                                }
120                                
121                                for(int y=0;y<names.length;y++){
122                                        if(names[y]!=null) {
123                                                ExpressionException ee = new InterpreterException("argument ["+names[y]+"] is not allowed for function ["+flf.getName()+"]");
124                                                UDFUtil.addFunctionDoc(ee, flf);
125                                                throw ee;
126                                        }
127                                }
128                        
129                }
130                else {
131                        arguments = RefUtil.getValue(pc,refArgs);
132                }
133        }
134        BIF bif=flf.getBIF();
135        
136        if(flf.getMemberChaining() && obj!=null) {
137                bif.invoke(pc, arguments);
138                return obj;
139        }
140        
141        if(!isDynamic() && flf.getArgMin()>arguments.length) {
142                throw new FunctionException(pc, flf.getName(), flf.getArgMin(), flf.getArgMax(), arguments.length);
143        }
144        
145        return Caster.castTo(pc,flf.getReturnTypeAsString(),bif.invoke(pc, arguments),false);
146        }
147        
148
149
150    private VT getMatchingValueAndType(FunctionLibFunctionArg flfa, FunctionValue[] fvalues, String[] names) throws ExpressionException {
151        String flfan=flfa.getName();
152                
153                // first search if a argument match
154                for(int i=0;i<names.length;i++){
155                        if(names[i]!=null && names[i].equalsIgnoreCase(flfan)) {
156                                return new VT(fvalues[i].getValue(),flfa.getTypeAsString(),i);
157                        }
158                }
159                
160                // then check if a alias match
161                String alias=flfa.getAlias();
162                if(!StringUtil.isEmpty(alias)) {
163                        for(int i=0;i<names.length;i++){
164                                if(names[i]!=null && lucee.runtime.type.util.ListUtil.listFindNoCase(alias, names[i], ",")!=-1){
165                                        return new VT(fvalues[i].getValue(),flfa.getTypeAsString(),i);
166                                }
167                        }
168                }
169                
170                // if not required return the default value
171                if(!flfa.getRequired()) {
172                        String defaultValue = flfa.getDefaultValue();
173                        String type=flfa.getTypeAsString().toLowerCase();
174                        
175                        if(defaultValue==null) {
176                                if(type.equals("boolean") || type.equals("bool")) 
177                                        return new VT(Boolean.FALSE,type,-1);
178                                if(type.equals("number") || type.equals("numeric") || type.equals("double")) 
179                                        return new VT(Constants.DOUBLE_ZERO,type,-1);
180                                return new VT(null,type,-1);
181                        }
182                        return new VT(defaultValue,type,-1);
183                        
184                }
185                ExpressionException ee = new InterpreterException("missing required argument ["+flfan+"] for function ["+flfa.getFunction().getName()+"]");
186                UDFUtil.addFunctionDoc(ee, flfa.getFunction());
187                throw ee;
188        }
189
190        private String[] getNames(FunctionValue[] fvalues) {
191        String[] names=new String[fvalues.length];
192        for(int i=0;i<fvalues.length;i++){
193                        names[i]=fvalues[i].getNameAsString();
194                }
195                return names;
196        }
197
198        private FunctionValue[] getFunctionValues(PageContext pc,Ref[] refArgs) throws PageException {
199                FunctionValue[] fvalues=new FunctionValue[refArgs.length];
200        for(int i=0;i<refArgs.length;i++){
201                        fvalues[i]=(FunctionValue) ((LFunctionValue) ((Casting)refArgs[i]).getRef()).getValue(pc);
202                }
203                return fvalues;
204        }
205
206        private boolean isNamed(PageContext pc,Ref[] refArgs) throws PageException {
207        if(ArrayUtil.isEmpty(refArgs)) return false;
208                Casting cast;
209                int count=0;
210                for(int i=0;i<refArgs.length;i++){
211                        if(refArgs[i] instanceof Casting){
212                                cast=(Casting) refArgs[i];
213                                if(cast.getRef() instanceof LFunctionValue && ((LFunctionValue)cast.getRef()).getValue(pc) instanceof FunctionValue) {
214                                        count++;
215                                }
216                        }
217                }
218                if(count!=0 && count!=refArgs.length){
219                        ExpressionException ee = new InterpreterException("invalid argument for function "+flf.getName()+", you can not mix named and unnamed arguments");
220                        UDFUtil.addFunctionDoc(ee, flf);
221                        throw ee;
222                }
223                return count!=0;
224        }
225
226        private boolean isDynamic() {
227        return flf.getArgType()==FunctionLibFunction.ARG_DYNAMIC;
228    }
229    
230        @Override
231    public String getTypeName() {
232                return "built in function";
233        }
234
235}
236
237class VT{
238
239        Object value;
240        String type;
241        int index;
242
243        public VT(Object value, String type, int index) {
244                this.value=value;
245                this.type=type;
246                this.index=index;
247        }
248        
249}