1

I would like to cast the ScriptObjectMirror object to other class called Point that I defined by myself as follow:

public class Point {

    public double x;
    public double y;

    public Point(double x, double y) {
        this.x=x;
        this.y=y;
    }

}

My javascript code is something like this:

function getCenter(points) {
    var center = {x: 0, y: 0};
    print(points);   
    print(points.length);
    for (var i =0; i < points.length; ++i ) {
        center.x += points[i].x;
        center.y += points[i].y;
    }
    center.x /= points.length;
    center.y /= points.length;
    return center;
}

and I would like to invoke getCenter function in the js by supplying multiple Points using the code:

    ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
    //      engine.eval("print('Hello World!');");
    engine.eval(new FileReader("circleintersection.js"));

    Invocable invocable = (Invocable) engine;

    Point a = new Point(3,2);
    Point b = new Point(5,3);
    Point c = new Point(1,4);
    Point d = new Point(2,5);
    Point e = new Point(6,6);

    Point[] points = {a,b,c,d,e};

    Point result = (Point) invocable.invokeFunction("getCenter", points);
    System.out.println(result.x);

But it gave me error like

Exception in thread "main" java.lang.ClassCastException: jdk.nashorn.api.scripting.ScriptObjectMirror cannot be cast to Point

I tried another method to convert to Map using the code:

@SuppressWarnings("restriction")
    private static Object convertIntoJavaObject(Object scriptObj) {
        if (scriptObj instanceof ScriptObjectMirror) {
            ScriptObjectMirror scriptObjectMirror = (ScriptObjectMirror) scriptObj;
            if (scriptObjectMirror.isArray()) {
                List<Object> list = Lists.newArrayList();
                for (Map.Entry<String, Object> entry : scriptObjectMirror.entrySet()) {
                    list.add(convertIntoJavaObject(entry.getValue()));
                }
                return list;
            } else {
                Map<String, Object> map = Maps.newHashMap();
                for (Map.Entry<String, Object> entry : scriptObjectMirror.entrySet()) {
                    map.put(entry.getKey(), convertIntoJavaObject(entry.getValue()));
                }
                return map;
            }
        } else {
            return scriptObj;
        }
    }

and in main() method the code is something like this:

ScriptObjectMirror result = (ScriptObjectMirror) invocable.invokeFunction("getCenter", points);
    Object con = convertIntoJavaObject(result);
    Map<String, Object> map = (Map<String, Object>) con;
    for (Map.Entry<String, Object> entry : map.entrySet()) {
        System.out.println(entry.getKey() + ":" + entry.getValue());
    }

but it prints x:NaN and y:NaN as the results.

Is there something wrong with the way I insert the variables to javascript code or how to get the result?

Ihsan Haikal
  • 1,085
  • 4
  • 16
  • 42

1 Answers1

0

When you try to add the following print statement to your script, (you also add a toString to the Point class)

print(points);   
print(points.length);

you can see that it prints,

Point(x=3.0, y=2.0) //the first point
undefined

So, it is clear that only the first value from the array is passed to the javascript function. Since points.length is undefined, you get a NaN in javascript code itself.

I couldn't find the reason for this (I will update my answer if I could find a valid explanation for this)

But you could work around this if you are willing to change your javascript code to use arguments

function getCenter() {
    var center = {x: 0, y: 0};
    for (var i =0; i < arguments.length; ++i ) {
        center.x += arguments[i].x;
        center.y += arguments[i].y;
    }
    center.x /= arguments.length;
    center.y /= arguments.length;
    return center;
}

See: Is it possible to send a variable number of arguments to a JavaScript function?

Thiyagu
  • 17,362
  • 5
  • 42
  • 79
  • I tried the print method but it doesn't print anything – Ihsan Haikal Aug 28 '18 at 16:23
  • Where did you add those print statements? I meant to add them in *your* posted code (to verify that only the first Point is passed to the js function) – Thiyagu Aug 29 '18 at 03:55
  • It prints Point@757277dc undefined not something like you mentioned – Ihsan Haikal Aug 29 '18 at 07:55
  • That's the default `toString`. I added a `toString` function as I told (relied on [Lombok](https://projectlombok.org/) for this to be exact). You can add `toString` function and just return a string concatenating the values of x and y – Thiyagu Aug 29 '18 at 08:55
  • yup you're right about that it only prints the first point. so the solution is only to change it to argument from the js script? do i need to change the java code to accommodate that? – Ihsan Haikal Aug 29 '18 at 15:55
  • I couldn't find time to spend on this. If I could find a way to pass arrays(there must be one), I will update the answer. – Thiyagu Aug 29 '18 at 16:21