Writing your own view using Web technologies
First of all - eclipse SWT is great for this. You can evoke javascript functions from java and vice versa. And since the introduction of java 8 this even result in a very clean code!
Preliminaries
Assuming you read ATHEN - correctly write your own view. This tutorial shows how to write the UI of your View using Web Technologies. So you should have your own project with a class the represents a RCP part. In this tutorial we rebuilt the UI of the coreference view using Web-technologies.
Goal
The goal is to create the CorefView, a view with the purpose to quickly annotate character references and their corresponding cluster-ID. The final result should look like this:
But of course we want to use Web technologies instead of SWT widgets. During this tutorial i am making use of the famous Bootstrap framework for styling your UI Elements.
How can you establish a communication between Java and HTML using SWT
Probably the most interesting question if you want to start writing your view in HTML
.
The communication works in both directions, let us look at it using an example. We create a subclass of Composite
and create our HTML view inside. This results in the following class
public class CorefViewHTMLComposite extends Composite {
private Browser browser;
private CorefView controller;
public CorefViewHTMLComposite(Composite parent, int style) {
super(parent, style);
}
Aside from the regular constructor you might notice the Browser
widget. This SWT widget allows us to "simply" invoke javascript functions and vice versa.
As in most my other tutorials about writing a view you should create a setInput()
method as shown here:
public void setInput(CorefView controller) {
this.controller = controller;
initLayout();
initEventHandler();
}
This saves a reference to our controller, creates the initial HTML layout and registers any listener. Well you might ask this is stil all plain java, where are the Web-technologies. They start to be important if we look into initLayout()
this.setLayout(new FillLayout());
browser = new Browser(this, SWT.NONE);
String page = createHTMLContent();
browser.setText(page);
This methods reads a .html
which contains the initial layout for our view and displays it. In this tutorial i am using this file:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Bootstrap Example</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet"
href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script
src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.0/jquery.min.js"></script>
<script
src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</head>
<body>
<!-- The corresponding entry to the buttoncomposite -->
<div class="container-fluid">
<!--first row-->
<div class="row bottom5">
<div class=col-xs-4>
<button type="button" class="btn btn-primary"
onclick="createEntity();">New Entity</button>
</div>
<div class=col-xs-4>
<button type="button" class="btn btn-primary"
onclick="entityAccepted();">Is Entity</button>
</div>
<div class=col-xs-4>
<button type="button" class="btn btn-primary"
onclick="deleteEntity();">Delete Entity</button>
</div>
</div>
<!--Second row-->
<div class="row bottom5">
<div class=col-xs-4>
<button type="button" class="btn btn-primary"
onclick="addRelation();">Add Relation</button>
</div>
<div class=col-xs-4>
<button type="button" class="btn btn-primary">Basic</button>
</div>
<div class=col-xs-4>
<button type="button" class="btn btn-primary"
onclick="deleteRelation();">Remove Relation</button>
</div>
</div>
<!-- Third row-->
<div class="row bottom5">
<div class=col-xs-4>
<button type="button" class="btn btn-primary"
onclick="preprocess();">Preprocess</button>
</div>
<div class=col-xs-4>
<button type="button" class="btn btn-primary"
onclick="deleteAllHandled();">Delete Handled</button>
</div>
<div class=col-xs-4>
<button type="button" class="btn btn-primary"
onclick="showSocialNetwork();">Show social network</button>
</div>
</div>
</div>
<!-- The corresponding entry to the CheckboxOptionComposite -->
<div class="container-fluid">
<div class="row bottom5">
<div class=col-xs-6>
<label class="checkbox-inline"><input type="checkbox"
value="">Draw just Core</label>
</div>
<div class=col-xs-6>
<label class="checkbox-inline"><input type="checkbox"
value="" checked>Jump to next?</label>
</div>
</div>
</div>
<!-- The corresponding entry to the TableComposite -->
<div class="container-fluid">
<div class="row bottom5">
<div class=col-xs-6>
<label class="checkbox-inline"><input type="checkbox"
value="">Use ID Filter</label>
</div>
<div class=col-xs-6>
<label class="checkbox-inline"><input type="checkbox"
value="" checked>Show only handled</label>
</div>
</div>
</div>
<div class="container-fluid">
<div class="row bottom5">
<div class=col-xs-2>
<label class="label-search">Search</label>
</div>
<div class=col-xs-10>
<input type="text" class="form-control" id="usr">
</div>
</div>
</div>
</body>
</html>
This renders to something like:
Which looks kinda like what we had in the first place, using SWT.
Styling using CSS and adding dynamic using Javascript
Because you are reading your view from a file which is located within a jar, once the application is built you cant just place your .css
or your .js
aside your page and hope it gets found, because it wont! What you need to do is illustrated in the following snippet:
private String createHTMLContent() {
String page = "";
String css = "";
String jscript ="";
URL resourceHTML = CorefViewHTMLComposite.class.getResource("corefView.html");
URL resourceCSS = CorefViewHTMLComposite.class.getResource("corefView.css");
URL resourceJSCRIPT = CorefViewHTMLComposite.class.getResource("corefView.js");
try {
page =IOUtils.toString(resourceHTML);
css =IOUtils.toString(resourceCSS);
jscript = IOUtils.toString(resourceJSCRIPT);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//use JSoup to add a style element
Document doc = Jsoup.parse(page);
Element head = doc.head();
Element styleElement = head.appendElement("style");
styleElement.text(css);
//also add a script element the same way
Element scriptElement = head.appendElement("script");
scriptElement.text(jscript);
return doc.toString();
}
This function reads the content of corefView.html
, corefView.css
and corefView.js
using an inputstream and IOUtils of apache.commons.io. Next, by using JSoup, a <script>
and a <style>
element is added to the header in order to integrate the styling and functions to your page.
Executing a Java function using Javascript
Executing a Java function from within the html or another Javascript function is actually really comfortable. For this purpose we will call the createNewEntity()
method of CorefView.java
once a click on the according button is performed. This linking can be seen here:
private void initEventHandler() {
//java function to create a new entity
//first row of buttons
new CorefViewParameterlessFunction(browser, "createEntity",controller::createNewEntity);
}
This snippet create an instance of a CorefViewParameterlessFunction
and passes the function that should get called, the browser widget and the name of the function as parameter. By just creating this function, we basicly ntegrate it into the known functions of the browser. We can now actually call this function from within our HTML:
<button type="button" class="btn btn-primary" onclick="createEntity();">New Entity</button>
This could obviously also happen in your separate javascript file. The mystery seems to be hidden in the implementation of the CorefViewParameterlessFunction
, which is shown below:
public class CorefViewParameterlessFunction extends BrowserFunction {
Browser browser = null;
String functionName = null;
Runnable runnable;
public CorefViewParameterlessFunction(Browser browser, String name,Runnable r) {
super(browser, name);
this.runnable = r;
this.functionName = name;
System.out.println(name);
}
public Object function(Object[] args) {
if (args.length != 0)
return null;
runnable.run();
return null;
}
}
This class extends Browserfunction
and the delegation to super()
registers our function to the browser. The function function(Object[] args)
gets called, whenever someone pushes the according button. Since we allowed this implementation to take a Runnable
as input (which is a procedure that has no param and no return value) we can just run it inside the method.
Note: If you need parameter you obviously need to change the Runnable into any other functional interface!
Executing Javascript from within Java
This is probably the direction you need less often, since the interactions in between the layout should be handled entirely using HTML and jscript and the controller usually only passes new input data which should get rendered into new html code. However we usually also have a method to refresh the input or other stuff. Since this is shown in two other tutorials and i currently dont have any uses im just gonna link to them:
- Javascript and Java I
- [Javascript and Java - Vogella] (http://blog.vogella.com/2009/12/21/javascript-swt/)