Thursday, February 9, 2017

GEF4 + 1 = GEF 5

It's been roughly eight months since we graduated GEF4 1.0.0 as part of the GEF 4.0.0 (Neon) release, and we have since not kept still. As its always hard to get a debut right, we first concentrated on some bug fixes and shipped GEF4 1.1.0 as part of the GEF 4.1.0 (Neon.1) bugfix release in September. Since then, we have concentrated on the Oxygen stream.

In order to emphasize that through its graduation, the GEF4 new code base has now officially superseded the former production components, we thought it important to adopt our wording accordingly. That is, we now refer to the original code base as GEF-Legacy, while we use GEF to refer to the new code base alone.


We reflected this when migrating our org.eclipse.gef and org.eclipse.gef4 Git repositories to respective GEF-Legacy and GEF GitHub repositories, and we further adopted the namespace of all GEF(4) bundles from org.eclipse.gef4.* to org.eclipse.gef.* (the namespace of the former production components was preserved, and as there is no overlap, they can still be installed in parallel). The Oxygen release is accordingly referred to as the GEF 5.0.0 (Oxygen) release: GEF4 + 1 = GEF 5.

While the renaming alone would already have enforced a major release because of semantic versioning constraints, we also had the strong intention to do more than mere 'cosmetics'. Let me point out just two things that have concerned us so far on our way to Oxygen.

Merger of MVC and MVC.FX

When we started working on the GEF model-view-controller framework (MVC) component, we took much care of separating generic, rendering-toolkit independent concepts (MVC) from JavaFX-specific aspects (MVC.FX). The motivation behind was to enable a potential reuse of generic concepts also for other rendering toolkits than JavaFX. As it turned out, this was not a wise decision, which is why we have merged MVC and MVC.FX, and the GEF model-view-controller framework is now dedicated to JavaFX alone. Why?

First and foremost, because it lead to overcomplicated code that turned out to be a hurdle for adopters. The root visual type parameter, which we had to introduce in order to be able to reason about rendering-related aspects already inside MVC, may be quoted as a good evidence. It provoked that most types within MVC needed to be parameterized, thus leading to a lot of overhead. For instance, to query the current viewer selection, the following had to be done: 

SelectionModel<VR> selectionModel = viewer.getAdapter(new TypeToken<SelectionModel<VR>>(){}
  .where(new TypeParameter<VR>() {}, Types.<VR>argumentOf(viewer.getClass())));
  
SelectionModel<VR> selectionModel = getSelectionModel();
List<IContentPart<VR, ? extends VR>> currentSelection
  new ArrayList<>(selectionModel.getSelectionUnmodifiable());

When applying GEF in customer projects we further learned that client code does indeed need to access concrete, visualization-specific parts more often than we had thought. Many type casts were required to access JavaFX-specifics like the viewer's canvas: 

((FXViewer) viewer).getCanvas().setStyle(FXViewer.FOCUSED_STYLE);

Further, the separation of generic and JavaFX-specific aspects lead to overcomplicated implementations especially in those parts of the client code that are related to interaction handling (behaviors and policies).

It was also unwise because we became aware that supporting multiple rich-client rendering technologies with a single code base does not seem to be as desirable as we initially thought. As far as Java-based rich-client applications, and especially Eclipse, are concerned, JavaFX seems to be the single, obvious choice: it is feature-complete, open-source, hardware accelerated, supported on all major platforms, and can (quite) easily be integrated with SWT. There is no real need for an alternative.

And for browser-based applications, a Java-based client framework does not seem to be a valid choice anyway. Here, a framework that makes use of web languages and technologies alone is the only viable option. While there already is a whole zoo of such technologies around, I have the impression that the same flexibility, we currently have reached with GEF for the rich client, is not yet available. We will thus (have to) do something, but that's a different story.

Java 9 Compatibility

While the new JavaFX-based code base that we have developed through GEF4 has made it feasible to use GEF for building standalone Java richt-client applications, the Eclipse platform is still our major target platform. We thus rely on the ability to integrate JavaFX with SWT, and we need to be able to do this in an OSGi environment.

