Warning! if you have no experience building plug-ins, this post will probably mean very little to you.
A few days ago, I was working with Anton, one of my Google Summer of Code students, trying to figure out how one might implement Eclipse plug-ins using scripts. More specifically, we were trying to sort out how rather than specifying an implementation class, you could specify a JRuby or Groovy script. We started with the obvious: generate a Java class that locates and executes the contents of the script. Fortunately, we both agreed that while generating code makes sense in some circumstances, it would be yucky and clumsy in this case.
Then we had the idea that it’d be cool if, when you specify an implementation class in an extension, you could somehow parameterize it. A quick review of ConfigurationElement#createExecutableExtension()
revealed that you can indeed do just that. When you specify a class that implements IExecutableExtension
, you can also tack on a colon followed by a value. Just after the instance of the class is created, it will be handed the value via the setInitializationData()
method. Very cool, this allowed us to create facades for each of the types required by the extension-points. These facades use the parameter to find the actual script to execute.
So now, we can create extensions like this:
<extension point="org.eclipse.ui.views"> <view class="org.eclipse.soc.yummy.facade.views.ScriptView:view.groovy" id="org.eclipse.soc.yummy.views.ScriptView" name="Script View"> </view> </extension>
With this, the ScriptView
facade (which need only be implemented once) uses the script named “view.groovy” to provide actual behavior.
But Anton wasn’t satisfied with that. Providing a facade for each type required by every extension point is just not possible. So we set about trying to make it more dynamic. We explored the idea of using java.lang.reflect.Proxy
to dynamically create facades. This is a great idea, except that you need to know the names of the interfaces you want the proxy to implement. No problem, you can just add that to the value tacked on behind class name.
The real problem is that the class you specify has to be a real class that createExecutableExtension()
can instantiate. It occurred to us that the solution of the problem was to somehow recognise that the class specified is actually a factory and then use the factory to create the proxy stand-in. So we set about figuring out how we were going to change the platform code to make this work. The prospect of changing platform code to make this work was exciting. Unfortunately, after about five minutes, our excitement was dashed: we discovered that it’s already implemented (those darned platform developers keep stealing my good ideas before I even have them).
So, we can now implement plug-ins using scripts with extensions defined along the lines of:
<extension point="org.eclipse.ui.views"> <view class="org.eclipse.soc.yummy.core.ScriptExtensionProxy:org.eclipse.ui.IViewPart/view.groovy" id="org.eclipse.soc.yummy.views.DemoView" name="Demo View"> </view> </extension>
Where ScriptExtensionProxy
implements both IExecutableExtension
and IExecutableExtensionFactory
. When this extension is created (via the createExecutableExtension()
method) it creates an instance of ScriptExtensionProxy
, sets the initialization data (“org.eclipse.ui.IViewPart/view.groovy”) and calls the create()
method which builds an instance of Proxy
that acts as an implementor of IViewPart
, using the code in the script file “view.groovy” to implement a view.
How cool is that?