Как использовать Rhino для соединения класса Java, который содержит перегруженные методы и индексируется в JavaScript? - PullRequest
3 голосов
/ 26 октября 2011

При переносе проекта C # на Java, который использует JavaScript для пользовательских сценариев, существует класс с перегруженным методом:

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 приводит к ожидаемому Exception in thread "AWT-EventQueue-0" org.mozilla.javascript.EvaluatorException: Java class "TreeNode" has no public instance field or method named "Text".


Чтобы исправить это, Rhino поддерживает это, позволяя вам реализовать интерфейс Scriptable (или расширить ScriptableObject, который содержит несколько общих шаблонных кодов). После этого привязка, по-видимому, исчезает:

Exception in thread "AWT-EventQueue-0" org.mozilla.javascript.EcmaError: TypeError: Cannot find default value for object.

При отладке кода особое взаимодействие заключается в том, что Rhino выполняет четыре вызова метода get() из Scriptable:

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_* в JaavScript. Однако это приводит к тому же виду ошибки:

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

Наконец, я посмотрел на добавление логики внутри get(), чтобы попытаться выбрать, какой FunctionObject мы хотим, и вернуться в Rhino. Тем не менее, вам не предоставлена ​​какая-либо параметризация функции или какой-либо способ смотреть вперед, кроме как очень хакерским, громоздким способом.


Я что-то упустил? Есть ли способ как индексировать в сопоставленный объект Java в Rhino, так и перегружать функции Java? Rhino явно поддерживает их обоих и наверняка поддерживает их вместе, но не кажется очевидным, как это сделать.

Спасибо за любые идеи!

1 Ответ

1 голос
/ 02 мая 2012

Мне пришлось сделать что-то похожее, и я столкнулся с этим вопросом, пытаясь выяснить, как это сделать.

Для методов с переменными аргументами предусмотрено решение, использующее подход ScriptableObject.defineClass / jsFunction_упомянуть в вашем вопросе.Это продемонстрировано в примере Foo.java, включенном в дистрибутив rhino:

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 вам придется проверять аргументы, чтобы решить, какую версию GetChild вызывать.В моем случае у меня есть переменное количество аргументов, поэтому я смог просто проверить args.length.В вашем случае, похоже, вам придется проверить типы .Затем в логике переключения вы можете привести параметр thisObj к типу вашего класса (поскольку ваш метод должен быть статическим, вам нужно использовать переданную ссылку для выполнения вызова), чтобы вызвать и вернуть значение изсправа GetChild.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...