/* RhinoInterpreter.java
Purpose:
Description:
History:
Fri Feb 9 00:23:47 2007, Created by tomyeh
Copyright (C) 2007 Potix Corporation. All Rights Reserved.
{{IS_RIGHT
This program is distributed under LGPL Version 2.1 in the hope that
it will be useful, but WITHOUT ANY WARRANTY.
}}IS_RIGHT
*/
package org.zkoss.zk.scripting.rhino;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.ContextFactory;
import org.mozilla.javascript.Function;
import org.mozilla.javascript.ImporterTopLevel;
import org.mozilla.javascript.ScriptRuntime;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.Undefined;
import org.zkoss.zk.scripting.util.GenericInterpreter;
import org.zkoss.zk.ui.Page;
/**
* Rhino-based JavaScript interpreter.
*
* @author tomyeh
*/
public class RhinoInterpreter extends GenericInterpreter {
private Scriptable _global;
public RhinoInterpreter() {
}
/** Returns the top-level scope.
*/
/*package*/ Scriptable getGlobalScope() {
return _global;
}
/** Returns the native interpreter, or null if it is not initialized
* or destroyed.
* From application's standpoint, it never returns null, and the returned
* object must be an instance of {@link org.mozilla.javascript.Scriptable}
* @since 3.0.2
*/
public Object getNativeInterpreter() {
return _global;
}
//GenericInterpreter//
protected void exec(String script) {
Context.getCurrentContext().evaluateString(_global, script, "zk", 1, null);
}
protected boolean contains(String name) {
return _global.has(name, _global);
}
protected Object get(String name) {
final Object val = _global.get(name, _global);
if (val == Scriptable.NOT_FOUND || val == Undefined.instance || val == null)
return null;
return Context.getCurrentContext().jsToJava(val, ScriptRuntime.ObjectClass);
}
protected void set(String name, Object value) {
_global.put(name, _global, toJS(value));
}
private Object toJS(Object value) {
return value == null || (value instanceof Number) || (value instanceof String) || (value instanceof Boolean)
? value : Context.toObject(value, _global);
}
protected void unset(String name) {
_global.delete(name);
}
protected void beforeExec() {
enterContext();
}
protected void afterExec() {
exitContext();
}
private Context enterContext() {
return ContextFactory.getGlobal().enterContext();
}
private void exitContext() {
Context.exit();
}
//Interpreter//
public void init(Page owner, String zslang) {
super.init(owner, zslang);
final Context ctx = enterContext();
try {
_global = new GlobalScope(ctx);
} finally {
exitContext();
}
}
public void destroy() {
_global = null;
super.destroy();
}
/**TODO: feasible but need to read manual/source first
public Class getClass(String clsnm) {
}
*/
/** Returns the method.
* <p>Note: JavaScript identifies a function with the name only.
*/
public org.zkoss.xel.Function getFunction(String name, Class[] argTypes) {
enterContext();
try {
final Object val = _global.get(name, _global);
if (!(val instanceof Function))
return null;
return new RhinoFunction((Function) val);
} finally {
exitContext();
}
}
//supporting class//
/** Extends ImporterTopLevel to support ZK namespaces.
*/
private class GlobalScope extends ImporterTopLevel {
private GlobalScope(Context ctx) {
super(ctx);
}
/* Not sure the side effect yet, so disable it
public boolean has(String name, Scriptable start) {
return super.has(name, start) || getFromNamespace(name) != UNDEFINED;
}*/
public Object get(String name, Scriptable start) {
final Object val = super.get(name, start);
if (val == Scriptable.NOT_FOUND || val == Undefined.instance) {
final Object v = getFromNamespace(name);
if (v != UNDEFINED)
return toJS(v);
}
return val;
}
}
private class RhinoFunction implements org.zkoss.xel.Function {
private final Function _func;
private RhinoFunction(Function func) {
if (func == null)
throw new IllegalArgumentException("null");
_func = func;
}
//-- Function --//
public Class[] getParameterTypes() {
return new Class[0];
}
public Class getReturnType() {
return Object.class;
}
public Object invoke(Object obj, Object[] args) throws Exception {
final Context ctx = enterContext();
try {
final Scriptable scope = getGlobalScope();
return _func.call(ctx, scope, scope, args);
} finally {
exitContext();
}
}
public java.lang.reflect.Method toMethod() {
return null;
}
}
}