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