Search code examples
javajavafxeasybind

Bind sum of integer from values of a map to text property


I got the following data model:

public class Player {
  // enum to integer (count)
  @Getter
  private MapProperty<ResourceType, Integer> resourceCards = new SimpleMapProperty<>();

  public void addResource(final ResourceType resourceType, final int amount) {
    this.resourceCards.get().put(resourceType, this.resourceCards.get().get(resourceType) + amount);
  }
}


public class Game {

  @Getter
  @Setter
  private ObjectProperty<Player> currentPlayer = new SimpleObjectProperty<>();

 }

Binding the map to a text property is quite easy:

final Label label = new Label();
label.textProperty().bind(EasyBind.select(game.getCurrentPlayer()).selectObject(player -> player.getResourceCards().asString()));

I would like to display the total amount of resource cards in the label.

Example:

Map: {Water=0, Gold=2, Lumber=5}

Label: 7

Map: {Water=0, Gold=0, Lumber=0}

Label: 0

How can I achieve this with JavaFX Bindings or/and EasyBind?


Solution

  • A MapProperty<S, T> can be used as ObservableMap<S, T> and gets notified of changes of the wrapped map. This means you can use Bindings.createStringBinding on the map:

    label.textProperty().bind(EasyBind.select(game.getCurrentPlayer())
                                      .selectObject(player -> Bindings.createStringBinding(() -> Integer.toString(player.getResourceCards().values().stream().mapToInt(Integer::intValue).sum()),
                                                                                           player.getResourceCards())
                                      .orElse("0")));
    

    Note: Convention for the method name of the method returning the property object itself is using <propertyName>Property. In case of your Game's currentPlayer property this would be currentPlayerProperty() instead of getCurrentPlayer() and resourceCardsProperty() instead of getResourceCards(). I used the method names including get in the above code.

    Furthermore using a MapProperty is unnecessary unless you're planing to replace the whole map instead of simply modifying the Map's contents. You could simply use a readonly property of type ObservableMap in this case.