I have an instance of this Java class accessible in my Javascript program
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;
}
}
This is the code inside 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
);
The simple expression ctx.c(1)
prints:
rhino:> 1.1 (java.lang.Double)
Now here is what happens with ctx.c(1) + ctx.c(2)
:
rhino:> 1.12.2 (java.lang.String)
And finally (ctx.c(1) + ctx.c(2)) * ctx.c(3)
:
rhino:> nan (java.lang.Double)
Rhino is performing string concatenation instead of number arithmetics! The following program works as expected instead:
engine.put("a", 1.1);
engine.put("b", 2.2);
engine.put("c", 3.3);
res = engine.eval("(a + b) * c");
Outputs:
rhino:> 10,89 (java.lang.Double)
This is a strange feature of Rhino: a Java Number
set with engine.put("one", new Double(1))
works as expected, while the result of a Java method depends on the return type declared by the method itself, which is read with the reflection API:
double
, it's converted to a Javascript number+
means concatenation, either Object
like in your sample as well as Double
You can configure this behavior with wrapFactory.setJavaPrimitiveWrap(false)
on the WrapFactory
in the current Context
. This way the Rhino code can be kept in the bootstrap lines of your program and doesn't clutter ContentProvider
(which I guess is some sort of configuration proxy)
From the live Javadoc of WrapFactory.isJavaPrimitiveWrap()
By default the method returns true to indicate that instances of String, Number, Boolean and Character should be wrapped as any other Java object and scripts can access any Java method available in these objects
So you can set this flag to false
to indicate that Java Number
's should be converted to Javascript numbers. It takes just two lines of code
Context ctx = Context.enter();
ctx.getWrapFactory().setJavaPrimitiveWrap(false);
Here is the Gist with the full code I used to test
I created a value wrapper:
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;
}
}
and an edit function:
public static class ContentProvider {
public Object c(int n) {
... return new JSValue(1.1);
Now the expression works as expected. Thanks all.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With