Seamlessly pass Arrays and Lists to and from Nashorn

拜拜、爱过 提交于 2019-12-03 13:05:00

I think you have to go the hard road down an implement a AbstractJSObject. I think a lot of Functions like getMember can be done via Refelction. But what would you do is somebody thinking it is a JS Array and try to extend the Prototype? Do you want to handle this too? In that case I would implement a JS Array as property in a list like wrapper class and delegate all set/add to a JS function updating the JS object.

Solution 1:

public static void main(String args[]) {
        NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
        ScriptEngine engine = factory.getScriptEngine();

        try {
            engine.eval("function print_array(arr) { print(arr); for(var i=0; i<arr.length; i++) {print(arr[i]);}}");
            engine.eval("function print_native() { print_array([1, 2, 3, 4]); }");
            engine.eval("function get_native() { return [1, 2, 3, 4]; }");
            Invocable invocable = (Invocable) engine;
            invocable.invokeFunction("print_array", new int[]{1, 2, 3, 4});
            invocable.invokeFunction("print_array", Arrays.asList(1, 2, 3, 4));
            invocable.invokeFunction("print_array", new Foo());
            invocable.invokeFunction("print_native");

            ScriptObjectMirror a = (ScriptObjectMirror) invocable.invokeFunction("get_native");
            System.out.println(invocable.invokeFunction("get_native"));

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    static class Foo extends AbstractJSObject {
        Map<Integer, Object> arrayValues = new HashMap<>();

        public Foo() {
            arrayValues.put(0, 1);
            arrayValues.put(1, 2);
            arrayValues.put(2, 3);
        }
        @Override
        public Object call(Object thiz, Object... args) {
            System.out.println("call");
            return super.call(thiz, args);
        }

        @Override
        public Object newObject(Object... args) {
            System.out.println("new Object");
            return super.newObject(args);
        }

        @Override
        public Object eval(String s) {
            System.out.println("eval");
            return super.eval(s);
        }

        @Override
        public Object getMember(String name) {
            System.out.println("getMember " + name);
            return name.equals("length") ? arrayValues.size() : arrayValues.get(Integer.valueOf(name));
        }

        @Override
        public Object getSlot(int index) {
            //System.out.println("getSlot");
            return arrayValues.get(index);
        }

        @Override
        public boolean hasMember(String name) {
            System.out.println("hasMember");
            return super.hasMember(name);
        }

        @Override
        public boolean hasSlot(int slot) {
            System.out.println("hasSlot");
            return super.hasSlot(slot);
        }

        @Override
        public void removeMember(String name) {
            System.out.println("removeMember");
            super.removeMember(name);
        }

        @Override
        public void setMember(String name, Object value) {
            System.out.println("setMember");
            super.setMember(name, value);
        }

        @Override
        public void setSlot(int index, Object value) {
            System.out.println("setSlot");
            super.setSlot(index, value);
        }

        @Override
        public Set<String> keySet() {
            System.out.println("keySet");
            return arrayValues.keySet().stream().map(k -> "" + k).collect(Collectors.toSet());
        }

        @Override
        public Collection<Object> values() {
            System.out.println("values");
            return arrayValues.values();
        }

        @Override
        public boolean isInstance(Object instance) {
            System.out.println("isInstance");
            return super.isInstance(instance);
        }

        @Override
        public boolean isInstanceOf(Object clazz) {
            System.out.println("isINstanceOf");
            return super.isInstanceOf(clazz);
        }

        @Override
        public String getClassName() {
            System.out.println("getClassName");
            return super.getClassName();
        }

        @Override
        public boolean isFunction() {
            return false;
        }

        @Override
        public boolean isStrictFunction() {
            return false;
        }

        @Override
        public double toNumber() {
            return super.toNumber();
        }

        @Override
        public boolean isArray() {
            return true;
        }

        @Override
        public String toString() {
            return arrayValues.values().toString();
        }
    }

Solution 2 would be (in pseudo code):

static class FooList implements List {
        final ScriptObjectMirror wrapped;

        public FooList(ScriptObjectMirror wrapped) {
            this.wrapped = wrapped;
        }

        @Override
        public int size() {
            return engine.eval("get length of wrapped JS object");
        }

        ... and so on ...
    }
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!