Background
Although not being documented yet, making plugins for the biocep workbench is easy (well if you are familiar with Swing). This tutorial presents the making of a really simple plugin, although not using swing directly for the user interface but using jaxx to generate the appropriate verbose swing code. JAXX is a way to make swing user interfaces using XML tags to describe the user interface structure. This article will get you started on the concept of jaxx.
The application
The application we are demonstrating here is really simple and may not be useful beyond getting started at making other plugins for biocep. It will add a single view into the workbench allowing to retrieve data from yahoo finance using the function get.hist.quote
from the beginning of a year to today, and display the result in a graphical device. This will look something like that:
Structure of the plugin
A workbench plugin is more or less just any java class that takes an RGui
interface and does something with it. Here is how I've structured the plugin so that I can build and install using ant
.
$ tree . |-- build.properties |-- build.xml |-- descriptor.xml |-- lib | |-- dt.jar | |-- jaxx-runtime.jar | |-- jaxx-swing.jar | `-- jaxxc.jar `-- src `-- com `-- addictedtor `-- workbench `-- plugin `-- simple |-- SimplePlugin.jaxx `-- SimplePlugin.jaxxscript 7 directories, 10 files
build.properties
The build.properties
file contains some properties to describe to ant where the workbench is installed and where we should install the plugin. Here is how it looks on my system:
install.dir=/home/romain/RWorkbench/plugins biocep.dir=/opt/biocep biocep.jar=biocep.jar
The build.xml file
The build.xml is a standard ant build file with a set of targets to compile the plugin, create a zip for distribution and install the plugin in the installation directory (as indicated in the build.properties file). Let's take a look at the steps specifically involving jaxx. To compile jaxx files in ant, you need to define an additional ant task
24 <target name="defineAntTask"> 25 <taskdef name="jaxxc" classname="jaxx.JaxxcAntTask" 26 classpath="lib/jaxxc.jar"/> 27 </target>
and then use this task to compile the jaxx files in your source tree, here we are
compiling the SimplePlugin.jaxx
file. After that, classes
have been generated and you may compile the java files
using the standard javac
ant task.
31 <target name="compile" depends="clean,defineAntTask"> 32 <mkdir dir="build/classes"/> 33 <jaxxc srcdir="src" keepJavaFiles='yes' 34 destdir="build/classes" classpath="${full.biocep.jar}" /> 35 <javac srcdir="src" destdir="build/classes" source="1.5" target="1.5" > 36 <classpath refid="simple.class.path"/> 37 </javac> 38 </target>
Finally, we can make the jar file. I usually prefer one jar file per biocep plugin, so I unjar the content of the jaxx runtime classes to jar it back into a single jar file
42 <target name="build" depends="compile"> 43 <mkdir dir="build/lib"/> 44 <unjar src="lib/jaxx-runtime.jar" dest="build/classes" /> 45 <jar jarfile="build/lib/simple.jar"> 46 <fileset dir="build/classes" /> 47 <fileset dir="src"> 48 <include name="*.xml" /> 49 <include name="**/*.props" /> 50 <include name="**/*.properties" /> 51 <include name="**/*.html" /> 52 <include name="**/*.gif" /> 53 <include name="**/*.png" /> 54 </fileset> 55 </jar> 56 </target>
The descriptor.xml file
The descriptor.xml
file is used by the
workbench to load the plugin, it identifies the
plugin main class
1 <plugin> 2 <view name="Simple Plugin" 3 class="com.addictedtor.workbench.plugin.simple.SimplePlugin" /> 4 </plugin>
We are pointing the workbench to the class com.addictedtor.workbench.plugin.simple.SimplePlugin
and the workbench will instanciate one object of the
class using the constructor that takes an RGui
interface, through which we will communicate with R.
The SimplePlugin.* files
The SimplePlugin.jaxx
file contains the description
of the user interface using XML and the SimplePlugin.jaxxscript
file contains java code to implement some of the logic of
the application
If you are already familiar with Swing, it does not take too much effort to grab what is going on with this jaxx file
1 <JPanel layout="{new BorderLayout()}"> 2 3 <script source="SimplePlugin.jaxxscript" /> 4 5 <JPanel constraints="BorderLayout.NORTH" id="toolbar"> 6 <JComboBox id ="instruments" > 7 <item value='{null}' 8 label='Select an instrument'/> 9 <item value='Nasdaq' /> 10 <item value='Dow' /> 11 <item value='SP 500' /> 12 <item value='CAC 40' /> 13 <item value='FTSE 100' /> 14 <item value='DAX' /> 15 </JComboBox> 16 <JLabel text="start year :" /> 17 <JTextField id="startyear" text="2003" 18 onActionPerformed='go()' /> 19 <JButton id="submit" text="go" 20 onActionPerformed='go()' /> 21 22 </JPanel> 23 24 <JScrollPane constraints="BorderLayout.CENTER"> 25 <org.kchine.r.workbench.views.PDFPanel id = "pdf" /> 26 </JScrollPane> 27 28 <JPanel constraints="BorderLayout.SOUTH" id="statusbar"> 29 <JLabel id="info" text = " " /> 30 </JPanel> 31 32 </JPanel> 33
The SimplePlugin.jaxx
file complements the jaxx code
by implementing a set of java methods, including the constructor
for the class which needs to take an RGui
interface
as its only parameter
8 public SimplePlugin( RGui rgui){ 9 this.rgui = rgui ; 10 initMap( ) ; 11 loadRPackage( "tseries" ); 12 } 13
... a simple utility method to load an R package. We can see
here of of the main design decision about the RGui interface, the instance
of R that is running on the background may only do one
thing at a time, which is why when you want to do something with it, you
need to lock
it, do whatever, and then unlock
it
14 public void loadRPackage( String pack ){ 15 try{ 16 rgui.getRLock().lock() ; 17 rgui.getR().evaluate( "require( 'tseries' )" ) ; 18 } catch(Exception e) { 19 JOptionPane.showMessageDialog(this, 20 "Please install the tseries package"); 21 } finally{ 22 rgui.getRLock().unlock() ; 23 } 24 }
Finally, the go
function does the actual work of
retrieving data from yahoo about the chosen instrument since the
start of the chosen year, and then plot the result in the PDF panel
36 public void go( ){ 37 Object instrument = instruments.getSelectedItem() ; 38 if( instrument == null ) { 39 JOptionPane.showMessageDialog(this, "Please select an instrument"); 40 return ; 41 } 42 43 int start = 2003 ; 44 try{ 45 start = Integer.parseInt( startyear.getText( ) ) ; 46 } catch( Exception e ){ 47 JOptionPane.showMessageDialog(this, "Invalid start year"); 48 return ; 49 } 50 String ins = (String)map.get(instrument) ; 51 String cmd = 52 "x = get.hist.quote(instrument = '^" + ins + 53 "', start = '" + 54 start + "-01-01', quote = 'Close' )" ; 55 String plot = 56 "plot( x, ylab = '"+ instrument + "' )" ; 57 try{ 58 rgui.getRLock().lock( ) ; 59 rgui.getR().evaluate(cmd) ; 60 pdf.setPDFContent( rgui.getR().getPdf( plot , 800, 400) ); 61 } catch(Exception e){ 62 e.printStackTrace( ) ; 63 } finally{ 64 rgui.getRLock().unlock( ) ; 65 } 66 67 } 68
Perspectives
Extend JAXX to make it more R-friendly
As opposed to other XML based user interfaces in java, Jaxx is not only restricted to swing tags and any other class might be included in a jaxx tree. Moreover, we can extend jaxx to define how to interpret a given tag, so with a bit of work we could embed R code in a gentle way into jaxx files, I am thinking something like that :
1 <JButton> 2 <action language="R"> 3 cat( "hello world" ) 4 </action> 5 </JButton> 6
What about rgg
There also is the rgg
package which has similar ideas except as far as I can see the
nesting is done the other way, XML tags are
embedded in R code within an rgg
file.
1 <rgg> 2 file = <filechooser label="CSV" description="Csv Dateien" 3 extensions="csv" fileselection-mode="files-only"/> 4 5 myIris = read.table(file, header=<checkbox label="header" span="2"/>, 6 sep=<combobox items="\t,;" label="Seperator" selected="T"/>) 7 summary(myIris) 8 </rgg> 9
the XML is processed and the script is
transformed into an R script. One of the advantages of
rgg though is that it defines a set of R related tags such as
<matrix>
Files
Here is the source of the pluginplugin and the simple.zip which you can simply unzip into yourRWorkbench/plugins
directory.