I'm trying to extend a Java class in a JavaScript project using ES4X/Graal. The class that I want to extend, has methods with overloeaded parameters that I need to override. I know that you can call a specific Java method by using the square brackets notation and specifying the type (example below), but, apparently, there's no way to specify the parameter types when overriding, per the Graal/Oracle/Nashorn documentation. So if you have the following:
package com.mycomp;
class A {
public void method(String parameter) {...}
public void method(int parameter) {...}
}
you can call either method in JavaScript like so:
var a = Java.type("com.mycomp.A");
a["method(String)"]("Some string parameter")
a["method(int)"](42)
a.method(stringOrIntParam)
However, when extending, you can only do the following:
var ClassAFromJava = Java.type("com.mycom.A");
var MyJavaScriptClassA = Java.extend(ClassAFromJava, {
method: function(stringOrIntParam) {...}
}
I want to be able to extend only one of the method(...), um, methods. And what about overloaded methods with different return types?
Thanks!
The sj4js allows you to do all that.
This example is slightly modified from the documentation...
public class TestObject extends JsProxyObject {
// the constructor with the property
public TestObject (String name) {
super ();
this.name = name;
// we hvae to initialize the proxy object
// with all properties of this object
init(this);
}
// this is a mandatory override,
// the proxy object needs this method
// to generate new objects if necessary
#Override
protected Object newInstance() {
return new TestClass(this.name);
}
// a method with a String parameter
public String method (String s) {
return "String";
}
// a method with a int parameter
public String method (int i) {
return "42";
}
}
And you can access this object as you would access a JS object and
the library takes care of selecting the appropriate method.
try (JScriptEngine engine = new JScriptEngine()) {
engine.addObject("test", new TestClass("123"));
// calling the method with the String parameter would
// result in calling the appropriate java method
engine.exec("test.method('123')");
// returns "String"
// calling the method with the int parameter would
// result in calling the appropriate java method
engine.exec("test.method(123)");
// returns "42"
}
Related
i have a class and this class i need to use the static method and in that method i use the varibale but it show me this errror :
Property 'ec' does not exist on type 'typeof Wallet'.ts
and i not need to set static that variable .
how can i sovle this problem ?
export class Wallet {
ec: any;
constructor() {
this.EC = elliptic.ec;
this.ec = new this.EC('secp256k1');
}
static verifySignuture(address: string, data: any, signature: any): boolean {
const keyfromPublic = this.ec.keyFromPublic(address, 'hex');
return keyfromPublic.verify(Utility.GenerateHash(data), signature);
}
}
how can solve this prblem ?
Javascript class != an instance of this class.
Imagine class as some shape defined by a fields inside it. You instantiate this shape by using a new keyword. In javascript (typescript as well) you can assign your class' instance to a variable. For example
const myWallet = new Wallet();
and then you can have another instance assigned to yet another variable
const someOtherWallet = new Wallet();
In this case myWallet and someOtherWallet has the same shape, but there are two separated instances of the same shape (class). By using static word, you are assigning field of your shape to shape itself instead of constructed instance. So if your method is static, instead of calling this.myMethod() you need to call MyClass.myMethod() - in your case Wallet.verifySignature(). The difference is about the this word. It represents a javascript context within you are operating in specific part of the code. You can find more about it here. Advantage of a non-static methods of a class is being able to access this context within those methods. If you do not need to access this you can go ahead and use static method. In first case (when I assigned new instance of a class Wallet to a local variable named myVallet, this context (inside a Wallet class) is equivalent to a variable named myVallet, but the difference is from where you are gonna access it. In your case you are trying to access this context inside a verifySignature method, which is wrong, because inside static method of a class, this context is not an instance of your class. Instead remove a static keyword before method declaration and try something like this:
const wallet = new Wallet();
const signatureVerified = wallet.verifySignature(address, data, signature);
I have the following example:
class MyClass {
constructor(name) {
this.name = name;
}
toString() {
return this.name;
}
valueOf() {
return this.name;
}
}
const list = [new MyClass("b"), new MyClass("รค")];
list.sort();
console.log(list.join());
list.sort(new Intl.Collator('de').compare);
console.log(list.join());
Here, the Collator is required to get the correct sort order, but I want to get rid of it and I want to be able to compare instances of MyClass with a simple myClass1 < myClass2. This should theoretically be possible by changing the valueOf function.
My question now is, is there any JavaScript built-in way to get a "locale" value of a string (similar to Intl.Collator or localeCompare) that I can return in my valueOf function to get the order right?
No, there is no such info. Basically, unless you use Intl with specified locale directly (as with Intl.Collator) or indirectly (as with localeCompare), you will compare codepoints based on browser implementation of strings which is, most likely, by UTF-16 code points.
I have this engine which runs user-input (trusted) javascript function to filter some data for them using Nashorn. Don't want to go into the details of the requirements but let's say it's some sort of plugin system.
This javascript gets a Java map(which acts like a JS object) as a parameter to get some contextual data.
So I want to add some convenience extension methods to this JS object for the users. So I did the following:
Map<String, String> inputMap = ....
inputMap.put("containsMagic", (Function<String, Boolean>) param -> some complex magic check and return boolean);
Works perfectly!
if(inputMap.containsMagic('Im Por Ylem'))
do stuff;
However, I want it to accept no parameter as well, because null is a valid value and containsMagic() looks better than containsMagic(null).
if(inputMap.containsMagic())
do stuff;
But I get this:
TypeError: Can not invoke method ......$$Lambda$15/1932831450#30c15d8b with the passed arguments; they do not match any of its method signatures.
I guess that's kind of normal given how Java works. If I pass null, it works of course but that's not very intuitive.
I can't add another function with 0 parameters and the same name because Java maps are single keyed. The complex magic check needs to be in java, so I can't assign a javascript function (JSObject?) either. Any ideas how I can accomplish this?
You can implement containsMagic as explicit method with a varargs parameter:
public static class InputMap extends HashMap<String, Object> {
public Boolean containsMagic(String... args) {
String arg = args.length == 1 ? args[0] : null;
// do magic
}
}
...
Map<String, Object> inputMap = new InputMap();
java.util.function unfortunately lacks a standard interface for variadic functions, but you could add your own:
#FunctionalInterface
public interface VariadicFunction<T,R> {
public R apply(T... args);
}
and then
inputMap.put("containsMagic", (VariadicFunction<String, Boolean>) param -> some complex magic check and return boolean);
I created an object in my js file:
function Shape(name, color, count) {
this.Name = name;
this.Color = color;
this.Count = count;
}
var apples = new Shape('apple', 'green', 5);
I want to pass this "class" into the controller via load'function, so I wrote:
$('#myDiv').load(url, { 'Shape': apples });
The url is the url of a function in the controller:
function GetShape(shape as ShapeModel) as actionResult
End Function
While ShapeModel is:
Class ShapeModel
Public Property Name As String
Public Property Color As String
Public Property Count As Integer
Public Property IsHot As Boolean
End Class
The function is called by the load function but the parameters of the class are equal to nothing.
I want to convert the class that came in the js file (Shape) into the class in the controller(ShapeModel) In the function above (GetShape).
These classes have the same properties, except one property (IsHot).
How can I do it?
I tried to send the class of the js as JSON.stringify and then make a deserialization in the controller from the given string into the class of ShapeModel, but the deserializtion failed..
The optimal solution is to send it as a class and not stringify that..
Any help appreciated!
I believe that your issue is in how you are passing the parameters in the jQuery load call. You are wrapping your Shape object in another object with a Shape property. Instead, just pass the Shape object directly as the data.
$('#myDiv').load(url, apples);
I think that MVC only supports binding a single parameter from the body, so you don't need to have something that specifies the parameter name.
I'm trying to integrate Mixpanel with GWT, but I have problems calling an event with a property and one value.
My function to track an simple event (without values):
public native void trackEvent(String eventName)/*-{
$wnd.mixpanel.track(eventName);
}-*/;
It works.
But when I want to add some properties and values, it doesn't work properly:
public native void trackComplexEvent(String eventName, String property,
String value)/*-{
$wnd.mixpanel.track(eventName, {property:value});
}-*/;
I have 2 problems with this:
1) Mixpanel says the property name is: "property"(yes, the name of the variable that I'm passing, not the value).
2) Mixpanel says the value is:undefined
An example from mixpanel web is:
mixpanel.track("Video Play", {"age": 13, "gender": "male"});
So, I guess the problem is I'm doing a wrong call or with wrong type of arguments.
your problem is, that when you pass the you create the property object, you don't create a parameter, taken from your property name, but rather a property named property. If you debug your code, you can check, that a parameter property is passed to your mixpanel.track function.
To do what you want, you have to use an other syntax.
public native void trackComplexEvent(String eventName, String property,
String value)/*-{
//create the property object you want to pass
var propertyPassed = {}
// set the value you want to pass on the propertyPassed object
propertyPassed[property] = value;
//call your function with the argumetn you want to pass
$wnd.mixpanel.track(eventName, propertyPassed);
}-*/;
I tested your code, by creating a JavaScript funciton on my site:
window.mixpanel = {
track : function(eventName, props) {
alert(props.age);
}
}
and calling it with
trackComplexEvent("hallo", "age", "13");
The alert was '13'
BR,
Stefan