In general, integrating JavaFX with SWT is realized through javafx.embed.swt.FXCanvas, an integration that is directly shipped as part of JavaFX in its javafx-swt.jar. FXCanvas subclasses org.eclipse.swt.widgets.Canvas and embeds a JavaFX scene, whose contents is rendered as an image and to which (nearly) all SWT events are forwarded. The ability to use this integration in an OSGi-environment is provided through an Equinox framework extension that is delivered by e(fx)clipse. It ensures that the javafx-swt.jar is available on the classpath of all OSGi bundles that require it. The combination of both thus enables us to use JavaFX within an Eclipse editor or view. 

However, while e(fx)clipse works quite nicely out of the box, the FXCanvas implementation provided with Java 8 has some problems. Horizontal mouse scroll events and gesture events are, for instance, not forwarded from SWT to the embedded JavaFX scene, image cursors cannot be set, focus cannot be properly traversed, etc. We have thus been providing an FXCanvasEx implementation as well as some helper classes as part of the GEF FX component, that incorporates workarounds for the following issues:
  • JDK-8088862: Provide possibility to traverse focus out of FX scene.
  • JDK-8161282: FXCanvas does not forward horizontal mouse scroll events to the embedded scene.
  • JDK-8143596: Forward touch gestures to FXCanvas embedded scene.
  • JDK-8088147: Image cursors not supported.
  • JDK-8159227: KeyEvent.doit should be forwarded to KeyEvent.consumed().
  • JDK-8161587: FXCanvas does not update SWT display.
While that works fine for Java 8, some of the workarounds require access to JDK-internals, which makes them inoperable for Java 9, where such access is now prevented by the newly introduced modularity system.

The only option we saw was to contribute fixes for at least the affected workarounds directly to JavaFX 9, so workarounds within our code base would no longer be needed. That was immanently clear to us when the JIGSAW-fog finally started to lift. Consequently, I contacted the OpenJFX team and became a contributor. And while Oracle has recently had some bad press regarding its open-source engagement, let me emphasize that the OpenJFX team, in particular Kevin Rushforth, made me feel very welcome and greatly supported me in contributing.

As the most invasive fixes will thus be covered by FXCanvas in Java 9 directly, I could guard the related workarounds within our FXCanvasEx to only be effective in Java 8. As a result, users of FXCanvasEx can now transparently rely on identical functionality on Java 8 and Java 9. And with Tom Schindl having added support for Java 9 to e(fx)clipse just a few weeks ago (see bug 482428 for details), running GEF on Java 9 has (finally) become feasible. You can try it yourself:
The 'GEF MVC Logo Example' and the 'GEF Zest Graph Example' views (located in the 'Other' category) can now be opened:



















In case, as an adopter, you already want to try developing bundles against a JavaSE-9 execution environment (BREE), you will have to install 'e(fx)clipse - IDE - PDE' 3.0.0 from http://download.eclipse.org/efxclipse/updates-nightly/site in addition (which will 'inject' javafx-swt.jar to your bundle dependencies), and you will have to update the 'Eclipse Java Developer Tools' to the Java 9 preview available at http://download.eclipse.org/eclipse/updates/4.7-Y-builds.

Give it a try, all early feedback will be more than welcome!

Wednesday, June 22, 2016

GEF4 Dot Oh Dot Oh

I am proud to announce that with today's Eclipse Neon release we have officially published the new GEF4 components in version 1.0.0, exposing their up to now only preliminary API. We have worked intensively on the code base since the 0.2.0 (Mars.1) release and have achieved quite a lot.

Polishing API

