Wednesday, January 16, 2013

Decorating with EMF

I recently added something totally cool to EMF.  It's so cool, you might want to use it even if you don't use EMF for anything else.  You're probably aware that JFace provides tool tip support for column viewers and of course, as a user of JDT, you've likely made extensive use of the Java editor's Javadoc hovers which display HTML-based information that even allows hyperlink navigation.  It's effectively a mini browser.  With the new feature I added, you can easily display HMTL-based hover information in any column viewer. You can see that in action with the sample I created; you'll need the latest build.  It's an RCP application that looks like this:

It displays a simple hand-written file system "model" where the files have links to other files. The hovers allow you to navigate those links within the tree. Note that EMF ships with many fine-grained features, so you need only depend on EMF's Common UI feature to exploit this, i.e., just two very small plug-ins, not the whole EMF runtime.

I used this basic feature to implement improved validation support. You can try this out by enabling Sample Ecore Editor → Live Validation.  With that enabled, whenever you change the model, the tree view will be decorated with validation results.  For example, when you create a new EClass, it won't have a name, and that's invalid.  The hover displays that as follows:

Note that the validation decorations propagate up the tree. The hovers at these higher levels display a composed result which contains links to allow you to navigate directly to any child object with a problem.

Also, the properties view itself is decorated, so a feature with a problematic value will be called out and selecting it provides details in the status line.

Pretty cool hey? And now for the best part: this has all been generated.  So if you'd like to see this type of thing in your own model, just set the GenModel's Editor Decoration property to Live, regenerate the editor, and enjoy the results. Have a look at the underlying infrastructure and let your imagination run wild with creative ideas of your own.

I'd like to thank itemis, the generous sponsor of cool things, for funding this year's "Christmas tree decoration" effort. The benefits will definitely outlast your decorated Christmas tree.

3 comments:

  1. Hi Ed,

    First of all, thanks for this great feature. Unfortunately I've found a bug in it which results in a ConcurrentModificationException when unchecking Live Validation. Here's the offending stack trace:
    BasicNotifierImpl$EAdapterList<E>.add(E) line: 192
    ResourceItemProviderAdapterFactory(AdapterFactoryImpl).associate(Adapter, Notifier) line: 150
    ResourceItemProviderAdapterFactory.associate(Adapter, Notifier) line: 225
    ResourceItemProviderAdapterFactory(AdapterFactoryImpl).adaptNew(Notifier, Object) line: 102
    ResourceItemProviderAdapterFactory(AdapterFactoryImpl).adapt(Notifier, Object) line: 87
    ResourceItemProviderAdapterFactory.adapt(Notifier, Object) line: 161
    ComposedAdapterFactory.adapt(Notifier, Object, Collection<Object>, Class<?>, boolean) line: 361
    ComposedAdapterFactory.adapt(Notifier, Object, Collection<Object>, Class<?>, boolean) line: 377
    ComposedAdapterFactory.adapt(Notifier, Object, Collection<Object>, Class<?>, boolean) line: 370
    ComposedAdapterFactory.adapt(Notifier, Object, Collection<Object>, Class<?>) line: 342
    ComposedAdapterFactory.adapt(Notifier, Object, boolean) line: 334
    ComposedAdapterFactory.adapt(Notifier, Object) line: 271
    ComposedAdapterFactory.adapt(Object, Object) line: 258
    AdapterFactoryEditingDomain.getParent(Object) line: 622
    AdapterFactoryEditingDomain.getRoot(Object) line: 633
    AdapterFactoryEditingDomain.getWrapper(Object, EditingDomain) line: 688
    AdapterFactoryEditingDomain.getWrapper(Object) line: 674
    EditUIMarkerHelper.getTargetObjects(Object, IMarker) line: 195
    EditUIMarkerHelper.getMarkerDiagnostics(Object, IFile) line: 287
    DiagnosticDecorator$DiagnosticDecoratorAdapter.refreshResourceDiagnostics(List<Resource>) line: 779
    DiagnosticDecorator$LiveValidator$LiveValidationAction.run() line: 351

    At this point it tries to add an adapter (ResourceSetItemProvider) for a ResourceSet object, while it is already iterating over the list of adapters in "for (Adapter adapter : resourceSet.eAdapters())" (see DiagnosticDecorator$LiveValidator$LiveValidationAction.run() line: 346). You should create a defensive copy of the list of adapters before iterating over them.

    I was able to workaround this bug by calling editingDomain.getParent(editingDomain.getResourceSet()), so that ResourceSetItemProvider adapter will be forcibly added, before LiveValidationAction is invoked.

    I'm using EMF 2.9.0.

    Best regards,
    Maciek

    ReplyDelete