Tuesday, May 20, 2008

A radical approach to explore new paths for e4

After EclipseCon 2008 and the E4 noise I started to rewrite the prototype shown there to use an EMF-Model (I'm going to refer to this prototype in the rest of this posting as PROTOTYPE_1).

It was quite cool to get insights into the platform code and with the code already created for EclipseCon it was not really an achievement to get something running within a fairly short time but finally I wasn't really happy because of multiple things:

  • I had to work around problems from the beginning to get a workbench up and running

  • It was hard to add new features because I was limited to things I could find a work-around for or going to learn the complete platform code which would have driven me mad

  • There was lack of feedback on the real code and we talked more about EMF pros and cons than concentrating on how we want to solve things (I'm still an EMF believer :-)


I asked myself how to solve the following problems:

  • How can I get up an Workbench-Window without any of the legacy code

  • How can I easily add features

  • How can I encourage people to work on E4 without having a deep understanding what's going on inside the current platform. In fact how can I get people outside the Platform-UI-Team to look at the code and understand in an affordable amount of time the concepts behind the workbench and bring up interesting ideas or outline how they think a feature can be implemented
Approximately 1 month ago I sat down and started to define a project which had the following targets:

  • A workbench with dependency on org.eclipse.jface, org.eclipse.equinox.common, org.eclipse.osgi, org.eclipse.emf

  • Built around an extensible EMF-Model

  • NO plugin.xml and NO .exsd (you'll see later how I extend the platform - it's a radical approach I know and looking back it got even more radical then I first thought it's going to be)

  • Similar but slightly improved EMF-Model compared the original one used to straight port the EclipseCon-Example

  • Allow multiple instances of the workbench inside one OSGi-Env - no singletons, no static variables!

  • As few API-Methods as possible (=suppress all the EMF-Methods and adding API-Methods e.g. to attach listeners or traverse the DOM generically) but still strongly typed

  • provide support for Scripting

Step 1: Redesign The Model From PROTOTYPE_1


I started to redesign the original model and removed some things I didn't like or found they are not needed yet. For example styles/styleclasses now work like they do in a Browser:
.myActiveView {
color: #FF0000;
background-color: #0000FF;
}

<span class="myActiveView" >Green on Blue</span>
where the style-properties and the class-properties are merged. I also moved all UI-Data (Font,Color,Gradient,...) from the UI-Element to the "css"-style definition whether an UI-Element reacts on a style property is the choice of the UI-Element and its implementor.

Step 2: Implementing the Workbench-UI-Core From Scratch

With the .ecore-Definition from Step 1 I created a static workbench.xmi file which defines a model of a static workbench.(Yes I started from a static model you'll see how dynamic such a model gets later). The final source code is ~ 88KB and the resulting .jar 36KB (the model, emf-dependencies are not part of this figures of course). But I think the target is reached I think there's no class having more than 500 Lines of Code (including the comments).
Creating .xmi-Files is a fairly trivial task because EMF comes with a generic editor and I guess people who know GMF could have written a graphical one with in a minute (any GMF-volunteers around?). When this step was finished my XMI-File looked like this

Step 3: Making the model dynamic


When starting Step 3 I first headed of and created .exsd-Files (you can see them here) to contribute the information to my model (views/perspectives). But while doing this I recognized that I'm redefining my .ecore-Model-Elements using .exsd so why the hell do I not provide plugin.xmi-artefacts instead of those plugin.xml files and at runtime create a complete workbench.xmi from all those artefacts. The hard part was to reimplement the loading of xmi-File (or rather to identify the source-code where the plugin.xml is processed to copy the logic). This left me with 3 files
  • workbench.xmi in org.eclipse.e4.workbench.ui

  • plugin.xmi in org.eclipse.e4.workbench.ui.ide contributing to workbench.xmi

  • plugin.xmi in org.eclipse.e4.workbench.ui.rhino contributing to plugin.xmi from org.eclipse.e4.workbench.ui.ide

Step 4: Implementing a Scritable-DOM


So now I had a workbench-model constructed at runtime using XMI-Artefacts. Doing some cool UI-Stuff was the next on my list. Wraping up an EObject as an Object-Scritable for Rhino was something I had already written for PROTOTYPE_1 so I "stole" the code from myself. The top feature on my list was to move a View (in 3.3 a ViewPart) in the model and automatically update the UI. So I wrote a view which presented the current workbench model inside a TreeViewer and allowed me to drag a view from stack to stack. It wasn't really is to distinguish a move from a remove but with help from Ed I managed to get it working. See the screen cast at the end of this posting.

Step 5: Persisting the current workbench state


So I was able to drag around the views but everytime I shut down the workbench and restarted model was recreated from the artifacts and started in the initial state. So adding persistance was next the next "big" issue. Well there's nothing more to say than these lines of code:

if (!restore) {
uri = URI.createPlatformPluginURI(
"/org.eclipse.e4.workbench.ui/META-INF/EWorkbench.xmi",true);
} else {
uri = URI.createFileURI(restoreFile);
}

// Save
try {
((EObject)workbench).eResource().save(null);
} catch (IOException e) {
e.printStackTrace();
}

Step 6: Extending the extension


Haven't you ever dreamed of extending an extension. I decided that my WorkbenchStructureViewPart should restore the current selection when coming up from a restored state but my generic model-element I contribute didn't had a slot to restore the information. So I digged into and searched how EMF allows me to extend an existing element (in fact once more Ed pointed me in the right direction). Now the plugin.xmi contributed by org.eclipse.e4.workbench.ui.rhino looks like this.

Step 7: Multiple Instances of the workbench


Not using org.eclipse.ui, no singletons and static variables automatically allows to have multiple workbench instances running. So in theory making this workbench run on top of the RAP framework should be possible without patching any code parts.

Acknowledgement / A Screencast / How to get it run


Before I forget about it two other guys (Boris and Ed) provided ideas, code and input to those freaking lines of code available from Eclipse-CVS using this ProjectSet.

Finally I created a Screencast for you to look at (it's a bit big (15MB) because I have no idea how to do Screencasting with OSS-Software on OS-X).



You'll see that I radically stripped down everything and made some strange decisions (e.g. no plugin.xml), backwards compatibility is not addressed at all, ... . Whether you like the idea of contributing XMI-Artefacts instead of .exsd & plugin.xml is not the question. The interesting thing IMHO is that it takes so few lines of code to show a nice workbench backed up by a model and reacting on (structural-)changes inside of it.