package railo.runtime.type.scope; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import railo.commons.lang.ExceptionUtil; import railo.runtime.ComponentScope; import railo.runtime.PageContext; import railo.runtime.PageContextImpl; import railo.runtime.config.Config; import railo.runtime.config.ConfigImpl; import railo.runtime.config.NullSupportHelper; import railo.runtime.dump.DumpData; import railo.runtime.dump.DumpProperties; import railo.runtime.exp.ExpressionException; import railo.runtime.exp.PageException; import railo.runtime.op.Duplicator; import railo.runtime.type.Collection; import railo.runtime.type.KeyImpl; import railo.runtime.type.Query; import railo.runtime.type.Struct; import railo.runtime.type.StructImpl; import railo.runtime.type.UDF; import railo.runtime.type.UDFPlus; import railo.runtime.type.dt.DateTime; import railo.runtime.type.util.KeyConstants; import railo.runtime.type.util.StructSupport; import railo.runtime.util.QueryStack; import railo.runtime.util.QueryStackImpl; /** * Undefined Scope */ public final class UndefinedImpl extends StructSupport implements Undefined { private static final long serialVersionUID = -5626787508494702023L; private Scope[] scopes; private QueryStackImpl qryStack=new QueryStackImpl(); private Variables variable; private boolean allowImplicidQueryCall; private boolean checkArguments; private boolean localAlways; private short type; private boolean isInit; private Local local; private Argument argument; private PageContextImpl pc; private boolean debug; /** * constructor of the class * @param pageContextImpl * @param type type of the undefined scope (ServletConfigImpl.SCOPE_STRICT;ServletConfigImpl.SCOPE_SMALL;ServletConfigImpl.SCOPE_STANDART) */ public UndefinedImpl(PageContextImpl pc, short type) { this.type=type; this.pc=pc; } @Override public Local localScope() { return local; } @Override public Argument argumentsScope() { return argument; } @Override public Variables variablesScope() { return variable; } @Override public int setMode(int mode) { int m=Undefined.MODE_NO_LOCAL_AND_ARGUMENTS; if(checkArguments) { if(localAlways)m=Undefined.MODE_LOCAL_OR_ARGUMENTS_ALWAYS; else m=Undefined.MODE_LOCAL_OR_ARGUMENTS_ONLY_WHEN_EXISTS; } checkArguments=mode!=Undefined.MODE_NO_LOCAL_AND_ARGUMENTS; localAlways=mode==Undefined.MODE_LOCAL_OR_ARGUMENTS_ALWAYS; return m; } public boolean getLocalAlways(){ return localAlways; } @Override public void setFunctionScopes(Local local, Argument argument) { this.local=local; this.argument=argument; } @Override public QueryStack getQueryStack() { return qryStack; } @Override public void setQueryStack(QueryStack qryStack) { this.qryStack=(QueryStackImpl) qryStack; } @Override public void addQuery(Query qry) { if(allowImplicidQueryCall) qryStack.addQuery(qry); } @Override public void removeQuery() { if(allowImplicidQueryCall) qryStack.removeQuery(); } @Override public int size() { return variable.size(); } @Override public Collection.Key[] keys() { return variable.keys(); } @Override public Object remove(Collection.Key key) throws PageException { if(checkArguments && local.containsKey(key)) return local.remove(key); return variable.remove(key); } @Override public Object removeEL(Collection.Key key) { if(checkArguments && local.containsKey(key)) return local.removeEL(key); return variable.removeEL(key); } @Override public void clear() { variable.clear(); } public Object get(Collection.Key key) throws PageException { Object rtn; if(checkArguments) { rtn=local.get(key,NullSupportHelper.NULL()); if(rtn!=NullSupportHelper.NULL()) return rtn; rtn=argument.getFunctionArgument(key,NullSupportHelper.NULL()); if(rtn!=NullSupportHelper.NULL()) { if(debug) debugCascadedAccess(pc,argument.getTypeAsString(), key); return rtn; } } // get data from queries if(allowImplicidQueryCall && !qryStack.isEmpty()) { rtn=qryStack.getDataFromACollection(pc,key,NullSupportHelper.NULL()); if(rtn!=NullSupportHelper.NULL()) { if(debug) debugCascadedAccess(pc,"query", key); if(!NullSupportHelper.full() && rtn==null) return ""; return rtn; } } // variable rtn=variable.get(key,NullSupportHelper.NULL()); if(rtn!=NullSupportHelper.NULL()) { if(debug && checkArguments) debugCascadedAccess(pc,variable,rtn, key); return rtn; } // thread scopes if(pc.hasFamily()) { rtn = pc.getThreadScope(key,NullSupportHelper.NULL()); if(rtn!=NullSupportHelper.NULL()) { if(debug) debugCascadedAccess(pc,"thread", key); return rtn; } } // get a scope value for(int i=0;i<scopes.length;i++) { rtn=scopes[i].get(key,NullSupportHelper.NULL()); if(rtn!=NullSupportHelper.NULL()) { if(debug) debugCascadedAccess(pc,scopes[i].getTypeAsString(),key); return rtn; } } if(pc.getConfig().debug()) throw new ExpressionException(ExceptionUtil.similarKeyMessage(this, key.getString(), "key", "keys",false)); throw new ExpressionException("variable ["+key.getString()+"] doesn't exist"); } public static void debugCascadedAccess(PageContext pc,Variables var, Object value, Collection.Key key) { if(var instanceof ComponentScope){ if(key.equals(KeyConstants._THIS) || key.equals(KeyConstants._SUPER)) return; if(value instanceof UDF) { return; } } debugCascadedAccess(pc,"variables", key); } public static void debugCascadedAccess(PageContext pc,String name, Collection.Key key) { if(pc!=null)pc.getDebugger().addImplicitAccess(name,key.getString()); } @Override public Object getCollection(String key) throws PageException { return getCollection(KeyImpl.init(key)); } public Struct getScope(Collection.Key key) { Object rtn=null; Struct sct=new StructImpl(Struct.TYPE_LINKED); if(checkArguments) { rtn=local.get(key,NullSupportHelper.NULL()); if(rtn!=NullSupportHelper.NULL()) sct.setEL(KeyConstants._local, rtn); rtn=argument.getFunctionArgument(key,NullSupportHelper.NULL()); if(rtn!=NullSupportHelper.NULL()) sct.setEL(KeyConstants._arguments, rtn); } // get data from queries if(allowImplicidQueryCall && !qryStack.isEmpty()) { rtn=qryStack.getColumnFromACollection(key); if(rtn!=null) sct.setEL(KeyConstants._query, rtn); } // variable rtn=variable.get(key,NullSupportHelper.NULL()); if(rtn!=NullSupportHelper.NULL()) { sct.setEL(KeyConstants._variables, rtn); } // thread scopes if(pc.hasFamily()) { rtn = pc.getThreadScope(key,NullSupportHelper.NULL()); if(rtn!=NullSupportHelper.NULL()) sct.setEL(KeyConstants._thread, rtn); } // get a scope value for(int i=0;i<scopes.length;i++) { rtn=scopes[i].get(key,NullSupportHelper.NULL()); if(rtn!=NullSupportHelper.NULL()) { sct.setEL(KeyImpl.init(scopes[i].getTypeAsString()), rtn); } } return sct; } /** * return a list of String with the scope names * @param key * @return */ public List<String> getScopeNames() { List<String> scopeNames=new ArrayList<String>(); if(checkArguments) { scopeNames.add("local"); scopeNames.add("arguments"); } scopeNames.add("variables"); // thread scopes if(pc.hasFamily()) { String[] names = pc.getThreadScopeNames(); for(int i=0;i<names.length;i++)scopeNames.add(i,names[i]); } for(int i=0;i<scopes.length;i++) { scopeNames.add((scopes[i]).getTypeAsString()); } return scopeNames; } public Object getCollection(Key key) throws PageException { Object rtn=null; if(checkArguments) { rtn=local.get(key,NullSupportHelper.NULL()); if(rtn!=NullSupportHelper.NULL()) return rtn; rtn=argument.getFunctionArgument(key,NullSupportHelper.NULL()); if(rtn!=NullSupportHelper.NULL()) { if(debug)debugCascadedAccess(pc,argument.getTypeAsString(), key); return rtn; } } // get data from queries if(allowImplicidQueryCall && !qryStack.isEmpty()) { rtn=qryStack.getColumnFromACollection(key); if(rtn!=null) { if(debug)debugCascadedAccess(pc,"query", key); return rtn; } } // variable rtn=variable.get(key,NullSupportHelper.NULL()); if(rtn!=NullSupportHelper.NULL()) { if(debug && checkArguments) debugCascadedAccess(pc,variable,rtn, key); return rtn; } // thread scopes if(pc.hasFamily()) { rtn = pc.getThreadScope(key,NullSupportHelper.NULL()); if(rtn!=NullSupportHelper.NULL()) { if(debug) debugCascadedAccess(pc,"thread", key); return rtn; } } // get a scope value for(int i=0;i<scopes.length;i++) { rtn=scopes[i].get(key,NullSupportHelper.NULL()); if(rtn!=NullSupportHelper.NULL()) { if(debug)debugCascadedAccess(pc,scopes[i].getTypeAsString(),key); return rtn; } } throw new ExpressionException("variable ["+key.getString()+"] doesn't exist"); } public Object get(Collection.Key key, Object defaultValue) { Object rtn=null; if(checkArguments) { rtn=local.get(key,NullSupportHelper.NULL()); if(rtn!=NullSupportHelper.NULL()) return rtn; rtn=argument.getFunctionArgument(key,NullSupportHelper.NULL()); if(rtn!=NullSupportHelper.NULL()) { if(debug) debugCascadedAccess(pc,argument.getTypeAsString(), key); return rtn; } } // get data from queries if(allowImplicidQueryCall && !qryStack.isEmpty()) { rtn=qryStack.getDataFromACollection(pc,key,NullSupportHelper.NULL()); if(rtn!=NullSupportHelper.NULL()) { if(debug) debugCascadedAccess(pc,"query", key); return rtn; } } // variable rtn=variable.get(key,NullSupportHelper.NULL()); if(rtn!=NullSupportHelper.NULL()) { if(debug && checkArguments) debugCascadedAccess(pc,variable, rtn, key); return rtn; } // thread scopes if(pc.hasFamily()) { rtn = pc.getThreadScope(key,NullSupportHelper.NULL()); if(rtn!=NullSupportHelper.NULL()) { if(debug && checkArguments) debugCascadedAccess(pc,"thread", key); return rtn; } } // get a scope value for(int i=0;i<scopes.length;i++) { rtn=scopes[i].get(key,NullSupportHelper.NULL()); if(rtn!=NullSupportHelper.NULL()) { if(debug) debugCascadedAccess(pc,scopes[i].getTypeAsString(), key); return rtn; } } return defaultValue; } @Override public Object getCascading(String strKey) { return getCascading(KeyImpl.init(strKey)); } @Override public Object getCascading(Collection.Key key) { throw new RuntimeException("this method is no longer supported, use getCascading(Collection.Key key, Object defaultValue) instead"); } // FUTURE add to interface and set above to deprecated public Object getCascading(Collection.Key key, Object defaultValue) { Object rtn; // get a scope value for(int i=0;i<scopes.length;i++) { rtn=scopes[i].get(key,NullSupportHelper.NULL()); if(rtn!=NullSupportHelper.NULL()) { return rtn; } } return defaultValue; } @Override public Object setEL(Collection.Key key, Object value) { if(checkArguments) { if(localAlways || local.containsKey(key)) return local.setEL(key,value); if(argument.containsFunctionArgumentKey(key)) { if(debug)debugCascadedAccess(pc,argument.getTypeAsString(), key); return argument.setEL(key,value); } } if(debug && checkArguments)debugCascadedAccess(pc,variable.getTypeAsString(), key); return variable.setEL(key,value); } @Override public Object set(Collection.Key key, Object value) throws PageException { if(checkArguments) { if(localAlways || local.containsKey(key)) return local.set(key,value); if(argument.containsFunctionArgumentKey(key)) { if(debug)debugCascadedAccess(pc,argument.getTypeAsString(), key); return argument.set(key,value); } } if(debug && checkArguments)debugCascadedAccess(pc,variable.getTypeAsString(), key); return variable.set(key,value); } @Override public DumpData toDumpData(PageContext pageContext, int maxlevel, DumpProperties dp) { return variable.toDumpData(pageContext, maxlevel,dp); } @Override public Iterator<Collection.Key> keyIterator() { return variable.keyIterator(); } @Override public Iterator<String> keysAsStringIterator() { return variable.keysAsStringIterator(); } @Override public Iterator<Entry<Key, Object>> entryIterator() { return variable.entryIterator(); } @Override public Iterator<Object> valueIterator() { return variable.valueIterator(); } @Override public boolean isInitalized() { return isInit; } @Override public void initialize(PageContext pc) { if(isInitalized()) return; isInit=true; variable=pc.variablesScope(); argument=pc.argumentsScope(); local=pc.localScope(); allowImplicidQueryCall=pc.getConfig().allowImplicidQueryCall(); type=pc.getConfig().getScopeCascadingType(); debug=pc.getConfig().debug() && ((ConfigImpl)pc.getConfig()).hasDebugOptions(ConfigImpl.DEBUG_IMPLICIT_ACCESS); // Strict if(type==Config.SCOPE_STRICT) { //print.ln("strict"); scopes=new Scope[] {}; } // small else if(type==Config.SCOPE_SMALL) { //print.ln("small"); if(pc.getConfig().mergeFormAndURL()) { scopes=new Scope[] { pc.formScope() }; } else { scopes=new Scope[] { pc.urlScope(), pc.formScope() }; } } // standard else { reinitialize( pc); } } public void reinitialize(PageContext pc) { if(type!=Config.SCOPE_STANDARD) return; Client cs = pc.clientScopeEL(); // print.ln("standard"); if(pc.getConfig().mergeFormAndURL()) { scopes=new Scope[cs==null?3:4]; scopes[0]=pc.cgiScope(); scopes[1]=pc.formScope(); scopes[2]=pc.cookieScope(); if(cs!=null)scopes[3]=cs; } else { scopes=new Scope[cs==null?4:5]; scopes[0]=pc.cgiScope(); scopes[1]=pc.urlScope(); scopes[2]=pc.formScope(); scopes[3]=pc.cookieScope(); if(cs!=null)scopes[4]=cs; } } @Override public final void release() { isInit=false; argument=null; local=null; variable=null; scopes=null; checkArguments=false; localAlways=false; if(allowImplicidQueryCall)qryStack.clear(); } @Override public final void release(PageContext pc) { isInit=false; argument=null; local=null; variable=null; scopes=null; checkArguments=false; localAlways=false; if(allowImplicidQueryCall)qryStack.clear(); } @Override public Collection duplicate(boolean deepCopy) { UndefinedImpl dupl = new UndefinedImpl(pc, type); dupl.allowImplicidQueryCall=allowImplicidQueryCall; dupl.checkArguments=checkArguments; dupl.argument=deepCopy?(Argument)Duplicator.duplicate(argument,deepCopy):argument; dupl.isInit=isInit; dupl.local=deepCopy?(Local)Duplicator.duplicate(local,deepCopy):local; dupl.localAlways=localAlways; dupl.qryStack= (deepCopy?(QueryStackImpl)Duplicator.duplicate(qryStack,deepCopy):qryStack); dupl.variable=deepCopy?(Variables)Duplicator.duplicate(variable,deepCopy):variable; dupl.pc=pc; dupl.debug=debug; // scopes if(deepCopy) { dupl.scopes=new Scope[scopes.length]; for(int i=0;i<scopes.length;i++) { dupl.scopes[i]=(Scope)Duplicator.duplicate(scopes[i],deepCopy); } } else dupl.scopes=scopes; return dupl; } @Override public boolean containsKey(Key key) { return get(key,null)!=null; } @Override public String castToString() throws ExpressionException { throw new ExpressionException("Can't cast Complex Object Type Struct to String", "Use Built-In-Function \"serialize(Struct):String\" to create a String from Struct"); } @Override public String castToString(String defaultValue) { return defaultValue; } @Override public boolean castToBooleanValue() throws ExpressionException { throw new ExpressionException("Can't cast Complex Object Type Struct to a boolean value"); } @Override public Boolean castToBoolean(Boolean defaultValue) { return defaultValue; } @Override public double castToDoubleValue() throws ExpressionException { throw new ExpressionException("Can't cast Complex Object Type Struct to a number value"); } @Override public double castToDoubleValue(double defaultValue) { return defaultValue; } @Override public DateTime castToDateTime() throws ExpressionException { throw new ExpressionException("Can't cast Complex Object Type Struct to a Date"); } @Override public DateTime castToDateTime(DateTime defaultValue) { return defaultValue; } @Override public int compareTo(boolean b) throws ExpressionException { throw new ExpressionException("can't compare Complex Object Type Struct with a boolean value"); } @Override public int compareTo(DateTime dt) throws PageException { throw new ExpressionException("can't compare Complex Object Type Struct with a DateTime Object"); } @Override public int compareTo(double d) throws PageException { throw new ExpressionException("can't compare Complex Object Type Struct with a numeric value"); } @Override public int compareTo(String str) throws PageException { throw new ExpressionException("can't compare Complex Object Type Struct with a String"); } @Override public void setVariableScope(Variables scope) { variable=scope; } @Override public int getType() { return SCOPE_UNDEFINED; } @Override public String getTypeAsString() { return "undefined"; } /** * @return the allowImplicidQueryCall */ public boolean isAllowImplicidQueryCall() { return allowImplicidQueryCall; } /** * @param allowImplicidQueryCall the allowImplicidQueryCall to set */ public boolean setAllowImplicidQueryCall(boolean allowImplicidQueryCall) { boolean old=this.allowImplicidQueryCall; this.allowImplicidQueryCall = allowImplicidQueryCall; return old; } /** * @return the checkArguments */ public boolean getCheckArguments() { return checkArguments; } @Override public Object call(PageContext pc, Key methodName, Object[] args) throws PageException { Object obj = get(methodName,null); // every none UDF value is fine as default argument if(obj instanceof UDFPlus) { return ((UDFPlus)obj).call(pc,methodName,args,false); } throw new ExpressionException("No matching function ["+methodName+"] found"); } @Override public Object callWithNamedValues(PageContext pc, Key methodName, Struct args) throws PageException { Object obj = get(methodName,null); if(obj instanceof UDFPlus) { return ((UDFPlus)obj).callWithNamedValues(pc,methodName,args,false); } throw new ExpressionException("No matching function ["+methodName+"] found"); } }