On Wed, 13 May 2026 14:01:46 GMT, Petr Štechmüller <[email protected]> wrote:

> ProxyBuilder is used by the FXML loader to instantiate classes whose 
> constructors are annotated with `@NamedArg`. When such a class also exposes a 
> read-only `Map` property (e.g. getProperties() — the same pattern used by 
> `javafx.scene.Node`), setting child elements under that property in FXML 
> caused incorrect behaviour or a runtime error.
> 
> **Root cause:** _getReadOnlyProperty()_ always returned an `ArrayListWrapper` 
> regardless of the actual **getter** return type. When the getter returns a 
> `Map`, an `ArrayListWrapper` is the wrong container and the entries are never 
> transferred to the real map on the object.
> 
> - [x] I confirm that I make this contribution in accordance with the [OpenJDK 
> Interim AI Policy](https://openjdk.org/legal/ai).

Hello,

I sent a reproducer to a mailing list, but I can attach it here if it's 
allowed...

Find a reproducer here: 
[javafx_bug_report.tar.gz](https://github.com/user-attachments/files/28173230/javafx_bug_report.tar.gz).

The fix for the bug you mentioned could be done in the same place I already 
changed in my first commit. So I decided to include it in this PR as well.

When the constructor is annotated by `@NamedArg`, the `ProxyBuilder` class will 
handle instantiating. Container support for properties was added but with 
limitations described in the bug ticket. Every container was treated as 
[`ArrayListWrapper`](https://github.com/openjdk/jfx/blob/c10ff9f3b67e760d15dbc218cdc132d5d7d63dd2/modules/javafx.fxml/src/main/java/com/sun/javafx/fxml/builder/ProxyBuilder.java#L179).
 This had two consequences: Map properties of the Node were ignored completely 
and List container was initialized only with a [single 
value](https://github.com/openjdk/jfx/blob/c10ff9f3b67e760d15dbc218cdc132d5d7d63dd2/modules/javafx.fxml/src/main/java/com/sun/javafx/fxml/builder/ProxyBuilder.java#L456).


    private Object getUserValue(String key, Class<?> type) {
        Object val = userValues.get(key);
        if (val == null) {
            return null;
        }

        if (type.isAssignableFrom(val.getClass())) {
            return val;
        }
        ...


The type check is crutial for the fix. Up until now, the `type` was always 
`ArrayListWrapper`, but `val.getClass()` was usually `ObservableList` or 
`ObservableMap`. Those two types never matched and so few lines later:


        if (ArrayListWrapper.class.equals(val.getClass())) {
            // user given value is an ArrayList but the constructor doesn't
            // accept an ArrayList so the ArrayList comes from
            // the getTemporaryContainer method
            // we take the first argument
            List l = (List) val;
            return l.get(0);
        }


By introspecting the needs for return type in the method `getReadOnlyProperty` 
we can use correct collection so later type will match.

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

PR Comment: https://git.openjdk.org/jfx/pull/2167#issuecomment-4524849551
PR Comment: https://git.openjdk.org/jfx/pull/2167#issuecomment-4524861592
PR Comment: https://git.openjdk.org/jfx/pull/2167#issuecomment-4552656373

Reply via email to