2015-06-01 32 views
9

Tôi biết bạn có thể làm việc với các mảng Java trong Nashorn và có rất nhiều ví dụ về cách thực hiện điều này. Vấn đề đối với tôi với cách tiếp cận tiêu chuẩn là nó làm cho mã javascript nhận thức rõ ràng về môi trường thời gian chạy của nó. Hiện tại tôi có một giải pháp sử dụng Rhino và nó liên tục chuyển đổi giữa các kiểu Java và các kiểu JavaScript gốc.Liền mạch truyền các mảng và danh sách đến và đi từ Nashorn

Đối với tê giác, tôi đã thực hiện điều này bằng cách triển khai org.mozilla.javascript.ContextFactoryorg.mozilla.javascript.WrapFActory và đặt WrapFactory trên số Context khi gọi makeContext. Việc thực hiện WrapFactory này đảm nhiệm việc chuyển đổi giữa các mảng Java và các danh sách và các mảng và danh sách JavaScript gốc. Nó cũng đề cập đến việc tôi phải lấy mã nguồn Rhino từ JDK để có được cách tiếp cận này để làm việc.

Tôi cần tìm một giải pháp tương tự cho Nashorn. Đây là một ví dụ về những gì tôi đang cố gắng hoàn thành.

public static void main(String args[]) { 
    NashornScriptEngineFactory factory = new NashornScriptEngineFactory(); 
    ScriptEngine engine = factory.getScriptEngine(); 
    try { 
     engine.eval("function print_array(arr) { print(arr); }"); 
     engine.eval("function print_native() { print_array([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_native"); 
    } catch (ScriptException | NoSuchMethodException e) { 
     e.printStackTrace(); 
    } 
} 

Kết quả của mã này là

[Tôi @ 169e6180

[1, 2, 3, 4]

1,2,3,4

Tôi đang tìm cách triển khai ScriptObjectMirror, giả sử rằng đó là chính xác, điều đó sẽ làm cho đầu ra trong số ba cuộc gọi invokeFunction này giống nhau.

Tôi đã cố gắng sử dụng wrap chức năng trên ScriptUtils, nhưng vẫn kết quả là sai

CẬP NHẬT

tôi đã cố gắng để tạo ra một proxy năng động của loại Invocable và làm chuyển đổi trong InvocationHandler. Để tạo ra một NativeArray với Nashorn có vẻ như bạn nên sử dụng jdk.nashorn.internal.objects.Global.allocate, nhưng điều này luôn luôn làm tăng một ngoại lệ.

Global.allocate(new int[] {1, 2, 3, 4}) 

Tăng

Exception in thread "main" java.lang.NullPointerException 
    at jdk.nashorn.internal.objects.Global.instance(Global.java:491) 
    at jdk.nashorn.internal.objects.NativeArray.<init>(NativeArray.java:141) 
    at jdk.nashorn.internal.objects.Global.allocate(Global.java:1584) 
+0

của bạn 'Arrays.asList() 'gọi dường như để tạo ra một danh sách với một yếu tố duy nhất của loại 'int []' thay vì những gì bạn muốn. Hãy thử 'Arrays.asList (1,2,3,4)' thay thế. – biziclop

+0

ah, cảm ơn :) Điều đó làm cho mọi thứ tốt hơn một chút. Tôi sẽ cập nhật câu hỏi – Leon

+0

Vấn đề chính ở đây là các mảng JS không thực sự là mảng theo nghĩa Java?Chúng giống các bản đồ với khóa 'int' hơn. – biziclop

Trả lời

1

Tôi nghĩ rằng bạn phải đi con đường khó khăn ngã một thực hiện một AbstractJSObject. Tôi nghĩ rằng rất nhiều chức năng như getMember có thể được thực hiện thông qua Refelction. Nhưng những gì bạn sẽ làm là ai đó nghĩ rằng nó là một JS Array và cố gắng mở rộng Prototype? Bạn có muốn xử lý điều này không? Trong trường hợp đó, tôi sẽ triển khai thực hiện một JS Array làm thuộc tính trong một danh sách như lớp bao bọc và ủy nhiệm tất cả các thiết lập/thêm vào một hàm JS đang cập nhật đối tượng JS.

Giải pháp 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(); 
     } 
    } 

Giải pháp 2 sẽ là (trong mã giả):

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 ... 
    } 
Các vấn đề liên quan