In Rhino, I have a Scriptable bean like below
/**
*
*/
package test.rhino;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.mozilla.javascript.FunctionObject;
import org.mozilla.javascript.Scriptable;
/**
* #author
*
*/
public class SomeBean implements Scriptable, Map<String,Object> {
/**
* The current values for this object.
*/
private HashMap<String, Object> values = new HashMap<>();
/**
*
*/
public SomeBean() {
System.out.println("SomeBean();");
}
/*
* #see org.mozilla.javascript.Scriptable#getClassName()
*/
#Override
public String getClassName() {
return "SomeBean";
}
/*
* #see org.mozilla.javascript.Scriptable#get(java.lang.String,
* org.mozilla.javascript.Scriptable)
*/
#Override
public Object get(String name, Scriptable start) {
System.out.println("Get is called.");
System.out.println("Called for this" + name + " and returned :" + values.get(name));
return values.get(name);
}
/*
* #see org.mozilla.javascript.Scriptable#put(java.lang.String,
* org.mozilla.javascript.Scriptable, java.lang.Object)
*/
#Override
public void put(String name, Scriptable start, Object value) {
System.out.println("Put is called. Input name: " + name + "\n Input values: " + value);
values.put(name, value);
}
#Override
public Object get(int index, Scriptable start) {
// TODO Auto-generated method stub
return null;
}
#Override
public boolean has(String name, Scriptable start) {
// TODO Auto-generated method stub
return false;
}
#Override
public boolean has(int index, Scriptable start) {
// TODO Auto-generated method stub
return false;
}
#Override
public void put(int index, Scriptable start, Object value) {
// TODO Auto-generated method stub
}
#Override
public void delete(String name) {
// TODO Auto-generated method stub
}
#Override
public void delete(int index) {
// TODO Auto-generated method stub
}
#Override
public Scriptable getPrototype() {
// TODO Auto-generated method stub
return null;
}
#Override
public void setPrototype(Scriptable prototype) {
// TODO Auto-generated method stub
}
#Override
public Scriptable getParentScope() {
// TODO Auto-generated method stub
return null;
}
#Override
public void setParentScope(Scriptable parent) {
// TODO Auto-generated method stub
}
#Override
public Object[] getIds() {
// TODO Auto-generated method stub
return null;
}
#Override
public Object getDefaultValue(Class<?> hint) {
// TODO Auto-generated method stub
return null;
}
#Override
public boolean hasInstance(Scriptable instance) {
// TODO Auto-generated method stub
return false;
}
#Override
public int size() {
// TODO Auto-generated method stub
return 0;
}
#Override
public boolean isEmpty() {
// TODO Auto-generated method stub
return false;
}
#Override
public boolean containsKey(Object key) {
// TODO Auto-generated method stub
return false;
}
#Override
public boolean containsValue(Object value) {
// TODO Auto-generated method stub
return false;
}
#Override
public Object get(Object key) {
System.out.println("MAP -- Get is called.");
// TODO Auto-generated method stub
return values.get(key);
}
#Override
public Object put(String key, Object value) {
values.put(key, value);
System.out.println("MAP -- Put is called.");
// TODO Auto-generated method stub
return value;
}
#Override
public Object remove(Object key) {
// TODO Auto-generated method stub
return null;
}
#Override
public void putAll(Map<? extends String, ? extends Object> m) {
// TODO Auto-generated method stub
}
#Override
public void clear() {
// TODO Auto-generated method stub
}
#Override
public Set<String> keySet() {
// TODO Auto-generated method stub
return null;
}
#Override
public Collection<Object> values() {
// TODO Auto-generated method stub
return null;
}
#Override
public Set<java.util.Map.Entry<String, Object>> entrySet() {
// TODO Auto-generated method stub
return null;
}
private static Scriptable globalPrototype;
public static void finishInit(Scriptable scope, FunctionObject constructor, Scriptable prototype) {
System.out.println("finishInit is called.");
globalPrototype = prototype;
}
}
I can retrieve this bean as a Scriptable object and pass it to the eval() function of a compiled script like below.
String src5 = "(function(){return Name;})();\n";
Script sc = getCompiledScript(src5);
SomeBean sb = new SomeBean();
sb.put("Name", "Matt Murdock");
Scriptable sp = (Scriptable) sb;
result = script.exec(context, sp);
The result print Matt Murdock. We can refer to properties directly here since SomeBean instance is passed as scope to the script execution.
I am unable to find an equivalent way in Nashorn. I can use the source to get a CompiledScript instance but i cannot add SomeBean to the binding without giving it a key and i cannot call its members ( Name ) directly in my JavaScript function. The below code throws error which is obvious since we cannot access the properties without the prefix key.
NSomeOtherBean nsob = new NSomeOtherBean(); // extends AbstractJSObject
nsob.setMember("Name", "Bruce Wayne");
Bindings binding = engine.createBindings();
binding.put("nsob", nsob);
engine.setBindings(binding, ScriptContext.GLOBAL_SCOPE);
result = engine.eval("(function(){return Name;})();\n",binding);
Is there a way in Nashorn to add properties of NSomeOtherBean instance to the scope and access them without using a prefix? like how we can do it in Rhino.
Nashorn equivalent of Rhino Scriptable is jdk.nashorn.api.scripting.JSObject
See javadoc:
https://docs.oracle.com/javase/8/docs/jdk/api/nashorn/jdk/nashorn/api/scripting/JSObject.html
https://docs.oracle.com/javase/8/docs/jdk/api/nashorn/jdk/nashorn/api/scripting/AbstractJSObject.html
Examples:
http://hg.openjdk.java.net/jdk9/dev/nashorn/file/efeb16c75392/samples/jsobj_example.js
http://hg.openjdk.java.net/jdk9/dev/nashorn/file/efeb16c75392/samples/BufferArray.java
http://hg.openjdk.java.net/jdk9/dev/nashorn/file/efeb16c75392/samples/jsobject_mapreduce.js
That said, java.util.Map instances are treated as "special" by Nashorn. i.e., Map keys can be accessed like object properties in Nashorn.
Related
I have made some code inside of the spring boot back-end application which allows me to change a property of a specific object, this property which needs to be changed is the "status" property:
#Entity
public class Pakketje {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private int code;
private String status = "In magazijn";
public Pakketje() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
}
To change the property of that object, i will need to state the ID of that object (i think) inside of the react frontend application, and then fetch the PUT-method.
I will show you the method to change the property inside of my spring boot back-end application, this method can be found in the 'Service' class. 'bestaandPakketje' means existing pakketje.
#Override
public Pakketje getPakketjeById(int id) {
return pakketjeRepository.findById(id).orElse(null);
}
#Override
public Pakketje statusOnderweg(Pakketje pakketje) {
Pakketje bestaandPakketje = pakketjeRepository.findById(pakketje.getId()).orElse(null);
bestaandPakketje.setStatus(pakketje.getStatus());
return pakketjeRepository.save(bestaandPakketje);
}
}
And here is the controller:
public class PakketjeController {
#SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
#Autowired
private PakketjeService pakketjeService;
#PostMapping("/add")
public String add(#RequestBody Pakketje pakketje) {
pakketjeService.pakketjeOpslaan(pakketje);
return "Pakketje opgeslagen!";
}
#GetMapping("/getAll")
public List<Pakketje> getAllePakketjes(){
return pakketjeService.getAllePakketjes();
}
#GetMapping("/getById/{id}")
public Pakketje findPakketjeById(int id) {
return pakketjeService.getPakketjeById(id);
}
#PutMapping("/updateOnderweg")
public Pakketje statusIsOnderweg(#RequestBody Pakketje pakketje) {
return pakketjeService.statusOnderweg(pakketje);
}
}
Now the next step is verry hard for me. Each 'pakketje' has his own button which can be clicked to change the property (I will upload the picture so please check it). When that button is clicked the property automatically should change from "In magazijn "to "onderweg".
I would appreciate some help!
I'd like to import a module written natively (java, Android) into my React Native sources, in JS.
To access your functionality implemented in java you have to create a bridge. You can see the most recent instructions in the RN documentation site*.
The steps, assuming React Native 0.61, for a hello world, to be implemented in the android project inside the react native app directory (android directory):
1) First you create a simple POJO class to be returned to the react native context:
class MyData{
private int timeSpentSleeping;
public int getTimeSpentSleeping() {
return timeSpentSleeping;
}
public void setTimeSpentSleeping(int timeSpentSleeping) {
this.timeSpentSleeping = timeSpentSleeping;
}
#NonNull
#Override
public String toString() {
Gson gson = new Gson();
String json = gson.toJson(this);
return json;
}
static MyData build(final int timeSpentSleeping){
MyData newInstance = new MyData();
newInstance.timeSpentSleeping = timeSpentSleeping;
return newInstance;
}
}
And the react native module that do something and return objects of this class as javascript Promises:
public class HelloPromiseModule extends ReactContextBaseJavaModule {
public HelloPromiseModule(#NonNull ReactApplicationContext reactContext) {
super(reactContext);
}
#NonNull
#Override
public String getName() {
return "HelloPromise";
}
#ReactMethod
public void foobar(Promise promise){
Random r = new Random();
final int timeToSleep = r.nextInt(1000);
runThreadAndCallPromiseToJavascript(timeToSleep, promise);
}
//Cria um thread pra executar algo em paralelo
private void runThreadAndCallPromiseToJavascript(final int timeToSleep,final Promise promise){
Thread t = new Thread(new Runnable() {
#Override
public void run() {
try {
Thread.sleep(timeToSleep);
MyData result = MyData.build(timeToSleep);
promise.resolve(result.toString());
} catch (InterruptedException e) {
e.printStackTrace();
promise.reject(e);
}
}
});
t.run();
}
}
Now, we create the React Native Package (that is different from java packages):
public class HelloWorldPackage implements ReactPackage{
#NonNull
#Override
public List<NativeModule> createNativeModules(#NonNull ReactApplicationContext reactContext) {
return Arrays.<NativeModule>asList(
new HelloPromiseModule(reactContext));
}
#NonNull
#Override
public List<ViewManager> createViewManagers(#NonNull ReactApplicationContext reactContext) {
return Collections.emptyList();
}
}
The last step in the android version of your react native app is to register your HelloWorldPackage:
In the MainApplication.java inside your android project, inside the getPackages(), in the list of packages (new PackageList(this)...):
packages.add(new HelloWorldPackage());
Something like that:
protected List<ReactPackage> getPackages() {
#SuppressWarnings("UnnecessaryLocalVariable")
List<ReactPackage> packages = new PackageList(this).getPackages();
// Packages that cannot be autolinked yet can be added manually here, for example:
// packages.add(new MyReactNativePackage());
packages.add(new HelloWorldPackage());
return packages;
}
Now, to get your native class in the javascript world:
import {
NativeModules,
} from 'react-native';
...
const {HelloPromise} = NativeModules;
Your native class is accessible from the variable HelloPromise.
You can get the result of HelloPromise.foobar() with something like this, in the react native side of your code:
async function handleHelloPromisesPress() {
let result = await HelloPromise.foobar();
console.log(result);
}
You may notice that 'result' is a json whose structure is equal to the POJO class we created in the beginning.
I'm learning GWT and currently I'm struggeling with RPC. I have a simple Project: a Label, a Textbox, an outputLabel and a Button.
I want when the user enters his Name in the TextBox and press the "send" Button he will get a Message from the Server "Hello "+name+"Here speaks the Server" - stupid example.
However in my CLient I have a package GUI and a Package Service and my entrypoint class
public class TestGwt270 implements EntryPoint {
public void onModuleLoad()
{
TestGwt270ClientImpl clientImpls = new TestGwt270ClientImpl("/TestGwt270/testgwt270service");
GWT.log("Main "+GWT.getModuleBaseURL() );
RootPanel.get().add(clientImpls.getMainGUI());
}
MyGui:
public class MainGUI extends Composite
{
private TestGwt270ClientImpl serviceImpl;
private VerticalPanel vPanel;
private TextBox inputTB;
private Label outputLbl;
public MainGUI(TestGwt270ClientImpl serviceImpl)
{
this.vPanel = new VerticalPanel();
initWidget(vPanel);
this.inputTB = new TextBox();
this.inputTB.setText("Gib deinen Namen ein");
this.outputLbl = new Label("Hier kommt der output");
this.vPanel.add(this.inputTB);
this.vPanel.add(this.outputLbl);
Button sendBtn = new Button("send");
sendBtn.addClickHandler(new MyClickhandler());
this.vPanel.add(sendBtn);
}
public void updateOutputLbl(String output)
{
this.outputLbl.setText(output);
}
private class MyClickhandler implements ClickHandler
{
#Override
public void onClick(ClickEvent event) {
// TODO Auto-generated method stub
serviceImpl.sayHello(inputTB.getText());
}
}
}
TheService:
#RemoteServiceRelativePath("testgwt270service")
public interface TestGwt270Service extends RemoteService
{
String sayHello(String name);
}
AsyncService:
public interface TestGwt270ServiceAsync
{
void sayHello(String name, AsyncCallback<String> callback);
}
ClientInterface:
public interface TestGwt270ServiceClientInt
{
void sayHello(String name);
}
Client Implementation:
public class TestGwt270ClientImpl implements TestGwt270ServiceClientInt
{
private TestGwt270ServiceAsync service;
private MainGUI maingui;
public TestGwt270ClientImpl(String url)
{
GWT.log(url);
// TODO Auto-generated constructor stub
this.service = GWT.create(TestGwt270Service.class);
ServiceDefTarget endpoint = (ServiceDefTarget) this.service;
endpoint.setServiceEntryPoint(url);
this.maingui = new MainGUI(this);
}
public MainGUI getMainGUI()
{
return this.maingui;
}
#Override
public void sayHello(String name) {
// TODO Auto-generated method stub
this.service.sayHello(name, new MyCallback());
}
private class MyCallback implements AsyncCallback<String>
{
#Override
public void onFailure(Throwable arg0) {
// TODO Auto-generated method stub
GWT.log("Failure");
maingui.updateOutputLbl("An Error has occured");
}
#Override
public void onSuccess(String arg0) {
// TODO Auto-generated method stub
GWT.log("Success");
maingui.updateOutputLbl(arg0);
}
}
}
ServerSideCode:
public class TestGwt270ServiceImpl extends RemoteServiceServlet implements TestGwt270Service
{
#Override
public String sayHello(String name) {
// TODO Auto-generated method stub
GWT.log("Hello " + name + "\nHier spricht der Server mit dir");
return "Hello " + name + "\nHier spricht der Server mit dir";
}
}
My Problem is, when I press the Button to send my Name to the server I get following Error:
HandlerManager.java:129 Uncaught com.google.gwt.event.shared.UmbrellaException: Exception caught: (TypeError) : Cannot read property 'sayHello_2_g$' of undefined
I don't know where this Error comes from and I hope you can help me.
I found the answer myself - I made a simple mistake:
In the class MyGUI I got this:
public class MainGUI extends Composite
{
private TestGwt270ClientImpl serviceImpl;
...
public MainGUI(TestGwt270ClientImpl serviceImpl)
{
...
I forgot to assign the serviceImpl
the Fix:
public class MainGUI extends Composite
{
private TestGwt270ClientImpl serviceImpl;
...
public MainGUI(TestGwt270ClientImpl serviceImpl)
{
this.serviceImpl = serviceImpl; //this line is the solution to my problem
...
I have a bean that implements JSObject ( & Map ) interface as shown below. I have removed some overridden methods to make it easy to read.
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;
}
}
When i run the test shown below
#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));
}
I see the below error in the console. Ideally Nashorn should have directly called the toString() method of the bean to get the String implementation. Not sure what is going wrong here. I did try adding a call to 'toString()' explicitly in the getMember() method call but that did not fix the problem.
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
print or "toString" conversions from scripts call "toString" method on the script object. Any property access (including function valued property) on a JSObject is routed to getMember method. So, to make "valueOf" or "toString" you've to implement appropriate getMember in your JSObject subtype.
Example:
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)");
}
}
Alternatively, you can also override
Object getDefaultValue(final Class<?> hint) throws UnsupportedOperationException
method in your AbstractJSObject subclass.
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)");
}
}
I am trying to define a javascript type using JsInterop but could not figure out how to define an array propery inside a type.
#JsType
public interface Task
{
// this works
#JsProperty String getTitle();
#JsProperty void setTitle(String title);
}
#JsType
public interface ViewModelInterface
{
// the following works
#JsProperty String getValue();
#JsProperty void setValue(String val);
// this does not work
#JsProperty Task[] getTasks();
#JsProperty void setTasks(Task[] tasks);
}
Is there any special syntax I should use?
Update:
I have come up with a temporary solution to the problem. I tried to use JNSI together with JsInterop and because the type define is javascript type we can add any property in that type after it is initialized.
The code is here
public class ViewModel implements ViewModelInterface
{
private String value;
public ViewModel()
{
value = "";
initTasksArray();
}
public String getValue() { return value; }
public void setValue(String val) { this.value = val; }
private native void initTasksArray()
/*-{
this.tasks =
[
{ title: 'Get high enough to grab the flag' },
{ title: 'Find the Princess' }
];
}*-/;
}
This way, the tasks property can be accessed from javascript as if it is defined as a #JsProperty. This is only a temporary solution and I am sure there will be a more proper one when JsInterop formally come out.
It should work as described.
Here is an example:
#JsType
public interface Task {
#JsProperty
String getTitle();
#JsProperty
void setTitle(String title);
public static Task getTask(String title) {
return new Task() {
private String title;
#Override
public String getTitle() {
return title;
}
#Override
public void setTitle(String title) {
this.title = title;
}
};
}
}
and
#JsType
public interface ViewModel {
#JsProperty
Task[] getTasks();
#JsProperty
void setTasks(Task[] tasks);
public static ViewModel getModel() {
return new ViewModel() {
Task[] tasks;
#Override
public void setTasks(Task[] tasks) {
this.tasks = tasks;
}
#Override
public Task[] getTasks() {
return tasks;
}
};
}
}
and the Javascript:
var model = ViewModel.getModel();
model.tasks = [Task.getTask('title1'), Task.getTask('title2')];
alert(model.tasks);