001    package railo.runtime.type.scope;
002    
003    import java.util.ArrayList;
004    import java.util.Iterator;
005    import java.util.List;
006    
007    import railo.runtime.ComponentScope;
008    import railo.runtime.PageContext;
009    import railo.runtime.PageContextImpl;
010    import railo.runtime.config.Config;
011    import railo.runtime.config.ConfigImpl;
012    import railo.runtime.config.NullSupportHelper;
013    import railo.runtime.dump.DumpData;
014    import railo.runtime.dump.DumpProperties;
015    import railo.runtime.exp.ExpressionException;
016    import railo.runtime.exp.PageException;
017    import railo.runtime.op.Duplicator;
018    import railo.runtime.type.Collection;
019    import railo.runtime.type.KeyImpl;
020    import railo.runtime.type.Null;
021    import railo.runtime.type.Query;
022    import railo.runtime.type.Struct;
023    import railo.runtime.type.StructImpl;
024    import railo.runtime.type.UDF;
025    import railo.runtime.type.dt.DateTime;
026    import railo.runtime.type.util.KeyConstants;
027    import railo.runtime.type.util.StructSupport;
028    import railo.runtime.util.QueryStack;
029    import railo.runtime.util.QueryStackImpl;
030    
031    /**
032     * Undefined Scope
033     */
034    public final class UndefinedImpl extends StructSupport implements Undefined {
035    
036            private static final long serialVersionUID = -5626787508494702023L;
037    
038            private Scope[] scopes;
039            private QueryStackImpl qryStack=new QueryStackImpl();
040            private Variables variable;
041            private boolean allowImplicidQueryCall;
042            private boolean checkArguments;
043            
044    
045    
046            private boolean localAlways;
047            private short type;
048            private boolean isInit;
049            private Local local;
050            private Argument argument;
051            private PageContextImpl pc;
052            private boolean debug;
053            
054            /**
055             * constructor of the class
056             * @param pageContextImpl 
057             * @param type type of the undefined scope (ServletConfigImpl.SCOPE_STRICT;ServletConfigImpl.SCOPE_SMALL;ServletConfigImpl.SCOPE_STANDART)
058             */
059            public UndefinedImpl(PageContextImpl pc, short type) {
060                    this.type=type;
061                    this.pc=pc;
062                    this.debug=pc.getConfig().debug() && ((ConfigImpl)pc.getConfig()).hasDebugOptions(ConfigImpl.DEBUG_IMPLICIT_ACCESS);
063            }
064            
065            
066            @Override
067            public Local localScope() {
068                    return local;
069            }
070            
071            @Override
072            public Argument argumentsScope() {
073                    return argument;
074            }
075    
076            @Override
077            public Variables variablesScope() {
078                    return variable;
079            }
080            
081            @Override
082            public int setMode(int mode) {
083                    int m=Undefined.MODE_NO_LOCAL_AND_ARGUMENTS;
084                    if(checkArguments) {
085                            if(localAlways)m=Undefined.MODE_LOCAL_OR_ARGUMENTS_ALWAYS;
086                            else m=Undefined.MODE_LOCAL_OR_ARGUMENTS_ONLY_WHEN_EXISTS;
087                    }
088                    
089                    checkArguments=mode!=Undefined.MODE_NO_LOCAL_AND_ARGUMENTS;
090                    localAlways=mode==Undefined.MODE_LOCAL_OR_ARGUMENTS_ALWAYS;
091                    return m;
092            }
093            
094            public boolean getLocalAlways(){
095                    return localAlways;
096            }
097            
098            
099            @Override
100            public void setFunctionScopes(Local local, Argument argument) {
101                    this.local=local;
102                    this.argument=argument;
103            }
104            
105            @Override
106            public QueryStack getQueryStack() {
107                    return qryStack;
108            }
109            
110            @Override
111            public void setQueryStack(QueryStack qryStack) {
112                    this.qryStack=(QueryStackImpl) qryStack;
113            }
114    
115            @Override
116            public void addQuery(Query qry) {
117                    if(allowImplicidQueryCall)
118                            qryStack.addQuery(qry);
119            }
120    
121            @Override
122            public void removeQuery() {
123                    if(allowImplicidQueryCall)
124                            qryStack.removeQuery();
125            }
126    
127            @Override
128            public int size() {
129                    return variable.size();
130            }
131    
132            @Override
133            public Collection.Key[] keys() {
134                    return variable.keys();
135            }
136    
137            @Override
138            public Object remove(Collection.Key key) throws PageException {
139                    if(checkArguments && local.containsKey(key))
140                            return local.remove(key);
141                    return variable.remove(key);
142            }
143    
144            @Override
145            public Object removeEL(Collection.Key key) {
146                    if(checkArguments && local.containsKey(key))
147                            return local.removeEL(key);
148                    return variable.removeEL(key);
149            }
150    
151            @Override
152            public void clear() {
153                    variable.clear();
154            }
155            
156            public Object get(Collection.Key key) throws PageException {
157                    
158                    Object rtn;
159                    if(checkArguments) {
160                        rtn=local.get(key,NullSupportHelper.NULL());
161                        if(rtn!=NullSupportHelper.NULL()) return rtn;
162    
163                        rtn=argument.getFunctionArgument(key,NullSupportHelper.NULL());
164                        if(rtn!=NullSupportHelper.NULL()) {
165                            if(debug) debugCascadedAccess(pc,argument.getTypeAsString(), key);
166                                    return rtn;
167                        }
168                    }
169                    
170                    // get data from queries
171                    if(allowImplicidQueryCall && !qryStack.isEmpty()) {
172                            rtn=qryStack.getDataFromACollection(pc,key,Null.NULL);
173                            if(rtn!=Null.NULL) {
174                                    if(debug) debugCascadedAccess(pc,"query", key);
175                                    if(!NullSupportHelper.full() && rtn==null) return "";
176                                    return rtn;
177                        }
178                    }
179                    
180                    // variable
181                    rtn=variable.get(key,NullSupportHelper.NULL());
182                    if(rtn!=NullSupportHelper.NULL()) {
183                            if(debug && checkArguments) debugCascadedAccess(pc,variable,rtn, key);
184                            return rtn;
185                }
186                    
187                    // thread scopes
188                    if(pc.hasFamily()) {
189                            rtn = pc.getThreadScope(key,NullSupportHelper.NULL());
190                            if(rtn!=NullSupportHelper.NULL()) {
191                                    if(debug) debugCascadedAccess(pc,"thread", key);
192                                    return rtn;
193                            }
194                    }
195                    
196                    // get a scope value
197                    for(int i=0;i<scopes.length;i++) {
198                        rtn=scopes[i].get(key,NullSupportHelper.NULL());
199                            if(rtn!=NullSupportHelper.NULL()) {
200                                    if(debug) debugCascadedAccess(pc,scopes[i].getTypeAsString(),key);
201                                    return rtn;
202                            }
203                    }
204                    throw new ExpressionException("variable ["+key.getString()+"] doesn't exist");
205            }
206            
207            public static void debugCascadedAccess(PageContext pc,Variables var, Object value, Collection.Key key) {
208                    if(var instanceof ComponentScope){
209                            if(key.equals(KeyConstants._THIS) || key.equals(KeyConstants._SUPER)) return;
210                            if(value instanceof UDF) {
211                                    return;
212                            }
213                    }
214                    
215                    debugCascadedAccess(pc,"variables", key);
216            }
217            
218            public static void debugCascadedAccess(PageContext pc,String name, Collection.Key key) {
219                    if(pc!=null)pc.getDebugger().addImplicitAccess(name,key.getString());
220            }
221            
222            @Override
223            public Object getCollection(String key) throws PageException {
224                    return getCollection(KeyImpl.init(key));
225            }
226            
227            public Struct getScope(Collection.Key key) {
228                    Object rtn=null;
229                    Struct sct=new StructImpl(Struct.TYPE_LINKED);
230                    
231                    if(checkArguments) {
232                        rtn=local.get(key,NullSupportHelper.NULL());
233                        if(rtn!=NullSupportHelper.NULL()) sct.setEL(KeyConstants._local, rtn);
234                        rtn=argument.getFunctionArgument(key,NullSupportHelper.NULL());
235                        if(rtn!=NullSupportHelper.NULL()) sct.setEL(KeyConstants._arguments, rtn);
236                    }
237                                    
238                    // get data from queries
239                    if(allowImplicidQueryCall && !qryStack.isEmpty()) {
240                            rtn=qryStack.getColumnFromACollection(key);
241                            if(rtn!=null) sct.setEL(KeyConstants._query, rtn);
242                    }
243                    
244                    // variable
245                    rtn=variable.get(key,NullSupportHelper.NULL());
246                    if(rtn!=NullSupportHelper.NULL()) {
247                            sct.setEL(KeyConstants._variables, rtn);
248                    }
249                    
250                    // thread scopes
251                    if(pc.hasFamily()) {
252                            rtn = pc.getThreadScope(key,NullSupportHelper.NULL());
253                            if(rtn!=NullSupportHelper.NULL()) sct.setEL(KeyConstants._thread, rtn); 
254                    }
255                    
256                    // get a scope value
257                    for(int i=0;i<scopes.length;i++) {
258                            rtn=scopes[i].get(key,NullSupportHelper.NULL());
259                            if(rtn!=NullSupportHelper.NULL()) {
260                                    sct.setEL(KeyImpl.init(scopes[i].getTypeAsString()), rtn); 
261                            }
262                    }
263                    return sct;
264            }
265    
266    
267            /**
268             * return a list of String with the scope names
269             * @param key
270             * @return
271             */
272            public List<String> getScopeNames() {
273                    List<String> scopeNames=new ArrayList<String>();
274                    
275                    if(checkArguments) {
276                            scopeNames.add("local");
277                            scopeNames.add("arguments");
278                    }
279                    scopeNames.add("variables");
280                    
281                    // thread scopes
282                    if(pc.hasFamily()) {
283                            String[] names = pc.getThreadScopeNames();
284                            for(int i=0;i<names.length;i++)scopeNames.add(i,names[i]);
285                    }
286                    
287                    for(int i=0;i<scopes.length;i++) {
288                            scopeNames.add((scopes[i]).getTypeAsString()); 
289                    }
290                    return scopeNames;
291            }
292    
293            public Object getCollection(Key key) throws PageException {
294                    Object rtn=null;
295                    
296                    if(checkArguments) {
297                        rtn=local.get(key,NullSupportHelper.NULL());
298                        if(rtn!=NullSupportHelper.NULL()) return rtn;
299                        rtn=argument.getFunctionArgument(key,NullSupportHelper.NULL());
300                        if(rtn!=NullSupportHelper.NULL()) {
301                            if(debug)debugCascadedAccess(pc,argument.getTypeAsString(), key);
302                            return rtn;
303                        }
304                    }
305                                    
306                    // get data from queries
307                    if(allowImplicidQueryCall && !qryStack.isEmpty()) {
308                            rtn=qryStack.getColumnFromACollection(key);
309                            if(rtn!=null) {
310                                    if(debug)debugCascadedAccess(pc,"query", key);
311                                    return rtn;
312                            }
313                    }
314                    
315                    // variable
316                    rtn=variable.get(key,NullSupportHelper.NULL());
317                    if(rtn!=NullSupportHelper.NULL()) {
318                            if(debug && checkArguments) debugCascadedAccess(pc,variable,rtn, key);
319                            return rtn;
320                    }
321                    
322                    // thread scopes
323                    if(pc.hasFamily()) {
324                            rtn = pc.getThreadScope(key,NullSupportHelper.NULL());
325                            if(rtn!=NullSupportHelper.NULL()) {
326                                    if(debug) debugCascadedAccess(pc,"thread", key);
327                                    return rtn;
328                            }
329                    }
330                    
331                    // get a scope value
332                    for(int i=0;i<scopes.length;i++) {
333                            rtn=scopes[i].get(key,NullSupportHelper.NULL());
334                            if(rtn!=NullSupportHelper.NULL()) {
335                                    if(debug)debugCascadedAccess(pc,scopes[i].getTypeAsString(),key);
336                                    return rtn;
337                            }
338                    }
339                    throw new ExpressionException("variable ["+key.getString()+"] doesn't exist");
340            }
341    
342        public Object get(Collection.Key key, Object defaultValue) {
343            Object rtn=null;
344                    if(checkArguments) {
345                            rtn=local.get(key,NullSupportHelper.NULL());
346                            if(rtn!=NullSupportHelper.NULL()) return rtn;
347                
348                rtn=argument.getFunctionArgument(key,NullSupportHelper.NULL());
349                if(rtn!=NullSupportHelper.NULL()) {
350                    if(debug) debugCascadedAccess(pc,argument.getTypeAsString(), key);
351                                    return rtn;
352                }
353            }
354            
355            // get data from queries
356            if(allowImplicidQueryCall && !qryStack.isEmpty()) {
357                    rtn=qryStack.getDataFromACollection(pc,key,NullSupportHelper.NULL());
358                            if(rtn!=NullSupportHelper.NULL()) {
359                    if(debug) debugCascadedAccess(pc,"query", key);
360                                    return rtn;
361                }
362            }
363            
364            // variable
365            rtn=variable.get(key,NullSupportHelper.NULL());
366            if(rtn!=NullSupportHelper.NULL()) {
367                    if(debug && checkArguments) debugCascadedAccess(pc,variable, rtn, key);
368                            return rtn;
369            }
370            
371                    // thread scopes
372                    if(pc.hasFamily()) {
373                            rtn = pc.getThreadScope(key,NullSupportHelper.NULL());
374                            if(rtn!=NullSupportHelper.NULL()) {
375                                    if(debug && checkArguments) debugCascadedAccess(pc,"thread", key);
376                                    return rtn;
377                            }
378                    }
379    
380            // get a scope value
381            for(int i=0;i<scopes.length;i++) {
382                rtn=scopes[i].get(key,NullSupportHelper.NULL());
383                if(rtn!=NullSupportHelper.NULL()) {
384                    if(debug) debugCascadedAccess(pc,scopes[i].getTypeAsString(), key);
385                            return rtn;
386                }
387            }
388            
389            return defaultValue;
390        }
391        
392        
393        @Override
394        public Object getCascading(String strKey) {
395            return getCascading(KeyImpl.init(strKey));
396        }
397    
398    
399            @Override
400            public Object getCascading(Collection.Key key) {
401            throw new RuntimeException("this method is no longer supported, use getCascading(Collection.Key key, Object defaultValue) instead");
402            }
403            
404            // FUTURE add to interface and set above to deprecated
405            public Object getCascading(Collection.Key key, Object defaultValue) {
406            Object rtn;
407              
408            // get a scope value
409            for(int i=0;i<scopes.length;i++) {
410                rtn=scopes[i].get(key,NullSupportHelper.NULL());
411                if(rtn!=NullSupportHelper.NULL()) {
412                    return rtn;
413                }
414            }
415            return defaultValue;
416            }
417    
418            @Override
419            public Object setEL(Collection.Key key, Object value) {
420                    if(checkArguments) {
421                if(localAlways || local.containsKey(key))     return local.setEL(key,value);
422                if(argument.containsFunctionArgumentKey(key))  {
423                    if(debug)debugCascadedAccess(pc,argument.getTypeAsString(), key);
424                    return argument.setEL(key,value);
425                }
426            }
427                            
428                    if(debug && checkArguments)debugCascadedAccess(pc,variable.getTypeAsString(), key);
429            return variable.setEL(key,value);
430            }
431    
432            @Override
433            public Object set(Collection.Key key, Object value) throws PageException {
434                    if(checkArguments) {
435                    if(localAlways || local.containsKey(key))     return local.set(key,value);
436                if(argument.containsFunctionArgumentKey(key))  {
437                    if(debug)debugCascadedAccess(pc,argument.getTypeAsString(), key);
438                    return argument.set(key,value);
439                }
440                
441            }
442                    if(debug && checkArguments)debugCascadedAccess(pc,variable.getTypeAsString(), key);
443            return variable.set(key,value);
444            }
445            
446            @Override
447            public DumpData toDumpData(PageContext pageContext, int maxlevel, DumpProperties dp) {
448                    return variable.toDumpData(pageContext, maxlevel,dp);
449            }
450    
451            @Override
452            public Iterator<Collection.Key> keyIterator() {
453                    return variable.keyIterator();
454            }
455        
456        @Override
457            public Iterator<String> keysAsStringIterator() {
458            return variable.keysAsStringIterator();
459        }
460            
461            @Override
462            public Iterator<Entry<Key, Object>> entryIterator() {
463                    return variable.entryIterator();
464            }
465            
466            @Override
467            public Iterator<Object> valueIterator() {
468                    return variable.valueIterator();
469            }
470            
471            @Override
472            public boolean isInitalized() {
473                    return isInit;
474            }
475    
476            @Override
477            public void initialize(PageContext pc) {
478                    if(isInitalized()) return;
479                    isInit=true;
480                    variable=pc.variablesScope();
481            argument=pc.argumentsScope();
482                    local=pc.localScope();
483                    allowImplicidQueryCall=pc.getConfig().allowImplicidQueryCall();
484            type=pc.getConfig().getScopeCascadingType();
485            
486                    // Strict
487                    if(type==Config.SCOPE_STRICT) {
488                            //print.ln("strict");
489                            scopes=new Scope[] {};
490                    }
491                    // small
492                    else if(type==Config.SCOPE_SMALL) {
493                            //print.ln("small");
494                            if(pc.getConfig().mergeFormAndURL()) {
495                                    scopes=new Scope[] {
496                                                    pc.formScope()
497                                            };
498                            }
499                            else {
500                                    scopes=new Scope[] {
501                                                    pc.urlScope(),
502                                                    pc.formScope()
503                                            };
504                            }
505                    }
506                    // standard
507                    else  {
508                            reinitialize( pc);
509                    }
510                    
511                    
512            }
513    
514            public void reinitialize(PageContext pc) {
515                    if(type!=Config.SCOPE_STANDARD) return;
516                    Client cs = pc.clientScopeEL();
517    //              print.ln("standard");
518                    if(pc.getConfig().mergeFormAndURL()) {
519                scopes=new Scope[cs==null?3:4]; 
520                scopes[0]=pc.cgiScope();
521                scopes[1]=pc.formScope();
522                scopes[2]=pc.cookieScope();
523                if(cs!=null)scopes[3]=cs;
524                    }
525                    else {
526                scopes=new Scope[cs==null?4:5]; 
527                scopes[0]=pc.cgiScope();
528                scopes[1]=pc.urlScope();
529                scopes[2]=pc.formScope();
530                scopes[3]=pc.cookieScope();
531                if(cs!=null)scopes[4]=cs;
532                    }
533            }
534    
535    
536            @Override
537            public final void release() {
538                    isInit=false;
539                    argument=null;
540                    local=null;
541                    variable=null;
542                    scopes=null;
543                    checkArguments=false;
544                    localAlways=false;
545                    if(allowImplicidQueryCall)qryStack.clear();
546            }
547            
548            @Override
549            public final void release(PageContext pc) {
550                    isInit=false;
551                    argument=null;
552                    local=null;
553                    variable=null;
554                    scopes=null;
555                    checkArguments=false;
556                    localAlways=false;
557                    if(allowImplicidQueryCall)qryStack.clear();
558            }
559    
560            @Override
561            public Collection duplicate(boolean deepCopy) {
562                    UndefinedImpl dupl = new UndefinedImpl(pc, type);
563                    dupl.allowImplicidQueryCall=allowImplicidQueryCall;
564                    dupl.checkArguments=checkArguments;
565                    dupl.argument=deepCopy?(Argument)Duplicator.duplicate(argument,deepCopy):argument;
566                    dupl.isInit=isInit;
567                    dupl.local=deepCopy?(Local)Duplicator.duplicate(local,deepCopy):local;
568                    dupl.localAlways=localAlways;
569                    dupl.qryStack= (deepCopy?(QueryStackImpl)Duplicator.duplicate(qryStack,deepCopy):qryStack);
570                    
571                    dupl.variable=deepCopy?(Variables)Duplicator.duplicate(variable,deepCopy):variable;
572                    dupl.pc=pc;
573                    dupl.debug=debug;
574                    
575                    // scopes
576                    if(deepCopy) {
577                            dupl.scopes=new Scope[scopes.length];
578                            for(int i=0;i<scopes.length;i++) {
579                                    dupl.scopes[i]=(Scope)Duplicator.duplicate(scopes[i],deepCopy);
580                            }
581                    }
582                    else dupl.scopes=scopes;
583                    
584                    return dupl;
585            }
586            
587    
588            @Override
589            public boolean containsKey(Key key) {
590            return get(key,null)!=null;
591            }
592    
593        @Override
594        public String castToString() throws ExpressionException {
595            throw new ExpressionException("Can't cast Complex Object Type Struct to String",
596              "Use Built-In-Function \"serialize(Struct):String\" to create a String from Struct");
597        }
598        
599            @Override
600            public String castToString(String defaultValue) {
601                    return defaultValue;
602            }
603    
604        @Override
605        public boolean castToBooleanValue() throws ExpressionException {
606            throw new ExpressionException("Can't cast Complex Object Type Struct to a boolean value");
607        }
608        
609        @Override
610        public Boolean castToBoolean(Boolean defaultValue) {
611            return defaultValue;
612        }
613    
614    
615        @Override
616        public double castToDoubleValue() throws ExpressionException {
617            throw new ExpressionException("Can't cast Complex Object Type Struct to a number value");
618        }
619        
620        @Override
621        public double castToDoubleValue(double defaultValue) {
622            return defaultValue;
623        }
624    
625    
626        @Override
627        public DateTime castToDateTime() throws ExpressionException {
628            throw new ExpressionException("Can't cast Complex Object Type Struct to a Date");
629        }
630        
631        @Override
632        public DateTime castToDateTime(DateTime defaultValue) {
633            return defaultValue;
634        }
635    
636            @Override
637            public int compareTo(boolean b) throws ExpressionException {
638                    throw new ExpressionException("can't compare Complex Object Type Struct with a boolean value");
639            }
640    
641            @Override
642            public int compareTo(DateTime dt) throws PageException {
643                    throw new ExpressionException("can't compare Complex Object Type Struct with a DateTime Object");
644            }
645    
646            @Override
647            public int compareTo(double d) throws PageException {
648                    throw new ExpressionException("can't compare Complex Object Type Struct with a numeric value");
649            }
650    
651            @Override
652            public int compareTo(String str) throws PageException {
653                    throw new ExpressionException("can't compare Complex Object Type Struct with a String");
654            }
655    
656        @Override
657        public void setVariableScope(Variables scope) {
658            variable=scope;
659        }
660    
661        @Override
662        public int getType() {
663            return SCOPE_UNDEFINED;
664        }
665    
666        @Override
667        public String getTypeAsString() {
668            return "undefined";
669        }
670    
671    
672            /**
673             * @return the allowImplicidQueryCall
674             */
675            public boolean isAllowImplicidQueryCall() {
676                    return allowImplicidQueryCall;
677            }
678    
679    
680            /**
681             * @param allowImplicidQueryCall the allowImplicidQueryCall to set
682             */
683            public boolean setAllowImplicidQueryCall(boolean allowImplicidQueryCall) {
684                    boolean old=this.allowImplicidQueryCall;
685                    this.allowImplicidQueryCall = allowImplicidQueryCall;
686                    return old;
687            }
688            
689            /**
690             * @return the checkArguments
691             */
692            public boolean getCheckArguments() {
693                    return checkArguments;
694            }
695            
696            @Override
697            public Object call(PageContext pc, Key methodName, Object[] args) throws PageException {
698                    Object obj = get(methodName,null); // every none UDF value is fine as default argument
699                    if(obj instanceof UDF) {
700                            return ((UDF)obj).call(pc,args,false);
701                    }
702                    throw new ExpressionException("No matching function ["+methodName+"] found");
703            }
704    
705        @Override
706            public Object callWithNamedValues(PageContext pc, Key methodName, Struct args) throws PageException {
707                    Object obj = get(methodName,null);
708                    if(obj instanceof UDF) {
709                            return ((UDF)obj).callWithNamedValues(pc,args,false);
710                    }
711                    throw new ExpressionException("No matching function ["+methodName+"] found");
712            }
713    }