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