001    package railo.runtime.interpreter;
002    
003    import railo.commons.lang.ParserString;
004    import railo.commons.lang.StringList;
005    import railo.commons.lang.StringUtil;
006    import railo.runtime.PageContext;
007    import railo.runtime.PageContextImpl;
008    import railo.runtime.config.NullSupportHelper;
009    import railo.runtime.exp.ExpressionException;
010    import railo.runtime.exp.PageException;
011    import railo.runtime.op.Caster;
012    import railo.runtime.type.Collection;
013    import railo.runtime.type.KeyImpl;
014    import railo.runtime.type.ref.VariableReference;
015    import railo.runtime.type.scope.Argument;
016    import railo.runtime.type.scope.CallerImpl;
017    import railo.runtime.type.scope.Local;
018    import railo.runtime.type.scope.Scope;
019    import railo.runtime.type.scope.ScopeSupport;
020    import railo.runtime.type.scope.Undefined;
021    import railo.runtime.type.scope.Variables;
022    import railo.runtime.type.util.KeyConstants;
023    /**
024     * Class to check and interpret Variable Strings
025     */
026    public final class VariableInterpreter {
027        
028            private static final Object NULL = new Object();
029    
030            /**
031             * reads a subelement from a struct
032             * @param pc
033             * @param collection
034             * @param var
035             * @return matching Object
036             * @throws PageException
037             */
038            public static Object getVariable(PageContext pc, Collection collection,String var) throws PageException {                       
039                StringList list = parse(pc,new ParserString(var),false);
040            if(list==null) throw new ExpressionException("invalid variable declaration ["+var+"]");
041            
042            while(list.hasNextNext()) {
043                collection=Caster.toCollection(collection.get(KeyImpl.init(list.next())));
044            }
045            return collection.get(KeyImpl.init(list.next()));
046            }
047            
048            public static String scopeInt2String(int type) {
049                    switch(type) {
050                            case Scope.SCOPE_APPLICATION:   return "application";
051                            case Scope.SCOPE_ARGUMENTS:             return "arguments";
052                            case Scope.SCOPE_CGI:                   return "cgi";
053                            case Scope.SCOPE_COOKIE:                return "cookie";
054                            case Scope.SCOPE_CLIENT:                return "client";
055                            case Scope.SCOPE_FORM:                  return "form";
056                            case Scope.SCOPE_REQUEST:               return "request";
057                            case Scope.SCOPE_SESSION:               return "session";
058                            case Scope.SCOPE_SERVER:                return "server";
059                            case Scope.SCOPE_URL:                   return "url";
060                            case Scope.SCOPE_VARIABLES:             return "variables";
061                            case Scope.SCOPE_CLUSTER:               return "cluster";
062                    }
063                    return null;
064            }
065            
066    
067            public static Object getVariableEL(PageContext pc, Collection collection,String var) {                  
068                StringList list = parse(pc,new ParserString(var),false);
069            if(list==null) return null;
070           
071            while(list.hasNextNext()) {
072                    collection=Caster.toCollection(collection.get(list.next(),null),null);
073                    if(collection==null) return null;
074            }
075            return collection.get(list.next(),null);
076            }
077        
078        /**
079             * get a variable from page context
080             * @param pc Page Context
081             * @param var variable string to get value to
082             * @return the value
083         * @throws PageException
084             */
085            public static Object getVariable(PageContext pc,String var) throws PageException {
086            StringList list = parse(pc,new ParserString(var),false);
087            if(list==null) throw new ExpressionException("invalid variable declaration ["+var+"]");
088            
089                    int scope=scopeString2Int(list.next());
090                    Object coll =null; 
091                    if(scope==Scope.SCOPE_UNDEFINED) {
092                        coll=pc.undefinedScope().get(list.current());
093                    }
094                    else {
095                            coll=VariableInterpreter.scope(pc, scope, list.hasNext());
096                    }
097                    
098                    while(list.hasNext()) {
099                            coll=pc.getVariableUtil().get(pc,coll,list.next());
100                    }
101                    return coll;
102        }
103            
104    
105    
106            public static Object getVariableAsCollection(PageContext pc,String var) throws PageException {
107            StringList list = parse(pc,new ParserString(var),false);
108            if(list==null) throw new ExpressionException("invalid variable declaration ["+var+"]");
109            
110                    int scope=scopeString2Int(list.next());
111                    Object coll =null; 
112                    if(scope==Scope.SCOPE_UNDEFINED) {
113                        coll=pc.undefinedScope().getCollection(list.current());
114                    }
115                    else {
116                        coll=VariableInterpreter.scope(pc, scope, list.hasNext());
117                    }
118                    
119                    while(list.hasNext()) {
120                            coll=pc.getVariableUtil().getCollection(pc,coll,list.next());
121                    }
122                    return coll;
123        }
124            
125    
126            public static Object getVariable(PageContext pc,String str,Scope scope) throws PageException {
127                    return _variable(pc, str,NULL, scope);
128            }
129            public static Object setVariable(PageContext pc,String str,Object value,Scope scope) throws PageException {
130                    return _variable(pc, str,value, scope);
131            }
132            
133            public static Object _variable(PageContext pc,String str,Object value,Scope scope) throws PageException {
134                    // define a ohter enviroment for the function
135                    if(scope!=null){
136                            
137                            // Variables Scope
138                            Variables var=null;
139                            if(scope instanceof Variables){
140                                    var=(Variables) scope;
141                            }
142                            else if(scope instanceof CallerImpl){
143                                    var=((CallerImpl) scope).getVariablesScope();
144                            }
145                            if(var!=null){
146                                    Variables current=pc.variablesScope();
147                                    pc.setVariablesScope(var);
148                            try{
149                                    if(value!=NULL) return setVariable(pc, str,value);
150                                    return getVariable(pc, str);
151                            }
152                            finally{
153                                    pc.setVariablesScope(current);
154                            }
155                            }
156                            
157                            // Undefined Scope
158                            else if(scope instanceof Undefined) {
159                                    PageContextImpl pci=(PageContextImpl) pc;
160                                    Undefined undefined=(Undefined) scope;
161                                    
162                                    boolean check=undefined.getCheckArguments();
163                                    Variables orgVar=pc.variablesScope();
164                                    Argument orgArgs=pc.argumentsScope();
165                            Local orgLocal=pc.localScope();
166                                    
167                                    pci.setVariablesScope(undefined.variablesScope());
168                                    if(check)pci.setFunctionScopes(undefined.localScope(), undefined.argumentsScope());
169                            try{
170                                    if(value!=NULL) return setVariable(pc, str,value);
171                                    return getVariable(pc, str);
172                            }
173                            finally{
174                                    pc.setVariablesScope(orgVar);
175                                    if(check)pci.setFunctionScopes(orgLocal,orgArgs);
176                            }
177                            }
178                    }
179                    if(value!=NULL) return setVariable(pc, str,value);
180                    return getVariable(pc, str);
181            }
182            
183            /**
184             * get a variable from page context
185             * @param pc Page Context
186             * @param var variable string to get value to
187             * @param defaultValue value returnded if variable was not found
188             * @return the value or default value if not found
189             */
190            public static Object getVariableEL(PageContext pc,String var, Object defaultValue) {
191            StringList list = parse(pc,new ParserString(var),false);
192            if(list==null) return defaultValue;
193            
194                    int scope=scopeString2Int(list.next());
195                    Object coll =null; 
196                    if(scope==Scope.SCOPE_UNDEFINED) {
197                        coll=pc.undefinedScope().get(KeyImpl.init(list.current()),NullSupportHelper.NULL());
198                        if(coll==NullSupportHelper.NULL()) return defaultValue;
199                    }
200                    else {
201                        try {
202                    coll=VariableInterpreter.scope(pc, scope, list.hasNext());
203                            //coll=pc.scope(scope);
204                } 
205                        catch (PageException e) {
206                    return defaultValue;
207                }
208                    }
209                    
210                    while(list.hasNext()) {
211                            coll=pc.getVariableUtil().get(pc,coll,KeyImpl.init(list.next()),NullSupportHelper.NULL());
212                            if(coll==NullSupportHelper.NULL()) return defaultValue;
213                    }
214                    return coll;
215        }
216            
217            public static Object getVariableELAsCollection(PageContext pc,String var, Object defaultValue) {
218            StringList list = parse(pc,new ParserString(var),false);
219            if(list==null) return defaultValue;
220            
221                    int scope=scopeString2Int(list.next());
222                    Object coll =null; 
223                    if(scope==Scope.SCOPE_UNDEFINED) {
224                        try {
225                                    coll=pc.undefinedScope().getCollection(list.current());
226                            } 
227                        catch (PageException e) {
228                                    coll=null;
229                            }
230                        if(coll==null) return defaultValue;
231                    }
232                    else {
233                        try {
234                    coll=VariableInterpreter.scope(pc, scope, list.hasNext());
235                            //coll=pc.scope(scope);
236                } 
237                        catch (PageException e) {
238                    return defaultValue;
239                }
240                    }
241                    
242                    while(list.hasNext()) {
243                            coll=pc.getVariableUtil().getCollection(pc,coll,list.next(),null);
244                            if(coll==null) return defaultValue;
245                    }
246                    return coll;
247        }
248            /**
249             * return a variable reference by string syntax ("scopename.key.key" -> "url.name")
250             * a variable reference, references to variable, to modifed it, with global effect.
251             * @param pc
252             * @param var variable name to get
253             * @return variable as Reference
254             * @throws PageException
255             */
256            public static VariableReference getVariableReference(PageContext pc,String var) throws PageException { 
257                StringList list = parse(pc,new ParserString(var),false);
258            if(list==null) throw new ExpressionException("invalid variable declaration ["+var+"]");
259            
260                    if(list.size()==1) {
261                            return new VariableReference(pc.undefinedScope(),list.next()); 
262                    }
263                    int scope=scopeString2Int(list.next());
264                    
265                    Object coll;
266                    if(scope==Scope.SCOPE_UNDEFINED){
267                            coll=pc.touch(pc.undefinedScope(),list.current());
268                    }
269                    else{
270                            coll=VariableInterpreter.scope(pc, scope, list.hasNext());
271                            //coll=pc.scope(scope);
272                    }
273                    
274                    
275                    while(list.hasNextNext()) {
276                            coll=pc.touch(coll,list.next());
277                    }
278    
279                    if(!(coll instanceof Collection))
280                            throw new ExpressionException("invalid variable ["+var+"]");
281                    return new VariableReference((Collection)coll,list.next());
282            } 
283            
284            /**
285             * sets a variable to page Context
286             * @param pc pagecontext of the new variable
287             * @param var String of variable definition
288             * @param value value to set to variable
289             * @return value setted
290             * @throws PageException
291             */
292            public static Object setVariable(PageContext pc,String var, Object value) throws PageException {                        
293                StringList list = parse(pc,new ParserString(var),false);
294            if(list==null) throw new ExpressionException("invalid variable name declaration ["+var+"]");
295    
296                    if(list.size()==1) {
297                            return pc.undefinedScope().set(list.next(),value);
298                    }
299                    
300                    // min 2 elements
301                    int scope=scopeString2Int(list.next());
302                    Object coll;
303                    if(scope==Scope.SCOPE_UNDEFINED){
304                            coll=pc.touch(pc.undefinedScope(),list.current());
305                    }
306                    else {
307                            coll=VariableInterpreter.scope(pc, scope, true);
308                            //coll=pc.scope(scope);
309                    }
310                    
311                    
312                    while(list.hasNextNext()) {
313                        coll=pc.touch(coll,list.next());
314                    }
315                    return pc.set(coll,list.next(),value);
316            }
317            
318            /**
319             * removes a variable eith matching name from page context
320             * @param pc
321             * @param var
322             * @return has removed or not
323             * @throws PageException
324             */
325            public static Object removeVariable(PageContext pc,String var) throws PageException {   
326                //print.ln("var:"+var);
327                StringList list = parse(pc,new ParserString(var),false);
328            if(list==null) throw new ExpressionException("invalid variable declaration ["+var+"]");
329            
330                    if(list.size()==1) {
331                            return pc.undefinedScope().remove(KeyImpl.init(list.next()));
332                    }
333            
334                    int scope=scopeString2Int(list.next());
335                    
336                    Object coll;
337                    if(scope==Scope.SCOPE_UNDEFINED){
338                            coll=pc.undefinedScope().get(list.current());
339                    }
340                    else {
341                            coll=VariableInterpreter.scope(pc, scope, true);
342                            //coll=pc.scope(scope);
343                    }
344                    
345                    while(list.hasNextNext()) {
346                        coll=pc.get(coll,list.next());
347                    }
348                    return Caster.toCollection(coll).remove(KeyImpl.init(list.next()));
349            }
350    
351            
352    
353            /**
354             * check if a variable is defined in Page Context
355             * @param pc PageContext to check
356             * @param var variable String
357             * @return exists or not
358             */
359            public static boolean isDefined(PageContext pc,String var) {
360                    StringList list = parse(pc,new ParserString(var),false);
361                    if(list==null) return false;
362            try {
363                            int scope=scopeString2Int(list.next());
364                            Object coll =NULL; 
365                            if(scope==Scope.SCOPE_UNDEFINED) {
366                                    coll=pc.undefinedScope().get(list.current(),null);
367                                    if(coll==null)return false;
368                            }
369                            else {
370                                    coll=VariableInterpreter.scope(pc, scope, list.hasNext());
371                                    //coll=pc.scope(scope);
372                            }
373                            
374                            while(list.hasNext()) {
375                                    coll=pc.getVariableUtil().getCollection(pc,coll,list.next(),null);
376                                    if(coll==null)return false;
377                            }
378                    } catch (PageException e) {
379                    return false;
380                }
381                    return true;
382        }
383                    
384    
385            
386            
387            /*
388            public static boolean isDefined(PageContext pc,String var) {
389            StringList list = parse(pc,new ParserString(var));
390            if(list==null) return false;
391            
392                    int scope=scopeString2Int(list.next());
393                    Object coll =NULL; 
394                    if(scope==Scope.SCOPE_UNDEFINED) {
395                        coll=pc.undefinedScope().get(list.current(),NULL);
396                        if(coll==NULL) return false;
397                    }
398                    else {
399                        try {
400                    coll=pc.scope(scope);
401                } catch (PageException e) {
402                    return false;
403                }
404                    }
405                    
406                    while(list.hasNext()) {
407                            coll=pc.getVariableUtil().get(pc,coll,list.next(),NULL);
408                            //print.out(coll);
409                            if(coll==NULL) return false;
410                    }
411           
412                    return true;
413        }
414             */
415            
416        
417        /**
418         * parse a Literal variable String and return result as String List
419         * @param pc Page Context
420         * @param ps ParserString to read
421         * @return Variable Definition in a String List
422         */
423        private static StringList parse(PageContext pc,ParserString ps, boolean doLowerCase) {
424            String id=readIdentifier(ps,doLowerCase);
425            if(id==null)return null;
426            StringList list=new StringList(id);
427            CFMLExpressionInterpreter interpreter=null;
428            
429            while(true) {
430                if(ps.forwardIfCurrent('.')) {
431                        id=readIdentifier(ps,doLowerCase);
432                        if(id==null)return null;
433                        list.add(id);
434                }
435                else if(ps.forwardIfCurrent('[')) {
436                    if(interpreter==null)interpreter=new CFMLExpressionInterpreter();
437                    try {
438                        list.add(Caster.toString(interpreter.interpretPart(pc,ps)));
439                    } catch (PageException e) {
440                        return null;
441                    }
442                    if(!ps.forwardIfCurrent(']')) return null;
443                    ps.removeSpace();
444                }
445                else break;
446            }
447            if(ps.isValidIndex()) return null;
448            list.reset();
449            return list;
450        }
451        
452        public static StringList parse(String var, boolean doLowerCase) {
453            ParserString ps = new ParserString(var);
454            String id=readIdentifier(ps,doLowerCase);
455            if(id==null)return null;
456            StringList list=new StringList(id);
457            
458            while(true) {
459                if(ps.forwardIfCurrent('.')) {
460                        id=readIdentifier(ps,doLowerCase);
461                        if(id==null)return null;
462                        list.add(id);
463                }
464                else break;
465            }
466            if(ps.isValidIndex()) return null;
467            list.reset();
468            return list;
469        }
470    
471            /**
472             * translate a string type definition to its int representation
473             * @param type type to translate
474             * @return int representation matching to given string
475             */
476            public static int scopeString2Int(String type) {
477                    type=StringUtil.toLowerCase(type);
478                    char c=type.charAt(0);
479                    if('a'==c) {
480                            if("application".equals(type))          return Scope.SCOPE_APPLICATION;
481                            else if("arguments".equals(type))       return Scope.SCOPE_ARGUMENTS;
482                    }
483                    else if('c'==c) {
484                            if("cgi".equals(type))                          return Scope.SCOPE_CGI;
485                            if("cookie".equals(type))                       return Scope.SCOPE_COOKIE;
486                            if("client".equals(type))                       return Scope.SCOPE_CLIENT;
487                            if("cluster".equals(type))                      return Scope.SCOPE_CLUSTER;
488                    }
489                    else if('f'==c) {
490                            if("form".equals(type))                         return Scope.SCOPE_FORM;
491                    }
492                    else if('l'==c) {
493                            if("local".equals(type))                                return Scope.SCOPE_LOCAL;// LLL
494                    }
495                    else if('r'==c) {
496                            if("request".equals(type))                      return Scope.SCOPE_REQUEST;
497                    }
498                    else if('s'==c) {
499                            if("session".equals(type))                      return Scope.SCOPE_SESSION;
500                            if("server".equals(type))                       return Scope.SCOPE_SERVER;
501                    }
502                    else if('u'==c) {
503                            if("url".equals(type))                          return Scope.SCOPE_URL;
504                    }
505                    else if('v'==c) {
506                            if("variables".equals(type))            return Scope.SCOPE_VARIABLES;
507                    }
508                    return Scope.SCOPE_UNDEFINED;
509            }
510            
511            public static int scopeKey2Int(Collection.Key type) {
512                    char c=type.lowerCharAt(0);
513                    if('a'==c) {
514                            if(KeyConstants._application.equalsIgnoreCase(type))            return Scope.SCOPE_APPLICATION;
515                            else if(KeyConstants._arguments.equalsIgnoreCase(type)) return Scope.SCOPE_ARGUMENTS;
516                    }
517                    else if('c'==c) {
518                            if(KeyConstants._cgi.equalsIgnoreCase(type))                            return Scope.SCOPE_CGI;
519                            if(KeyConstants._cookie.equalsIgnoreCase(type))                 return Scope.SCOPE_COOKIE;
520                            if(KeyConstants._client.equalsIgnoreCase(type))                 return Scope.SCOPE_CLIENT;
521                            if(KeyConstants._cluster.equalsIgnoreCase(type))                        return Scope.SCOPE_CLUSTER; 
522                    }
523                    else if('f'==c) {
524                            if(KeyConstants._form.equalsIgnoreCase(type))                           return Scope.SCOPE_FORM;
525                    }
526                    else if('r'==c) {
527                            if(KeyConstants._request.equalsIgnoreCase(type))                        return Scope.SCOPE_REQUEST;
528                    }
529                    else if('s'==c) {
530                            if(KeyConstants._session.equalsIgnoreCase(type))                        return Scope.SCOPE_SESSION;
531                            if(KeyConstants._server.equalsIgnoreCase(type))                 return Scope.SCOPE_SERVER;
532                    }
533                    else if('u'==c) {
534                            if(KeyConstants._url.equalsIgnoreCase(type))                            return Scope.SCOPE_URL;
535                    }
536                    else if('v'==c) {
537                            if(KeyConstants._variables.equalsIgnoreCase(type))              return Scope.SCOPE_VARIABLES;
538                    }
539                    return Scope.SCOPE_UNDEFINED;
540            }
541        
542        private static String readIdentifier(ParserString ps, boolean doLowerCase) {
543            
544            ps.removeSpace();
545            if(ps.isAfterLast())return null;
546            int start=ps.getPos();
547            if(!isFirstVarLetter(ps.getCurrentLower())) return null;
548            ps.next();
549            
550            while(ps.isValidIndex()) {
551                if(isVarLetter(ps.getCurrentLower()))ps.next();
552                else break;
553            }
554            ps.removeSpace();
555            return doLowerCase?ps.substringLower(start,ps.getPos()-start):ps.substring(start,ps.getPos()-start);
556        }
557    
558    
559        private static boolean isFirstVarLetter(char c) {
560            return (c>='a' && c<='z') || c=='_' || c=='$';
561        }
562    
563        private static boolean isVarLetter(char c) {
564            return (c>='a' && c<='z') || (c>='0' && c<='9') || c=='_' || c=='$';
565        }
566    
567            public static Object scope(PageContext pc, int scope, boolean touch) throws PageException {
568                    switch(scope) {
569                    case Scope.SCOPE_UNDEFINED:     return pc.undefinedScope();
570                    case Scope.SCOPE_URL:           return pc.urlScope();
571                    case Scope.SCOPE_FORM:          return pc.formScope();
572                    case Scope.SCOPE_VARIABLES:     return pc.variablesScope();
573                    case Scope.SCOPE_REQUEST:       return pc.requestScope();
574                    case Scope.SCOPE_CGI:           return pc.cgiScope();
575                    case Scope.SCOPE_APPLICATION:   return pc.applicationScope();
576                    case Scope.SCOPE_ARGUMENTS:     return pc.argumentsScope();
577                    case Scope.SCOPE_SESSION:       return pc.sessionScope();
578                    case Scope.SCOPE_SERVER:        return pc.serverScope();
579                    case Scope.SCOPE_COOKIE:        return pc.cookieScope();
580                    case Scope.SCOPE_CLIENT:        return pc.clientScope();
581                    case ScopeSupport.SCOPE_VAR:            return pc.localScope();
582                    case Scope.SCOPE_CLUSTER:               return pc.clusterScope();
583                    
584                    case Scope.SCOPE_LOCAL:         
585                                    if(touch) return ((PageContextImpl)pc).localTouch();
586                                    return ((PageContextImpl)pc).localGet();
587                }
588                return pc.variablesScope();
589            }
590        
591    }