The Javascript Manifold (Manifold.js) is a Manifold extension library that allows for seamless interaction with javascript resources from Java using the Rhino project.
The library supports the use of javascript programs from Java, the use of ES6-flavored javascript classes from java, the use of Java classes from javascript, as well as the creation of type-safe javascript expressions for use in Java as a scripting layer.
Manifold.js makes standard ES5-style Javascript programs available as types in Java.
The entire javascript program is evaluated when the type is first accessed. Top level functions are accessible as static methods on the program type.
Here is an example top-level function found in ExampleProgram.js
:
function hello(name) {
return "Hello " + name;
}
This function could be invoked from Java like so:
System.out.println( ExampleProgram.hello("Java Programmers") )
The parameters and the return type of javascript functions are all of type Object
.
Top level variables in javascript programs are treated as global variables and will retain their values between evaluation. Given this function:
var i = 0;
function nextNum() {
return i++;
}
The following code
System.out.println( ExampleProgram.nextNum() )
System.out.println( ExampleProgram.nextNum() )
will print
0.0
1.0
In order to allow for greater control and readability in Java, Manifold.js allows you to specify the types parameters and return types using Typescript syntax.
Javascript:
import java.util.ArrayList;
class Sample {
constructor(a : String) {
this.foo = a;
}
foo (bar: String, baz : Integer) : ArrayList {
var arrlist = new ArrayList();
for(var i = 0 ; i < baz ; i ++) {
arrlist.add(bar);
}
return arrlist;
}
}
Java:
Sample sample = new Sample();
System.out.println(foo("Hello", 5)) // ["Hello","Hello","Hello","Hello","Hello"]
Manifold.js supports the use of ES6 Arrow Functions inside any Javascript program or class.
Javascript:
//Arrow function expression
function filterEvens(list) {
return list.filter( a => a % 2 == 0);
}
//Arrow function statements
function incrementList(list) {
return list.map( a => {return a + 1});
}
The following features are experimental.
Javascript classes are exposed as regular classes in Java. They have the same functionality as Java classes, including constructors, methods, static methods, and properties.
Javascript: foo.js
class Foo {
//Constructor
constructor(a) {
this.foo = a;
this._bars = 5;
}
//Methods
function bar() {
return this.foo * 2;
}
function baz(a,b) {
return a+b + this.foo;
}
//Static Methods
static hello() {
return "hello";
}
//Properties
get bars() {
return this._bars*2;
}
set bars(a) {
this._bars = a;
}
}
The constructor is called when a new object is created. It initializes the properties within the object.
Javascript:
class Foo {
constructor(a) {
this.bar = a;
}
}
Java:
Foo foo = new Foo(5); // Creates new Foo object and sets this.bar to a
Methods are functions that are assigned to classes. They can interact with properties of the class and call other internal methods.
Javascript:
class Foo {
constructor(a) {
this.foo = a;
}
function bar() {
return this.foo * 2;
}
}
Java:
Foo foo = new Foo(21);
System.out.println(foo.bar); // prints 42
Javascript:
class Foo {
constructor(a) {
this.foo = a;
this._bars = 5;
}
static function staticFoo() {
return 42;
}
}
Java:
System.out.println(Foo.staticFoo()); // Prints 42
Classes can have getter and setter properties which abstract the properties held within the class.
Javascript:
class Foo {
constructor(a) {
this.foo = a;
this._bars = 5;
}
get bars() {
return this._bars*2;
}
set bars(a) {
this._bars = a;
}
}
Java:
Foo foo = new Foo();
foo.setBars(21);
System.out.println(foo.getBars()) // Prints 42
Javascript templates are supported as first class citizens. A Javascript String Template is a file that ends in the .jst extension.
Javascript Template: SampleJSTemplate.jst
<%@ params(names) %>
All Names: <%for (var i = 0; i < names.length; i++) { %>
${names[i]}
<% } %>
The template declares the parameters using the <%@ params() %>
directive, and can import Java classes using
the <%@ import %> directive.
Javascript statements can be added between the <%
and %>
punctuators, which are evaluated as Javascript but
added directly to the generated string.
Javascript expressions can be added either between the ${
and }
punctuators or the <%=
and %>
punctuators,
and are evaluated and added to the generated string.
Javascript templates can then be rendered from Java like so:
Java:
String str = SampleJSTemplate.renderToString({"Carson", "Kyle", "Lucca"});
System.out.println(str)
Javascript classes can be accessed using the same syntax as Java classes.
Java:
Foo foo = new Foo(10);
System.out.println(foo.bar()); // 20
System.out.println(foo.getBars()); // 5
foo.setBars(20);
System.out.println(foo.getBars()) // 40
System.out.println(Foo.hello()) // Hello
The (non-standard javascript) import statement is used to extend Java classes with javascript methods.
Here is some example javascript: hello.js
import java.util.ArrayList;
function hello() {
var arrlist = new ArrayList();
arrlist.add(1);
arrlist.add(2);
arrlist.add(3);
System.out.println(arrlist.toArray(new Integer[arrlist.size()]));
}
This can be invoked from Java like so:
hello.hello(); //prints [1,2,3]
NB: The import statement in Manifold.js acts like the java import statement, not the (unsupported) javascript version.
Java classes can be extended using javascript, allowing for the creation of modified classes. One known limitation is that the constructor of the superclass cannot be overwritten.
Javascript:
import java.util.ArrayList;
class SizePrints extends ArrayList {
printSize() {
System.out.println(super.size());
}
}
Java:
SizePrints demo = new sizePrints();
demo.add(1);
demo.add(2);
demo.add(3);
demo.printSize(); // Prints 3