From an adopters viewpoint, the (up to now provisional) API has been cleaned up and harmonized in nearly all places. We have for instance adopted JavaFX collections and properties throughout, so that we can now use only a single notification mechanism in all places. As outlined in detail in an earlier post, this also enables us to use JavaFX bindings, which makes framework and client code more concise. Especially the GEF4 FX component has profited a lot from this. We have further simplified the contract of the GEF4 Layout component to no longer rely on own interface abstractions for data exchange, but to use a GEF4 Graph model, similar to what GEF4 Zest already did for rendering. And we have harmonized the GEF4 Zest API, especially those parts related to JFace. The New and Noteworthy page gives a quite detailed overview of the most important API changes.

Improving DOT

From an end-users viewpoint, the GEF4 DOT component has been brought a huge step nearer to its final goal of being a full Graphviz DOT authoring environment (even if its still a far way to go). The Xtext editor can now also parse and validate certain attribute values, and the Graph view is capable of rendering a first set of node shapes, as well as edge styles and decorations. All DOT spline types are now also supported. In addition to the 'emulated' layout we provided so far, where a GEF4 Layout algorithm is used to mimic the Graphviz layout, we now support a 'native' mode, in which the native dot executable (the path to which is specified via a preference) is used:

















'Pimping' FX and MVC

Connection handling has been significantly improved. We have added support for orthogonal routing in addition to straight routing, and we have separated out an IConnectionRouter, which is responsible for manipulating the bend/control points, as well as an IConnectionInterpolator, which determines the to be rendered curve geometry. Routers and interpolators can be freely combined, so a connection can now be routed orthogonally, while it can still be rendered ‚smoothly‘ using Bézier curves:



















In addition, connection decorations are now properly clipped (which lies within the responsibility of the interpolator), and a 'clickable area‘ (a transparent fat curve that overlays the connection) can now be used to make a connection clickable even when the zoom level is large (we have added this functionality to the GeometryNode so it is usable in an even broader scope).

Within GEF4 MVC we have accordingly enhanced FXBendConnectionPolicy to provide interaction support for manipulating connections that are orthogonally routed. These can now be manipulated using segment handles rather than handles for way/control points:


We have further enabled that distinct bindings can be specified for viewers with different roles. That allows us to use an FXViewer both as palette and content viewer, so a palette can now be easily realized. We have augmented the MVC Logo example to serve as a demonstrator:


Sunday, April 24, 2016

GEF4 Common Collections and Properties - Guava goes FX

