如何使用Rhino桥接一个包含重载方法且可在JavaScript中进行索引的Java类?

3

将一个使用JavaScript作为用户脚本的C#项目移植到Java中时,有一个类有一个重载方法:

public class TreeNode {
    public TreeNode GetChild(int index) { ... }
    public TreeNode GetChild(String childName) { ... }
    public TreeNode GetChild(String targetAttributeName, String targetAttributeValue) { ... }
    ...
}

使用Rhino,我可以通过以下方式在Java和JavaScript之间进行对象桥接:
ScriptableObject.putProperty(scope, "TreeNode", Context.javaToJS(new TreeNode(), scope));

对于简单地调用函数的脚本,这样做非常有效(所有重载函数将正确地解析为正确的类型)。然而,C#应用程序还会索引到TreeNode中。例如,JavaScript用户函数是:

function NumericButtonClick() {
    screen = TreeNode.FindNodeById("Screen");
    screen["Text"] = screen["Text"] + Source["Text"];
}

运行这段JavaScript代码会得到预期的结果:在线程“AWT-EventQueue-0”中,异常org.mozilla.javascript.EvaluatorException:Java类“TreeNode”没有公共实例字段或名为“Text”的方法。
要修复此问题,Rhino通过允许您实现Scriptable接口(或扩展包含一些常见样板代码的ScriptableObject)来支持此功能。在执行此操作后,绑定似乎消失了。
Exception in thread "AWT-EventQueue-0" org.mozilla.javascript.EcmaError: TypeError: Cannot find default value for object.

调试代码时,具体交互是Rhino对Scriptableget()方法作了四次调用:

get(name = "FindNodeByID")
get(name = "__noSuckMethod__")
get(name = "toString")
get(name = "valueOf")

为了解决这个问题,我们可以创建特定的FunctionObject对象,让Rhino知道FindNodeByID是一段Java代码中的函数。(虽然我也试过手动创建,但只使用Scriptable.defineFunctionProperties可以用更少的代码自动完成。) 直到我们遇到重载的GetChild函数时,才会出现以下异常:
org.mozilla.javascript.EvaluatorException: Method "GetChild" occurs multiple times in class "TreeNode".

或者,ScriptableObject.defineClass(scope, TreeNode.class) 将会将 jsFunction_* 函数映射到 JavaScript 中。然而,这也会生成相同类型的错误:

org.mozilla.javascript.EvaluatorException: Invalid method "jsFunction_GetChild": name "GetChild" is already in use.

最后,我尝试在 get()内部添加逻辑,以尝试选择我们想要并将其返回到Rhino中的 FunctionObject 。然而,你没有提供任何函数参数化或预测的方法,除非使用一种非常混乱的方式。
我是否遗漏了什么?有没有办法同时索引Rhino中映射的Java对象和具有重载Java函数?Rhino显然都支持,肯定支持它们一起使用,但是似乎不明显如何做到这一点。
感谢任何想法!
1个回答

1

我曾经需要做类似的事情,在尝试如何实现时遇到了这个问题。

对于使用ScriptableObject.defineClass/jsFunction_方法的可变参数方法,您在问题中提到了解决方案。它在随rhino发行版一起包含的Foo.java示例中得到了演示:

public static Object jsFunction_varargs(Context cx, Scriptable thisObj, Object[] args, Function funObj)

请注意方法签名必须与上述匹配,因此它必须是静态的,因此您将拥有:
public static Object jsFunction_GetChild(Context cx, Scriptable thisObj, Object[] args, Function funObj)

在jsFunction_GetChild函数体内,您需要检查args以决定调用哪个版本的GetChild。在我的情况下,我有可变数量的args,所以我只需检查args.length即可。在您的情况下,看起来您需要检查类型。然后,在您的切换逻辑中,您可以将thisObj参数转换为您类的类型(因为您的方法必须是静态的,所以您需要使用传入的引用来进行调用),以便从正确的GetChild调用并返回值。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接