package railo.runtime.interpreter; import railo.commons.lang.ParserString; import railo.commons.lang.StringList; import railo.commons.lang.StringUtil; import railo.runtime.PageContext; import railo.runtime.PageContextImpl; import railo.runtime.config.NullSupportHelper; import railo.runtime.exp.PageException; import railo.runtime.op.Caster; import railo.runtime.type.Collection; import railo.runtime.type.KeyImpl; import railo.runtime.type.ref.VariableReference; import railo.runtime.type.scope.Argument; import railo.runtime.type.scope.CallerImpl; import railo.runtime.type.scope.Local; import railo.runtime.type.scope.Scope; import railo.runtime.type.scope.ScopeSupport; import railo.runtime.type.scope.Undefined; import railo.runtime.type.scope.Variables; import railo.runtime.type.util.KeyConstants; /** * Class to check and interpret Variable Strings */ public final class VariableInterpreter { private static final Object NULL = new Object(); /** * reads a subelement from a struct * @param pc * @param collection * @param var * @return matching Object * @throws PageException */ public static Object getVariable(PageContext pc, Collection collection,String var) throws PageException { StringList list = parse(pc,new ParserString(var),false); if(list==null) throw new InterpreterException("invalid variable declaration ["+var+"]"); while(list.hasNextNext()) { collection=Caster.toCollection(collection.get(KeyImpl.init(list.next()))); } return collection.get(KeyImpl.init(list.next())); } public static String scopeInt2String(int type) { switch(type) { case Scope.SCOPE_APPLICATION: return "application"; case Scope.SCOPE_ARGUMENTS: return "arguments"; case Scope.SCOPE_CGI: return "cgi"; case Scope.SCOPE_COOKIE: return "cookie"; case Scope.SCOPE_CLIENT: return "client"; case Scope.SCOPE_FORM: return "form"; case Scope.SCOPE_REQUEST: return "request"; case Scope.SCOPE_SESSION: return "session"; case Scope.SCOPE_SERVER: return "server"; case Scope.SCOPE_URL: return "url"; case Scope.SCOPE_VARIABLES: return "variables"; case Scope.SCOPE_CLUSTER: return "cluster"; } return null; } public static Object getVariableEL(PageContext pc, Collection collection,String var) { StringList list = parse(pc,new ParserString(var),false); if(list==null) return null; while(list.hasNextNext()) { collection=Caster.toCollection(collection.get(list.next(),null),null); if(collection==null) return null; } return collection.get(list.next(),null); } /** * get a variable from page context * @param pc Page Context * @param var variable string to get value to * @return the value * @throws PageException */ public static Object getVariable(PageContext pc,String var) throws PageException { StringList list = parse(pc,new ParserString(var),false); if(list==null) throw new InterpreterException("invalid variable declaration ["+var+"]"); int scope=scopeString2Int(list.next()); Object coll =null; if(scope==Scope.SCOPE_UNDEFINED) { coll=pc.undefinedScope().get(list.current()); } else { coll=VariableInterpreter.scope(pc, scope, list.hasNext()); } while(list.hasNext()) { coll=pc.getVariableUtil().get(pc,coll,list.next()); } return coll; } public static Object getVariableAsCollection(PageContext pc,String var) throws PageException { StringList list = parse(pc,new ParserString(var),false); if(list==null) throw new InterpreterException("invalid variable declaration ["+var+"]"); int scope=scopeString2Int(list.next()); Object coll =null; if(scope==Scope.SCOPE_UNDEFINED) { coll=pc.undefinedScope().getCollection(list.current()); } else { coll=VariableInterpreter.scope(pc, scope, list.hasNext()); } while(list.hasNext()) { coll=pc.getVariableUtil().getCollection(pc,coll,list.next()); } return coll; } public static Object getVariable(PageContext pc,String str,Scope scope) throws PageException { return _variable(pc, str,NULL, scope); } public static Object setVariable(PageContext pc,String str,Object value,Scope scope) throws PageException { return _variable(pc, str,value, scope); } public static Object _variable(PageContext pc,String str,Object value,Scope scope) throws PageException { // define a ohter enviroment for the function if(scope!=null){ // Variables Scope Variables var=null; if(scope instanceof Variables){ var=(Variables) scope; } else if(scope instanceof CallerImpl){ var=((CallerImpl) scope).getVariablesScope(); } if(var!=null){ Variables current=pc.variablesScope(); pc.setVariablesScope(var); try{ if(value!=NULL) return setVariable(pc, str,value); return getVariable(pc, str); } finally{ pc.setVariablesScope(current); } } // Undefined Scope else if(scope instanceof Undefined) { PageContextImpl pci=(PageContextImpl) pc; Undefined undefined=(Undefined) scope; boolean check=undefined.getCheckArguments(); Variables orgVar=pc.variablesScope(); Argument orgArgs=pc.argumentsScope(); Local orgLocal=pc.localScope(); pci.setVariablesScope(undefined.variablesScope()); if(check)pci.setFunctionScopes(undefined.localScope(), undefined.argumentsScope()); try{ if(value!=NULL) return setVariable(pc, str,value); return getVariable(pc, str); } finally{ pc.setVariablesScope(orgVar); if(check)pci.setFunctionScopes(orgLocal,orgArgs); } } } if(value!=NULL) return setVariable(pc, str,value); return getVariable(pc, str); } /** * get a variable from page context * @param pc Page Context * @param var variable string to get value to * @param defaultValue value returnded if variable was not found * @return the value or default value if not found */ public static Object getVariableEL(PageContext pc,String var, Object defaultValue) { StringList list = parse(pc,new ParserString(var),false); if(list==null) return defaultValue; int scope=scopeString2Int(list.next()); Object coll =null; if(scope==Scope.SCOPE_UNDEFINED) { coll=pc.undefinedScope().get(KeyImpl.init(list.current()),NullSupportHelper.NULL()); if(coll==NullSupportHelper.NULL()) return defaultValue; } else { try { coll=VariableInterpreter.scope(pc, scope, list.hasNext()); //coll=pc.scope(scope); } catch (PageException e) { return defaultValue; } } while(list.hasNext()) { coll=pc.getVariableUtil().get(pc,coll,KeyImpl.init(list.next()),NullSupportHelper.NULL()); if(coll==NullSupportHelper.NULL()) return defaultValue; } return coll; } public static Object getVariableELAsCollection(PageContext pc,String var, Object defaultValue) { StringList list = parse(pc,new ParserString(var),false); if(list==null) return defaultValue; int scope=scopeString2Int(list.next()); Object coll =null; if(scope==Scope.SCOPE_UNDEFINED) { try { coll=pc.undefinedScope().getCollection(list.current()); } catch (PageException e) { coll=null; } if(coll==null) return defaultValue; } else { try { coll=VariableInterpreter.scope(pc, scope, list.hasNext()); //coll=pc.scope(scope); } catch (PageException e) { return defaultValue; } } while(list.hasNext()) { coll=pc.getVariableUtil().getCollection(pc,coll,list.next(),null); if(coll==null) return defaultValue; } return coll; } /** * return a variable reference by string syntax ("scopename.key.key" -> "url.name") * a variable reference, references to variable, to modifed it, with global effect. * @param pc * @param var variable name to get * @return variable as Reference * @throws PageException */ public static VariableReference getVariableReference(PageContext pc,String var) throws PageException { StringList list = parse(pc,new ParserString(var),false); if(list==null) throw new InterpreterException("invalid variable declaration ["+var+"]"); if(list.size()==1) { return new VariableReference(pc.undefinedScope(),list.next()); } int scope=scopeString2Int(list.next()); Object coll; if(scope==Scope.SCOPE_UNDEFINED){ coll=pc.touch(pc.undefinedScope(),list.current()); } else{ coll=VariableInterpreter.scope(pc, scope, list.hasNext()); //coll=pc.scope(scope); } while(list.hasNextNext()) { coll=pc.touch(coll,list.next()); } if(!(coll instanceof Collection)) throw new InterpreterException("invalid variable ["+var+"]"); return new VariableReference((Collection)coll,list.next()); } /** * sets a variable to page Context * @param pc pagecontext of the new variable * @param var String of variable definition * @param value value to set to variable * @return value setted * @throws PageException */ public static Object setVariable(PageContext pc,String var, Object value) throws PageException { StringList list = parse(pc,new ParserString(var),false); if(list==null) throw new InterpreterException("invalid variable name declaration ["+var+"]"); if(list.size()==1) { return pc.undefinedScope().set(list.next(),value); } // min 2 elements int scope=scopeString2Int(list.next()); Object coll; if(scope==Scope.SCOPE_UNDEFINED){ coll=pc.touch(pc.undefinedScope(),list.current()); } else { coll=VariableInterpreter.scope(pc, scope, true); //coll=pc.scope(scope); } while(list.hasNextNext()) { coll=pc.touch(coll,list.next()); } return pc.set(coll,list.next(),value); } /** * removes a variable eith matching name from page context * @param pc * @param var * @return has removed or not * @throws PageException */ public static Object removeVariable(PageContext pc,String var) throws PageException { //print.ln("var:"+var); StringList list = parse(pc,new ParserString(var),false); if(list==null) throw new InterpreterException("invalid variable declaration ["+var+"]"); if(list.size()==1) { return pc.undefinedScope().remove(KeyImpl.init(list.next())); } int scope=scopeString2Int(list.next()); Object coll; if(scope==Scope.SCOPE_UNDEFINED){ coll=pc.undefinedScope().get(list.current()); } else { coll=VariableInterpreter.scope(pc, scope, true); //coll=pc.scope(scope); } while(list.hasNextNext()) { coll=pc.get(coll,list.next()); } return Caster.toCollection(coll).remove(KeyImpl.init(list.next())); } /** * check if a variable is defined in Page Context * @param pc PageContext to check * @param var variable String * @return exists or not */ public static boolean isDefined(PageContext pc,String var) { StringList list = parse(pc,new ParserString(var),false); if(list==null) return false; try { int scope=scopeString2Int(list.next()); Object coll =NULL; if(scope==Scope.SCOPE_UNDEFINED) { coll=pc.undefinedScope().get(list.current(),null); if(coll==null)return false; } else { coll=VariableInterpreter.scope(pc, scope, list.hasNext()); //coll=pc.scope(scope); } while(list.hasNext()) { coll=pc.getVariableUtil().getCollection(pc,coll,list.next(),null); if(coll==null)return false; } } catch (PageException e) { return false; } return true; } /* public static boolean isDefined(PageContext pc,String var) { StringList list = parse(pc,new ParserString(var)); if(list==null) return false; int scope=scopeString2Int(list.next()); Object coll =NULL; if(scope==Scope.SCOPE_UNDEFINED) { coll=pc.undefinedScope().get(list.current(),NULL); if(coll==NULL) return false; } else { try { coll=pc.scope(scope); } catch (PageException e) { return false; } } while(list.hasNext()) { coll=pc.getVariableUtil().get(pc,coll,list.next(),NULL); //print.out(coll); if(coll==NULL) return false; } return true; } */ /** * parse a Literal variable String and return result as String List * @param pc Page Context * @param ps ParserString to read * @return Variable Definition in a String List */ private static StringList parse(PageContext pc,ParserString ps, boolean doLowerCase) { String id=readIdentifier(ps,doLowerCase); if(id==null)return null; StringList list=new StringList(id); CFMLExpressionInterpreter interpreter=null; while(true) { if(ps.forwardIfCurrent('.')) { id=readIdentifier(ps,doLowerCase); if(id==null)return null; list.add(id); } else if(ps.forwardIfCurrent('[')) { if(interpreter==null)interpreter=new CFMLExpressionInterpreter(); try { list.add(Caster.toString(interpreter.interpretPart(pc,ps))); } catch (PageException e) { return null; } if(!ps.forwardIfCurrent(']')) return null; ps.removeSpace(); } else break; } if(ps.isValidIndex()) return null; list.reset(); return list; } public static StringList parse(String var, boolean doLowerCase) { ParserString ps = new ParserString(var); String id=readIdentifier(ps,doLowerCase); if(id==null)return null; StringList list=new StringList(id); while(true) { if(ps.forwardIfCurrent('.')) { id=readIdentifier(ps,doLowerCase); if(id==null)return null; list.add(id); } else break; } if(ps.isValidIndex()) return null; list.reset(); return list; } /** * translate a string type definition to its int representation * @param type type to translate * @return int representation matching to given string */ public static int scopeString2Int(String type) { type=StringUtil.toLowerCase(type); char c=type.charAt(0); if('a'==c) { if("application".equals(type)) return Scope.SCOPE_APPLICATION; else if("arguments".equals(type)) return Scope.SCOPE_ARGUMENTS; } else if('c'==c) { if("cgi".equals(type)) return Scope.SCOPE_CGI; if("cookie".equals(type)) return Scope.SCOPE_COOKIE; if("client".equals(type)) return Scope.SCOPE_CLIENT; if("cluster".equals(type)) return Scope.SCOPE_CLUSTER; } else if('f'==c) { if("form".equals(type)) return Scope.SCOPE_FORM; } else if('l'==c) { if("local".equals(type)) return Scope.SCOPE_LOCAL;// LLL } else if('r'==c) { if("request".equals(type)) return Scope.SCOPE_REQUEST; } else if('s'==c) { if("session".equals(type)) return Scope.SCOPE_SESSION; if("server".equals(type)) return Scope.SCOPE_SERVER; } else if('u'==c) { if("url".equals(type)) return Scope.SCOPE_URL; } else if('v'==c) { if("variables".equals(type)) return Scope.SCOPE_VARIABLES; } return Scope.SCOPE_UNDEFINED; } public static int scopeKey2Int(Collection.Key type) { char c=type.lowerCharAt(0); if('a'==c) { if(KeyConstants._application.equalsIgnoreCase(type)) return Scope.SCOPE_APPLICATION; else if(KeyConstants._arguments.equalsIgnoreCase(type)) return Scope.SCOPE_ARGUMENTS; } else if('c'==c) { if(KeyConstants._cgi.equalsIgnoreCase(type)) return Scope.SCOPE_CGI; if(KeyConstants._cookie.equalsIgnoreCase(type)) return Scope.SCOPE_COOKIE; if(KeyConstants._client.equalsIgnoreCase(type)) return Scope.SCOPE_CLIENT; if(KeyConstants._cluster.equalsIgnoreCase(type)) return Scope.SCOPE_CLUSTER; } else if('f'==c) { if(KeyConstants._form.equalsIgnoreCase(type)) return Scope.SCOPE_FORM; } else if('r'==c) { if(KeyConstants._request.equalsIgnoreCase(type)) return Scope.SCOPE_REQUEST; } else if('s'==c) { if(KeyConstants._session.equalsIgnoreCase(type)) return Scope.SCOPE_SESSION; if(KeyConstants._server.equalsIgnoreCase(type)) return Scope.SCOPE_SERVER; } else if('u'==c) { if(KeyConstants._url.equalsIgnoreCase(type)) return Scope.SCOPE_URL; } else if('v'==c) { if(KeyConstants._variables.equalsIgnoreCase(type)) return Scope.SCOPE_VARIABLES; } return Scope.SCOPE_UNDEFINED; } private static String readIdentifier(ParserString ps, boolean doLowerCase) { ps.removeSpace(); if(ps.isAfterLast())return null; int start=ps.getPos(); if(!isFirstVarLetter(ps.getCurrentLower())) return null; ps.next(); while(ps.isValidIndex()) { if(isVarLetter(ps.getCurrentLower()))ps.next(); else break; } ps.removeSpace(); return doLowerCase?ps.substringLower(start,ps.getPos()-start):ps.substring(start,ps.getPos()-start); } private static boolean isFirstVarLetter(char c) { return (c>='a' && c<='z') || c=='_' || c=='$'; } private static boolean isVarLetter(char c) { return (c>='a' && c<='z') || (c>='0' && c<='9') || c=='_' || c=='$'; } public static Object scope(PageContext pc, int scope, boolean touch) throws PageException { switch(scope) { case Scope.SCOPE_UNDEFINED: return pc.undefinedScope(); case Scope.SCOPE_URL: return pc.urlScope(); case Scope.SCOPE_FORM: return pc.formScope(); case Scope.SCOPE_VARIABLES: return pc.variablesScope(); case Scope.SCOPE_REQUEST: return pc.requestScope(); case Scope.SCOPE_CGI: return pc.cgiScope(); case Scope.SCOPE_APPLICATION: return pc.applicationScope(); case Scope.SCOPE_ARGUMENTS: return pc.argumentsScope(); case Scope.SCOPE_SESSION: return pc.sessionScope(); case Scope.SCOPE_SERVER: return pc.serverScope(); case Scope.SCOPE_COOKIE: return pc.cookieScope(); case Scope.SCOPE_CLIENT: return pc.clientScope(); case ScopeSupport.SCOPE_VAR: return pc.localScope(); case Scope.SCOPE_CLUSTER: return pc.clusterScope(); case Scope.SCOPE_LOCAL: if(touch) return ((PageContextImpl)pc).localTouch(); return ((PageContextImpl)pc).localGet(); } return pc.variablesScope(); } }