On Wed, 5 Jan 2022 12:29:01 GMT, John Hendrikx <jhendr...@openjdk.org> wrote:

>> This is an implementation of the proposal in 
>> https://bugs.openjdk.java.net/browse/JDK-8274771 that me and Nir Lisker 
>> (@nlisker) have been working on.  It's a complete implementation including 
>> good test coverage.  
>> 
>> This was based on https://github.com/openjdk/jfx/pull/434 but with a smaller 
>> API footprint.  Compared to the PoC this is lacking public API for 
>> subscriptions, and does not include `orElseGet` or the `conditionOn` 
>> conditional mapping.
>> 
>> **Flexible Mappings**
>> Map the contents of a property any way you like with `map`, or map nested 
>> properties with `flatMap`.
>> 
>> **Lazy**
>> The bindings created are lazy, which means they are always _invalid_ when 
>> not themselves observed. This allows for easier garbage collection (once the 
>> last observer is removed, a chain of bindings will stop observing their 
>> parents) and less listener management when dealing with nested properties.  
>> Furthermore, this allows inclusion of such bindings in classes such as 
>> `Node` without listeners being created when the binding itself is not used 
>> (this would allow for the inclusion of a `treeShowingProperty` in `Node` 
>> without creating excessive listeners, see this fix I did in an earlier PR: 
>> https://github.com/openjdk/jfx/pull/185)
>> 
>> **Null Safe**
>> The `map` and `flatMap` methods are skipped, similar to `java.util.Optional` 
>> when the value they would be mapping is `null`. This makes mapping nested 
>> properties with `flatMap` trivial as the `null` case does not need to be 
>> taken into account in a chain like this: 
>> `node.sceneProperty().flatMap(Scene::windowProperty).flatMap(Window::showingProperty)`.
>>   Instead a default can be provided with `orElse`.
>> 
>> Some examples:
>> 
>>     void mapProperty() {
>>       // Standard JavaFX:
>>       label.textProperty().bind(Bindings.createStringBinding(() -> 
>> text.getValueSafe().toUpperCase(), text));
>> 
>>       // Fluent: much more compact, no need to handle null
>>       label.textProperty().bind(text.map(String::toUpperCase));
>>     }
>> 
>>     void calculateCharactersLeft() {
>>       // Standard JavaFX:
>>       
>> label.textProperty().bind(text.length().negate().add(100).asString().concat("
>>  characters left"));
>> 
>>       // Fluent: slightly more compact and more clear (no negate needed)
>>       label.textProperty().bind(text.orElse("").map(v -> 100 - v.length() + 
>> " characters left"));
>>     }
>> 
>>     void mapNestedValue() {
>>       // Standard JavaFX:
>>       label.textProperty().bind(Bindings.createStringBinding(
>>         () -> employee.get() == null ? ""
>>             : employee.get().getCompany() == null ? ""
>>             : employee.get().getCompany().getName(),
>>         employee
>>       ));
>> 
>>       // Fluent: no need to handle nulls everywhere
>>       label.textProperty().bind(
>>         employee.map(Employee::getCompany)
>>                 .map(Company::getName)
>>                 .orElse("")
>>       );
>>     }
>> 
>>     void mapNestedProperty() {
>>       // Standard JavaFX:
>>       label.textProperty().bind(
>>         Bindings.when(Bindings.selectBoolean(label.sceneProperty(), 
>> "window", "showing"))
>>           .then("Visible")
>>           .otherwise("Not Visible")
>>       );
>> 
>>       // Fluent: type safe
>>       label.textProperty().bind(label.sceneProperty()
>>         .flatMap(Scene::windowProperty)
>>         .flatMap(Window::showingProperty)
>>         .orElse(false)
>>         .map(showing -> showing ? "Visible" : "Not Visible")
>>       );
>>     }
>> 
>> Note that this is based on ideas in ReactFX and my own experiments in 
>> https://github.com/hjohn/hs.jfx.eventstream.  I've come to the conclusion 
>> that this is much better directly integrated into JavaFX, and I'm hoping 
>> this proof of concept will be able to move such an effort forward.
>
> John Hendrikx has updated the pull request incrementally with one additional 
> commit since the last revision:
> 
>   Apply changes suggested in review and updated copyright years to 2022

The changes look good. I added some minor grammar comments. I think that the 
API is in a good spot.

modules/javafx.base/src/main/java/javafx/beans/value/ObservableValue.java line 
145:

> 143:     /**
> 144:      * Creates an {@code ObservableValue} that holds the result of 
> applying a
> 145:      * mapping on the value held by this {@code ObservableValue}. The 
> result is

After one of your suggestions, another phrasing for `on the value held by this 
{@code ObservableValue}` is `on this {@code ObservableValue}'s value`, which is 
a bit shorter. If you think it helps, you can make this change here and in 
other places. Fine to leave as it.

modules/javafx.base/src/main/java/javafx/beans/value/ObservableValue.java line 
164:

> 162:      * @param mapper a {@code Function} which converts a given value to 
> a new value, cannot be null
> 163:      * @return an {@code ObservableValue} holding a mapping of this 
> {@code ObservableValue}'s value
> 164:      *     or holds {@code null} when the value is {@code null}; never 
> returns {@code null}

* Comma before "or"
* holds -> holding (or the "holding" in the first line -> "that holds", just 
needs to be consistent)

modules/javafx.base/src/main/java/javafx/beans/value/ObservableValue.java line 
178:

> 176:      * <p>
> 177:      * For example, mapping a string to an upper case string but leaving 
> it blank
> 178:      * if the input was {@code null}:

* Comma before "but"
* "input was" -> "input is"

modules/javafx.base/src/main/java/javafx/beans/value/ObservableValue.java line 
190:

> 188:      *     holds {@code null}; can be {@code null}
> 189:      * @return an {@code ObservableValue} holding this {@code 
> ObservableValue}'s value
> 190:      *     or the given value when the value is {@code null}; never 
> returns {@code null}

Comma before "or"

modules/javafx.base/src/main/java/javafx/beans/value/ObservableValue.java line 
201:

> 199:      * The result is updated when either this {@code ObservableValue} or 
> the {@code ObservableValue}
> 200:      * resulting from the mapping changes. If this value is {@code 
> null}, no mapping is applied
> 201:      * and the resulting value is {@code null}. If the mapping resulted 
> in {@code null} then

Comma before "then"

modules/javafx.base/src/main/java/javafx/beans/value/ObservableValue.java line 
204:

> 202:      * the resulting value is also {@code null}.
> 203:      * <p>
> 204:      * For example, a property which is only true when a UI element is 
> part of a {@code Scene}

* {@code true}
* "which" -> "that"

modules/javafx.base/src/main/java/javafx/beans/value/ObservableValue.java line 
205:

> 203:      * <p>
> 204:      * For example, a property which is only true when a UI element is 
> part of a {@code Scene}
> 205:      * which is part of a {@code Window} that is currently shown on 
> screen:

"which" -> "that"

modules/javafx.base/src/main/java/javafx/beans/value/ObservableValue.java line 
232:

> 230:      *
> 231:      * @param <U> the type of values held by the resulting {@code 
> ObservableValue}
> 232:      * @param mapper a {@code Function} which converts a given value to 
> an

"which" -> "that"

modules/javafx.base/src/main/java/javafx/beans/value/ObservableValue.java line 
236:

> 234:      * @return an {@code ObservableValue} holding the value of an {@code 
> ObservableValue}
> 235:      *     resulting from a mapping of this {@code ObservableValue}'s 
> value or
> 236:      *     holds {@code null} when the value is {@code null}; never 
> returns {@code null}

* Comma before "or"
* holds -> holding (or the "holding" in the first line -> "that holds", just 
needs to be consistent)

-------------

PR: https://git.openjdk.java.net/jfx/pull/675

Reply via email to