2016-07-03 7 views
0

У меня есть компонент, который реализует интерфейс JSObject (&), как показано ниже. Я удалил некоторые переопределенные методы, чтобы упростить чтение.Исключение во время выполнения Nashorn во время печати Реализация JSObject

package test.nashorn; 

import java.util.Collection; 
import java.util.HashMap; 
import java.util.Map; 
import java.util.Set; 

import jdk.nashorn.api.scripting.JSObject; 

public class JSBean implements JSObject, Map<String,Object>{ 

    /** 
    * The current values for this object. 
    */ 
    private HashMap<String, Object> values = new HashMap<>(); 

    @Override 
    public String toString() { 
     System.out.println("ToString"); 
     Set<Entry<String,Object>> entries = values.entrySet(); 
     StringBuilder sb = new StringBuilder(); 

     for(Entry<String,Object> entry:entries){ 
      sb.append(entry.getKey()+ " "+(String)entry.getValue()); 
     } 
     System.out.println("Completed ToString"); 
     return sb.toString(); 
    } 
    @Override 
    public boolean hasMember(String name) { 
     return has(name); 
    } 

    // get the value of that named property 
    @Override 
    public Object getMember(String name) { 

     return get(name); 

    } 

    // get the value of that named property 
    @Override 
    public void setMember(String name,Object value) { 

     put(name,value); 

    } 

    public Object get(String name) { 

     System.out.println("JAVA Get is called."+name); 
     System.out.println("Called for this"+name+" and returned" 
     +":"+values.get(name)); 

     return values.get(name); 
    } 

    @Override 
    public Object put(String name, Object value) { 
     System.out.println("JAVA Put is called. Input name: " + name + "\n Input values: " + value); 

     return values.put(name, value); 

    } 

    public boolean has(String name) { 
     System.out.println("JAVA Has is called. Input name: " + name); 

     return values.containsKey(name); 

    } 



    public JSBean() { 
     // TODO Auto-generated constructor stub 
    } 

    @Override 
    public Object call(Object arg0, Object... arg1) { 
     // TODO Auto-generated method stub 
     return null; 
    } 

    @Override 
    public Object eval(String arg0) { 
     // TODO Auto-generated method stub 
     return null; 
    } 

    @Override 
    public String getClassName() { 
     // TODO Auto-generated method stub 
     return null; 
    } 


    @Override 
    public Object getSlot(int arg0) { 
     // TODO Auto-generated method stub 
     return null; 
    } 


    @Override 
    public boolean hasSlot(int arg0) { 
     // TODO Auto-generated method stub 
     return false; 
    } 

    @Override 
    public boolean isArray() { 
     // TODO Auto-generated method stub 
     return false; 
    } 

    @Override 
    public boolean isFunction() { 
     // TODO Auto-generated method stub 
     return false; 
    } 

    @Override 
    public boolean isInstance(Object arg0) { 
     // TODO Auto-generated method stub 
     return false; 
    } 

    @Override 
    public boolean isInstanceOf(Object arg0) { 
     // TODO Auto-generated method stub 
     return false; 
    } 

    @Override 
    public boolean isStrictFunction() { 
     // TODO Auto-generated method stub 
     return false; 
    } 



} 

Когда я запустить тест показано ниже

@Test 
public void testDefaultValMethod(){ 

    JSBean bean = new JSBean(); 

    bean.setMember("hello", " Sport "); 

    //Add stuff to engine. 
    engine.put("jsBean", bean); 

    String source = "(function(){\n" 
      + "print(jsBean);" 
      + "})();"; 

    Object obj=null; 
    try { 
     obj = engine.eval(source); 
    } catch (ScriptException e) { 
     // TODO Auto-generated catch block 
     e.printStackTrace(); 
    } 
    System.out.println("Returned : " + String.valueOf(obj)); 

} 

Я вижу ошибку ниже в консоли. В идеале Nashorn должен был непосредственно вызвать метод toString() компонента, чтобы получить реализацию String. Не уверен, что здесь происходит. Я попытался добавить вызов toString() 'явно в вызове метода getMember(), но это не устранило проблему.

