From 3e772826a7142969a67933468a72f63dcd44a397 Mon Sep 17 00:00:00 2001 From: LeStegii Date: Thu, 14 Mar 2024 13:29:48 +0100 Subject: [PATCH 01/11] feat(framework): Don't override field values if parameter is null --- ERROR_CODES.md | 7 +++-- docs/controller/4-parameters.md | 3 +- .../fx/controller/ControllerManager.java | 30 +++++++++++-------- .../fulib/fx/util/reflection/Reflection.java | 2 -- 4 files changed, 25 insertions(+), 17 deletions(-) diff --git a/ERROR_CODES.md b/ERROR_CODES.md index 14fc0fc1..776935ab 100644 --- a/ERROR_CODES.md +++ b/ERROR_CODES.md @@ -143,7 +143,8 @@ public class NotAController { // Wrong, should be a controller or component - Runtime: ✅ - Annotation Processor: ✅ -This error is thrown when a method annotated with `@onKey` has more than one parameter or a parameter that is not of type +This error is thrown when a method annotated with `@onKey` has more than one parameter or a parameter that is not of +type `KeyEvent`. ```java @@ -239,7 +240,8 @@ This can happen if the field isn't initialized. - Runtime: ✅ - Annotation Processor: ❌ -This error is thrown when a title is specified using a language key, but no resource bundle is provided using `@Resource` +This error is thrown when a title is specified using a language key, but no resource bundle is provided +using `@Resource` in the controller or component class. ## Routes @@ -430,6 +432,7 @@ public class MyController { - Annotation Processor: ❌ This error is thrown if the type of the parameter value does not match the type of the field. +This error also occurs if the expected type is a primitive type and the provided value is `null`. ### 4008: `Parameter '*' in method '*' in class '*' is of type '*' but the provided value is of type '*'.` diff --git a/docs/controller/4-parameters.md b/docs/controller/4-parameters.md index a3980f09..acedf2a1 100644 --- a/docs/controller/4-parameters.md +++ b/docs/controller/4-parameters.md @@ -53,7 +53,8 @@ public class FooController { } ``` -If a controller expects an argument but no argument with a suitable name is passed, `null` will be passed instead. +If a controller expects an argument but no argument with a suitable name is passed, `null` will be passed instead, except +for fields which will be left unchanged ("default value"). Any arguments not expected by the controller will be ignored. If an argument is provided, but the type doesn't match the type of the field or method parameter, an exception will be diff --git a/framework/src/main/java/org/fulib/fx/controller/ControllerManager.java b/framework/src/main/java/org/fulib/fx/controller/ControllerManager.java index c561aea6..677275f5 100644 --- a/framework/src/main/java/org/fulib/fx/controller/ControllerManager.java +++ b/framework/src/main/java/org/fulib/fx/controller/ControllerManager.java @@ -463,24 +463,32 @@ private void fillParametersIntoFields(@NotNull Object instance, @NotNull Map<@No // Fill the parameters into fields annotated with @Param for (Field field : Reflection.getFieldsWithAnnotation(instance.getClass(), Param.class).toList()) { try { - boolean accessible = field.canAccess(instance); field.setAccessible(true); + // Don't fill the parameter if it's not present (field will not be overwritten, "default value") + if (!parameters.containsKey(field.getAnnotation(Param.class).value())) return; + + Object value = parameters.get(field.getAnnotation(Param.class).value()); + // If the field is a WriteableValue, use the setValue method if (WritableValue.class.isAssignableFrom(field.getType())) { - field.get(instance).getClass().getMethod("setValue", Object.class).invoke(field.get(instance), parameters.get(field.getAnnotation(Param.class).value())); - } else { - Object value = parameters.get(field.getAnnotation(Param.class).value()); - if (value == null) { // If the value is null, we don't need to check the type - field.set(instance, null); + field.get(instance).getClass().getMethod("setValue", Object.class).invoke(field.get(instance), value); + } + + // If not, set the field's value directly + else { + if (value == null) { + // If the value is null and the field is a primitive, throw an error + if (field.getType().isPrimitive()) { + throw new RuntimeException(error(4007).formatted(field.getAnnotation(Param.class).value(), field.getName(), instance.getClass().getName(), field.getType().getName(), "null")); + } + field.set(instance, null); // If the value is null and the field is not a primitive, no type check is necessary } else if (Reflection.canBeAssigned(field.getType(), value)) { - field.set(instance, value); + field.set(instance, value); // If the value is not null, we need a type check (respects primitive types) } else { throw new RuntimeException(error(4007).formatted(field.getAnnotation(Param.class).value(), field.getName(), instance.getClass().getName(), field.getType().getName(), value.getClass().getName())); } } - - field.setAccessible(accessible); } catch (IllegalAccessException e) { throw new RuntimeException(error(4000).formatted(field.getAnnotation(Param.class).value(), field.getName(), instance.getClass().getName()), e); } catch (InvocationTargetException | NoSuchMethodException e) { @@ -496,10 +504,9 @@ private void fillParametersIntoFields(@NotNull Object instance, @NotNull Map<@No } try { - boolean accessible = field.canAccess(instance); field.setAccessible(true); - // Check if field is final + // If the map is final, clear it and put all parameters into it if (Modifier.isFinal(field.getModifiers())) { @SuppressWarnings("unchecked") Map map = (Map) field.get(instance); @@ -508,7 +515,6 @@ private void fillParametersIntoFields(@NotNull Object instance, @NotNull Map<@No } else { field.set(instance, parameters); } - field.setAccessible(accessible); } catch (IllegalAccessException e) { throw new RuntimeException(error(4010).formatted(field.getName(), instance.getClass().getName()), e); } diff --git a/framework/src/main/java/org/fulib/fx/util/reflection/Reflection.java b/framework/src/main/java/org/fulib/fx/util/reflection/Reflection.java index f4dfdeef..0ac0be4b 100644 --- a/framework/src/main/java/org/fulib/fx/util/reflection/Reflection.java +++ b/framework/src/main/java/org/fulib/fx/util/reflection/Reflection.java @@ -42,10 +42,8 @@ public static Stream getFieldsWithAnnotation(Class clazz, Class fields, Consumer method) { for (Field field : fields) { try { - boolean accessible = field.canAccess(instance); field.setAccessible(true); Object component = field.get(instance); - field.setAccessible(accessible); method.accept(component); } catch (IllegalAccessException e) { throw new RuntimeException(error(9000).formatted(field.getName(), instance.getClass().getName()), e); From 60796079ea647f44296a03ec9c0d6f960efc23a4 Mon Sep 17 00:00:00 2001 From: Paul Mertens <50475262+LeStegii@users.noreply.github.com> Date: Sun, 24 Mar 2024 11:16:59 +0100 Subject: [PATCH 02/11] refactor(docs): Format error messages Co-authored-by: Adrian Kunz --- ERROR_CODES.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ERROR_CODES.md b/ERROR_CODES.md index 776935ab..ce1623f7 100644 --- a/ERROR_CODES.md +++ b/ERROR_CODES.md @@ -240,8 +240,7 @@ This can happen if the field isn't initialized. - Runtime: ✅ - Annotation Processor: ❌ -This error is thrown when a title is specified using a language key, but no resource bundle is provided -using `@Resource` +This error is thrown when a title is specified using a language key, but no resource bundle is provided using `@Resource`. in the controller or component class. ## Routes From ead552974f5d9c096c9da1ddd071611f3a34af55 Mon Sep 17 00:00:00 2001 From: Paul Mertens <50475262+LeStegii@users.noreply.github.com> Date: Sun, 24 Mar 2024 11:17:25 +0100 Subject: [PATCH 03/11] refactor(docs): Format error messages Co-authored-by: Adrian Kunz --- ERROR_CODES.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ERROR_CODES.md b/ERROR_CODES.md index ce1623f7..f43d22df 100644 --- a/ERROR_CODES.md +++ b/ERROR_CODES.md @@ -143,9 +143,7 @@ public class NotAController { // Wrong, should be a controller or component - Runtime: ✅ - Annotation Processor: ✅ -This error is thrown when a method annotated with `@onKey` has more than one parameter or a parameter that is not of -type -`KeyEvent`. +This error is thrown when a method annotated with `@onKey` has more than one parameter or a parameter that is not of type `KeyEvent`. ```java From 2a4de08ae805a10f8b35c7d76ffddf8ec9520b98 Mon Sep 17 00:00:00 2001 From: Paul Mertens <50475262+LeStegii@users.noreply.github.com> Date: Sun, 24 Mar 2024 11:17:46 +0100 Subject: [PATCH 04/11] refactor(docs): Format message correctly Co-authored-by: Adrian Kunz --- docs/controller/4-parameters.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/controller/4-parameters.md b/docs/controller/4-parameters.md index acedf2a1..39b70383 100644 --- a/docs/controller/4-parameters.md +++ b/docs/controller/4-parameters.md @@ -53,8 +53,7 @@ public class FooController { } ``` -If a controller expects an argument but no argument with a suitable name is passed, `null` will be passed instead, except -for fields which will be left unchanged ("default value"). +If a controller expects an argument but no argument with a suitable name is passed, `null` will be passed instead, except for fields which will be left unchanged ("default value"). Any arguments not expected by the controller will be ignored. If an argument is provided, but the type doesn't match the type of the field or method parameter, an exception will be From c7f46902dd3a7fcf04238260e67104a3908c9d2a Mon Sep 17 00:00:00 2001 From: LeStegii Date: Sun, 24 Mar 2024 11:23:39 +0100 Subject: [PATCH 05/11] refactor(framework): Cleanup code --- .../fx/controller/ControllerManager.java | 39 ++++++++++--------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/framework/src/main/java/org/fulib/fx/controller/ControllerManager.java b/framework/src/main/java/org/fulib/fx/controller/ControllerManager.java index 677275f5..6344bda8 100644 --- a/framework/src/main/java/org/fulib/fx/controller/ControllerManager.java +++ b/framework/src/main/java/org/fulib/fx/controller/ControllerManager.java @@ -462,37 +462,38 @@ private void callMethodsWithAnnotation(@NotNull Object instance, @NotNull Class< private void fillParametersIntoFields(@NotNull Object instance, @NotNull Map<@NotNull String, @Nullable Object> parameters) { // Fill the parameters into fields annotated with @Param for (Field field : Reflection.getFieldsWithAnnotation(instance.getClass(), Param.class).toList()) { + + String param = field.getAnnotation(Param.class).value(); + Class type = field.getType(); + try { field.setAccessible(true); // Don't fill the parameter if it's not present (field will not be overwritten, "default value") - if (!parameters.containsKey(field.getAnnotation(Param.class).value())) return; - - Object value = parameters.get(field.getAnnotation(Param.class).value()); + if (!parameters.containsKey(param)) return; + Object value = parameters.get(param); // If the field is a WriteableValue, use the setValue method - if (WritableValue.class.isAssignableFrom(field.getType())) { - field.get(instance).getClass().getMethod("setValue", Object.class).invoke(field.get(instance), value); + if (field.get(instance) instanceof WritableValue) { + // noinspection unchecked + ((WritableValue) field.get(instance)).setValue(value); } // If not, set the field's value directly - else { - if (value == null) { - // If the value is null and the field is a primitive, throw an error - if (field.getType().isPrimitive()) { - throw new RuntimeException(error(4007).formatted(field.getAnnotation(Param.class).value(), field.getName(), instance.getClass().getName(), field.getType().getName(), "null")); - } - field.set(instance, null); // If the value is null and the field is not a primitive, no type check is necessary - } else if (Reflection.canBeAssigned(field.getType(), value)) { - field.set(instance, value); // If the value is not null, we need a type check (respects primitive types) - } else { - throw new RuntimeException(error(4007).formatted(field.getAnnotation(Param.class).value(), field.getName(), instance.getClass().getName(), field.getType().getName(), value.getClass().getName())); + else if (value == null) { + // If the value is null and the field is a primitive, throw an error + if (type.isPrimitive()) { + throw new RuntimeException(error(4007).formatted(param, field.getName(), instance.getClass().getName(), type.getName(), "null")); } + field.set(instance, null); // If the value is null and the field is not a primitive, no type check is necessary + } else if (Reflection.canBeAssigned(type, value)) { + field.set(instance, value); // If the value is not null, we need a type check (respects primitive types) + } else { + throw new RuntimeException(error(4007).formatted(param, field.getName(), instance.getClass().getName(), type.getName(), value.getClass().getName())); } + } catch (IllegalAccessException e) { - throw new RuntimeException(error(4000).formatted(field.getAnnotation(Param.class).value(), field.getName(), instance.getClass().getName()), e); - } catch (InvocationTargetException | NoSuchMethodException e) { - throw new RuntimeException(error(4001).formatted(field.getAnnotation(Param.class).value(), field.getName(), instance.getClass().getName()), e); + throw new RuntimeException(error(4000).formatted(param, field.getName(), instance.getClass().getName()), e); } } From d65fadb8674d1222ad3d58439bc6b5ff21081f60 Mon Sep 17 00:00:00 2001 From: LeStegii Date: Sun, 24 Mar 2024 10:24:00 +0000 Subject: [PATCH 06/11] docs: Update ERROR_CODES.md --- ERROR_CODES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ERROR_CODES.md b/ERROR_CODES.md index f43d22df..6f57b8e6 100644 --- a/ERROR_CODES.md +++ b/ERROR_CODES.md @@ -334,7 +334,7 @@ This error is thrown when the framework fails to put a parameter value into a fi ### 4001: `Couldn't call setter method with parameter '*' for field '*' in class '*'.` -- Runtime: ✅ +- Runtime: ❌ - Annotation Processor: ❌ This error is thrown when the framework fails to call the set method of a property field with the parameter value. From adbff5441bc2193ee85d28c84bd34b142429d2f8 Mon Sep 17 00:00:00 2001 From: LeStegii Date: Sun, 24 Mar 2024 12:19:04 +0100 Subject: [PATCH 07/11] fix(framework): Re-add error case --- .../org/fulib/fx/controller/ControllerManager.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/framework/src/main/java/org/fulib/fx/controller/ControllerManager.java b/framework/src/main/java/org/fulib/fx/controller/ControllerManager.java index 6344bda8..8bf0face 100644 --- a/framework/src/main/java/org/fulib/fx/controller/ControllerManager.java +++ b/framework/src/main/java/org/fulib/fx/controller/ControllerManager.java @@ -463,7 +463,8 @@ private void fillParametersIntoFields(@NotNull Object instance, @NotNull Map<@No // Fill the parameters into fields annotated with @Param for (Field field : Reflection.getFieldsWithAnnotation(instance.getClass(), Param.class).toList()) { - String param = field.getAnnotation(Param.class).value(); + Param paramAnnotation = field.getAnnotation(Param.class); + String param = paramAnnotation.value(); Class type = field.getType(); try { @@ -475,8 +476,12 @@ private void fillParametersIntoFields(@NotNull Object instance, @NotNull Map<@No Object value = parameters.get(param); // If the field is a WriteableValue, use the setValue method if (field.get(instance) instanceof WritableValue) { - // noinspection unchecked - ((WritableValue) field.get(instance)).setValue(value); + try { + // noinspection unchecked + ((WritableValue) field.get(instance)).setValue(value); + } catch (ClassCastException e) { + throw new RuntimeException(error(4001).formatted(param, field.getName(), instance.getClass().getName())); + } } // If not, set the field's value directly From fcbc270bd870c234201060986bd7d2d07374366c Mon Sep 17 00:00:00 2001 From: LeStegii Date: Sun, 24 Mar 2024 11:19:41 +0000 Subject: [PATCH 08/11] docs: Update ERROR_CODES.md --- ERROR_CODES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ERROR_CODES.md b/ERROR_CODES.md index 6f57b8e6..f43d22df 100644 --- a/ERROR_CODES.md +++ b/ERROR_CODES.md @@ -334,7 +334,7 @@ This error is thrown when the framework fails to put a parameter value into a fi ### 4001: `Couldn't call setter method with parameter '*' for field '*' in class '*'.` -- Runtime: ❌ +- Runtime: ✅ - Annotation Processor: ❌ This error is thrown when the framework fails to call the set method of a property field with the parameter value. From e9911d2b7de71b39b62cb32a3557afc1a2dfc649 Mon Sep 17 00:00:00 2001 From: LeStegii Date: Sun, 24 Mar 2024 15:42:51 +0100 Subject: [PATCH 09/11] feat(framework): More correct type check logic and tests --- .../fx/controller/ControllerManager.java | 34 +++++++++++-------- .../java/org/fulib/fx/app/FrameworkTest.java | 22 ++++++++++++ .../controller/InvalidParamController.java | 26 ++++++++++++++ 3 files changed, 68 insertions(+), 14 deletions(-) create mode 100644 framework/src/test/java/org/fulib/fx/app/controller/InvalidParamController.java diff --git a/framework/src/main/java/org/fulib/fx/controller/ControllerManager.java b/framework/src/main/java/org/fulib/fx/controller/ControllerManager.java index 8bf0face..74365835 100644 --- a/framework/src/main/java/org/fulib/fx/controller/ControllerManager.java +++ b/framework/src/main/java/org/fulib/fx/controller/ControllerManager.java @@ -34,10 +34,7 @@ import java.io.File; import java.io.IOException; import java.lang.annotation.Annotation; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; +import java.lang.reflect.*; import java.net.MalformedURLException; import java.net.URL; import java.util.*; @@ -465,36 +462,45 @@ private void fillParametersIntoFields(@NotNull Object instance, @NotNull Map<@No Param paramAnnotation = field.getAnnotation(Param.class); String param = paramAnnotation.value(); - Class type = field.getType(); + + // Don't fill the parameter if it's not present (field will not be overwritten, "default value") + if (!parameters.containsKey(param)) continue; + + Class fieldType = field.getType(); try { field.setAccessible(true); - // Don't fill the parameter if it's not present (field will not be overwritten, "default value") - if (!parameters.containsKey(param)) return; - Object value = parameters.get(param); + Object fieldValue = field.get(instance); + // If the field is a WriteableValue, use the setValue method - if (field.get(instance) instanceof WritableValue) { + if (WritableValue.class.isAssignableFrom(fieldType)) { + + // We cannot call setValue on a non-existing property + if (fieldValue == null) { + throw new RuntimeException(error(4001).formatted(param, field.getName(), instance.getClass().getName())); + } + try { // noinspection unchecked ((WritableValue) field.get(instance)).setValue(value); } catch (ClassCastException e) { - throw new RuntimeException(error(4001).formatted(param, field.getName(), instance.getClass().getName())); + throw new RuntimeException(error(4007).formatted(param, field.getName(), instance.getClass().getName(), fieldType.getName(), value == null ? "null" : value.getClass().getName())); } } // If not, set the field's value directly else if (value == null) { // If the value is null and the field is a primitive, throw an error - if (type.isPrimitive()) { - throw new RuntimeException(error(4007).formatted(param, field.getName(), instance.getClass().getName(), type.getName(), "null")); + if (fieldType.isPrimitive()) { + throw new RuntimeException(error(4007).formatted(param, field.getName(), instance.getClass().getName(), fieldType.getName(), "null")); } field.set(instance, null); // If the value is null and the field is not a primitive, no type check is necessary - } else if (Reflection.canBeAssigned(type, value)) { + } else if (Reflection.canBeAssigned(fieldType, value)) { field.set(instance, value); // If the value is not null, we need a type check (respects primitive types) } else { - throw new RuntimeException(error(4007).formatted(param, field.getName(), instance.getClass().getName(), type.getName(), value.getClass().getName())); + throw new RuntimeException(error(4007).formatted(param, field.getName(), instance.getClass().getName(), fieldType.getName(), value.getClass().getName())); } } catch (IllegalAccessException e) { diff --git a/framework/src/test/java/org/fulib/fx/app/FrameworkTest.java b/framework/src/test/java/org/fulib/fx/app/FrameworkTest.java index 09b9b7a8..05168190 100644 --- a/framework/src/test/java/org/fulib/fx/app/FrameworkTest.java +++ b/framework/src/test/java/org/fulib/fx/app/FrameworkTest.java @@ -2,6 +2,7 @@ import javafx.application.Platform; import org.fulib.fx.FulibFxApp; +import org.fulib.fx.app.controller.InvalidParamController; import org.fulib.fx.app.controller.types.BasicComponent; import org.fulib.fx.app.controller.ParamController; import org.fulib.fx.app.controller.TitleController; @@ -206,6 +207,27 @@ public void params() { assertEquals('a', controller.getSetterMultiParams1()); assertEquals(true, controller.getSetterMultiParams2()); + + runAndWait(() -> + assertThrows( + RuntimeException.class, // Fails because the field is of type Integer, but a String is provided + () -> app.show(new InvalidParamController(), Map.of("one", "string")) + ) + ); + + runAndWait(() -> + assertThrows( + RuntimeException.class, // Fails because the property expects an Integer, but a String is provided + () -> app.show(new InvalidParamController(), Map.of("two", "123")) + ) + ); + + runAndWait(() -> + assertThrows( + RuntimeException.class, // Fails because the property is null + () -> app.show(new InvalidParamController(), Map.of("three", 123)) + ) + ); } @Test diff --git a/framework/src/test/java/org/fulib/fx/app/controller/InvalidParamController.java b/framework/src/test/java/org/fulib/fx/app/controller/InvalidParamController.java new file mode 100644 index 00000000..800316b9 --- /dev/null +++ b/framework/src/test/java/org/fulib/fx/app/controller/InvalidParamController.java @@ -0,0 +1,26 @@ +package org.fulib.fx.app.controller; + +import javafx.beans.property.IntegerProperty; +import javafx.beans.property.SimpleIntegerProperty; +import javafx.scene.layout.VBox; +import org.fulib.fx.annotation.controller.Controller; +import org.fulib.fx.annotation.param.Param; + +@Controller(view = "#render") +public class InvalidParamController { + + @Param("one") + private Integer integer; + + @Param("two") + private IntegerProperty integerProperty = new SimpleIntegerProperty(); + + @Param("three") + private IntegerProperty integerProperty2; + + public VBox render() { + return new VBox(); + } + + +} From 7ab3109eb7f13e09edcc35a0e4e1459f1bad2033 Mon Sep 17 00:00:00 2001 From: LeStegii Date: Sun, 24 Mar 2024 15:48:23 +0100 Subject: [PATCH 10/11] docs(framework): Change error description --- ERROR_CODES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/ERROR_CODES.md b/ERROR_CODES.md index f43d22df..8f621970 100644 --- a/ERROR_CODES.md +++ b/ERROR_CODES.md @@ -338,6 +338,7 @@ This error is thrown when the framework fails to put a parameter value into a fi - Annotation Processor: ❌ This error is thrown when the framework fails to call the set method of a property field with the parameter value. +This can happen if the property is not initialized. ### 4002: `Field '*' annotated with @ParamsMap in class '*' is not of type Map.` From 7c44c3d4bebe21dbe4f6d63786ca0de07a76cc88 Mon Sep 17 00:00:00 2001 From: LeStegii Date: Sun, 24 Mar 2024 22:07:39 +0100 Subject: [PATCH 11/11] feat(framework): Slightly adjust property parameter behavior --- docs/controller/4-parameters.md | 4 +++- .../org/fulib/fx/controller/ControllerManager.java | 2 +- .../test/java/org/fulib/fx/app/FrameworkTest.java | 14 ++++++++++---- .../fulib/fx/app/controller/ParamController.java | 10 ++++++---- 4 files changed, 20 insertions(+), 10 deletions(-) diff --git a/docs/controller/4-parameters.md b/docs/controller/4-parameters.md index 350aa428..4fc7199f 100644 --- a/docs/controller/4-parameters.md +++ b/docs/controller/4-parameters.md @@ -10,7 +10,9 @@ the field will be injected with the value of the parameter before the controller on a method, the method will be called with the value of the parameter before the controller is initialized. If the annotation is used on a method parameter of a render/init method, the method will be called with the value of the parameter. -If `@Param` is used on a field containing a `WriteableValue` (e.g. a `StringProperty`), the value will be set to the parameter. +If `@Param` is used on a field containing a `WriteableValue` (e.g. a `StringProperty`), its value will be set to the +parameter's value if the parameter has the correct type (e.g. a `String` for a `StringProperty`). If the parameter is +a `WritableValue` as well, the logic will be the same as for a normal field. Instead of accessing the parameters one by one, you can also use the `@ParamsMap` annotation to inject a map of all parameters. This annotation can be used for fields and method parameters of type `Map`. If the annotated field is final, diff --git a/framework/src/main/java/org/fulib/fx/controller/ControllerManager.java b/framework/src/main/java/org/fulib/fx/controller/ControllerManager.java index 74365835..3f96a100 100644 --- a/framework/src/main/java/org/fulib/fx/controller/ControllerManager.java +++ b/framework/src/main/java/org/fulib/fx/controller/ControllerManager.java @@ -475,7 +475,7 @@ private void fillParametersIntoFields(@NotNull Object instance, @NotNull Map<@No Object fieldValue = field.get(instance); // If the field is a WriteableValue, use the setValue method - if (WritableValue.class.isAssignableFrom(fieldType)) { + if (WritableValue.class.isAssignableFrom(fieldType) && !(value instanceof WritableValue)) { // We cannot call setValue on a non-existing property if (fieldValue == null) { diff --git a/framework/src/test/java/org/fulib/fx/app/FrameworkTest.java b/framework/src/test/java/org/fulib/fx/app/FrameworkTest.java index 05168190..c6a8609d 100644 --- a/framework/src/test/java/org/fulib/fx/app/FrameworkTest.java +++ b/framework/src/test/java/org/fulib/fx/app/FrameworkTest.java @@ -1,6 +1,8 @@ package org.fulib.fx.app; import javafx.application.Platform; +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; import org.fulib.fx.FulibFxApp; import org.fulib.fx.app.controller.InvalidParamController; import org.fulib.fx.app.controller.types.BasicComponent; @@ -187,11 +189,13 @@ public void modalTest() { @Test public void params() { ParamController controller = new ParamController(); + StringProperty property = new SimpleStringProperty("string"); Map params = Map.of( "integer", 1, "string", "string", "character", 'a', - "bool", true + "bool", true, + "property", property ); runAndWait(() -> app.show(controller, params)); @@ -201,13 +205,15 @@ public void params() { assertEquals("string", controller.fieldPropertyParamProperty().get()); - assertEquals(Map.of("integer", 1, "string", "string", "character", 'a', "bool", true), controller.getOnInitParamsMap()); - assertEquals(Map.of("integer", 1, "string", "string", "character", 'a', "bool", true), controller.getSetterParamsMap()); - assertEquals(Map.of("integer", 1, "string", "string", "character", 'a', "bool", true), controller.getFieldParamsMap()); + assertEquals(Map.of("integer", 1, "string", "string", "character", 'a', "bool", true, "property", property), controller.getOnInitParamsMap()); + assertEquals(Map.of("integer", 1, "string", "string", "character", 'a', "bool", true, "property", property), controller.getSetterParamsMap()); + assertEquals(Map.of("integer", 1, "string", "string", "character", 'a', "bool", true, "property", property), controller.getFieldParamsMap()); assertEquals('a', controller.getSetterMultiParams1()); assertEquals(true, controller.getSetterMultiParams2()); + assertEquals(property, controller.stringPropertyProperty()); + runAndWait(() -> assertThrows( RuntimeException.class, // Fails because the field is of type Integer, but a String is provided diff --git a/framework/src/test/java/org/fulib/fx/app/controller/ParamController.java b/framework/src/test/java/org/fulib/fx/app/controller/ParamController.java index 6b6143c1..cb5ce8c6 100644 --- a/framework/src/test/java/org/fulib/fx/app/controller/ParamController.java +++ b/framework/src/test/java/org/fulib/fx/app/controller/ParamController.java @@ -28,6 +28,9 @@ public class ParamController { @ParamsMap private Map fieldParamsMap; + @Param("property") + private StringProperty stringProperty; + private Character setterMultiParams1; private Boolean setterMultiParams2; @@ -73,10 +76,6 @@ public int getFieldParam() { return fieldParam; } - public String getFieldPropertyParam() { - return fieldPropertyParam.get(); - } - public StringProperty fieldPropertyParamProperty() { return fieldPropertyParam; } @@ -101,4 +100,7 @@ public Boolean getSetterMultiParams2() { return setterMultiParams2; } + public StringProperty stringPropertyProperty() { + return stringProperty; + } }