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.type.scope;
020
021import java.util.ArrayList;
022import java.util.Comparator;
023import java.util.Iterator;
024import java.util.LinkedHashMap;
025import java.util.List;
026import java.util.Map;
027import java.util.Set;
028
029import lucee.commons.lang.CFTypes;
030import lucee.runtime.PageContext;
031import lucee.runtime.config.NullSupportHelper;
032import lucee.runtime.dump.DumpData;
033import lucee.runtime.dump.DumpProperties;
034import lucee.runtime.dump.DumpTable;
035import lucee.runtime.dump.DumpUtil;
036import lucee.runtime.dump.SimpleDumpData;
037import lucee.runtime.exp.ExpressionException;
038import lucee.runtime.exp.PageException;
039import lucee.runtime.exp.PageRuntimeException;
040import lucee.runtime.op.Caster;
041import lucee.runtime.op.Decision;
042import lucee.runtime.type.Array;
043import lucee.runtime.type.ArrayImpl;
044import lucee.runtime.type.Collection;
045import lucee.runtime.type.KeyImpl;
046import lucee.runtime.type.Null;
047import lucee.runtime.type.Struct;
048import lucee.runtime.type.StructImpl;
049import lucee.runtime.type.UDFPlus;
050import lucee.runtime.type.util.CollectionUtil;
051import lucee.runtime.type.util.MemberUtil;
052import lucee.runtime.type.wrap.ArrayAsList;
053
054/**
055 * implementation of the argument scope 
056 */
057public final class ArgumentImpl extends ScopeSupport implements Argument {
058                
059        private boolean bind;
060        private Set functionArgumentNames;
061        //private boolean supportFunctionArguments; 
062        
063        /**
064         * constructor of the class
065         */
066        public ArgumentImpl() {
067                super("arguments",SCOPE_ARGUMENTS,Struct.TYPE_LINKED);
068                //this(true);
069        }
070
071
072        @Override
073        public void release() {
074                release(null);
075        }
076        
077        @Override
078        public void release(PageContext pc) {
079                functionArgumentNames=null;
080                if(pc==null)super.release();
081                else super.release(pc);
082        }
083        
084        
085        
086     @Override 
087    public void setBind(boolean bind) { 
088            this.bind=bind; 
089    }       
090    
091    @Override 
092    public boolean isBind() { 
093        return this.bind; 
094    } 
095    
096    public Object getFunctionArgument(String key, Object defaultValue) {
097                return getFunctionArgument(KeyImpl.getInstance(key), defaultValue);
098        }
099
100        public Object getFunctionArgument(Collection.Key key, Object defaultValue) {
101                return super.get(key,defaultValue);
102        }
103        
104
105        @Override
106        public boolean containsFunctionArgumentKey(Key key) {
107                return super.containsKey(key);//functionArgumentNames!=null && functionArgumentNames.contains(key);
108        }
109        
110        
111        
112        @Override
113        public Object get(Collection.Key key, Object defaultValue) {
114                /*if(NullSupportHelper.full()) {
115                        Object o=super.get(key,NullSupportHelper.NULL());
116                        if(o!=NullSupportHelper.NULL())return o;
117                        
118                        o=get(Caster.toIntValue(key.getString(),-1),NullSupportHelper.NULL());
119                        if(o!=NullSupportHelper.NULL())return o;
120                        return defaultValue;
121                }*/
122                
123                Object o=super.g(key,Null.NULL);
124                if(o!=Null.NULL)return o;
125
126                o=get(Caster.toIntValue(key.getString(),-1),Null.NULL);
127                if(o!=Null.NULL)return o;
128                return defaultValue;
129        }
130        
131        
132
133        @Override
134        public Object get(Collection.Key key) throws ExpressionException {
135                /*if(NullSupportHelper.full()) {
136                        Object o=super.get(key,NullSupportHelper.NULL());
137                        if(o!=NullSupportHelper.NULL())return o;
138        
139                        o=get(Caster.toIntValue(key.getString(),-1),NullSupportHelper.NULL());
140                        if(o!=NullSupportHelper.NULL())return o;
141                        throw new ExpressionException("key ["+key.getString()+"] doesn't exist in argument scope. existing keys are ["+
142                                        lucee.runtime.type.List.arrayToList(CollectionUtil.keys(this),", ")
143                                        +"]");
144                }*/
145                
146                // null is supported as returned value with argument scope
147                Object o=super.g(key,Null.NULL);
148                if(o!=Null.NULL)return o;
149
150                o=get(Caster.toIntValue(key.getString(),-1),Null.NULL);
151                if(o!=Null.NULL)return o;
152                
153                throw new ExpressionException("key ["+key.getString()+"] doesn't exist in argument scope. existing keys are ["+
154                                lucee.runtime.type.util.ListUtil.arrayToList(CollectionUtil.keys(this),", ")
155                                +"]");
156        }
157        
158        
159
160        @Override
161        public Object get(int intKey, Object defaultValue) {
162                Iterator<Object> it = valueIterator(); //keyIterator();//getMap().keySet().iterator();
163                int count=0;
164                Object o;
165                while(it.hasNext()) {
166                        o=it.next();
167                        if((++count)==intKey) {
168                                return o;//super.get(o.toString(),defaultValue);
169                        }
170                }
171                return defaultValue;
172        }
173
174        /**
175         * return a value matching to key
176         * @param intKey
177         * @return value matching key
178         * @throws PageException
179         */
180        public Object getE(int intKey) throws PageException {
181                Iterator it = valueIterator();//getMap().keySet().iterator();
182                int count=0;
183                Object o;
184                while(it.hasNext()) {
185                        o=it.next();
186                        if((++count)==intKey) {
187                                return o;//super.get(o.toString());
188                        }
189                }
190                throw new ExpressionException("invalid index ["+intKey+"] for argument scope"); 
191        }
192        
193        @Override
194        public DumpData toDumpData(PageContext pageContext, int maxlevel, DumpProperties dp) {
195                DumpTable htmlBox = new DumpTable("struct","#9999ff","#ccccff","#000000");
196                htmlBox.setTitle("Scope Arguments");
197                if(size()>10 && dp.getMetainfo())htmlBox.setComment("Entries:"+size());
198            
199                maxlevel--;
200                //Map mapx=getMap();
201                Iterator<Key> it = keyIterator();//mapx.keySet().iterator();
202                int count=0;
203                Collection.Key key;
204                int maxkeys=dp.getMaxKeys();
205                int index=0;
206                while(it.hasNext()) {
207                        key=it.next();//it.next();
208                        
209                        if(DumpUtil.keyValid(dp, maxlevel,key)){
210                                if(maxkeys<=index++)break;
211                                htmlBox.appendRow(3,
212                                                new SimpleDumpData(key.getString()),
213                                                new SimpleDumpData(++count),
214                                                DumpUtil.toDumpData(get(key,null), 
215                                                pageContext,maxlevel,dp));
216                        }
217                }
218                return htmlBox;
219        }
220
221
222        @Override
223        public int getDimension() {
224                return 1;
225        }
226
227        @Override
228        public Object setEL(int intKey, Object value) {
229                int count=0;
230                
231                if(intKey>size()) {
232                        return setEL(Caster.toString(intKey),value);
233                }
234                //Iterator it = keyIterator();
235                Key[] keys = keys();
236                for(int i=0;i<keys.length;i++) {
237                        if((++count)==intKey) {
238                                return super.setEL(keys[i],value);
239                        }
240                }
241                return value;
242        }
243
244
245        @Override
246        public Object setE(int intKey, Object value) throws PageException {
247
248                if(intKey>size()) {
249                        return set(Caster.toString(intKey),value);
250                }
251                //Iterator it = keyIterator();
252                Key[] keys = keys();
253                for(int i=0;i<keys.length;i++) {
254                        if((i+1)==intKey) {
255                                return super.set(keys[i],value);
256                        }
257                }
258                throw new ExpressionException("invalid index ["+intKey+"] for argument scope"); 
259        }
260
261
262        @Override
263        public int[] intKeys() {
264                int[] ints=new int[size()];
265                for(int i=0;i<ints.length;i++)ints[i]=i+1;
266                return ints;
267        }
268
269
270        @Override
271        public boolean insert(int index, Object value) throws ExpressionException {
272                return insert(index, ""+index, value);
273        }
274        
275        @Override
276        public boolean insert(int index, String key, Object value) throws ExpressionException {
277                int len=size();
278                if(index<1 || index>len)
279                        throw new ExpressionException("invalid index to insert a value to argument scope",len==0?"can't insert in a empty argument scope":"valid index goes from 1 to "+(len-1));
280                
281                // remove all upper
282                        LinkedHashMap lhm = new LinkedHashMap();
283                        Collection.Key[] keys=keys();
284                        
285                        Collection.Key k;
286                        for(int i=1;i<=keys.length;i++) {
287                                if(i<index)continue;
288                                k=keys[i-1];
289                                lhm.put(k.getString(),get(k,null));
290                                removeEL(k);
291                        }
292                
293                // set new value
294                        setEL(key,value);
295                
296                // reset upper values
297                        Iterator it = lhm.entrySet().iterator();
298                        Map.Entry entry;
299                        while(it.hasNext()) {                   
300                                entry=(Entry) it.next();
301                                setEL(KeyImpl.toKey(entry.getKey()),entry.getValue());
302                        }               
303                return true;
304        }
305
306
307        @Override
308        public Object append(Object o) throws PageException {
309                return set(Caster.toString(size()+1),o);
310        }
311        
312        @Override
313        public Object appendEL(Object o) {
314                try {
315                        return append(o);
316                } catch (PageException e) {
317                        return null;
318                }
319        }
320
321
322        @Override
323        public Object prepend(Object o) throws PageException {
324                for(int i=size();i>0;i--) {
325                        setE(i+1,getE(i));
326                }
327                setE(1,o);
328                return o;
329        }
330
331
332        @Override
333        public void resize(int to) throws PageException {
334                for(int i=size(); i<to; i++) {
335                        append(null);
336                }
337                //throw new ExpressionException("can't resize this array");
338        }
339
340
341
342        @Override
343        public void sort(String sortType, String sortOrder) throws ExpressionException {
344                // TODO Impl.
345                throw new ExpressionException("can't sort ["+sortType+"-"+sortOrder+"] Argument Scope","not Implemnted Yet");
346        }
347
348        public void sort(Comparator com) {
349                // TODO Impl.
350                throw new PageRuntimeException("can't sort Argument Scope","not Implemnted Yet");
351        }
352
353        @Override
354        public Object[] toArray() {
355                Iterator it = keyIterator();//getMap().keySet().iterator();
356                Object[] arr=new Object[size()];
357                int count=0;
358                
359                while(it.hasNext()) {
360                        arr[count++]=it.next();
361                }
362                return arr;
363        }
364        
365        public Object setArgument(Object obj) throws PageException {
366                if(obj==this) return obj;
367                
368                
369                if(Decision.isStruct(obj)) {
370                        clear(); // TODO bessere impl. anstelle vererbung wrao auf struct
371                        Struct sct=Caster.toStruct(obj);
372                        Iterator<Key> it = sct.keyIterator();
373                        Key key;
374                        while(it.hasNext()) {
375                                key=it.next();
376                                setEL(key, sct.get(key,null));
377                        }
378                        return obj;
379                }
380                throw new ExpressionException("can not overwrite arguments scope");
381        }
382
383
384        public ArrayList toArrayList() {
385                ArrayList list = new ArrayList();
386                Object[] arr = toArray();
387                for(int i=0;i<arr.length;i++) {
388                        list.add(arr[i]);
389                }
390                return list;
391        }
392
393        @Override
394        public Object removeE(int intKey) throws PageException {
395                Key[] keys = keys();
396                for(int i=0;i<keys.length;i++) {
397                        if((i+1)==intKey) {
398                                return super.remove(keys[i]);
399                        }
400                }
401                throw new ExpressionException("can't remove argument number ["+intKey+"], argument doesn't exist");
402        }
403
404        @Override
405        public Object removeEL(int intKey) {
406                Key[] keys = keys();
407                for(int i=0;i<keys.length;i++) {
408                        if((i+1)==intKey) {
409                                return super.removeEL (keys[i]);
410                        }
411                }
412                return null;
413        }
414
415    @Override
416    public boolean containsKey(Collection.Key key) {
417        if(NullSupportHelper.full()) return super.containsKey(key);
418        
419                return super.g(key,null)!=null;
420        // return get(key,NullSupportHelper.NULL())!=NullSupportHelper.NULL() && super.containsKey(key);
421    }
422    /*
423    public boolean containsKey(Collection.Key key) {
424        return get(key,null)!=null && super.containsKey(key);
425    }*/
426
427    @Override
428    public boolean containsKey(int key) {
429        return key>0 && key<=size();
430    }
431
432        @Override
433        public List toList() {
434                return ArrayAsList.toList(this);
435        }
436        
437
438        @Override
439        public Collection duplicate(boolean deepCopy) {
440                ArgumentImpl trg=new ArgumentImpl();
441                trg.bind=false;
442                trg.functionArgumentNames=functionArgumentNames;
443                //trg.supportFunctionArguments=supportFunctionArguments;
444                copy(this,trg,deepCopy);
445                return trg;
446        }
447
448        public void setFunctionArgumentNames(Set functionArgumentNames) {// future add to interface
449                this.functionArgumentNames=functionArgumentNames;
450        }
451/*
452        public void setNamedArguments(boolean namedArguments) {
453                this.namedArguments=namedArguments;
454        }
455        public boolean isNamedArguments() {
456                return namedArguments;
457        }
458*/
459
460        /**
461         * converts a argument scope to a regular struct
462         * @param arg argument scope to convert
463         * @return resulting struct
464         */
465        public static Struct toStruct(Argument arg) {
466                Struct trg=new StructImpl();
467                StructImpl.copy(arg, trg, false);
468                return trg;
469        }
470
471        /**
472         * converts a argument scope to a regular array
473         * @param arg argument scope to convert
474         * @return resulting array
475         */
476        public static Array toArray(Argument arg) {
477                ArrayImpl trg=new ArrayImpl();
478                int[] keys = arg.intKeys();
479                for(int i=0;i<keys.length;i++){
480                        trg.setEL(keys[i],
481                                        arg.get(keys[i],null));
482                }
483                return trg;
484        }
485
486        @Override
487        public Object get(PageContext pc, Key key, Object defaultValue) {
488                return get(key, defaultValue);
489        }
490
491        @Override
492        public Object get(PageContext pc, Key key) throws PageException {
493                return get(key);
494        }
495
496        @Override
497        public Object set(PageContext pc, Key propertyName, Object value) throws PageException {
498                return set(propertyName, value);
499        }
500
501        @Override
502        public Object setEL(PageContext pc, Key propertyName, Object value) {
503                return setEL(propertyName, value);
504        }
505
506        @Override
507        public Object call(PageContext pc, Key methodName, Object[] args) throws PageException {
508                Object obj = get(methodName,null);
509                if(obj instanceof UDFPlus) {
510                        return ((UDFPlus)obj).call(pc,methodName,args,false);
511                }
512                return MemberUtil.call(pc, this, methodName, args, CFTypes.TYPE_STRUCT, "struct");
513                //return MemberUtil.call(pc, this, methodName, args, CFTypes.TYPE_ARRAY, "array");
514        }
515
516        @Override
517        public Object callWithNamedValues(PageContext pc, Key methodName, Struct args) throws PageException {
518                Object obj = get(methodName,null);
519                if(obj instanceof UDFPlus) {
520                        return ((UDFPlus)obj).callWithNamedValues(pc,methodName,args,false);
521                }
522                return MemberUtil.callWithNamedValues(pc,this,methodName,args, CFTypes.TYPE_STRUCT, "struct");
523                //return MemberUtil.callWithNamedValues(pc,this,methodName,args, CFTypes.TYPE_ARRAY, "array");
524        }
525}