As a preparation for our upcoming Neon release, I intensively worked on adopting JavaFX collections and properties to the entire GEF4 code base (see #484774).

We had already used them before, but only in those parts of the framework that directly depend on the JavaFX toolkit. In all other places we had used our own extensions to Java Beans properties, because we did not want to introduce JavaFX dependencies there. However, as JavaFX is part of JavaSE 1.7 and JavaSE-1.8 and its collections and properties are completely independent of the UI toolkit, we (no longer) considered the additional dependencies as being a real show stopper. Instead, we highly valued the chance to provide only a single notification mechanism to our adopters. The very rich possibilities offered by JavaFX's binding mechanism in addition tipped the scales.

As JavaFX only provides observable variants for Set, Map, and List, I had to create observable variants and related (collection) properties for those Google Guava collections we use (Multiset and SetMultimap) as a prerequisite. I had to dig deep into the implementation details of JavaFX collections and properties to achieve this, learned quite a lot, and found the one or other oddity that I think is worth to be shared. I also implemented a couple of replacement classes to fix problems in JavaFX collections and properties, which are published as part of GEF4 Common. 

But before going into details, let me shortly explain what JavaFX observable collection and properties are about.

Properties, Observable Collections, and (Observable) Collection Properties


In general, a JavaFX property may be regarded as a specialization of a Java Beans property. What it adds is support for lazy computation of its value as well as for notification of invalidation (a lazily computed value may need to be re-computed) and change listeners, which may - in contrast to Java Beans - be registered directly at the property, not at the enclosing bean.

Using JavaFX properties implies a certain API style (similar to Java Beans), as it is expected to provide a getter and setter to access the property value, as well as an accessor for the property itself. We may consider javafx.scene.Node as an example, which amongst various others provides a boolean pickOnBounds property that (is lazily created and) controls whether picking is computed by intersecting with the rectangular bounds of the node or not:

  public abstract class Node implements EventTarget, Stylable {
    ...
    private BooleanProperty pickOnBounds;
    
    public final void setPickOnBounds(boolean value) {
      pickOnBoundsProperty().set(value);
    }
    
    public final boolean isPickOnBounds() {
      return pickOnBounds == null ? false : pickOnBounds.get();
    }
    
    public final BooleanProperty pickOnBoundsProperty() {
      if (pickOnBounds == null) {
        pickOnBounds = new SimpleBooleanProperty(this, "pickOnBounds");
      }
      return pickOnBounds;
    }
  }

In addition to the notification support already mentioned, values of properties may be bound to values of others, or even to values that are computed by more complex expressions, via so called bindings. This is a quite powerful mechanism that reduces the need for custom listener implementations significantly. If the pickOnBounds value of one node should for instance be kept equal to the pickOnBounds value of another, the following binding is all that is required:

  node1.pickOnBoundsProperty().bind(node2.pickOnBoundsProperty());

Binding it to a more complex boolean expression is pretty easy as well:

  node1.pickOnBoundsProperty().bind(node2.pickOnBoundsProperty().or(node3.visibleProperty()));

One can even define own bindings that compute the property value (lazily) based on values of arbitrary other properties:

  node1.pickOnBoundsProperty().bind(new BooleanBinding() {
    {
      // specify dependencies to other properties, whose changes
      // will trigger the re-computation of our value
      super.bind(node2.pickOnBoundsProperty());
      super.bind(node3.layoutBoundsProperty());
    }
    
    @Override
    protected boolean computeValue() {
      // some arbitrary expression based on the values of our dependencies
      return node2.pickOnBoundsProperty().get() &&
             node3.layoutBoundsProperty().get().isEmpty();
    }
  });

JavaFX provides property implementations for all Java primitives (BooleanPropertyDoublePropertyFloatPropertyIntegerPropertyLongPropertyStringProperty, as well as a generic ObjectProperty, which can be used to wrap arbitrary object values. It is important to point out that an ObjectProperty will of course only notify invalidation and change listeners in case the property value is changed, i.e. it is altered to refer to a different object identity, not when changes are applied to the contained property value. Accordingly, an ObjectProperty that wraps a collection only notifies about changes in case a different collection is set as property value, not when the currently observed collection is changed by adding elements to or removing elements from it:

  ObjectProperty<List<Integer>> observableListObjectProperty = new SimpleObjectProperty<>();
  observableListObjectProperty.addListener(new ChangeListener<List<Integer>>() {
    @Override
    public void changed(ObservableValue<? extends List<Integer>> observable,
                List<Integer> oldValue, List<Integer> newValue)    
      System.out.println("Change from " + oldValue + " to " + newValue);
    }
  });
  
  // change listener will be notified about identity change from 'null' to '[]'
  observableListObjectProperty.set(new ArrayList<Integer>());
  // change listener will not be notified
  observableListObjectProperty.get().addAll(Arrays.asList(1, 2, 3));

This is where JavaFX observable collections come into play. As Java does not provide notification support in its standard collections, JavaFX delivers dedicated observable variants: ObservableListObservableMap and ObservableSet. They all support invalidation listener notification (as properties do) and in addition define their own respective change listeners (ListChangeListenerMapChangeListener, and SetChangeListener).

ObservableList also extends List by adding setAll(E... elements) and setAll(Collection<? extends E> c), which combines a clear() with an addAll(Collection< ? extends E> c) into a single atomic replace operation, as well as a remove(int from, int to) that supports removal within an index interval. This allows to 'reduce noise', which is quite important to a graphical framework like GEF, where complex computations might be triggered by changes.

List changes are iterable, i.e. they comprise several sub-changes, so that even a complex operation like setAll(Collection<? extends E> c) results in a single change notification:

  ObservableList<Integer> observableList = FXCollections.observableArrayList();
  observableList.addListener(new ListChangeListener<Integer>() {
    
    @Override
    public void onChanged(Change<? extends Integer> change) {
      while (change.next()) {
        int from = change.getFrom();
        int to = change.getTo();
        // iterate through the sub-changes
        if (change.wasReplaced()) {
          // replacement (simultaneous removal and addition in a continuous range)
         System.out.println("Replaced " + change.getRemoved()  
                   + " with " + change.getAddedSubList() + ".");
        } else if (change.wasAdded()) {
          // addition (added sublist within from-to range)
         System.out.println("Added " + change.getAddedSubList() 
                   + " within [" + from + ", " + to + ").");
        } else if (change.wasRemoved()) {
          // removal (change provides removed sublist and from index)
  System.out.println("Removed " + change.getRemoved() + " at " + from + ".");
        } else if (change.wasPermutated()) {
          // permutation (change provides mapping of old indexes to new indexes)
  System.out.print("Permutated within [" + change.getFrom() + ", " + to + "):");
  for (int i = from; i < to; i++) {
    System.out.print((i == from ? " " : ", "
                        +  i + " -> " + change.getPermutation(i)
                        + (i == to - 1 ? ".\n" : ""));
  }
        }
      }
    }
  });
  
  // one comprised change: 'Added [3, 1, 2] within [0, 3).'
  observableList.setAll(Arrays.asList(3, 1, 2));
  
  // one comprised change: 'Permutated within [0, 3): 0 -> 2, 1 -> 0, 2 -> 1.'
  Collections.sort(observableList);
  
  // one comprised change: 'Replaced [1, 2, 3] with [4, 5, 6].'     
  observableList.setAll(Arrays.asList(4, 5, 6));
  
  // two comprised changes: 'Removed [4] at index 0.', 'Removed [6] at index 1.'
  observableList.removeAll(Arrays.asList(4, 6));

Similar to properties, observable collections may even be used to establish bindings using so called content bindings:

  // ensure that elements of list are synchronized with that of observableList
  List<Integer> list = new ArrayList<>();
  Bindings.bindContent(list, observableList);

As such, observable collections are quite usable, even if not being wrapped into a property. As long as the identity of an observable collection is not to be changed, it may directly be exposed without being wrapped into a property. And that's exactly how JavaFX uses them in its own API. As an example consider javafx.scene.Parent, which exposes its children via an ObservableList:

  public abstract class Parent extends Node {
    ...
  protected ObservableList<Node> getChildren() {
      return children;
    }
    
    @ReturnsUnmodifiableCollection
    public ObservableList<Node> getChildrenUnmodifiable() {
      return unmodifiableChildren;
    }
  }

Wrapping it into a property however is required, if a collection's identity is to be changed (in a way transparent to listeners) or properties are to be bound to it. In principle an observable collection could be wrapped directly into an ObjectProperty but this has the disadvantage that two listeners are required if collection changes are to be properly tracked.

Consider an ObservableList being wrapped into a SimpleObjectProperty as an example. While changes to the list can be observed by registering a ListChangeListener a ChangeListener is required in addition to keep track of changes to the property's value itself (and to transfer the list change listener from an old property value to a new one):

  ObjectProperty<ObservableList<Integer>> observableListObjectProperty
    new SimpleObjectProperty<>();
  
  final ListChangeListener<Integer> listChangeListener = new  ListChangeListener<Integer>(){
    @Override
    public void onChanged(ListChangeListener.Change<? extends Integer> c) {
      // react to list changes
    }
  };
  
  // register list change listener at (current) property value
  observableListObjectProperty.get().addListener(listChangeListener);
  
  // register change listener to transfer list change listener
  observableListObjectProperty.addListener(new ChangeListener<ObservableList<Integer>>() {
      @Override
      public void changed(ObservableValue<? extends ObservableList<Integer>> observable,
                  ObservableList<Integer> oldValue,
                  ObservableList<Integer> newValue) {
        // transfer list change listener from old value to new one
        if(oldValue != null && oldValue != newValue){
          oldValue.removeListener(listChangeListener);
        }
        if(newValue != null && oldValue != newValue){
          newValue.addListener(listChangeListener);
        }
      }
    });
  }

As this is quite cumbersome, JavaFX offers respective collection properties that can be used as an alternative: ListPropertySetProperty, and MapProperty. They support invalidation and change listeners as well as the respective collection specific listeners and will even synthesize a collection change when the observed property value is changed:

  ListProperty<Integer> listProperty = new SimpleListProperty<>(
    FXCollections.<Integer> observableArrayList());
  
  final ListChangeListener<Integer> listChangeListener = new ListChangeListener<Integer>() {
    @Override
    public void onChanged(ListChangeListener.Change<? extends Integer> change) {
      // handle list changes
    }
  };
  listProperty.addListener(listChangeListener);
  
  // forwarded list change: 'Added [1, 2, 3] within [0, 3).'
  listProperty.addAll(Arrays.asList(1, 2, 3));
  
  // synthesized list change: 'Replaced [1, 2, 3] with [4, 5, 6].'
  listProperty.set(FXCollections.observableArrayList(4, 5, 6));

In addition, collection properties define their own (read-only) properties for emptiness, equality, size, etc., so that advanced bindings can also be created:

  // bind boolean property to 'isEmpty'
  BooleanProperty someBooleanProperty = new SimpleBooleanProperty();
  someBooleanProperty.bind(listProperty.emptyProperty());
  
  // bind integer property to 'size'
  IntegerProperty someIntegerProperty = new SimpleIntegerProperty();
  someIntegerProperty.bind(listProperty.sizeProperty());

While observable properties are thus not required to notify about collection changes (which is already possible using observable collections alone), they add quite some comfort when having to deal with situations where collections may be replaced or where bindings have to rely on certain properties (emptiness, size, etc.) of a collection.


GEF4 Common Collections


As already mentioned, GEF4 MVC uses some of Google Guava's collection classes, while JavaFX only offers observable variants of SetMap, and List. In order to facilitate a unique style for property change notifications in our complete code base, observable variants had to be created up front. That actually involved more design decisions than I had expected. To my own surprise, I also ended up with a replacement class for ObservableList and a utility class to augment the original API, because that seemed quite necessary in a couple of places.

Obtaining atomic changes

As this might not be directly obvious from what was said before, let me point out that only ObservableList is indeed capable of notifying about changes atomically in the way laid out beforeObservableSet and ObservableMap notify their listeners for each elementary change individually. That is, an ObservableMap notifies change listeners independently for each affected key change, while ObservableSet will do likewise for each affected element. Calling clear() on an ObservableMap or can thus lead to various change notifications.

I have no idea why the observable collections API was designed in such an inhomogeneous way (it's discussed at JDK-8092534 without providing much more insight), but I think that an observable collection should rather behave like ObservableList, i.e. fire only a single change notification for each  method call. If all required operations can be performed atomically via dedicated methods, a client can fully control which notifications are produced. As already laid out, ObservableList follows this to some extend with the additionally provided setAll() methods that combine clear() and addAll() into a single atomic operation, which would otherwise yield two notifications. However, an atomic move() operation is still lacking for ObservableList, so that movement of elements currently cannot be performed atomically.

When creating ObservableSetMultimap and ObservableMultiset, I tried to follow the contract of ObservableList for the above mentioned reasons. Both notify their listeners through a single atomic change for each method call, which provides details about elementary sub-changes (related to a single element or key), very similar to ListChangeListener#ChangeIn accordance to the addAll() of ObservableList, I added a replaceAll() operation to both to offer an atomic operation via which the contents of the collections can be replaced. Change notifications are iterable, as for ObservableList:

  ObservableSetMultimap<Integer, String> observableSetMultimapCollectionUtils.<Integer, String> observableHashMultimap();
  observableSetMultimap.addListener(new SetMultimapChangeListener<Integer, String>() {
    @Override
    public void onChanged(SetMultimapChangeListener.Change<? extends Integer,
                                                           ? extends String> change) {
      while (change.next()) {
        if (change.wasAdded()) {
          // values added for key
          System.out.println("Added " + change.getValuesAdded() 
                           + " for key " + change.getKey() + ".");
        } else if (change.wasRemoved()) {
          // values removed for key
          System.out.println("Removed " + change.getValuesRemoved() + " for key " 
                           + change.getKey() + ".");
        }
      }
    }
  });
  
  // one comprised change: 'Added [1] for key 1.'
  observableSetMultimap.put(1, "1");
  
  // one comprised change: 'Added [2] for key 2.'
  observableSetMultimap.put(2, "2");
  
  // two comprised changes: 'Removed [1] for key 1.', 'Removed [2] for key 2.'
  observableSetMultimap.clear();

I also though about providing replacement classes for ObservableMap and ObservableSet that rule out the inhomogeneity of the JavaFX collections API, but this would have required to extend their respective listener interfaces, and I thus abstained. 

Retrieving the "previous" contents of an observable collection

While in principle I like the API of ListChangeListener#Change, what really bothers me is that there is no convenience method to retrieve the old state of an ObservableList before it was changed. It has to be recomputed from the respective addition, removal, and permutation sub-changes (which will be propagated when sorting the list) that are comprised. 

When creating ObservableMultiset and ObservableSetMultimap, I added a getPreviousContent() method to both, so clients can easily access the contents the collection contained before the change was applied. I also added a utility method (within CollectionUtils) that can be used to retrieve the previous contents of an ObservableList:

  ObservableList<Integer> observableList = FXCollections.observableArrayList();
  observableList.addAll(4, 3, 1, 5, 2);
  observableList.addListener(new ListChangeListener<Integer>() {
    @Override
    public void onChanged(ListChangeListener.Change<? extends Integer> change) {
      System.out.println("Previous contents: " + CollectionUtils.getPreviousContents(change));
    }
  });
  
  // Previous contents: [4, 3, 1, 5, 2]
  observableList.set(3, 7);
  // Previous contents: [4, 3, 1, 7, 2]
  observableList.clear();

Obtaining immutable changes from an observable collection

While list fires atomic changes, its change objects are not immutable (see JDK-8092504). Thus, when a listener manipulates the observed list as a result to a change notification, the change object it currently processed will actually be changed. This is a likely pitfall, as client code may not even be aware that it is actually called from within a change notification context. Consider the following snippet:


  ObservableList<Integer> observableList = FXCollections
.observableArrayList();
  observableList.addListener(new ListChangeListener<Integer>() {
    @Override
    public void onChanged(ListChangeListener.Change<? extends Integer> change) {
      while (change.next()) {
        System.out.println(change);
      }
      // manipulate the list from within the change notification
      if (!change.getList().contains(2)) {
((List<Integer>) change.getList()).add(2);
      }
    }
  });
  // change list by adding element '1'
  observableList.add(1);

It will yield the following in valid change notifications:


  { [1] added at 0 }
  { [1, 2] added at 0 }

Again, I cannot understand why this was designed that way, but the documentation clearly states that the list may not be manipulated from within a change notification context and the implementation follows it consequently by finalizing the change object only after all listeners have been notified. In a framework as GEF having non immutable change objects is a real show stopper. Accordingly, I designed ObservableMultiset and ObservableSetMultimap to use immutable change objects only. I also created a replacement class for com.sun.javafx.collections.ObservableListWrapper (the class that is instantiated when calling FXCollections.observableArrayList()) that produces immutable change objects. It can be created using a respective utility method:

  ObservableList observableList = CollectionUtils.observableArrayList();

When using the replacement class in the above quoted scenario, it will yield the following output:

  Added[1] at 0.
  Added[2] at 1.

As ObservableSet and ObservableMap are not capable of comprising several changes into a single atomic one, the immutability problem does not arise there, so the alternative ObservableList implementation is all that is needed.

GEF4 Common (Collection) Properties


In addition to the ObservableMultiset and ObservableSetMultimap collections, I also added respective properties (and related bindings) to wrap these values to GEF4 Common: SimpleMultisetPropertyReadOnlyMultisetPropertySimpleSetMultimapProperty, and ReadOnlySetMultimapProperty.

It should no longer be surprising that I did not only create these, but also ended up with replacement classes for JavaFX's own collection properties (SimpleSetPropertyExReadOnlySetWrapperExSimpleMapPropertyExReadOnlyMapWrapperExSimpleListPropertyExReadOnlyListWrapperEx), because a comparable, consistent behavior could otherwise not be guaranteed.

Stopping the noise

As I have elaborated quite intensively already, observable collection notify about element changes, whereas properties notify about (identity) changes of their contained (observable) value. Collection properties in addition forward all collection notifications, so that a replacement of the property's value is transparent to collection listeners.

However, this is not the full story. The observable collection properties offered by JavaFX fire change notifications even if the observed value did not change (JDK-8089169). That is, every collection change will not only lead to the notification of collection-specific change listeners, but also to the notification of all property change listeners:

  SimpleListProperty<Integer> listProperty = new SimpleListProperty<>(
    FXCollections.<Integer> observableArrayList());
  
  listProperty.addListener(new ChangeListener<ObservableList<Integer>>() {
    @Override
    public void changed(ObservableValue<? extends ObservableList<Integer>> observable,
                        ObservableList<Integer> oldValue,
                        ObservableList<Integer> newValue) {
      System.out.println("Observable (collection) value changed.");
    }
  });
  
  listProperty.addListener(new ListChangeListener<Integer>() {
    @Override
    public void onChanged(ListChangeListener.Change<? extends Integer> c) {
      System.out.println("Collection changed.");
    }
  });
  
  // will (incorrectly) notify (property) change listeners in addition to
  // list change listeners
  listProperty.add(5);
  
  // will (correctly) notify list change listeners and (property) change
  // listeners
  listProperty.set(FXCollections.<Integer> observableArrayList());

As this leads to a lot of unwanted noise, I have ensured that MultisetProperty and SetMultimapProperty, which are provided as part of GEF4 Common, do not babble likewise. I ensured that the replacement classes we provide for the JavaFX collection properties behave accordingly, too.

Guarding notifications consistently

JavaFX observable collection capture all exceptions that occur during a listener notification. That is, the listener notification is guarded as follows:

  try {
    listener.onChanged(change);
  } catch (Exception e) {
    Thread.currentThread().getUncaughtExceptionHandler().
    uncaughtException(Thread.currentThread(), e);
  }

Interestingly, JavaFX properties don't behave like that. Thus, all observable collection properties are inconsistent in a sense that collection change listener invocations will be guarded, while (property) change listener notifications won't. Again, I have ensured that the GEF4 Common provided collection properties show consistent behavior by guarding all their listener notifications, and I have done likewise for the replacement classes we provide.

Fixing further issues and ensuring JavaSE-1.7 compatibility

Having the replacement classes at hand, I also fixed some further inconsistencies that bothered us quite severely. This includes the recently introduced regression that JavaFX MapProperty removes all attached change listeners when just one is to be removed (JDK-8136465), as well as the fact that read-only properties can currently not be properly bound (JDK-8089557).

As GEF4 still aims at providing support for JavaSE-1.7, I tried to ensure that all collection properties we provide, including the replacement classes, can be used in a JavaSE-1.7 environment, too. Except that the static methods JavaFX offers to create and remove bindings cannot be applied (because the binding mechanism has been completely revised from JavaSE-1.7 to JavaSE-1.8), this could be achieved.

While we make intensive use of GEF4 Common Collections and Properties in our own GEF4 framework, GEF4 Common can be used independently. If you need observable variants of Google Guava's Multiset or SetMultimap, or if you want to get rid of the aforementioned inconsistencies, it may be worth taking a look.