When an IContextFunction does not solve your problem – and the solution with e(fx)clipse 2.2.0

If you haven’t read my last blog post where I described the problem of letting the DI container inject an instance of ModelElementFactory directly you should do so before proceeding because it will help you understand the problem the following new concept introduced solves.

Our problem can be summerized as: To create an instance of ModelElementFactory we need information from the IEclipseContext (eg the EModelService) and the component instance the value is created for.

We can satisfy the first requirement through an IContextFunction who calculates its value on demand when first requested in the context but:

  1. an IContextFunction calculates the value ONLY once per context (technically this is not true because if you reparent an IEclipseContext the value is recalculated)
  2. because of 1. it does not provide information for which component (object type) the value is created

We also can not use @Creatable – which by the way was the worst idea ever in the history of Eclipse DI – because the type seen by the injector is only the interface (ModelElementFactory) and not the concrete type.

So what can be done?

At first we need something that provides the information what real type we should create an instance for a given interface and guess what there’s a service API in org.eclipse.fx.core named TypeTypeProviderService so if we want to teach the framework that it should create an instance of ModelElementFactoryImpl if someone request ModelElementFactory we’d register an OSGi-Service like this:

class ModelElementFactoryImpl implements ModelElementFactory {
  @Inject
  public ModelElementFactoryImpl(EModelService modelService, @Named(TypeTypeProviderService.DI_KEY) Class<?> ownerType) {
    // ...
  }
}

@Component
class ModelElementFactoryTypeProvider implements TypeTypeProviderService<ModelElementFactory> {
  public boolean test(Class<?> clazz) {
    return clazz == ModelElementFactory.class;
  }

  public Class<? extends ModelElementFactory> getType(Class<?> clazz) {
    return ModelElementFactoryImpl.class;
  }
}

and for the consumer we need a new annotation who informs the DI-Container to search a bit harder for the value to inject

class Component {
  @Inject
  public Component(@LocalInstance ModelElementFactory factory) {
    // ...
  }
}

Eclipse 4 Application Platform – contributorURI vs contributionURI

All Eclipse 4 applications (from the Eclipse 4.x IDE to your simple e4 RCP) leverage the Eclipse 4 application model who is a representation of your whole application in a well defined and typesafe EMF-Model (you can somehow compare it to your browsers DOM).

The model itself is not static but a so called live model and you can interact with it through your preferred VM-Language eg to add new elements. One of the things done most often is to programmatically add MPart instances to represent your UI-Component.

The code below is taken from the e(fx)clipse code editor component library

public static class DefaultEditorOpener implements EditorOpener {
  @Inject
  EModelService modelService;

  @Inject
  @Service
  List<FileIconProvider> fileIconProvider;

  @Override
  public boolean openEditor(String uri) {
    // .....
    MPart part = modelService.createModelElement(MPart.class);
    part.setCloseable(true);
    part.setLabel(URI.create(uri).lastSegment());
    part.setContributionURI("bundleclass://org.eclipse.fx.code.editor.fx/org.eclipse.fx.code.editor.fx.TextEditor");
    part.setContributorURI("platform:/plugin/org.eclipse.fx.code.editor.fx.e4");

    String iconUri = fileIconProvider
      .stream()
      .filter( f -> f.test(uri))
      .findFirst()
      .map( f -> f.getFileIconUri(uri))
      .orElse("platform:/plugin/org.eclipse.fx.code.editor.fx.e4/icons/file_16.png");
    part.setIconURI(iconUri);
    part.getPersistedState().put(Constants.DOCUMENT_URL, uri);
    part.getTags().add(EPartService.REMOVE_ON_HIDE_TAG);
    // .....
  }
}

While most of the code is straight forward most developers stumble upon the 2 very similar MPart attributes:

  • contributionURI: URI to identify the class that is responsible to create the MParts UI
  • contributorURI: URI to identify the bundle who create the model element

Even worse most developers only use contributionURI and never set the contributorURI value which might lead to problems for example if the value is to be translated because the information in the contributorURI is used to retrieve the ResourceBundle through the BundleLocalization-Service.

Effeciently dealing with @Translations in an e4 app with the help of Java8

In my JavaOne application i showed how one can flip the language of a running e4+JavaFX application on the fly using the new support available from the e4 framework using the @Translation-Annotation.

Generally speaking the advised way of doing translations is like this in case you don’t need to support language fliping on the fly:

  • Create a Messages-Java-Class like this
    public class Messages {
      public String MyLabel;
    }
    
  • Get access to the translations like this:

    public class MyUI {
      @Inject
      @Translation
      Messages messages;
    
      @PostConstruct
      void init(BorderPane parent) {
        Label l = new Label();
        l.setText(messages.MyLabel);
      }
    }
    

In case you want your application to support dynamic language flipping (take care a language flip does not only affect labels, you also need to update formats, …) the situation gets a bit more complex.

public class MyUI {
  private Messages messages;

  private Label l;

  @Inject
  public void applyMessages(@Translation Messages messages) {
    this.messages = messages;
    if( l != null ) {
      l.setText(messages.MyLabel);
    }
  }

