2015-05-12 16 views
17

ho un'istanza di questa classe Java accessibili nel mio programma JavaScriptRhino: numeri Java non si comportano come i numeri Javascript

public class ContentProvider { 
    public Object c(int n) { 
    switch (n) { 
     case 1: return 1.1; 
     case 2: return 2.2; 
     case 3: return 3.3; 
     case 4: return "4"; 
     case 5: return new java.util.Date(); 
    } 
    return null; 
    } 
} 

Questo è il codice all'interno main():

ScriptEngineManager mgr = new ScriptEngineManager(); 
ScriptEngine engine = mgr.getEngineByName("JavaScript"); 
engine.put("ctx", new ContentProvider()); 

res = engine.eval("ctx.c(1)"); 

System.out.printf("rhino:> %s (%s)%n" 
     , res 
     , res != null ? res.getClass().getName() : null 
); 

l'espressione semplice ctx.c(1) stampe:

rhino:> 1.1 (java.lang.Double) 

Ora qui è quello che succede con ctx.c(1) + ctx.c(2):

rhino:> 1.12.2 (java.lang.String) 

E infine (ctx.c(1) + ctx.c(2)) * ctx.c(3):

rhino:> nan (java.lang.Double) 

Rhino sta eseguendo concatenazione di stringhe, invece di aritmetica numero! Il seguente programma funziona come previsto, invece:

engine.put("a", 1.1); 
engine.put("b", 2.2); 
engine.put("c", 3.3); 
res = engine.eval("(a + b) * c"); 

Uscite:

rhino:> 10,89 (java.lang.Double) 
+19

Oh mio Dio, qualcuno effettivamente usato il "Java" e tag "JavaScript" insieme * correttamente *! – immibis

+0

Ma perché non provare semplicemente 'engine.eval (" typeof ctx.c (1) ")' e vedere quale JavaScript pensa che sia il tipo? – immibis

+0

typeof ctx.c (1) = oggetto – lunicon

risposta

4

Questa è una caratteristica strana di Rhino: un insieme di Java Number con engine.put("one", new Double(1)) opere come previsto, mentre il risultato di un metodo Java dipende dal tipo di ritorno dichiarato dal metodo stesso, che viene letto con l'API di riflessione:

  • se si tratta di un primitivo, come double, è convertito in un numero Javascript
  • altrimenti è gestito come gli altri oggetti di accoglienza e la + significa concatenazione, sia Object come nel vostro campione, così come Double

È possibile configurare questo comportamento con wrapFactory.setJavaPrimitiveWrap(false) nel numero WrapFactory nell'attuale Context. In questo modo il codice Rhino può essere mantenuto nelle linee bootstrap del programma e non ingombrare ContentProvider (che credo sia una sorta di delega configurazione)

Dal live Javadoc of WrapFactory.isJavaPrimitiveWrap()

Per impostazione predefinita il metodo restituisce true per indicare che i casi di stringa, numero, booleano e carattere devono essere avvolti come qualsiasi altro oggetto Java e gli script possono accedere a qualsiasi metodo Java disponibile in oggetti

Quindi è possibile impostare questo flag su false per indicare che Java Number deve essere convertito in numeri Javascript.Ci vogliono solo due righe di codice

Context ctx = Context.enter(); 
ctx.getWrapFactory().setJavaPrimitiveWrap(false); 

Ecco the Gist con il codice completo che ho usato per testare

+0

Grazie. Dovrebbe essere reso audace nella documentazione. – lunicon

+0

Accetto. Invece ho dovuto eseguire il debug e scaricare i sorgenti. Sembra anche un insolito default – Raffaele

+0

Ho riscontrato questo problema e ho cercato tutta la documentazione ma non ho trovato nulla. Grazie ho salvato il mio tempo. – VGaur

1

ho creato un valore di involucro:

public static class JSValue extends sun.org.mozilla.javascript.internal.ScriptableObject 
{ 
    Object value; 

    public JSValue(Object value) { 
     this.value = value; 
    } 

    public String getClassName() { 
     return value != null? value.getClass().getName(): null; 
    } 

    @Override 
    public Object getDefaultValue(Class typeHint) { 
     if (typeHint == null || Number.class.isAssignableFrom(typeHint)) { 
      if (value instanceof Number) 
       return ((Number) value).doubleValue(); 
     } 

     return toString(); 
    } 

    @Override 
    public String toString() { 
     return value != null? value.toString(): null; 
    } 
} 

e una funzione di editing:

public static class ContentProvider { 
    public Object c(int n) { 
    ... return new JSValue(1.1); 

Ora l'espressione funziona come previsto. Ringrazia tutti.