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