2015-06-01 10 views
9

So che puoi lavorare con gli array Java in Nashorn e ci sono molti esempi su come farlo. Il problema per me con l'approccio standard è che rende il codice javascript esplicitamente a conoscenza del suo ambiente di runtime. Attualmente ho una soluzione che fa uso di Rhino e converte senza soluzione di continuità tra tipi Java e tipi javascript nativi.Passa senza problemi a array e elenchi da e verso Nashorn

Per rinoceronte ho compiuto questo implementando org.mozilla.javascript.ContextFactory e org.mozilla.javascript.WrapFActory e impostazione WrapFactory sul Context quando makeContext è chiamato. Questa implementazione di WrapFactory si occupa della conversione tra array Java ed elenchi e matrici ed elenchi javascript nativi. Inoltre, ha detto che dovevo ottenere il codice sorgente di Rhino dal JDK per far funzionare questo approccio.

Ho bisogno di trovare una soluzione simile per Nashorn. Ecco un esempio di ciò che sto cercando di realizzare.

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(); 
    } 
} 

L'uscita di questo codice è

[I @ 169e6180

[1, 2, 3, 4]

1,2,3,4

Sto cercando un modo per implementare uno ScriptObjectMirror, supponendo che sia anche corretto, che renderebbe l'output di queste tre chiamate invokeFunction sono uguali.

Ho provato con wrap funzione ScriptUtils, ma ancora il risultato è sbagliato

UPDATE

Ho cercato di creare un proxy dinamica di tipo Invocable e fare conversioni in InvocationHandler. Per creare un NativeArray con Nashorn sembra che dovresti usare jdk.nashorn.internal.objects.Global.allocate, ma questo solleva sempre un'eccezione.

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

Aumenta

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

vostro 'Arrays.asList()' chiamata sembra creare una lista con un unico elemento di tipo 'int []' invece di quello che vuoi. Prova invece 'Arrays.asList (1,2,3,4)'. – biziclop

+0

ah, grazie :) Questo rende le cose un po 'migliori. Aggiornerò la domanda – Leon

+0

Non è il problema principale qui che gli array JS non sono in realtà matrici in senso Java?Sono più come mappe con una chiave 'int'. – biziclop

risposta

1

io penso che si debba andare la strada difficile verso il basso una implementare un AbstractJSObject. Penso che un sacco di funzioni come getMember possono essere fatte tramite Refelction. Ma cosa faresti se qualcuno pensasse che fosse un JS Array e provi ad estendere il Prototipo? Vuoi gestire anche questo? In tal caso, implementerei una matrice JS come proprietà in un elenco come classe wrapper e delegheremo tutto il set/add a una funzione JS che aggiorna l'oggetto JS.

Soluzione 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(); 
     } 
    } 

Soluzione 2 sarebbe (in pseudo codice):

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 ... 
    } 
Problemi correlati