  @PostConstruct
  void init(BorderPane parent) {
    l = new Label();

    applyMessages(messages);
  }
}

If you have many labels fields you see that you create a big amount of fields you remember because of the language switching.

If you start analyzing the situation you realize that you only remember the Label instance to call the setText(String) method or more generically speaking something that follows the Java8 interface Consumer<String> so you could write in Java8

public class MyUI {
  private Messages messages;

  private Consumer<String> text;

  @Inject
  public void applyMessages(@Translation Messages messages) {
    this.messages = messages;
    if( text != null ) {
      text.accept(messages.MyLabel);
    }
  }

  @PostConstruct
  void init(BorderPane parent) {
    Label l = new Label();
    text = l::setText

    applyMessages(messages);
  }
}

The situation for the String extraction fairly similar instead of directly access it you could abstract this using a Supplier<String> from Java8.

public class MyUI {
  private Messages messages;

  private Consumer<String> text;
  private Supplier<String> textProvider;

  @Inject
  public void applyMessages(@Translation Messages messages) {
    this.messages = messages;
    if( text != null ) {
      text.accept(textProvider.get());
    }
  }

  @PostConstruct
  void init(BorderPane parent) {
    Label l = new Label();
    text = l::setText
    textProvider = () -> messages.MyLabel;

    applyMessages(messages);
  }
}

You might say: Well now you have 2 fields instead of 1 so what’s the point? The point is the applyMessages only knows about Consumer & Supplier, the method and fields access has been abstracted away and so we can make it generic.

public class MyUI {
  private Messages messages;

  private Map<Consumer<String>, Supplier<String>> translations = new HashMap<>();

  @Inject
  public void applyMessages(@Translation Messages messages) {
    this.messages = messages;
    for( Entry<Consumer<String>, Supplier<String>> e : translations.entrySet() ) {
      e.getKey().accept(e.getValue().get());
    }
  }

  private void register(Consumer<String> c, Supplier<String> s) {
    c.accept(s.get());
    translations.put(c,s);
  }

  @PostConstruct
  void init(BorderPane parent) {
    Label l = new Label();
    register(l::setText, () -> messages.MyLabel);
  }
}

This is already very good but writing this all time is also a bit tedious, right? That’s why e(fx)clipse core-runtime (which means even if you are using e4+SWT you can make use of it!) provides you a base class which uses exactly that pattern. So this is the final solution which remove all the translation flipping clutter from your view code.

  • Create a class named MessageRegistry like this:
    @Creatable
    public class MessageRegistry<Messages> extends org.eclipse.fx.core.di.AbstractMessageRegistry {
        @Inject
        public void updateMessages(@Translation Messages messages) {
            super.updateMessages(messages);
        }
     
        // Optional provide method to access current message
        public String MyLabel() {
            return getMessages().MyLabel;
        }
    }
    
  • Use it in your view like this:

    @Inject
    MessageRegistry r;
     
    @PostConstruct
    void init(BorderPane parent) {
        Label l = new Label();
     
        // if you opted to NOT add the MyLabel() method
        r.register(l::setText, (m) -> m.MyLabel); 
     
        // if you opted to have a MyLabel() method
        r.register(l::setText, r::MyLabel);
    }
    

Restarting an e4 application with the intial workbench.e4xmi

e4 applications by default remember the state when getting shutdown and restore that state on the next restart. This is the behavior most people expect from an application but there are situations people would like to reset the application into its original state or maybe the developer wants that e.g. after having installed/uninstalled certain new bundles.

Unfortunately the workbench currently does not provide a way restart and clear the current state. So I’ve implemented a small library you can make use of in your e4 applications.

There are 2 parts:

  1. a service called RestartService which has a method restart(boolean)
  2. a Lifecycle-Addon you can reuse or copy the code from min to yours

Useage is very simple e.g. in your handler:

import org.eclipse.e4.core.di.annotations.Execute;

import at.bestsolution.e4.extensions.core.services.RestartService;

public class RestartHandler {
  @Execute
  public void restart(RestartService service) {
    service.restart(true);
  }
}

and to enable the addon you make your plugin.xml look like this:

<?xml version="1.0" encoding="UTF-8"?>
<plugin>
  <extension
    id="product"
    point="org.eclipse.core.runtime.products">
    <product
      name="SampleProject"
      application="org.eclipse.e4.ui.workbench.swt.E4Application">
      <property
        name="applicationCSS"
        value="platform:/plugin/SampleProject/css/default.css">
      </property>
      <property
        name="lifeCycleURI"
        value="bundleclass://at.bestsolution.e4.extensions.lifecycle/at.bestsolution.e4.extensions.lifecycle.ExtensibleLifecycle">
      </property>
      <property
        name="appName"
        value="SampleProject">
      </property>
    </product>
  </extension>
</plugin>

If you want to make use of it you can find the p2-repo at http://downloads.foss.bestsolution.at/e4-extensions/nightly/ or if you prefer the source it is hosted at the github account of BestSolution.at at https://github.com/BestSolution-at/e4-extensions.

e(fx)clipse runtime recipes

