();
private Page _owner;
private String _zslang;
protected GenericInterpreter() {
}
//interface to override//
/** Executes the specified script.
* Deriving class shall provide an implementation of this method, rather
* than overriding {@link #interpret}.
*/
abstract protected void exec(String script);
/** Tests whether a variable is defined in this interpreter.
* Optional. Implement it if the interpreter can tell the difference
* between null and undefined.
*
* By default, it tests whether {@link #get(String)} returns non-null.
* @since 2.4.0
*/
protected boolean contains(String name) {
return get(name) != null;
}
/** Gets the variable from the interpreter.
* Optional. Implement it if you want to expose variables defined
* in the interpreter to Java codes.
*
*
{@link #beforeExec} is called first, before this method is invoked.
*
*
An empty (and fake) scope is pushed so {@link #getFromNamespace}
* always returns null.
*/
protected Object get(String name) {
return null;
}
/** Sets the variable to the interpreter.
* Optional. Implement it if you want to allow Java codes to define
* a variable in the interpreter.
*
*
{@link #beforeExec} is called first, before this method is invoked.
*/
protected void set(String name, Object value) {
}
/** Removes the variable from the interpreter.
* Optional. Implement it if you want to allow Java codes to undefine
* a variable from the interpreter.
*
*
{@link #beforeExec} is called first, before this method is invoked.
*/
protected void unset(String name) {
}
/** Tests whether a variable is defined in the interpreter's scope
* associated with the specified scope.
* Optional. Implement it if the interpreter can tell the difference
* between null and undefined.
*
*
By default, it tests whether {@link #get(Scope, String)}
* returns non-null.
* @since 5.0.0
*/
protected boolean contains(Scope scope, String name) {
return get(scope, name) != null;
}
/** Gets the variable from the interpreter's scope associated with
* the giving scope.
* Optional. Implement it if you want to expose variables defined
* in the interpreter to Java codes.
*
*
This method is implemented only if the interpreter that supports
* hierachical scopes ({@link org.zkoss.zk.scripting.HierachicalAware}).
*
*
Default: the same as {@link #get(String)}.
*
*
{@link #beforeExec} is called first, before this method is invoked.
*
*
An empty (and fake) scope is pushed so {@link #getFromNamespace}
* always returns null.
* @since 5.0.0
*/
protected Object get(Scope scope, String name) {
return get(name);
}
/** Sets the variable to the interpreter's scope associated with the
* giving scope.
* Optional. Implement it if you want to allow Java codes to define
* a variable in the interpreter.
*
*
This method is implemented only if the interpreter that supports
* hierarchical scopes ({@link org.zkoss.zk.scripting.HierachicalAware}).
*
*
Default: the same as {@link #set(String, Object)}.
*
*
{@link #beforeExec} is called first, before this method is invoked.
* @since 5.0.0
*/
protected void set(Scope scope, String name, Object value) {
set(name, value);
}
/** Removes the variable from the interpreter.
* Optional. Implement it if you want to allow Java codes to undefine
* a variable from the interpreter.
*
*
This method is implemented only if the interpreter that supports
* hierarchical scopes ({@link org.zkoss.zk.scripting.HierachicalAware}).
*
*
Default: the same as {@link #unset(String)}.
*
*
{@link #beforeExec} is called first, before this method is invoked.
* @since 5.0.0
*/
protected void unset(Scope scope, String name) {
unset(name);
}
/** Called before {@link #exec}.
*
Default: call {@link #beforeExec} and push the scope
* as the active one.
* @since 5.0.0
*/
protected void beforeInterpret(Scope scope) {
beforeExec();
push(scope); //getFromNamespace will handle null
}
/** Called after {@link #exec}.
*
Default: call {@link #afterExec}.
* @since 5.0.0
*/
protected void afterInterpret(Scope scope) {
pop();
afterExec();
}
/** Called before {@link #exec}, {@link #get} and many others.
*
Default: does nothing.
*/
protected void beforeExec() {
}
/** Called after {@link #exec}, {@link #get} and many others.
*
Default: does nothing.
*/
protected void afterExec() {
}
//utilities//
/** Returns the variable through scopes and variable resolvers,
* or {@link #UNDEFINED} if the variable not defined.
*
*
It is usually called to search scopes and variable resolvers,
* when the real interpreter failed to find a variable in its own scope.
*
*
Note: We use {@link #UNDEFINED} to denote undefined since 2.4.0,
* while null is a valid value.
*/
protected Object getFromNamespace(String name) {
final Scope scope = getCurrent();
if (scope != null) { //null means no scope allowed!
final Execution exec = Executions.getCurrent();
if (exec != null && exec != scope) {
Object val = exec.getAttribute(name);
if (val != null /*|| exec.hasAttribute(name)*/) //request's hasAttribute same as getAttribute
return val;
}
if (scope instanceof Component) {
Component comp = (Component)scope;
Object val = comp.getAttributeOrFellow(name, true);
if (val != null || comp.hasAttributeOrFellow(name, true))
return val;
Page page = comp.getPage();
if (page != null) {
val = page.getXelVariable(null, null, name, true);
if (val != null)
return val;
}
} else if (scope instanceof Page) {
Page page = (Page)scope;
Object val = page.getAttributeOrFellow(name, true);
if (val != null || page.hasAttributeOrFellow(name, true))
return val;
val = page.getXelVariable(null, null, name, true);
if (val != null)
return val;
} else {
Object val = scope.getAttribute(name, true);
if (val != null || scope.hasAttribute(name, true))
return val;
}
}
return getImplicit(name);
}
/** Returns the value of the implicit variables.
* It is called by {@link #getFromNamespace}, so you don't need to
* invoke this method if you invoke {@link #getFromNamespace}.
* However, you have to invoke this method as the last step, if you
* implement your own getFromNamespace from scratch.
* @since 3.6.0
*/
protected static Object getImplicit(String name) {
return Scopes.getImplicit(name, UNDEFINED);
}
/** Returns the variable through the specified scopes and
* variable resolvers, or {@link #UNDEFINED} if the variable is not
* defined.
*
*
It is usually called to search scopes and variable resolvers,
* when the real interpreter failed to find a variable in its own scope.
*
*
This method is used with the interpreter that supports
* hierarchical scopes ({@link org.zkoss.zk.scripting.HierachicalAware}).
*
* @param scope the scope to search from (never null).
* Note: if {@link #getCurrent} returns null, this method simply returns
* null (i.e., ignoring scope).
* @param recurse whether to look for the parent scope, if any.
* @since 5.0.0
*/
protected Object getFromNamespace(Scope scope, String name, boolean recurse) {
if (getCurrent() != null) { //null means no scope allowed!
Object val = scope.getAttribute(name, recurse);
if (val != null || scope.hasAttribute(name, recurse))
return val;
}
return getImplicit(name);
}
//Interpreter//
public void init(Page owner, String zslang) {
_owner = owner;
_zslang = zslang;
}
/** Reset the owner ({@link #getOwner}) to null.
*/
public void destroy() {
_owner = null;
}
public Page getOwner() {
return _owner;
}
public String getLanguage() {
return _zslang;
}
/** Handles the scope and then invoke {@link #exec}.
*
Don't override this method, rather, override {@link #exec}.
* @since 5.0.0
*/
public void interpret(String script, Scope scope) {
final String each =
_owner.getLanguageDefinition().getEachTimeScript(_zslang);
if (each != null)
script = each + '\n' + script;
beforeInterpret(scope);
try {
exec(script);
} finally {
afterInterpret(scope);
}
}
/** Returns null since retrieving class is not supported.
*/
public Class getClass(String clsnm) {
return null;
}
/** Returns null since retrieving methods is not supported.
* @since 3.0.0
*/
public Function getFunction(String name, Class[] argTypes) {
return null;
}
/** Returns null since retrieving methods is not supported.
* @since 5.0.0
*/
public Function getFunction(Scope scope, String name, Class[] argTypes) {
return null;
}
/** Tests whether the variable exists.
*
*
Deriving class shall override {@link #contains(String)}, instead of this method.
* @since 2.4.0
*/
public boolean containsVariable(String name) {
beforeExec();
push(Objects.UNKNOWN);
//don't use null since it means Scopes#getCurrent, see below
try {
return contains(name);
} finally {
pop();
afterExec();
}
}
/** Retrieve the variable.
*
*
Deriving class shall override {@link #get(String)}, instead of this method.
*/
public Object getVariable(String name) {
beforeExec();
push(Objects.UNKNOWN);
//don't use null since it means Scopes#getCurrent, see below
try {
return get(name);
} finally {
pop();
afterExec();
}
}
/** Sets the variable to this interpreter.
*
*
Deriving class shall override {@link #set(String,Object)}, instead of this method.
*/
public final void setVariable(String name, Object value) {
beforeExec();
try {
set(name, value);
} finally {
afterExec();
}
}
/** Removes the variable from this interpreter.
*
*
Deriving class shall override {@link #unset(String)}, instead of this method.
*/
public final void unsetVariable(String name) {
beforeExec();
try {
unset(name);
} finally {
afterExec();
}
}
/** Tests whether the variable exists by using the specified name
* as a reference to identify the interpreter's scope.
*
*
Deriving class shall override {@link #contains(Scope,String)}, instead of this method.
* @since 5.0.0
*/
public boolean containsVariable(Scope scope, String name) {
beforeExec();
push(Objects.UNKNOWN);
//don't use null since it means Scopes#getCurrent, see below
try {
return contains(scope, name);
} finally {
pop();
afterExec();
}
}
/** Returns the value of a variable defined in this interpreter's
* scope identified by the specified scope.
* Note: it doesn't search the specified scope ({@link Scope}).
*
*
Deriving class shall override {@link #get(Scope,String)},
* instead of this method.
*
*
This method is part of {@link org.zkoss.zk.scripting.HierachicalAware}.
* It is defined here to simplify the implementation of the
* deriving classes, if they support the hierarchical scopes.
* @since 5.0.0
*/
public Object getVariable(Scope scope, String name) {
beforeExec();
push(Objects.UNKNOWN);
//don't use null since it means Scopes#getCurrent, see below
try {
return get(scope, name);
} finally {
pop();
afterExec();
}
}
/** Sets the value of a variable to this interpreter's scope
* identified by the specified scope.
*
*
Deriving class shall override {@link #set(Scope,String,Object)},
* instead of this method.
* @since 5.0.0
*/
public final void setVariable(Scope scope, String name, Object value) {
beforeExec();
try {
set(scope, name, value);
} finally {
afterExec();
}
}
/** Removes the value of a variable defined in the interpreter's
* scope identified by the specified scope.
*
*
Deriving class shall override {@link #unset(Scope,String)}, instead of this method.
* @since 5.0.0
*/
public final void unsetVariable(Scope scope, String name) {
beforeExec();
try {
unset(scope, name);
} finally {
afterExec();
}
}
/** Use the specified scope as the active scope.
*/
private void push(Object scope) {
_scopes.add(0, scope);
}
/** Remove the active scope.
*/
private void pop() {
_scopes.remove(0);
}
/** Returns the current scope, or null if no scope is allowed.
* Note: if this method returns null, it means the interpreter shall
* not search variables defined in ZK scope.
*
*
This method will handle {@link Scopes#getCurrent} automatically.
* @since 5.0.0
*/
protected Scope getCurrent() {
if (!_scopes.isEmpty()) {
Object o = _scopes.get(0);
if (o == Objects.UNKNOWN)
return null; //no scope allowed
if (o != null)
return (Scope)o;
//we assume owner's scope if null, because zscript might
//define a class that will be invoke thru, say, event listener
//In other words, interpret is not called, so scope is not specified
}
return Scopes.getCurrent(_owner); //never null
}
}