he recent Eclipse Europa release is the culmination of coordinated efforts between a number of Eclipse subprojects. Its major components, such as the Java Development Tools (JDT) and Web Tools Platform (WTP), garner a majority of the interest, but many other Europa projects are worth a try. This article introduces just such an under-the-radar project: Eclipse Monkey, a tool for creating, using, and sharing interpreted scripts (in the JavaScript programming language) that interact with the Eclipse platform.
Don't confuse Eclipse Monkey with the Eclipse Dynamic Languages Toolkit (DLTK) project. The former integrates user-created JavaScript that enrich platform capabilities; the latter extends the set of programming languages supported by the platform with new editors, debuggers, and other development tools.
Eclipse Monkey relies on the Mozilla Rhino JavaScript engine to evaluate scripts. This is the same engine that powers the Firefox web browser and ships with Java SE 6 as an implementation of the JSR-223 specifications for scripting engines (Rhino permits interoperation between scripts and the Java environment in which the scripts run.). So developers can leverage the JavaScript skills they gained from writing scripts either for the web or within Java SE 6 applications to create Monkey scripts.
Get Started
The first step in using Eclipse Monkey is installation. This is a straightforward process:
The Update Manager also installs the Mozilla Rhino JavaScript engine, on which Eclipse Monkey depends.
After the installation finishes, you are ready to test your first script:
/*
* Menu: Simple > Hello world
*/
function main() {
Packages.org.eclipse.jface.dialogs.MessageDialog.openInformation(
window.getShell(),
"Message",
"Hello world!");
}
On the menu bar, a new entry appears inside the Scripts menu. It points to your just created script. When you click it, a dialogs pops up with the classic hello world message.
Behind the Scenes
The "Hello World" script uses the Packages prefix to access the MessageDialog Java class available within the Eclipse platform and to invoke the static openInformation() method on it. This is the standard method that Eclipse components use to open a message dialog box. The Packages object is a Rhino-specific feature (as described in its official documentation) that provides the bridging capabilities between Java and JavaScript.
JavaScript is the only language that Eclipse Monkey supports out of the box, but you can contribute additional ones by using the org.eclipse.eclipsemonkey.language extension point and following the standard practice for creating additional components for the Eclipse platform. This extension point describes the interfaces to implement for integrating an additional language with Monkey.
However, keep in mind that Eclipse Monkey is not JSR-223 aware; you cannot plug in support for other scripting languages in the same way you do in a standard Java application based on Java SE 6. That is, you cannot provide new scripting engines by using the interfaces exposed in the javax.script packages. If you are interested in this kind of scripting support for Eclipse (e.g., plug-ins and extensions in interpreted languages such as Ruby), refer to my previous DevX article on the subject.
Scripts Metadata
Monkey allows you to associate metadata with your scripts. You place the metadata in the first multi-line comment block of the script (/* ... */), within which you can use various keywords to declare it. The "Hello World" script already showed one keyword: Menu, which defined where the script appears in the Eclipse menu. Other supported keywords include:
Document Object Models
There is a fundamental difference between the scripts you work with in JavaScript code and the ones you use within a web page. When working with JavaScript code, you do not have a Document Object Model (DOM) that describes the context the script belongs to (such as the web page itself in the case of scripts embedded in HTML code). You are reusing the language (JavaScript) but not the data structures (DOM) that are typical of the web development environment.
This implies that traversing object graphs and accessing the data structures exposed by the Eclipse platform (such as the JDT compiler, the workspace resources, or the user preferences) can be quite cumbersome. You have to explicitly navigate through the multitude of interfaces, factories, and providers exposed by the Eclipse APIs.
To overcome this difficulty, Eclipse Monkey provides a standard way to expose DOMs called a Monkey DOM, an Eclipse plug-in that extends the org.eclipse.eclipsemonkey.dom extension point. This extension requires two elements:
The IMonkeyDOMFactory interface defines only one method: public Object getDOMroot(). Implementers must use this method to return the root DOM object. Scripts will then access the DOM and traverse it starting from the root object, which is accessible via the named variable declared in the extension point.
A script can import a DOM by using the DOM metadata keyword and declaring the DOM in the form http://<update-site>/<dom-plugin-ID>. The following code provides an example. (Later on you will learn why you need to provide information about update sites in your DOM declaration.)
/*
* Menu: Simple > My Simple Script
* DOM: http://my.update.site.com/update/com.devx.monkey.doms
*/
// script content omitted ...
Eclipse Monkey also provides some DOMs out of the box, such as ones that simplify access to the workspace and the resources subsystem.
For example, suppose you want to expose your scripts to JDT functions so that you can use JavaScript to inspect the compilation units and Java source files available in the workspace. First, you declare the plug-in that contributes the DOM. Here's the plugin.xml descriptor file:
<plugin>
<extension
point="org.eclipse.eclipsemonkey.dom">
<dom
class="com.devx.monkey.doms.JavaCoreDomFactory"
id="com.devx.monkey.doms.JavaCoreDomFactory"
variableName="javacore">
</dom>
</extension>
</plugin>
The plugin descriptor points to the class JavaCoreDomFactory (shown in the listing below), which exposes the org.eclipse.jdt.core.JavaCore shared instance as the document root for this DOM.
package com.devx.monkey.doms;
import org.eclipse.eclipsemonkey.dom.IMonkeyDOMFactory;
import org.eclipse.jdt.core.JavaCore;
public class JavaCoreDomFactory
implements IMonkeyDOMFactory {
public Object getDOMroot() {
return JavaCore.getJavaCore();
}
}
Your scripts can now access the Java model of the workspace using the javacore global variable declared in the plug-in descriptor file. Here's a sample script that enumerates the packages within a Java project:
/*
* Menu: Simple > Package enumeration
*
* This dom exposes the 'workspace' variable
* DOM: http://download.eclipse.org/technology/
dash/update/org.eclipse.eclipsemonkey.doms
*
* This dom exposes the 'javacore' variable
* DOM: http://my.update.site.com/update/com.devx.monkey.doms
*/
function main() {
projectName = "YourProjectName";
project = workspace.root.getProject(projectName);
srcFolder = project.getFolder("src");
srcroot = javacore.create(project).getPackageFragmentRoot(srcFolder);
if (!srcroot.isOpen()) srcroot.open(null);
pkgList = "" ;
for each (pkg in srcroot.getChildren()) {
pkgList += pkg.elementName + "\n" ;
}
alert("List of packages for " + projectName + "\n" + pkgList);
}
function alert(message) {
Packages.org.eclipse.jface.dialogs.MessageDialog.openInformation(
window.getShell(),
message)
}
Figure 1 presents the script output. Notice the presence of DOM metadata keywords to import the required object models into the script.
| Figure 1. The Script Output with the List of Packages Extracted from a Sample Project |
"Shareability"
One of the principal aspects of Eclipse Monkey involves the ability to share scripts among developers. The goal is to facilitate and simplify the diffusion of scripts that developers can acquire from the web and integrate into their workspace in seconds.
Eclipse Monkey offers two mechanisms to achieve this. First, you can right click on a script and choose "Copy for publication" to copy the script to the clipboard in a format that you can publish to a variety of destinations (e-mails, blogs, bugzilla entries, etc.). Eclipse Monkey will handle the conversions required by the selected destination, such as converting the script into valid HTML content if you are targeting a web page or blog entry. In the same way, Eclipse Monkey can extract the contents of your clipboard (where you may have copied a web page that contains a script published by another developer) and convert them back into scripts.
Secondly, Eclipse Monkey automatically downloads DOMs from the web if you still don't have them on your local machine. By analyzing information from the DOM metadata within the script, Eclipse Monkey can extract the update site the DOM comes from and fire up the Eclipse Update Manager to download it.
Use Case: Counting Lines of Code (LOCs)
Consider an example: creating a script that extracts some metrics from the projects that are in your Eclipse workspace and then displays them in a nice graphical way. The script will count the lines of code (LOCs) in one of your project's source files and display them in a pie chart grouped by package. (Find the whole example in the source code attached to this article.)
To analyze source files, the script reuses the javacore DOM described previously. To create the charts, it uses a custom DOM that wraps the open source JFreeChart library (other charting libraries should work fine too). The DOM is exposed with the jfreechart script variable and offers methods to create a new dataset for pie charts and show charts on the screen. Here's the complete script where these methods are invoked:
/*
* Menu: SLOC > SLOC by Package
* DOM: http://download.eclipse.org/technology/
dash/update/org.eclipse.eclipsemonkey.doms
* DOM: http://my.update.site.com/update/com.devx.monkey.doms
*/
var text = "" ;
var i = 0 ;
function main() {
project = workspace.root.getProject("com.devx.monkey.doms");
srcFolder = project.getFolder("src");
srcroot = javacore.create(project).getPackageFragmentRoot(srcFolder);
p = jfreechart.dataSet.newPie();
// frg iterates over the packages of the project
for each (frg in openIfNeeded(srcroot).getChildren()) {
count = 0;
// cu iterates over the compilation units (source files)
for each( cu in openIfNeeded(frg).getCompilationUnits()){
source = cu.openable.buffer.contents;
sr = new Packages.java.io.StringReader(source);
lr = new Packages.java.io.LineNumberReader(sr);
while ( lr.readLine() != null) {}
count += lr.getLineNumber() ;
lr.close();
}
if (count != 0) {
// store the value into the chart
p.setValue(frg.elementName + "(" + count + ")",count);
}
}
chart = jfreechart.createPieChart3D("SLOC Chart",p);
jfreechart.show(chart);
}
function openIfNeeded(o) {
if (!o.isOpen())
o.open(null);
return o;
}
The calls to the methods jfreechart.dataset.newPie() and jfreechart.createPieChart3D() map to objects exposed through the DOM. The objects ultimately belong to the JFreeChart library, as shown here:
public JFreeChart createPieChart3D(String title, PieDataset dataset) {
JFreeChart chart =
ChartFactory.createPieChart3D(
title, dataset, false, false, false);
return chart;
}
The method jfreechart.show(), invoked at the end of the script, triggers the update() method of a custom Eclipse View that displays the chart. The following listing contains the relevant parts of the View source code:
public class ChartView extends ViewPart implements Observer {
// ChartComposite is a SWT widget to display charts
// provided by JFreechart
private ChartComposite chartComposite ;
private Composite parent;
private Label l;
@Override
public void createPartControl(Composite parent) {
this.parent = parent ;
l = new Label(parent, SWT.NONE);
l.setText("No chart to display");
}
public void update(Observable observable, Object obj) {
final JFreeChart chart = (JFreeChart) obj ;
if (chartComposite == null) {
l.dispose();
chartComposite = new ChartComposite(parent, SWT.NONE);
chartComposite.setLayoutData(
new GridData(GridData.FILL_BOTH));
}
chartComposite.setChart(chart);
chartComposite.redraw();
parent.layout();
}
// other non-relevant parts omitted...
}
Figure 2 shows the final result of the example.
| Figure 2. This Chart Counts Lines of Code Shown on the Workbench |
Extend Eclipse with Your Own Scripts
With Eclipse Monkey, you can integrate interpreted scripts into Eclipse and leverage the benefits of an interpreted programming language to contribute components to the platform. By using a try-test-retry approach in developing new tools for your workspace, you avoid the complexities of Eclipse plug-in development as well as long debugging sessions. This is especially useful for quick and dirty tools like the ones shown in the article's examples. In addition, Eclipse Monkey allows you to extend the base Eclipse offering with additional DOMs and distribute your scripts without any effort from other colleagues and developers.
| DevX is a division of Internet.com. © Copyright 2010 Internet.com. All Rights Reserved. Legal Notices |