Как да използвате Rhino за свързване на Java клас, който съдържа претоварени методи и може да се индексира в JavaScript?

При пренасянето на 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 очевидно ги подкрепя и двамата и със сигурност ги подкрепя заедно, но не изглежда очевидно как да го направя.

Благодаря за всякакви идеи!


person utopianheaven    schedule 26.10.2011    source източник


Отговори (1)


Трябваше да направя нещо подобно и попаднах на този въпрос, докато се опитвах да разбера как да го направя.

Има осигурено решение за методи с променливи аргументи, използващи подхода 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.

person ajh158    schedule 02.05.2012