JAVA Put is called. Input name: hello 
Input values: Sport 
JAVA Get is called.toString 
Called for thistoString and returned:null 
JAVA Get is called.valueOf 
Called for thisvalueOf and returned:null 
javax.script.ScriptException: TypeError: cannot.get.default.string in <eval> at line number 2 
    at jdk.nashorn.api.scripting.NashornScriptEngine.throwAsScriptException(NashornScriptEngine.java:467) 
    at jdk.nashorn.api.scripting.NashornScriptEngine.evalImpl(NashornScriptEngine.java:451) 
    at jdk.nashorn.api.scripting.NashornScriptEngine.evalImpl(NashornScriptEngine.java:403) 
    at jdk.nashorn.api.scripting.NashornScriptEngine.evalImpl(NashornScriptEngine.java:399) 
    at jdk.nashorn.api.scripting.NashornScriptEngine.eval(NashornScriptEngine.java:155) 
    at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:264) 
    at test.nashorn.NashornTest.testDefaultValMethod(NashornTest.java:386) 
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 
    at java.lang.reflect.Method.invoke(Method.java:497) 
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47) 
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) 
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44) 
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) 
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271) 
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70) 
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50) 
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238) 
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63) 
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236) 
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53) 
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229) 
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26) 
    at org.junit.runners.ParentRunner.run(ParentRunner.java:309) 
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86) 
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38) 
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459) 
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675) 
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382) 
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192) 
Caused by: <eval>:2 TypeError: cannot.get.default.string 
    at jdk.nashorn.internal.runtime.JSType.toPrimitive(JSType.java:514) 
    at jdk.nashorn.internal.runtime.JSType.toPrimitive(JSType.java:480) 
    at jdk.nashorn.internal.runtime.JSType.toStringImpl(JSType.java:1391) 
    at jdk.nashorn.internal.runtime.JSType.toString(JSType.java:589) 
    at jdk.nashorn.internal.objects.Global.printImpl(Global.java:2782) 
    at jdk.nashorn.internal.objects.Global.println(Global.java:1497) 
    at jdk.nashorn.internal.scripts.Script$Recompilation$1$11$\^eval\_.L:1(<eval>:2) 
    at jdk.nashorn.internal.scripts.Script$\^eval\_.:program(<eval>:1) 
    at jdk.nashorn.internal.runtime.ScriptFunctionData.invoke(ScriptFunctionData.java:640) 
    at jdk.nashorn.internal.runtime.ScriptFunction.invoke(ScriptFunction.java:228) 
    at jdk.nashorn.internal.runtime.ScriptRuntime.apply(ScriptRuntime.java:393) 
    at jdk.nashorn.api.scripting.NashornScriptEngine.evalImpl(NashornScriptEngine.java:446) 
    ... 29 more 
Caused by: java.lang.UnsupportedOperationException: cannot.get.default.string 
    at jdk.nashorn.api.scripting.DefaultValueImpl.getDefaultValue(DefaultValueImpl.java:53) 
    at jdk.nashorn.api.scripting.AbstractJSObject.getDefaultValue(AbstractJSObject.java:289) 
    at jdk.nashorn.internal.runtime.JSType.toPrimitive(JSType.java:512) 
    ... 40 more 
Returned : null 

ответ

2

print или преобразование «toString» из сценариев вызывает метод «toString» для объекта сценария. Любой доступ к свойствам (включая свойство функции) в JSObject направляется методу getMember. Итак, чтобы сделать «valueOf» или «toString», вы должны реализовать соответствующий getMember в подтипе JSObject.

Пример:

import jdk.nashorn.api.scripting.*; 
import javax.script.*; 

public class Main { 

    static class MyJSObject extends AbstractJSObject { 
    @Override 
    public Object getMember(String name) { 
     if (name.equals("toString")) { 
      // return a "function" object for "toString" property 
      return new AbstractJSObject() { 
       @Override 
       public boolean isFunction() { 
        return true; 
       } 

       @Override 
       public Object call(Object self, Object...args) { 
        return self.toString(); 
       } 
      }; 
     } 
     return null; // other properties here 
    } 

    @Override 
    public String toString() { 
     return "my js object"; 
    } 
    } 

    public static void main(String[] a) throws Exception { 
    ScriptEngineManager m = new ScriptEngineManager(); 
    ScriptEngine e = m.getEngineByName("nashorn"); 
    e.put("myObj", new MyJSObject()); 
    e.eval("print(myObj)"); 
    } 
} 

В качестве альтернативы, вы можете также переопределить

Object getDefaultValue(final Class<?> hint) throws UnsupportedOperationException 

метод в вашем AbstractJSObject подкласса.

import jdk.nashorn.api.scripting.*; 
import javax.script.*; 

public class Main2 { 

    static class MyJSObject extends AbstractJSObject { 
    @Override 
    public Object getDefaultValue(Class<?> hint) { 
     if (hint == String.class) { 
      return toString(); 
     } 
     throw new UnsupportedOperationException("no conversion for " + hint); 
    } 

    @Override 
    public String toString() { 
     return "my js object"; 
    } 
    } 

    public static void main(String[] a) throws Exception { 
    ScriptEngineManager m = new ScriptEngineManager(); 
    ScriptEngine e = m.getEngineByName("nashorn"); 
    e.put("myObj", new MyJSObject()); 
    e.eval("print(myObj)"); 
    } 
} 
+0

Я реализую это и обновляю ответ. Спасибо!. – NishM

+0

Я реализовал первый метод (возвращающий функцию AbstractJSObject), и он работал, как ожидалось. И как это здорово! – NishM

Смежные вопросы