How would I evaluate input with a ECMA 262 compliant regular expression?
After some reading I found out the Java 8's javascript engine nashorn can help me to do that. How can I use nashorn script engine to match a regular expression?
Assume you have a file regex.js containing a method used to determine if a given String matches a given regular expression:
var check = function(regex, str) {
return new RegExp(regex).test(str);
};
You can evaluate this function using the Nashorn script engine, and call it with the following code:
ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
engine.eval(new FileReader("regex.js"));
Invocable invocable = (Invocable) engine;
Object result = invocable.invokeFunction("check", "\\d+", "1");
System.out.println(result);
In order to call a function, you first have to cast the script engine to Invocable. This interface is implemented by the NashornScriptEngine and defines a method invokeFunction to call a JavaScript function for a given name. The rest of the method arguments are then directly passed as arguments to the JavaScript method.
Note that it is also possible to do this without an external JavaScript file. It is also possible to avoid the cast to Invocable by asking the script engine to evalute a String. This is the same code with those two ideas:
ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
engine.eval("function check(regex, str) { return new RegExp(regex).test(str); }");
Object result = engine.eval("check('\\\\d+', 1);");
System.out.println(result);
When passing variables from Java to JavaScript, you have to be very careful about the proper escaping of characters: the rule are not the same for the two languages.
Perhaps something like this:
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine se = manager.getEngineByName("nashorn");
se.put("str", "the quick brown fox");
se.put("rgx", "/quick/");
Object result = se.eval("str.matches(rgx).length");
System.out.println(result);
You need to run with Java8 to get the "nashorn" engine. The argument to se.eval(String) is the javascript. I kept it simple for this example. You can do plenty other stuff in terms of regex. See here.
Using Java 8 you can use the Nashorn JavaScript engine via the package javax.script using the nashorn identifier.
Here's an example:
ScriptEngineManager engineManager = new ScriptEngineManager();
ScriptEngine nashorn = engineManager.getEngineByName("nashorn");
String ecmaScript = //YOUR SCRIPT HERE
//add any additional variables here
Object returnVal = nashorn.eval(ecmaScript);
SOURCE: http://www.oracle.com/technetwork/articles/java/jf14-nashorn-2126515.html
You'll want to get familar with the ScriptEngine class and you'll need to do quite a bit of testing and trial & error to handle the data that's returned since the returned data won't be type safe. The debugger will be your best friend during this process.
Related
I'm using groovy/nashorn as java engine but not able to interpolate Strings.
jdk.nashorn.api.scripting.NashornScriptEngine scriptEngine =(NashornScriptEngine) factory.getEngineByName("nashorn");
ScriptContext context = scriptEngine.getContext();
Bindings bindings = context.getBindings(ScriptContext.ENGINE_SCOPE);
bindings.put("x","Guest");
engine.eval("Hello, ${x}",context);
But I'm getting javax.script.ScriptException.
Is Sttring interpolation supported?
Thanks
There are two things worth mentioning:
if you want to evaluate Groovy script, you might need to use
new ScriptEngineManager().getEngineByExtension("groovy");
the script passed to engine.eval() method has to be a valid Groovy code. The script code you passed to the eval method is not a valid Groovy code - you expect to interpolate a string, but you didn't put it inside the double quotes.
Consider the following example:
ScriptEngine engine = new ScriptEngineManager().getEngineByExtension("groovy");
ScriptContext context = engine.getContext();
Bindings bindings = context.getBindings(ScriptContext.ENGINE_SCOPE);
bindings.put("x","Guest");
Object result = engine.eval("\"Hello, ${x}\"", context);
System.out.println(result);
The output:
Hello, Guest
Alternatively, you may pass a Groovy script that prints the interpolated script. In this case the code may look like this:
ScriptEngine engine = new ScriptEngineManager().getEngineByExtension("groovy");
ScriptContext context = engine.getContext();
Bindings bindings = context.getBindings(ScriptContext.ENGINE_SCOPE);
bindings.put("x","Guest");
engine.eval("println \"Hello, ${x}\"", context);
It produces the same output, but does not assign Hello, Guest to a variable.
I want to create a JSObject and fill it with properties (kind of like a HashMap), but without casting the result of an eval("({})"), because I would think that constantly evaluating such a thing would really have an impact on performance. Is there a way?
If you want to use script objects like Maps, you might as well use java.util.HashMap instances from Nashorn scripts! In addition to supporting the usual java method calls, Nashorn's linker special cases java.util.Map instances and supports keys-as-property names idiom. This is more efficient than using a Javascript object as a Map.
$ jjs
jjs> var m = new java.util.HashMap
jjs> m.foo = "bar"
bar
jjs> m.foo
bar
jjs> m.get("foo")
bar
jjs> m.put("js", "nashorn")
null
jjs> m.js
nashorn
But, if you insist on using JS-object-as-map, then you can do the eval and cast to JSObject you mentioned. You may want to measure the perf. hit (which is assumed!) before making any further changes!
You can also get hold of JS "Object" constructor object and 'cache' it for repeated object creation from Java code. You can then call newObject() method on it to create a new empty object.
import javax.script.*;
import jdk.nashorn.api.scripting.JSObject;
public class Main {
public static void main(String[] args) throws Exception {
ScriptEngineManager m = new ScriptEngineManager();
ScriptEngine e = m.getEngineByName("nashorn");
// get JS "Object" constructor object
JSObject objConstructor = (JSObject)e.eval("Object");
// call 'newObject' on Object constructor
JSObject jsObj = (JSObject) objConstructor.newObject();
// fill properties of the new empty script object
jsObj.setMember("foo", "bar");
// expose the new JS object as global var!
e.put("obj", jsObj);
// print the object as a JSON string
e.eval("print(JSON.stringify(obj))");
}
}
Note that the above scheme works for any user-defined constructor function as well. For eg. if you want to create objects using specific user defined constructor function, you just have to replace
JSObject objConstructor = (JSObject)e.eval("Object");
with
JSObject objConstructor = (JSObject)e.eval("MyConstructorFunc");
(assuming you've eval'ed code to define MyConstructorFunc function earlier).
The rest of the code is same as above.
I'm trying to pass json data from javascript to java, like below. The idea is to pass the data to the java code as a javascript object (and not a a string which I know can be done ). I've tried the code below without success - idea was to use NativeJson.stringify to convert from a javascript object to a Java string, however this results in an Undefined instance instead of the expected string. Any idea on this could be achieved much appreciated.
in javascript file "test.js"
parser.fct ( {"abc":123,"def":456} )
in java
//1.binds javascript calls to Java
...
ScriptEngine engine = new ScriptEngine...
Bindings bindings = engine.createBindings();
bindings.put("parser", new Parser());
engine.eval("test.js");
//2. in Parser class
public void fct(Object obj){
Context ctx = Context.enter();
ScriptableObject scope = ctx.initStandardObjects();
Object json = NativeJSON.stringify(ctx, scope, obj, null,null);
//json returned is of type Undefined
}
Could not find a way to use NativeJSON.stringify(). I ended up building the json string "by hand", i.e Iterating on the property ids of the native javascript object and adding these properties to a java Map.
The Map is then transformed into a json object using new JSONObject(map);
link to source code
https://gist.github.com/eleco/c2bc77dca9ecc844a924
guys! I need to create some sort of meta language which I could embed in XML and then parse with Java. For example:
<code>
[if value1>value2 then "Hello, Bob!" else "Hello, Jack"]
</code>
or
<code>
[if value1+2>value2 return true]
</code>
I need to implement conditional statements,arithmetics.
Any suggestions where should I start looking?
Java has a built-in JavaScript interpreter:
ScriptEngine jsEngine = new ScriptEngineManager().getEngineByName("JavaScript");
jsEngine.put("value1", 8);
jsEngine.put("value2", 9);
String script = "if(value1 + 2 > value2) {'Foo'} else {'Bar'}";
final Object result = jsEngine.eval(script);
System.out.println(result); //yields "Foo" String
Of course you are free to both load the script from anywhere you need and to provide it with any context (value and value2 in this example) you want.
See also Scripting for the Java Platform article.
A user here, Bart Kiers. Wrote a tutorial about creating a simple language in Java with ANTLR.
Java has a scripting API that you could use for this. Lookup the API documentation of the package javax.script.
You could include code in for example JavaScript in the code element, and execute that using the scripting API.
If you really want to develop your own language, start off with the interpreter pattern. If you just want to leverage somebody else's language in your Java code, look to integration ala JSP style embedded languages.
It is almost certain that a homemade language would suck, especially in the long run, so don't roll something on your own.
There are several jsp-like frameworks available, maybe one of those would do the trick:
JSTL/JSP EL (Expression Language) in a non JSP (standalone) context
Like
String r = SomeThing.toExecString("new Object().toString()");
And when executed the value of r would be:
"new Object().toString() = java.lang.Object#c5e3974"
Is this even possible at all? Would it need a bunch of reflection? A built in compiler maybe?
ScriptEngine engine = new ScriptEngineManager().getEngineByName("beanshell");
Object result = engine.eval("new Object().toString();");
System.out.println(result);
You may get close to what you want using BeanShell. I ran the above code with Java 6 with BeanShell 2.0b4 and the JSR 223-based bsh-engine.jar engine on the classpath.
There is a great post here:
Generating Static Proxy Classes - http://www.javaspecialists.eu/archive/Issue180.html
Part one is enough for what you asked, I think
Don't know if you really wanted this. But your problem would be solved with this method:
String toExecString( String code ) {
return String.format(
"\"%s\" = %s#%x",
code,
code.getClass().getName(),
code.hashCode()
);
}