This is just a short notice to all user of e(fx)clipse runtime bundles. e(fx)clipse runtime provides many cool features most people don’t know about yet (e.g. our DI support for FXML-Controllers, the adapter system, …). So I started collecting them in our wiki at https://wiki.eclipse.org/Efxclipse/Runtime/Recipes.

Some of them are not bound to JavaFX, like our logging support, the adapter system and the @ContextValue annotation. I’m sure there’s more. If you have a recipe how you solved a common problem with e(fx)clipse runtime feel free to contribute.

Introducing SWT on FX

In my last few presentations I’ve introduced what I named “SonF” which is short for SWT on FX.

What is it?

SWT on FX is an SWT implementation which instead of using the native widget it use JavaFX controls under the covers (similar to what SWT on Swing did some years ago).

What is the target?

The target is to get as compliant with SWT as possible, there are some parts of SWT that are hard get working right, e.g. spinning an event-loop is something you really only hack into the system, other parts like ownerdraw in tables/trees is maybe doable but is not yet explored. At the moment our target is to get on par with what RWT can do today.

What is the audience? Where is it positioned?

We think there are 3 potential users/usecases:

  • You want move away from SWT in future but have a big legacy code base which uses SWT
  • You want to publish your application through RAP
  • You don’t yet want to fully buy into JavaFX but SWT does not do the job for you anymore because of its limitations

We’d like to position it as a transformation/migration path for people who want move away slowly from SWT, it is not meant be an additional implementation so that people stick to it forever. SWT is quite done and does not introduce new concepts as it looks like.

What is the license? Is it EPL?

Sorry no it is not EPL? We have not yet defined the business model we’ll use for it. Parts of the code are under EPL because they are directly copied from upstream SWT (e.g. all the listener definitions, …) but most of the implementation have been implemented from scratch.

Where can I get it?

At the moment it is hosted in a private bitbucket-repository and we are not yet publishing binary builds. If you want to have a binary you could drop a mail to swtfx@bestsolution.at

Where do we go from here?

Like said above we don’t know where to go. In our opinion people will have to move away from SWT in future but they want to take their legacy code with them. On the other hand we also doubt that companies will pay us license fees for our SWT-FX-Port. A though problem, right! We could start a kick-starter project but would enough people support it? We highly doubt that as well. In case your company thinks this makes sense and wants to contribute substantial get in touch with us through swtfx@bestsolution.at.

Samples

Below you see screen shots UIs defined in SWT and rendered through SonF

SWT-Button

One of the most basic controls

button

SWT-Canvas

Having access to SWT-Canvas is important for many make controls like CTabFolder work

canvas

Combo

One of the a bit more complex controls but still fairly easy. It also shows that we have e.g. support for color chooser.

combo

Table

One of the more complex controls, currently without support for owner draw

table

Tree

Another more complex one is a tree. We don’t yet support TreeColumns but this is not hard to add in the days to come

tree

e4 on FX with SWT-Content

The outerpart is rendered through e4 on JavaFX from e(fx)clipse whereas the real UI is copied 1:1 from the SWT-e4-Addressbook example.

e4-swt

What IEclipseContext#declareModifiable is good for

There was a comment on IEclipseContext less code Rene asked how the publishing into parent contexts is done.

IEclipseContext provides 2 ways to publish information:

  • IEclipseContext#set
  • IEclipseContext#modify

I think 99% of the people are using IEclipseContext#set because they could make up what IEclipseContext#modify is good for.

The problem with IEclipseContext#set is that pushes the value in the context you have called it on, if you want to publish a value somewhere in the parent hiearchy you have to search the IEclipseContext hierarchy yourself == you need to know exactly how the thing working and in which parent-context the original creator of the expected the value to reside.

So you often see code like this:

@Inject
IEclipseContext context;

public void publish() {
   TableViewer v = ...;
   v.addSelectionChangeListener( ... { context.getParent().set(Person.class, p) } );
}

for e4 parts the context you are publishing to is the PerspectiveContext or if there are no perspectives the WindowContext. Let’s suppose you have perspectives and now your requirements change and the value has to be published to WindowContext.

To break up this tight coupling between the publisher and publishing target you can make use of IEclipseContext#modify. Instead of publishing the value into the context instance the method is called on it search the parent hierarchy for the matching slot.

To know in which slot it publishes one has to call declare the variable on the context using IEclipseContext#declareModifiable.

If you are in a e4 application you don’t have to make this call. Haven’t you ever wondered about MContext#variables: List<String>. Congrats you’ve just learned how to define variables in contexts created by the e4 framework – no matter if you are using SWT or JavaFX as a rendering technology.

This makes the original code look like:

@Inject
IEclipseContext context;

public void publish() {
   TableViewer v = ...;
   v.addSelectionChangeListener( ... { context.modify(Person.class, p) } );
}

or if you want to remove the IEclipseContext

@Inject
@ContextValue(contextKey="my.Person")
ContextBoundValue<Person> value;

public void publish() {
   TableViewer v = ...;
   v.addSelectionChangeListener( ... { value.publish(p) } );
}