From 7ce82898c49a0e10977680ba3d3e49a0873eb2d9 Mon Sep 17 00:00:00 2001 From: Manuel Mauky Date: Mon, 6 Feb 2017 14:17:37 +0100 Subject: [PATCH 01/51] Update version to 1.7.0-SNAPSHOT on develop branch #472 --- examples/books-example/pom.xml | 2 +- examples/contacts-example/pom.xml | 2 +- examples/mini-examples/async-todoapp-futures/pom.xml | 2 +- examples/mini-examples/fx-root-example/pom.xml | 2 +- examples/mini-examples/helloworld-without-fxml/pom.xml | 2 +- examples/mini-examples/helloworld/pom.xml | 2 +- examples/mini-examples/pom.xml | 2 +- examples/mini-examples/scopes-example/pom.xml | 2 +- examples/mini-examples/synchronizefx-example/pom.xml | 2 +- examples/mini-examples/welcome-example/pom.xml | 2 +- examples/pom.xml | 2 +- examples/todomvc-example/pom.xml | 2 +- mvvmfx-archetype/pom.xml | 2 +- mvvmfx-archetype/src/main/resources/archetype-resources/pom.xml | 2 +- mvvmfx-cdi/pom.xml | 2 +- mvvmfx-easydi/pom.xml | 2 +- mvvmfx-guice/pom.xml | 2 +- mvvmfx-testing-utils/pom.xml | 2 +- mvvmfx-utils/pom.xml | 2 +- mvvmfx-validation/pom.xml | 2 +- mvvmfx/pom.xml | 2 +- pom.xml | 2 +- 22 files changed, 22 insertions(+), 22 deletions(-) diff --git a/examples/books-example/pom.xml b/examples/books-example/pom.xml index d6c97a89e..3ab531752 100644 --- a/examples/books-example/pom.xml +++ b/examples/books-example/pom.xml @@ -5,7 +5,7 @@ de.saxsys.mvvmfx examples - 1.6.0 + 1.7.0-SNAPSHOT 4.0.0 diff --git a/examples/contacts-example/pom.xml b/examples/contacts-example/pom.xml index 9542d4362..ed9aeab74 100644 --- a/examples/contacts-example/pom.xml +++ b/examples/contacts-example/pom.xml @@ -6,7 +6,7 @@ de.saxsys.mvvmfx examples - 1.6.0 + 1.7.0-SNAPSHOT contacts-example diff --git a/examples/mini-examples/async-todoapp-futures/pom.xml b/examples/mini-examples/async-todoapp-futures/pom.xml index 58ce74cb0..7ba67114f 100644 --- a/examples/mini-examples/async-todoapp-futures/pom.xml +++ b/examples/mini-examples/async-todoapp-futures/pom.xml @@ -5,7 +5,7 @@ mini-examples de.saxsys.mvvmfx - 1.6.0 + 1.7.0-SNAPSHOT 4.0.0 diff --git a/examples/mini-examples/fx-root-example/pom.xml b/examples/mini-examples/fx-root-example/pom.xml index 981d7917b..47b123a2f 100644 --- a/examples/mini-examples/fx-root-example/pom.xml +++ b/examples/mini-examples/fx-root-example/pom.xml @@ -5,7 +5,7 @@ mini-examples de.saxsys.mvvmfx - 1.6.0 + 1.7.0-SNAPSHOT 4.0.0 diff --git a/examples/mini-examples/helloworld-without-fxml/pom.xml b/examples/mini-examples/helloworld-without-fxml/pom.xml index 7feeff7ff..e1eef73f5 100644 --- a/examples/mini-examples/helloworld-without-fxml/pom.xml +++ b/examples/mini-examples/helloworld-without-fxml/pom.xml @@ -5,7 +5,7 @@ mini-examples de.saxsys.mvvmfx - 1.6.0 + 1.7.0-SNAPSHOT 4.0.0 diff --git a/examples/mini-examples/helloworld/pom.xml b/examples/mini-examples/helloworld/pom.xml index b74a10f5c..a4b2cf136 100644 --- a/examples/mini-examples/helloworld/pom.xml +++ b/examples/mini-examples/helloworld/pom.xml @@ -5,7 +5,7 @@ mini-examples de.saxsys.mvvmfx - 1.6.0 + 1.7.0-SNAPSHOT 4.0.0 diff --git a/examples/mini-examples/pom.xml b/examples/mini-examples/pom.xml index be330b12a..6c212c43e 100644 --- a/examples/mini-examples/pom.xml +++ b/examples/mini-examples/pom.xml @@ -5,7 +5,7 @@ examples de.saxsys.mvvmfx - 1.6.0 + 1.7.0-SNAPSHOT 4.0.0 pom diff --git a/examples/mini-examples/scopes-example/pom.xml b/examples/mini-examples/scopes-example/pom.xml index d92615620..c3c29124d 100644 --- a/examples/mini-examples/scopes-example/pom.xml +++ b/examples/mini-examples/scopes-example/pom.xml @@ -5,7 +5,7 @@ mini-examples de.saxsys.mvvmfx - 1.6.0 + 1.7.0-SNAPSHOT 4.0.0 diff --git a/examples/mini-examples/synchronizefx-example/pom.xml b/examples/mini-examples/synchronizefx-example/pom.xml index c25d80ba8..355e80d9a 100644 --- a/examples/mini-examples/synchronizefx-example/pom.xml +++ b/examples/mini-examples/synchronizefx-example/pom.xml @@ -5,7 +5,7 @@ mini-examples de.saxsys.mvvmfx - 1.6.0 + 1.7.0-SNAPSHOT 4.0.0 diff --git a/examples/mini-examples/welcome-example/pom.xml b/examples/mini-examples/welcome-example/pom.xml index 59107068f..d8966d556 100644 --- a/examples/mini-examples/welcome-example/pom.xml +++ b/examples/mini-examples/welcome-example/pom.xml @@ -5,7 +5,7 @@ mini-examples de.saxsys.mvvmfx - 1.6.0 + 1.7.0-SNAPSHOT 4.0.0 diff --git a/examples/pom.xml b/examples/pom.xml index 903d3e289..0666e89fa 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -5,7 +5,7 @@ de.saxsys mvvmfx-parent - 1.6.0 + 1.7.0-SNAPSHOT de.saxsys.mvvmfx diff --git a/examples/todomvc-example/pom.xml b/examples/todomvc-example/pom.xml index f5d291f25..895fb3409 100644 --- a/examples/todomvc-example/pom.xml +++ b/examples/todomvc-example/pom.xml @@ -5,7 +5,7 @@ de.saxsys.mvvmfx examples - 1.6.0 + 1.7.0-SNAPSHOT 4.0.0 diff --git a/mvvmfx-archetype/pom.xml b/mvvmfx-archetype/pom.xml index 4ff6f1c08..b49afbe93 100644 --- a/mvvmfx-archetype/pom.xml +++ b/mvvmfx-archetype/pom.xml @@ -5,7 +5,7 @@ de.saxsys mvvmfx-parent - 1.6.0 + 1.7.0-SNAPSHOT diff --git a/mvvmfx-archetype/src/main/resources/archetype-resources/pom.xml b/mvvmfx-archetype/src/main/resources/archetype-resources/pom.xml index 86c619a92..c7a975808 100644 --- a/mvvmfx-archetype/src/main/resources/archetype-resources/pom.xml +++ b/mvvmfx-archetype/src/main/resources/archetype-resources/pom.xml @@ -16,7 +16,7 @@ de.saxsys mvvmfx-parent - 1.6.0 + 1.7.0-SNAPSHOT pom import diff --git a/mvvmfx-cdi/pom.xml b/mvvmfx-cdi/pom.xml index 562d0235a..3254c0b8c 100644 --- a/mvvmfx-cdi/pom.xml +++ b/mvvmfx-cdi/pom.xml @@ -20,7 +20,7 @@ de.saxsys mvvmfx-parent - 1.6.0 + 1.7.0-SNAPSHOT mvvmfx-cdi diff --git a/mvvmfx-easydi/pom.xml b/mvvmfx-easydi/pom.xml index b8315a1bf..7f1af2dc0 100644 --- a/mvvmfx-easydi/pom.xml +++ b/mvvmfx-easydi/pom.xml @@ -5,7 +5,7 @@ mvvmfx-parent de.saxsys - 1.6.0 + 1.7.0-SNAPSHOT 4.0.0 diff --git a/mvvmfx-guice/pom.xml b/mvvmfx-guice/pom.xml index 2dc50d965..fa5dc8864 100644 --- a/mvvmfx-guice/pom.xml +++ b/mvvmfx-guice/pom.xml @@ -20,7 +20,7 @@ de.saxsys mvvmfx-parent - 1.6.0 + 1.7.0-SNAPSHOT mvvmfx-guice diff --git a/mvvmfx-testing-utils/pom.xml b/mvvmfx-testing-utils/pom.xml index d36aaf67d..7c5268347 100644 --- a/mvvmfx-testing-utils/pom.xml +++ b/mvvmfx-testing-utils/pom.xml @@ -5,7 +5,7 @@ mvvmfx-parent de.saxsys - 1.6.0 + 1.7.0-SNAPSHOT 4.0.0 diff --git a/mvvmfx-utils/pom.xml b/mvvmfx-utils/pom.xml index 13d3d33b2..3a597e38b 100644 --- a/mvvmfx-utils/pom.xml +++ b/mvvmfx-utils/pom.xml @@ -5,7 +5,7 @@ mvvmfx-parent de.saxsys - 1.6.0 + 1.7.0-SNAPSHOT 4.0.0 diff --git a/mvvmfx-validation/pom.xml b/mvvmfx-validation/pom.xml index 6b7ef3f1c..9ecc30059 100644 --- a/mvvmfx-validation/pom.xml +++ b/mvvmfx-validation/pom.xml @@ -20,7 +20,7 @@ de.saxsys mvvmfx-parent - 1.6.0 + 1.7.0-SNAPSHOT mvvmfx-validation diff --git a/mvvmfx/pom.xml b/mvvmfx/pom.xml index 85162d2a8..0e5c2c8b0 100644 --- a/mvvmfx/pom.xml +++ b/mvvmfx/pom.xml @@ -20,7 +20,7 @@ de.saxsys mvvmfx-parent - 1.6.0 + 1.7.0-SNAPSHOT mvvmfx diff --git a/pom.xml b/pom.xml index a74e31d7e..b49e6494c 100644 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,7 @@ de.saxsys mvvmfx-parent pom - 1.6.0 + 1.7.0-SNAPSHOT mvvmFX parent Application Framework for MVVM with JavaFX. http://www.saxsys.de From 7d4ecc5ba51993270e81d1f2b953695c702d8db8 Mon Sep 17 00:00:00 2001 From: Gleb Date: Mon, 27 Feb 2017 22:29:00 +0200 Subject: [PATCH 02/51] Added @Initialize annotation for ViewModel to define initialize method of any visibility scope --- .../java/de/saxsys/mvvmfx/Initialize.java | 16 +++++++++++++ .../viewloader/ViewLoaderReflectionUtils.java | 23 +++++++++++++++---- 2 files changed, 34 insertions(+), 5 deletions(-) create mode 100644 mvvmfx/src/main/java/de/saxsys/mvvmfx/Initialize.java diff --git a/mvvmfx/src/main/java/de/saxsys/mvvmfx/Initialize.java b/mvvmfx/src/main/java/de/saxsys/mvvmfx/Initialize.java new file mode 100644 index 000000000..15e25bb75 --- /dev/null +++ b/mvvmfx/src/main/java/de/saxsys/mvvmfx/Initialize.java @@ -0,0 +1,16 @@ +package de.saxsys.mvvmfx; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * + * @author Gleb Koval + * + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface Initialize { +} diff --git a/mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/viewloader/ViewLoaderReflectionUtils.java b/mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/viewloader/ViewLoaderReflectionUtils.java index 30fe3cbc7..ddfdc2515 100644 --- a/mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/viewloader/ViewLoaderReflectionUtils.java +++ b/mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/viewloader/ViewLoaderReflectionUtils.java @@ -16,6 +16,7 @@ package de.saxsys.mvvmfx.internal.viewloader; import de.saxsys.mvvmfx.Context; +import de.saxsys.mvvmfx.Initialize; import de.saxsys.mvvmfx.InjectContext; import de.saxsys.mvvmfx.InjectScope; import de.saxsys.mvvmfx.InjectViewModel; @@ -372,10 +373,10 @@ public static , ViewModelType ext } /** - * If a ViewModel has a method with the signature - * public void initialize() it will be invoked. If no such - * method is available nothing happens. - * + * If a ViewModel has a method annotated with {@link Initialize} + * or method with the signature public void initialize() + * it will be invoked. If no such method is available nothing happens. + * * @param viewModel * the viewModel that's initialize method (if available) will be * invoked. @@ -387,7 +388,9 @@ public static void initializeViewModel(ViewMod return; } try { - final Method initMethod = viewModel.getClass().getMethod("initialize"); + Method annotatedMethod = getInitializeMethod(viewModel); + // find method annotated with @Initialize or use initialize() otherwise + final Method initMethod = annotatedMethod != null ? annotatedMethod : viewModel.getClass().getMethod("initialize"); // if there is a @PostConstruct annotation, throw an exception to prevent double injection if(initMethod.isAnnotationPresent(PostConstruct.class)) { throw new IllegalStateException(String.format("initialize method of ViewModel [%s] is annotated with @PostConstruct. " + @@ -410,6 +413,16 @@ public static void initializeViewModel(ViewMod } } + private static Method getInitializeMethod(ViewModelType viewModel) { + for (Method method : viewModel.getClass().getDeclaredMethods()) { + if(method.isAnnotationPresent(Initialize.class)) { + method.setAccessible(true); + return method; + } + } + return null; + } + /** * This method adds listeners for the {@link SceneLifecycle}. */ From 9fb1c188f9a12bfa17dbc86679bba33d40e48061 Mon Sep 17 00:00:00 2001 From: Manuel Mauky Date: Fri, 3 Mar 2017 14:11:56 +0100 Subject: [PATCH 03/51] update javadoc links in README --- README.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index b6a9cf912..dcb96e94e 100644 --- a/README.md +++ b/README.md @@ -56,9 +56,11 @@ If you need help you can use the forums on [Google Groups](https://groups.google ### Links - [Project Page](http://sialcasa.github.io/mvvmFX/) -- [javadoc mvvmfx core](http://sialcasa.github.io/mvvmFX/javadoc/1.5.0/mvvmfx/) -- [javadoc mvvmfx-cdi](http://sialcasa.github.io/mvvmFX/javadoc/1.5.0/mvvmfx-cdi/) -- [javadoc mvvmfx-guice](http://sialcasa.github.io/mvvmFX/javadoc/1.5.0/mvvmfx-guice/) -- [javadoc mvvmfx-utils](http://sialcasa.github.io/mvvmFX/javadoc/1.5.0/mvvmfx-utils/) -- [javadoc mvvmfx-testing-utils](http://sialcasa.github.io/mvvmFX/javadoc/1.5.0/mvvmfx-testing-utils/) +- [javadoc mvvmfx core](http://sialcasa.github.io/mvvmFX/javadoc/1.6.0/mvvmfx/) +- [javadoc mvvmfx-cdi](http://sialcasa.github.io/mvvmFX/javadoc/1.6.0/mvvmfx-cdi/) +- [javadoc mvvmfx-guice](http://sialcasa.github.io/mvvmFX/javadoc/1.6.0/mvvmfx-guice/) +- [javadoc mvvmfx-easydi](http://sialcasa.github.io/mvvmFX/javadoc/1.6.0/mvvmfx-easydi/) +- [javadoc mvvmfx-validation](http://sialcasa.github.io/mvvmFX/javadoc/1.6.0/mvvmfx-validation/) +- [javadoc mvvmfx-utils](http://sialcasa.github.io/mvvmFX/javadoc/1.6.0/mvvmfx-utils/) +- [javadoc mvvmfx-testing-utils](http://sialcasa.github.io/mvvmFX/javadoc/1.6.0/mvvmfx-testing-utils/) From f6e8cb429368238dfba7f0bcc2f84a1fafd9401f Mon Sep 17 00:00:00 2001 From: Gleb Date: Thu, 20 Apr 2017 18:30:25 +0300 Subject: [PATCH 04/51] Added license header and short description with example --- .../java/de/saxsys/mvvmfx/Initialize.java | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/mvvmfx/src/main/java/de/saxsys/mvvmfx/Initialize.java b/mvvmfx/src/main/java/de/saxsys/mvvmfx/Initialize.java index 15e25bb75..ea357794c 100644 --- a/mvvmfx/src/main/java/de/saxsys/mvvmfx/Initialize.java +++ b/mvvmfx/src/main/java/de/saxsys/mvvmfx/Initialize.java @@ -1,3 +1,18 @@ +/******************************************************************************* + * Copyright 2017 Gleb Koval + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ package de.saxsys.mvvmfx; import java.lang.annotation.ElementType; @@ -6,6 +21,27 @@ import java.lang.annotation.Target; /** + * + * This annotation is used to mark the method in a ViewModel to be called after all mvvmFx injections. + * If no method is marked, public initialize() method will be used, if present.
+ * Example:
+ *
+ * + *
+ * public class SomeViewModel implements {@link ViewModel} {
+ *
+ *         // mvvmFx injections
+ *        {@literal @}{@link InjectScope}
+ *         private SomeScope someScope;
+ *         ...
+ *
+ *        {@literal @}Initialize
+ *         private void init() {
+ *             someScope.subscribe(...);
+ *             ...
+ *         }
+ * }
+ * 
* * @author Gleb Koval * From 8265b3ce704cb63bd7c0f7da494d9e52954c0098 Mon Sep 17 00:00:00 2001 From: Gleb Date: Thu, 20 Apr 2017 20:41:00 +0300 Subject: [PATCH 05/51] Changed ReflectionUtils.accessField(Field, Callable, String) method signature to ReflectionUtils.accessMember(AccessibleObject, Callable, String) so that we could invoke callback on methods too. Changed ViewLoaderReflectionUtils to use ReflectionUtils for invocation of an initialize method --- .../internal/viewloader/ReflectionUtils.java | 43 ++++++++++--------- .../viewloader/ViewLoaderReflectionUtils.java | 23 ++++------ 2 files changed, 30 insertions(+), 36 deletions(-) diff --git a/mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/viewloader/ReflectionUtils.java b/mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/viewloader/ReflectionUtils.java index c9b681bbb..71939838e 100644 --- a/mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/viewloader/ReflectionUtils.java +++ b/mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/viewloader/ReflectionUtils.java @@ -16,6 +16,7 @@ package de.saxsys.mvvmfx.internal.viewloader; import java.lang.annotation.Annotation; +import java.lang.reflect.AccessibleObject; import java.lang.reflect.Field; import java.security.AccessController; import java.security.PrivilegedAction; @@ -89,13 +90,13 @@ public static List getFieldsFromClassHierarchy(Class type) { /** - * Helper method to execute a callback on a given field. This method encapsulates the error handling logic and the - * handling of accessibility of the field. + * Helper method to execute a callback on a given member. This method encapsulates the error handling logic and the + * handling of accessibility of the member. * - * After the callback is executed the accessibility of the field will be reset to the originally state. + * After the callback is executed the accessibility of the member will be reset to the originally state. * - * @param field - * the field that is made accessible to run the callback + * @param member + * the member that is made accessible to run the callback * @param callable * the callback that will be executed. * @param errorMessage @@ -106,19 +107,19 @@ public static List getFieldsFromClassHierarchy(Class type) { * @throws IllegalStateException * when something went wrong. */ - public static T accessField(final Field field, final Callable callable, String errorMessage) { + public static T accessMember(final AccessibleObject member, final Callable callable, String errorMessage) { if (callable == null) { return null; } return AccessController.doPrivileged((PrivilegedAction) () -> { - boolean wasAccessible = field.isAccessible(); + boolean wasAccessible = member.isAccessible(); try { - field.setAccessible(true); + member.setAccessible(true); return callable.call(); } catch (Exception exception) { throw new IllegalStateException(errorMessage, exception); } finally { - field.setAccessible(wasAccessible); + member.setAccessible(wasAccessible); } }); } @@ -136,21 +137,21 @@ public static T accessField(final Field field, final Callable callable, S * the new value that the field should be set to. */ public static void setField(final Field field, Object target, Object value) { - accessField(field, () -> field.set(target, value), + accessMember(field, () -> field.set(target, value), "Cannot set the field [" + field.getName() + "] of instance [" + target + "] to value [" + value + "]"); } /** - * Helper method to execute a callback on a given field. This method encapsulates the error handling logic and the - * handling of accessibility of the field. The difference to - * {@link ReflectionUtils#accessField(Field, Callable, String)} is that this method takes a callback that doesn't + * Helper method to execute a callback on a given member. This method encapsulates the error handling logic and the + * handling of accessibility of the member. The difference to + * {@link ReflectionUtils#accessMember(AccessibleObject, Callable, String)} is that this method takes a callback that doesn't * return anything but only creates a sideeffect. * - * After the callback is executed the accessibility of the field will be reset to the originally state. + * After the callback is executed the accessibility of the member will be reset to the originally state. * - * @param field - * the field that is made accessible to run the callback + * @param member + * the member that is made accessible to run the callback * @param sideEffect * the callback that will be executed. * @param errorMessage @@ -159,19 +160,19 @@ public static void setField(final Field field, Object target, Object value) { * @throws IllegalStateException * when something went wrong. */ - public static void accessField(final Field field, final SideEffect sideEffect, String errorMessage) { + public static void accessMember(final AccessibleObject member, final SideEffect sideEffect, String errorMessage) { if (sideEffect == null) { return; } - AccessController.doPrivileged((PrivilegedAction) () -> { - boolean wasAccessible = field.isAccessible(); + AccessController.doPrivileged((PrivilegedAction) () -> { + boolean wasAccessible = member.isAccessible(); try { - field.setAccessible(true); + member.setAccessible(true); sideEffect.call(); } catch (Exception exception) { throw new IllegalStateException(errorMessage, exception); } finally { - field.setAccessible(wasAccessible); + member.setAccessible(wasAccessible); } return null; }); diff --git a/mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/viewloader/ViewLoaderReflectionUtils.java b/mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/viewloader/ViewLoaderReflectionUtils.java index ddfdc2515..a397289ab 100644 --- a/mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/viewloader/ViewLoaderReflectionUtils.java +++ b/mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/viewloader/ViewLoaderReflectionUtils.java @@ -31,7 +31,6 @@ import javax.annotation.PostConstruct; import java.lang.annotation.Annotation; import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.security.AccessController; import java.security.PrivilegedAction; @@ -167,7 +166,7 @@ public static , ViewModelType ext Optional fieldOptional = getViewModelField(view.getClass(), viewModelType); if (fieldOptional.isPresent()) { Field field = fieldOptional.get(); - return ReflectionUtils.accessField(field, () -> (ViewModelType) field.get(view), + return ReflectionUtils.accessMember(field, () -> (ViewModelType) field.get(view), "Can't get the viewModel of type <" + viewModelType + ">"); } else { return null; @@ -190,7 +189,7 @@ public static void injectViewModel(final View view, ViewModel viewModel) { final Optional fieldOptional = getViewModelField(view.getClass(), viewModel.getClass()); if (fieldOptional.isPresent()) { Field field = fieldOptional.get(); - ReflectionUtils.accessField(field, () -> { + ReflectionUtils.accessMember(field, () -> { Object existingViewModel = field.get(view); if (existingViewModel == null) { field.set(view, viewModel); @@ -259,7 +258,7 @@ public static , VM extends ViewModel> void createAn if (fieldOptional.isPresent()) { Field field = fieldOptional.get(); - ReflectionUtils.accessField(field, () -> { + ReflectionUtils.accessMember(field, () -> { Object existingViewModel = field.get(view); if (existingViewModel == null) { @@ -295,7 +294,7 @@ static void createAndInjectScopes(Object viewModel, ContextImpl context) { List scopeFields = getScopeFields(viewModel.getClass()); scopeFields.forEach(scopeField -> { - ReflectionUtils.accessField(scopeField, () -> injectScopeIntoField(scopeField, viewModel, context), + ReflectionUtils.accessMember(scopeField, () -> injectScopeIntoField(scopeField, viewModel, context), "Can't inject Scope into ViewModel <" + viewModel.getClass() + ">"); }); } @@ -306,7 +305,7 @@ public static void injectContext(View codeBehind, ContextImpl context) { if (contextField.isPresent()) { Field field = contextField.get(); - ReflectionUtils.accessField(field, () -> { + ReflectionUtils.accessMember(field, () -> { field.set(codeBehind, context); }, "Can't inject Context into the view <" + codeBehind + ">"); } @@ -400,14 +399,9 @@ public static void initializeViewModel(ViewMod "https://github.com/sialcasa/mvvmFX/wiki/Dependency-Injection#lifecycle-postconstruct", viewModel)); } - AccessController.doPrivileged((PrivilegedAction) () -> { - try { - return initMethod.invoke(viewModel); - } catch (InvocationTargetException | IllegalAccessException e) { - throw new IllegalStateException( - "mvvmFX wasn't able to call the initialize method of ViewModel [" + viewModel + "].", e); - } - }); + AccessController.doPrivileged((PrivilegedAction) () -> + ReflectionUtils.accessMember(initMethod, () -> initMethod.invoke(viewModel), "mvvmFX wasn't able to call the initialize method of ViewModel [" + viewModel + "].")); + } catch (NoSuchMethodException e) { // it's perfectly fine that a ViewModel has no initialize method. } @@ -416,7 +410,6 @@ public static void initializeViewModel(ViewMod private static Method getInitializeMethod(ViewModelType viewModel) { for (Method method : viewModel.getClass().getDeclaredMethods()) { if(method.isAnnotationPresent(Initialize.class)) { - method.setAccessible(true); return method; } } From 37ccef019da879934ee80a350935df97b72bcd12 Mon Sep 17 00:00:00 2001 From: Gleb Date: Thu, 20 Apr 2017 20:53:20 +0300 Subject: [PATCH 06/51] Call initMethod invocation without AccessController.doPrivileged() --- .../mvvmfx/internal/viewloader/ViewLoaderReflectionUtils.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/viewloader/ViewLoaderReflectionUtils.java b/mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/viewloader/ViewLoaderReflectionUtils.java index a397289ab..a9a6286eb 100644 --- a/mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/viewloader/ViewLoaderReflectionUtils.java +++ b/mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/viewloader/ViewLoaderReflectionUtils.java @@ -399,9 +399,7 @@ public static void initializeViewModel(ViewMod "https://github.com/sialcasa/mvvmFX/wiki/Dependency-Injection#lifecycle-postconstruct", viewModel)); } - AccessController.doPrivileged((PrivilegedAction) () -> - ReflectionUtils.accessMember(initMethod, () -> initMethod.invoke(viewModel), "mvvmFX wasn't able to call the initialize method of ViewModel [" + viewModel + "].")); - + ReflectionUtils.accessMember(initMethod, () -> initMethod.invoke(viewModel), "mvvmFX wasn't able to call the initialize method of ViewModel [" + viewModel + "]."); } catch (NoSuchMethodException e) { // it's perfectly fine that a ViewModel has no initialize method. } From e81771c4cb640a16db248eaa7471fdddc675624a Mon Sep 17 00:00:00 2001 From: Gleb Date: Thu, 20 Apr 2017 21:34:37 +0300 Subject: [PATCH 07/51] Added unit test for a ViewModel initialized with @Initialize --- .../FluentViewLoader_FxmlView_Test.java | 16 +++++++++ ...wWithViewModelWithAnnotatedInitialize.java | 32 +++++++++++++++++ .../TestViewModelWithAnnotatedInitialize.java | 35 +++++++++++++++++++ ...wWithViewModelWithAnnotatedInitialize.fxml | 6 ++++ 4 files changed, 89 insertions(+) create mode 100644 mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewWithViewModelWithAnnotatedInitialize.java create mode 100644 mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/example/TestViewModelWithAnnotatedInitialize.java create mode 100644 mvvmfx/src/test/resources/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewWithViewModelWithAnnotatedInitialize.fxml diff --git a/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/FluentViewLoader_FxmlView_Test.java b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/FluentViewLoader_FxmlView_Test.java index 20f324120..aed33a79e 100644 --- a/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/FluentViewLoader_FxmlView_Test.java +++ b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/FluentViewLoader_FxmlView_Test.java @@ -470,4 +470,20 @@ public void testExistingViewModelWithoutInjectionInView() { assertThat(TestViewModel.wasInitialized).isFalse(); } + + /** + * Method annotated with {@link de.saxsys.mvvmfx.Initialize} annotation initializes the ViewModel + * */ + @Test + public void testViewModelIsInitializedWithAnnotatatedMethod() { + TestViewModelWithAnnotatedInitialize.wasInitialized = false; + + ViewTuple tuple + = FluentViewLoader.fxmlView(TestFxmlViewWithViewModelWithAnnotatedInitialize.class).load(); + + TestViewModelWithAnnotatedInitialize viewModel = tuple.getViewModel(); + + assertThat(TestViewModelWithAnnotatedInitialize.wasInitialized).isTrue(); + + } } diff --git a/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewWithViewModelWithAnnotatedInitialize.java b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewWithViewModelWithAnnotatedInitialize.java new file mode 100644 index 000000000..2358e739b --- /dev/null +++ b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewWithViewModelWithAnnotatedInitialize.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright 2017 Gleb Koval + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ +package de.saxsys.mvvmfx.internal.viewloader.example; + +import de.saxsys.mvvmfx.FxmlView; +import de.saxsys.mvvmfx.InjectViewModel; + +/** + * This class is used as example View class that uses ViewModel initialized with + * method annotated with {@link de.saxsys.mvvmfx.Initialize} annotation + * + * @author Gleb Koval + */ +public class TestFxmlViewWithViewModelWithAnnotatedInitialize implements FxmlView { + + @InjectViewModel + private TestViewModelWithAnnotatedInitialize viewModel; + +} diff --git a/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/example/TestViewModelWithAnnotatedInitialize.java b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/example/TestViewModelWithAnnotatedInitialize.java new file mode 100644 index 000000000..791abc9f7 --- /dev/null +++ b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/example/TestViewModelWithAnnotatedInitialize.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright 2017 Gleb Koval + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ +package de.saxsys.mvvmfx.internal.viewloader.example; + +import de.saxsys.mvvmfx.Initialize; +import de.saxsys.mvvmfx.ViewModel; + +/** + * This class is used as example ViewModel class that uses init method annotated with {@link Initialize} + * + * @author Gleb Koval + */ +public class TestViewModelWithAnnotatedInitialize implements ViewModel { + + public static boolean wasInitialized = false; + + @Initialize + private void init() { + wasInitialized = true; + } + +} diff --git a/mvvmfx/src/test/resources/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewWithViewModelWithAnnotatedInitialize.fxml b/mvvmfx/src/test/resources/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewWithViewModelWithAnnotatedInitialize.fxml new file mode 100644 index 000000000..7bad2467f --- /dev/null +++ b/mvvmfx/src/test/resources/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewWithViewModelWithAnnotatedInitialize.fxml @@ -0,0 +1,6 @@ + + + + + + From 026368c7551cab161293e8465f82767331b942fc Mon Sep 17 00:00:00 2001 From: Manuel Mauky Date: Mon, 15 May 2017 15:19:11 +0200 Subject: [PATCH 08/51] Fix maven dependencies for ControlsFX library and junit. #482 --- mvvmfx-testing-utils/pom.xml | 1 + mvvmfx-validation/pom.xml | 3 ++- mvvmfx/pom.xml | 7 ------- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/mvvmfx-testing-utils/pom.xml b/mvvmfx-testing-utils/pom.xml index 7c5268347..24f1348cd 100644 --- a/mvvmfx-testing-utils/pom.xml +++ b/mvvmfx-testing-utils/pom.xml @@ -25,6 +25,7 @@ junit junit + compile org.mockito diff --git a/mvvmfx-validation/pom.xml b/mvvmfx-validation/pom.xml index 9ecc30059..5478accd1 100644 --- a/mvvmfx-validation/pom.xml +++ b/mvvmfx-validation/pom.xml @@ -38,7 +38,8 @@ org.controlsfx controlsfx - 8.40.9 + 8.40.12 + provided diff --git a/mvvmfx/pom.xml b/mvvmfx/pom.xml index 0e5c2c8b0..f7ea64ed5 100644 --- a/mvvmfx/pom.xml +++ b/mvvmfx/pom.xml @@ -44,13 +44,6 @@ doc-annotations - - org.controlsfx - controlsfx - 8.40.9 - provided - - From 663defe8f4b27d8b52580c43bbf7a2f7068e27c9 Mon Sep 17 00:00:00 2001 From: Manuel Mauky Date: Mon, 15 May 2017 16:05:28 +0200 Subject: [PATCH 09/51] Improve initialize annotation support for viewModels --- .../java/de/saxsys/mvvmfx/Initialize.java | 14 ++++- .../viewloader/ViewLoaderReflectionUtils.java | 63 ++++++++++++------- .../FluentViewLoader_FxmlView_Test.java | 14 +++++ ...odelWithMultipleInitializeAnnotations.java | 11 ++++ ...odelWithMultipleInitializeAnnotations.java | 28 +++++++++ ...odelWithMultipleInitializeAnnotations.fxml | 7 +++ 6 files changed, 110 insertions(+), 27 deletions(-) create mode 100644 mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewWithViewModelWithMultipleInitializeAnnotations.java create mode 100644 mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/example/TestViewModelWithMultipleInitializeAnnotations.java create mode 100644 mvvmfx/src/test/resources/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewWithViewModelWithMultipleInitializeAnnotations.fxml diff --git a/mvvmfx/src/main/java/de/saxsys/mvvmfx/Initialize.java b/mvvmfx/src/main/java/de/saxsys/mvvmfx/Initialize.java index ea357794c..681cbf263 100644 --- a/mvvmfx/src/main/java/de/saxsys/mvvmfx/Initialize.java +++ b/mvvmfx/src/main/java/de/saxsys/mvvmfx/Initialize.java @@ -22,8 +22,16 @@ /** * - * This annotation is used to mark the method in a ViewModel to be called after all mvvmFx injections. - * If no method is marked, public initialize() method will be used, if present.
+ * This annotation is used to mark a method in a ViewModel to be called after all mvvmFX injections are finished. + * It's possible to use this annotation on multiple methods in the ViewModel and all these methods will be invoked. + * However, you may not depend on any invocation order.
+ * + * This approach can be combined with the default naming convention of JavaFX. By this convention a method + * with the signature public initialize() will be invoked after initialization is finished.
+ * + * While the naming convention approach only works with a "public" method, this {@link Initialize} annotation can + * also be used on "private"/"protected"/default scope methods. + * * Example:
*
* @@ -43,7 +51,7 @@ * } * * - * @author Gleb Koval + * @author Gleb Koval, Manuel Mauky * */ @Retention(RetentionPolicy.RUNTIME) diff --git a/mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/viewloader/ViewLoaderReflectionUtils.java b/mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/viewloader/ViewLoaderReflectionUtils.java index a9a6286eb..c49447fe4 100644 --- a/mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/viewloader/ViewLoaderReflectionUtils.java +++ b/mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/viewloader/ViewLoaderReflectionUtils.java @@ -34,6 +34,9 @@ import java.lang.reflect.Method; import java.security.AccessController; import java.security.PrivilegedAction; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.List; import java.util.Optional; import java.util.function.Consumer; @@ -386,34 +389,46 @@ public static void initializeViewModel(ViewMod if (viewModel == null) { return; } - try { - Method annotatedMethod = getInitializeMethod(viewModel); - // find method annotated with @Initialize or use initialize() otherwise - final Method initMethod = annotatedMethod != null ? annotatedMethod : viewModel.getClass().getMethod("initialize"); - // if there is a @PostConstruct annotation, throw an exception to prevent double injection - if(initMethod.isAnnotationPresent(PostConstruct.class)) { - throw new IllegalStateException(String.format("initialize method of ViewModel [%s] is annotated with @PostConstruct. " + - "This will lead to unexpected behaviour and duplicate initialization. " + - "Please rename the method or remove the @PostConstruct annotation. " + - "See mvvmFX wiki for more details: " + - "https://github.com/sialcasa/mvvmFX/wiki/Dependency-Injection#lifecycle-postconstruct", viewModel)); - } - ReflectionUtils.accessMember(initMethod, () -> initMethod.invoke(viewModel), "mvvmFX wasn't able to call the initialize method of ViewModel [" + viewModel + "]."); - } catch (NoSuchMethodException e) { - // it's perfectly fine that a ViewModel has no initialize method. - } - } + final Collection initializeMethods = getInitializeMethods(viewModel.getClass()); - private static Method getInitializeMethod(ViewModelType viewModel) { - for (Method method : viewModel.getClass().getDeclaredMethods()) { - if(method.isAnnotationPresent(Initialize.class)) { - return method; - } - } - return null; + initializeMethods.forEach(initMethod -> { + // if there is a @PostConstruct annotation, throw an exception to prevent double injection + if(initMethod.isAnnotationPresent(PostConstruct.class)) { + throw new IllegalStateException(String.format("initialize method of ViewModel [%s] is annotated with @PostConstruct. " + + "This will lead to unexpected behaviour and duplicate initialization. " + + "Please rename the method or remove the @PostConstruct annotation. " + + "See mvvmFX wiki for more details: " + + "https://github.com/sialcasa/mvvmFX/wiki/Dependency-Injection#lifecycle-postconstruct", viewModel)); + } + + ReflectionUtils.accessMember(initMethod, () -> initMethod.invoke(viewModel), "mvvmFX wasn't able to call the initialize method of ViewModel [" + viewModel + "]."); + }); } + /** + * Returns a collection of {@link Method}s that represent initializer methods. + * A method is an "initializer method" if it either:
+ *
    + *
  1. has a signature of "public void initialize()"
  2. + *
  3. is annotated with {@link Initialize}
  4. + *
+ */ + private static Collection getInitializeMethods(Class classType) { + final List initializeMethods = new ArrayList<>(); + Arrays.stream(classType.getMethods()) + .filter(method -> "initialize".equals(method.getName())) + .findAny() + .ifPresent(initializeMethods::add); + + Arrays.stream(classType.getDeclaredMethods()) + .filter(method -> method.isAnnotationPresent(Initialize.class)) + .forEach(initializeMethods::add); + + return initializeMethods; + } + + /** * This method adds listeners for the {@link SceneLifecycle}. */ diff --git a/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/FluentViewLoader_FxmlView_Test.java b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/FluentViewLoader_FxmlView_Test.java index aed33a79e..813124f42 100644 --- a/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/FluentViewLoader_FxmlView_Test.java +++ b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/FluentViewLoader_FxmlView_Test.java @@ -486,4 +486,18 @@ public void testViewModelIsInitializedWithAnnotatatedMethod() { assertThat(TestViewModelWithAnnotatedInitialize.wasInitialized).isTrue(); } + + @Test + public void testViewModelHasMultipleInitializeAnnotations() { + TestViewModelWithMultipleInitializeAnnotations.init1 = false; + TestViewModelWithMultipleInitializeAnnotations.init2 = false; + TestViewModelWithMultipleInitializeAnnotations.initialize = false; + + ViewTuple viewTuple = FluentViewLoader + .fxmlView(TestFxmlViewWithViewModelWithMultipleInitializeAnnotations.class).load(); + + assertThat(TestViewModelWithMultipleInitializeAnnotations.init1).isTrue(); + assertThat(TestViewModelWithMultipleInitializeAnnotations.init2).isTrue(); + assertThat(TestViewModelWithMultipleInitializeAnnotations.initialize).isTrue(); + } } diff --git a/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewWithViewModelWithMultipleInitializeAnnotations.java b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewWithViewModelWithMultipleInitializeAnnotations.java new file mode 100644 index 000000000..8cd41ac74 --- /dev/null +++ b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewWithViewModelWithMultipleInitializeAnnotations.java @@ -0,0 +1,11 @@ +package de.saxsys.mvvmfx.internal.viewloader.example; + +import de.saxsys.mvvmfx.FxmlView; +import de.saxsys.mvvmfx.InjectViewModel; + +public class TestFxmlViewWithViewModelWithMultipleInitializeAnnotations implements FxmlView { + + @InjectViewModel + private TestViewModelWithMultipleInitializeAnnotations viewModel; + +} diff --git a/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/example/TestViewModelWithMultipleInitializeAnnotations.java b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/example/TestViewModelWithMultipleInitializeAnnotations.java new file mode 100644 index 000000000..43eefbd14 --- /dev/null +++ b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/example/TestViewModelWithMultipleInitializeAnnotations.java @@ -0,0 +1,28 @@ +package de.saxsys.mvvmfx.internal.viewloader.example; + +import de.saxsys.mvvmfx.Initialize; +import de.saxsys.mvvmfx.ViewModel; + +public class TestViewModelWithMultipleInitializeAnnotations implements ViewModel { + + public static boolean init1 = false; + public static boolean init2 = false; + public static boolean initialize = false; + + + @Initialize + public void init1() { + init1 = true; + } + + @Initialize + private void init2(){ + init2 = true; + } + + public void initialize() { + initialize = true; + } + + +} diff --git a/mvvmfx/src/test/resources/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewWithViewModelWithMultipleInitializeAnnotations.fxml b/mvvmfx/src/test/resources/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewWithViewModelWithMultipleInitializeAnnotations.fxml new file mode 100644 index 000000000..0eeff62ae --- /dev/null +++ b/mvvmfx/src/test/resources/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewWithViewModelWithMultipleInitializeAnnotations.fxml @@ -0,0 +1,7 @@ + + + + + + From 9b49686077764bdd5e52f36c0fc20f1fe900f232 Mon Sep 17 00:00:00 2001 From: gbalderas Date: Fri, 19 May 2017 18:23:28 +0200 Subject: [PATCH 10/51] 267 extend notification test helper (#487) * fix #267 add access to received notification list --- .../notifications/NotificationTestHelper.java | 8 +++ .../NotificationTestHelperTest.java | 49 +++++++++++++++++-- 2 files changed, 54 insertions(+), 3 deletions(-) diff --git a/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/notifications/NotificationTestHelper.java b/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/notifications/NotificationTestHelper.java index a911aa50c..8af397e14 100644 --- a/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/notifications/NotificationTestHelper.java +++ b/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/notifications/NotificationTestHelper.java @@ -111,6 +111,14 @@ public void receivedNotification(String key, Object... payload) { notifications.add(new Pair<>(key, payload)); } + /** + * @return the list of received notifications + */ + public List> getReceivedNotifications() { + waitForUiThread(); + return notifications; + } + /** * @return the number of received notifications. */ diff --git a/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/notifications/NotificationTestHelperTest.java b/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/notifications/NotificationTestHelperTest.java index cf532e97e..751644db4 100644 --- a/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/notifications/NotificationTestHelperTest.java +++ b/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/notifications/NotificationTestHelperTest.java @@ -15,12 +15,14 @@ ******************************************************************************/ package de.saxsys.mvvmfx.utils.notifications; -import static org.assertj.core.api.Assertions.*; - +import de.saxsys.mvvmfx.ViewModel; +import javafx.util.Pair; import org.junit.Before; import org.junit.Test; -import de.saxsys.mvvmfx.ViewModel; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; /** * @author manuel.mauky @@ -114,4 +116,45 @@ public void timeout() { assertThat(helper.numberOfReceivedNotifications()).isEqualTo(1); } + + @Test + public void notificationList() { + NotificationTestHelper helper = new NotificationTestHelper(); + viewModel.subscribe("test", helper); + viewModel.subscribe("something", helper); + + viewModel.publish("test", 1, "a", 2, 3, "b"); + + Pair notification1 = helper.getReceivedNotifications().get(0); + + assertThat(notification1.getValue().length).isEqualTo(5); + long integerValueCount = Stream.of(notification1.getValue()) + .filter(v -> v instanceof Integer) + .count(); + + long stringValueCount = Stream.of(notification1.getValue()) + .filter(v -> v instanceof String) + .count(); + + assertThat(integerValueCount).isEqualTo(3); + assertThat(stringValueCount).isEqualTo(2); + assertThat(notification1.getValue()[1]).isEqualTo("a"); + + //second message + viewModel.publish("test", false); + Pair notification2 = helper.getReceivedNotifications().get(1); + assertThat(notification2.getKey()).isEqualTo("test"); + assertThat(notification2.getValue()[0]).isEqualTo(false); + + //getting an empty message + viewModel.publish("something"); + Pair notification3 = helper.getReceivedNotifications().get(2); + assertThat(notification3.getKey()).isEqualTo("something"); + assertThat(notification3.getValue()).isEmpty(); + + //message should not be received + viewModel.publish("some other message"); + assertThat(helper.getReceivedNotifications().size()).isEqualTo(3); + + } } From 925c8fe03d281d5cbf260495af16683523e849cc Mon Sep 17 00:00:00 2001 From: gbalderas Date: Mon, 22 May 2017 14:38:41 +0200 Subject: [PATCH 11/51] fix #481. CompositeValidator provides list of validators (#486) --- .../utils/validation/CompositeValidator.java | 15 ++- .../validation/CompositeValidatorTest.java | 92 +++++++++++++++++-- 2 files changed, 96 insertions(+), 11 deletions(-) diff --git a/mvvmfx-validation/src/main/java/de/saxsys/mvvmfx/utils/validation/CompositeValidator.java b/mvvmfx-validation/src/main/java/de/saxsys/mvvmfx/utils/validation/CompositeValidator.java index 7cb1fb1d3..9fab2cd53 100644 --- a/mvvmfx-validation/src/main/java/de/saxsys/mvvmfx/utils/validation/CompositeValidator.java +++ b/mvvmfx-validation/src/main/java/de/saxsys/mvvmfx/utils/validation/CompositeValidator.java @@ -15,15 +15,15 @@ ******************************************************************************/ package de.saxsys.mvvmfx.utils.validation; -import java.util.HashMap; -import java.util.Map; - import javafx.beans.property.ListProperty; import javafx.beans.property.SimpleListProperty; import javafx.collections.FXCollections; import javafx.collections.ListChangeListener; import javafx.collections.ObservableList; +import java.util.HashMap; +import java.util.Map; + /** * This {@link Validator} implementation is used to compose multiple other validators. *

@@ -33,7 +33,7 @@ */ public class CompositeValidator implements Validator { - CompositeValidationStatus status = new CompositeValidationStatus(); + private CompositeValidationStatus status = new CompositeValidationStatus(); private ListProperty validators = new SimpleListProperty<>(FXCollections.observableArrayList()); private Map> listenerMap = new HashMap<>(); @@ -91,6 +91,13 @@ public CompositeValidator(Validator... validators) { } + /** + * @return an unmodifiable observable list of validators composed by this CompositeValidator. + */ + public ObservableList getValidators() { + return FXCollections.unmodifiableObservableList(this.validators); + } + public void addValidators(Validator... validators) { this.validators.addAll(validators); } diff --git a/mvvmfx-validation/src/test/java/de/saxsys/mvvmfx/utils/validation/CompositeValidatorTest.java b/mvvmfx-validation/src/test/java/de/saxsys/mvvmfx/utils/validation/CompositeValidatorTest.java index d35d58495..ac533bd5f 100644 --- a/mvvmfx-validation/src/test/java/de/saxsys/mvvmfx/utils/validation/CompositeValidatorTest.java +++ b/mvvmfx-validation/src/test/java/de/saxsys/mvvmfx/utils/validation/CompositeValidatorTest.java @@ -15,20 +15,18 @@ ******************************************************************************/ package de.saxsys.mvvmfx.utils.validation; -import static org.assertj.core.api.Assertions.assertThat; - import com.google.common.base.Strings; - +import javafx.beans.binding.Bindings; +import javafx.beans.binding.IntegerBinding; +import javafx.beans.property.*; import org.junit.Before; import org.junit.Test; import java.util.List; +import java.util.function.Predicate; import java.util.stream.Collectors; -import javafx.beans.property.BooleanProperty; -import javafx.beans.property.SimpleBooleanProperty; -import javafx.beans.property.SimpleStringProperty; -import javafx.beans.property.StringProperty; +import static org.assertj.core.api.Assertions.assertThat; /** * @author manuel.mauky @@ -246,4 +244,84 @@ private List asStrings(List messages) { .map(ValidationMessage::getMessage) .collect(Collectors.toList()); } + + + /** + * Issue #413 + */ + @Test + public void validatorPercentageTest(){ + + final IntegerProperty integerProperty1 = new SimpleIntegerProperty(30); + final IntegerProperty integerProperty2 = new SimpleIntegerProperty(-20); + final IntegerProperty integerProperty3 = new SimpleIntegerProperty(35); + final IntegerProperty integerProperty4 = new SimpleIntegerProperty(55); + + Predicate predicate1 = v -> v.doubleValue() > 50; + + final Validator validator1 = new FunctionBasedValidator<>(integerProperty1, predicate1, ValidationMessage.error("Value must be bigger than 50")); + final Validator validator2 = new FunctionBasedValidator<>(integerProperty2, predicate1, ValidationMessage.error("Value must be bigger than 50")); + final Validator validator3 = new FunctionBasedValidator<>(integerProperty3, predicate1, ValidationMessage.error("Value must be bigger than 50")); + final Validator validator4 = new FunctionBasedValidator<>(integerProperty4, predicate1, ValidationMessage.error("Value must be bigger than 50")); + + final CompositeValidator compositeValidator = new CompositeValidator(validator1, validator2, validator3); + assertThat(compositeValidator.getValidationStatus().getMessages()).hasSize(3); + + IntegerBinding percentage = Bindings.createIntegerBinding(() -> { + int numberOfValidators = compositeValidator.getValidators().size(); + + if (numberOfValidators == 0) { + return 100; + } else { + int numberOfValidValidators = (int) compositeValidator.getValidators().stream() + .map(Validator::getValidationStatus) + .filter(ValidationStatus::isValid) + .count(); + + return numberOfValidValidators * 100 / numberOfValidators; + } + }, compositeValidator.getValidationStatus().getMessages(), compositeValidator.getValidators()); + + + assertThat(percentage.intValue()).isEqualTo(0); + + // change values + integerProperty1.setValue(70); + assertThat(percentage.intValue()).isEqualTo(33); + integerProperty1.setValue(0); + assertThat(percentage.intValue()).isEqualTo(0); + + + integerProperty2.setValue(100); + assertThat(percentage.intValue()).isEqualTo(33); + + integerProperty1.setValue(70); + assertThat(percentage.intValue()).isEqualTo(66); + + integerProperty2.setValue(50); + assertThat(percentage.intValue()).isEqualTo(33); + + // add new Validator + compositeValidator.addValidators(validator4); + assertThat(percentage.intValue()).isEqualTo(50); + + // 0% valid + integerProperty1.setValue(50); + integerProperty4.setValue(50); + assertThat(percentage.intValue()).isEqualTo(0); + + // 100% valid + integerProperty1.setValue(51); + integerProperty2.setValue(100); + integerProperty3.setValue(80); + integerProperty4.setValue(75); + assertThat(percentage.intValue()).isEqualTo(100); + + // remove validator + compositeValidator.removeValidators(validator1); + assertThat(percentage.intValue()).isEqualTo(100); + integerProperty2.setValue(30); + assertThat(percentage.intValue()).isEqualTo(66); + + } } From 4651c9aae0785a0cebf06d5768a908991cac1545 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Guill=C3=A9n=20S=C3=A1nchez?= Date: Fri, 26 May 2017 12:07:58 -0400 Subject: [PATCH 12/51] Update CDI and Weld versions --- pom.xml | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/pom.xml b/pom.xml index b49e6494c..3b203d435 100644 --- a/pom.xml +++ b/pom.xml @@ -149,20 +149,15 @@ - - javax.inject - javax.inject - 1 - javax.enterprise cdi-api - 1.2 + 2.0 org.jboss.weld.se weld-se-core - 2.2.11.Final + 3.0.0.Final com.cathive.fx From 24f0e675a0e19aa7de6531ca9d610aaf59aed962 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Guill=C3=A9n=20S=C3=A1nchez?= Date: Fri, 26 May 2017 12:08:23 -0400 Subject: [PATCH 13/51] Removing Weld dependency --- mvvmfx-cdi/pom.xml | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/mvvmfx-cdi/pom.xml b/mvvmfx-cdi/pom.xml index 3254c0b8c..41f9d9a1b 100644 --- a/mvvmfx-cdi/pom.xml +++ b/mvvmfx-cdi/pom.xml @@ -52,21 +52,18 @@ provided - - javax.inject - javax.inject - javax.enterprise cdi-api - - org.jboss.weld.se - weld-se-core - + + org.jboss.weld.se + weld-se-core + test + ch.qos.logback logback-classic From 0d94df35a8f87fd266e511011a899da6341b931f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Guill=C3=A9n=20S=C3=A1nchez?= Date: Fri, 26 May 2017 12:08:56 -0400 Subject: [PATCH 14/51] Bootstrap CDI container with CDI 2.0 --- .../mvvmfx/cdi/MvvmfxCdiApplication.java | 68 +++++++++---------- 1 file changed, 33 insertions(+), 35 deletions(-) diff --git a/mvvmfx-cdi/src/main/java/de/saxsys/mvvmfx/cdi/MvvmfxCdiApplication.java b/mvvmfx-cdi/src/main/java/de/saxsys/mvvmfx/cdi/MvvmfxCdiApplication.java index e53018be4..62c835de3 100644 --- a/mvvmfx-cdi/src/main/java/de/saxsys/mvvmfx/cdi/MvvmfxCdiApplication.java +++ b/mvvmfx-cdi/src/main/java/de/saxsys/mvvmfx/cdi/MvvmfxCdiApplication.java @@ -20,13 +20,12 @@ import javafx.stage.Stage; import javax.enterprise.context.spi.CreationalContext; +import javax.enterprise.inject.se.SeContainer; +import javax.enterprise.inject.se.SeContainerInitializer; import javax.enterprise.inject.spi.BeanManager; import javax.enterprise.inject.spi.InjectionTarget; import javax.inject.Inject; -import org.jboss.weld.environment.se.Weld; -import org.jboss.weld.environment.se.WeldContainer; - import de.saxsys.mvvmfx.MvvmFX; import de.saxsys.mvvmfx.cdi.internal.MvvmfxProducer; @@ -37,29 +36,28 @@ * @author manuel.mauky */ public abstract class MvvmfxCdiApplication extends Application implements MvvmfxApplication { - - + private final BeanManager beanManager; private CreationalContext ctx; - private InjectionTarget injectionTarget; - private final Weld weld; - - @Inject + private InjectionTarget injectionTarget; + private final SeContainer container; + + @Inject private MvvmfxProducer producer; - - public MvvmfxCdiApplication() { - weld = new Weld(); - WeldContainer weldContainer = weld.initialize(); - - MvvmFX.setCustomDependencyInjector((type) -> weldContainer.instance().select(type).get()); - - MvvmfxProducer mvvmfxProducer = weldContainer.instance().select(MvvmfxProducer.class).get(); + + public MvvmfxCdiApplication() { + container = SeContainerInitializer + .newInstance() + .initialize(); + + MvvmFX.setCustomDependencyInjector((type) -> container.select(type).get()); + + MvvmfxProducer mvvmfxProducer = container.select(MvvmfxProducer.class).get(); mvvmfxProducer.setHostServices(getHostServices()); - - beanManager = weldContainer.getBeanManager(); - + + beanManager = container.getBeanManager(); } - + /** * This method is overridden to initialize the mvvmFX framework. Override the * {@link #startMvvmfx(javafx.stage.Stage)} method for your application entry point and startup code instead of this @@ -68,11 +66,11 @@ public MvvmfxCdiApplication() { @Override public final void start(Stage primaryStage) throws Exception { producer.setPrimaryStage(primaryStage); - + startMvvmfx(primaryStage); } - - + + /** * This method is called when the javafx application is initialized. See * {@link javafx.application.Application#init()} for more details. @@ -89,35 +87,35 @@ public final void init() throws Exception { ctx = beanManager.createCreationalContext(null); injectionTarget = beanManager.createInjectionTarget( beanManager.createAnnotatedType((Class) this.getClass())); - + injectionTarget.inject(this, ctx); injectionTarget.postConstruct(this); - + producer.setApplicationParameters(getParameters()); - + initMvvmfx(); } - - + + /** * This method is called when the application should stop. See {@link javafx.application.Application#stop()} for * more details. - * + * * Unlike the original stop method in {@link javafx.application.Application} this method contains logic to release * resources managed by the CDI container. Therefor it's important to call super.stop() when you * override this method. - * + * * @throws Exception */ @Override public final void stop() throws Exception { stopMvvmfx(); - + injectionTarget.preDestroy(this); injectionTarget.dispose(this); - + ctx.release(); - - weld.shutdown(); + + container.close(); } } From c7c8b1a9df9017a2396e91090d3c32cf80d2094e Mon Sep 17 00:00:00 2001 From: Rafael Guillen Date: Tue, 30 May 2017 08:24:48 -0400 Subject: [PATCH 15/51] 249 custom fxml path (#489) * Annotation to support custom path to fxml file * Custom path in fxml files implementation * Test custom fxml file path * Fix documentation * Fix automatic reformat * Adding license and documentation --- .../main/java/de/saxsys/mvvmfx/FxmlPath.java | 59 +++++++++++++++++++ .../internal/viewloader/FxmlViewLoader.java | 39 ++++++++---- .../FluentViewLoader_FxmlView_Test.java | 27 +++++++++ .../viewloader/example/TestFxmlPathView.java | 57 ++++++++++++++++++ .../example/TestFxmlViewWithCustomPath.fxml | 6 ++ 5 files changed, 176 insertions(+), 12 deletions(-) create mode 100644 mvvmfx/src/main/java/de/saxsys/mvvmfx/FxmlPath.java create mode 100644 mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlPathView.java create mode 100644 mvvmfx/src/test/resources/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewWithCustomPath.fxml diff --git a/mvvmfx/src/main/java/de/saxsys/mvvmfx/FxmlPath.java b/mvvmfx/src/main/java/de/saxsys/mvvmfx/FxmlPath.java new file mode 100644 index 000000000..ba34993e1 --- /dev/null +++ b/mvvmfx/src/main/java/de/saxsys/mvvmfx/FxmlPath.java @@ -0,0 +1,59 @@ +/******************************************************************************* + * Copyright 2017 Rafael Guillen + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ +package de.saxsys.mvvmfx; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * This annotation is used to define a {@link FxmlView} custom + * FXML file path. An empty file paths will be ignored. + * + * Note that the full path to the FXML file must be provided. + * + * Please be aware that this annotation only effects the parent + * view that is loaded by the {@link FluentViewLoader}. + * Views that are included via "" tag aren't + * affected because the path is then determined by the value of + * the "src" attribute in the include-tag. + * + * Example:
+ *
+ *

+ * package example.view;
+ *
+ *{@literal @}FxmlPath("/fxml/CustomPathView.fxml")
+ * public class CustomView implements {@link FxmlView} {
+ *
+ *         ...
+ *
+ * }
+ * 
+ * + * @author rafael.guillen + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface FxmlPath { + + /** + * Custom fxml file path, empty by default + * @return path to the fxml file + */ + String value() default ""; +} diff --git a/mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/viewloader/FxmlViewLoader.java b/mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/viewloader/FxmlViewLoader.java index 415c068f4..d84a05dc8 100644 --- a/mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/viewloader/FxmlViewLoader.java +++ b/mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/viewloader/FxmlViewLoader.java @@ -19,6 +19,7 @@ import de.saxsys.mvvmfx.Scope; import de.saxsys.mvvmfx.ViewModel; import de.saxsys.mvvmfx.ViewTuple; +import de.saxsys.mvvmfx.FxmlPath; import de.saxsys.mvvmfx.internal.ContextImpl; import javafx.beans.property.BooleanProperty; import javafx.beans.property.SimpleBooleanProperty; @@ -32,6 +33,7 @@ import java.io.IOException; import java.net.URL; import java.util.Collection; +import java.util.Optional; import java.util.ResourceBundle; import java.util.function.Consumer; @@ -47,7 +49,7 @@ public class FxmlViewLoader { /** * Load the viewTuple by it`s ViewType. - * + * * @param viewType * the type of the view to be loaded. * @param resourceBundle @@ -79,17 +81,20 @@ public , ViewModelType extends Vi /** * This method is used to create a String with the path to the FXML file for * a given View class. - * + * * This is done by taking the package of the view class (if any) and replace * "." with "/". After that the Name of the class and the file ending * ".fxml" is appended. - * + * + * If the View class is annotated with @FxmlPath then the String path supplied + * in the annotation value will be used. + * * Example: de.saxsys.myapp.ui.MainView as view class will be transformed to * "/de/saxsys/myapp/ui/MainView.fxml" - * + * * Example 2: MainView (located in the default package) will be transformed * to "/MainView.fxml" - * + * * @param viewType * the view class type. * @return the path to the fxml file as string. @@ -97,22 +102,32 @@ public , ViewModelType extends Vi private String createFxmlPath(Class viewType) { final StringBuilder pathBuilder = new StringBuilder(); - pathBuilder.append("/"); + final FxmlPath pathAnnotation = viewType.getDeclaredAnnotation(FxmlPath.class); //Get annotation from view + final String fxmlPath = Optional.ofNullable(pathAnnotation) + .map(FxmlPath::value) + .map(String::trim) + .orElse(""); - if (viewType.getPackage() != null) { - pathBuilder.append(viewType.getPackage().getName().replaceAll("\\.", "/")); + if (fxmlPath.isEmpty()) { pathBuilder.append("/"); - } - pathBuilder.append(viewType.getSimpleName()); - pathBuilder.append(".fxml"); + if (viewType.getPackage() != null) { + pathBuilder.append(viewType.getPackage().getName().replaceAll("\\.", "/")); + pathBuilder.append("/"); + } + + pathBuilder.append(viewType.getSimpleName()); + pathBuilder.append(".fxml"); + } else { + pathBuilder.append(fxmlPath); + } return pathBuilder.toString(); } /** * Load the viewTuple by the path of the fxml file. - * + * * @param resource * the string path to the fxml file that is loaded. * @param resourceBundle diff --git a/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/FluentViewLoader_FxmlView_Test.java b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/FluentViewLoader_FxmlView_Test.java index 813124f42..a25acae3b 100644 --- a/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/FluentViewLoader_FxmlView_Test.java +++ b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/FluentViewLoader_FxmlView_Test.java @@ -500,4 +500,31 @@ public void testViewModelHasMultipleInitializeAnnotations() { assertThat(TestViewModelWithMultipleInitializeAnnotations.init2).isTrue(); assertThat(TestViewModelWithMultipleInitializeAnnotations.initialize).isTrue(); } + + @Test + public void testLoadFxmlViewTupleWithCustomPath() throws IOException { + + TestFxmlPathView.instanceCounter = 0; + TestViewModel.instanceCounter = 0; + + TestViewModel.wasInitialized = false; + + final ViewTuple viewTuple = FluentViewLoader.fxmlView(TestFxmlPathView.class) + .resourceBundle(resourceBundle).load(); + + assertThat(viewTuple).isNotNull(); + + assertThat(viewTuple.getView()).isNotNull().isInstanceOf(VBox.class); + assertThat(viewTuple.getCodeBehind()).isNotNull(); + + final TestFxmlPathView codeBehind = viewTuple.getCodeBehind(); + assertThat(codeBehind.getViewModel()).isNotNull(); + assertThat(codeBehind.resourceBundle).hasSameContent(resourceBundle); + + assertThat(codeBehind.viewModelWasNull).isFalse(); + + assertThat(TestFxmlPathView.instanceCounter).isEqualTo(1); + assertThat(TestViewModel.instanceCounter).isEqualTo(1); + assertThat(TestViewModel.wasInitialized).isTrue(); + } } diff --git a/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlPathView.java b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlPathView.java new file mode 100644 index 000000000..a346aa579 --- /dev/null +++ b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlPathView.java @@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright 2015 Alexander Casall, Manuel Mauky + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ +package de.saxsys.mvvmfx.internal.viewloader.example; + +import de.saxsys.mvvmfx.FxmlPath; +import de.saxsys.mvvmfx.FxmlView; +import de.saxsys.mvvmfx.InjectViewModel; +import javafx.fxml.Initializable; + +import java.net.URL; +import java.util.ResourceBundle; + + +/** + * This class is used as example View class that uses a custom path FXML. + * + * @authors manuel.mauky, rafael.guillen + */ +@FxmlPath(value = "/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewWithCustomPath.fxml") +public class TestFxmlPathView implements FxmlView, Initializable { + public static int instanceCounter = 0; + public URL url; + public ResourceBundle resourceBundle; + public boolean viewModelWasNull = true; + @InjectViewModel + private TestViewModel viewModel; + + public TestFxmlPathView() { + instanceCounter++; + } + + @Override + public void initialize(URL url, ResourceBundle resourceBundle) { + this.url = url; + this.resourceBundle = resourceBundle; + + viewModelWasNull = viewModel == null; + } + + public TestViewModel getViewModel() { + return viewModel; + } + +} diff --git a/mvvmfx/src/test/resources/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewWithCustomPath.fxml b/mvvmfx/src/test/resources/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewWithCustomPath.fxml new file mode 100644 index 000000000..036fd7e8d --- /dev/null +++ b/mvvmfx/src/test/resources/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewWithCustomPath.fxml @@ -0,0 +1,6 @@ + + + + + + From 65f8b5fb3b8e50c15153da2f8c82372ae535e622 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Guill=C3=A9n=20S=C3=A1nchez?= Date: Fri, 9 Jun 2017 17:06:08 -0400 Subject: [PATCH 16/51] Adding weld dependency and updating Jandex version --- examples/contacts-example/pom.xml | 8 ++++++-- examples/mini-examples/welcome-example/pom.xml | 12 +++++------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/examples/contacts-example/pom.xml b/examples/contacts-example/pom.xml index ed9aeab74..8cccfe23d 100644 --- a/examples/contacts-example/pom.xml +++ b/examples/contacts-example/pom.xml @@ -48,12 +48,16 @@ de.saxsys mvvmfx-cdi
- + + org.jboss.weld.se + weld-se-core + org.jboss jandex - 1.2.4.Final + 2.0.3.Final + ch.qos.logback logback-classic diff --git a/examples/mini-examples/welcome-example/pom.xml b/examples/mini-examples/welcome-example/pom.xml index d8966d556..db7ddf4d0 100644 --- a/examples/mini-examples/welcome-example/pom.xml +++ b/examples/mini-examples/welcome-example/pom.xml @@ -18,21 +18,19 @@ mvvmfx - - javax.inject - javax.inject - 1 - - de.saxsys mvvmfx-cdi + + org.jboss.weld.se + weld-se-core + org.jboss jandex - 1.2.4.Final + 2.0.3.Final From 2abdd456817ea7c6ce94afa4ab12592eb3137d32 Mon Sep 17 00:00:00 2001 From: Manuel Mauky Date: Mon, 12 Jun 2017 10:14:21 +0200 Subject: [PATCH 17/51] refactor modelwrapper: extract some classes into separate files --- .../de/saxsys/mvvmfx/internal/SideEffect.java | 13 + .../internal/SideEffectWithException.java | 13 + .../internal/viewloader/ReflectionUtils.java | 14 +- .../utils/mapping/BeanListPropertyField.java | 82 ++ .../utils/mapping/BeanPropertyField.java | 72 ++ .../utils/mapping/FxListPropertyField.java | 78 ++ .../mvvmfx/utils/mapping/FxPropertyField.java | 68 ++ .../mvvmfx/utils/mapping/ModelWrapper.java | 1018 ++++++----------- .../mvvmfx/utils/mapping/PropertyField.java | 39 + .../utils/mapping/ModelWrapperTest.java | 67 +- 10 files changed, 779 insertions(+), 685 deletions(-) create mode 100644 mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/SideEffect.java create mode 100644 mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/SideEffectWithException.java create mode 100644 mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/BeanListPropertyField.java create mode 100644 mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/BeanPropertyField.java create mode 100644 mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/FxListPropertyField.java create mode 100644 mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/FxPropertyField.java create mode 100644 mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/PropertyField.java diff --git a/mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/SideEffect.java b/mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/SideEffect.java new file mode 100644 index 000000000..7f371c3e3 --- /dev/null +++ b/mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/SideEffect.java @@ -0,0 +1,13 @@ +package de.saxsys.mvvmfx.internal; + +/** + * A functional interface that is used in this class to express callbacks that don't take any argument and don't + * return anything. Such a callback has to work only by side effects. + * + * This interface doesn't allow the implementation to throw checked exceptions. + * If checked exceptions are needed, use {@link SideEffectWithException} instead. + */ +@FunctionalInterface +public interface SideEffect { + void call(); +} diff --git a/mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/SideEffectWithException.java b/mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/SideEffectWithException.java new file mode 100644 index 000000000..418579ff8 --- /dev/null +++ b/mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/SideEffectWithException.java @@ -0,0 +1,13 @@ +package de.saxsys.mvvmfx.internal; + +/** + * A functional interface that is used in this class to express callbacks that don't take any argument and don't + * return anything. Such a callback has to work only by side effects. + * + * This interface allows the implementation to throw checked exceptions. Therefore the caller has to handle this exception. + * If no checked exceptions are needed, use {@link SideEffect} instead. + */ +@FunctionalInterface +public interface SideEffectWithException { + void call() throws Exception; +} diff --git a/mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/viewloader/ReflectionUtils.java b/mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/viewloader/ReflectionUtils.java index 71939838e..65b3de91e 100644 --- a/mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/viewloader/ReflectionUtils.java +++ b/mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/viewloader/ReflectionUtils.java @@ -15,6 +15,8 @@ ******************************************************************************/ package de.saxsys.mvvmfx.internal.viewloader; +import de.saxsys.mvvmfx.internal.SideEffectWithException; + import java.lang.annotation.Annotation; import java.lang.reflect.AccessibleObject; import java.lang.reflect.Field; @@ -32,15 +34,7 @@ * @author manuel.mauky */ public class ReflectionUtils { - /** - * A functional interface that is used in this class to express callbacks that don't take any argument and don't - * return anything. Such a callback have to work only by side effects. - */ - @FunctionalInterface - public static interface SideEffect { - void call() throws Exception; - } - + /** * Returns all fields with the given annotation. Only fields that are declared in the actual class of the instance * are considered (i.e. no fields from super classes). This includes private fields. @@ -160,7 +154,7 @@ public static void setField(final Field field, Object target, Object value) { * @throws IllegalStateException * when something went wrong. */ - public static void accessMember(final AccessibleObject member, final SideEffect sideEffect, String errorMessage) { + public static void accessMember(final AccessibleObject member, final SideEffectWithException sideEffect, String errorMessage) { if (sideEffect == null) { return; } diff --git a/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/BeanListPropertyField.java b/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/BeanListPropertyField.java new file mode 100644 index 000000000..25b5429ae --- /dev/null +++ b/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/BeanListPropertyField.java @@ -0,0 +1,82 @@ +package de.saxsys.mvvmfx.utils.mapping; + +import de.saxsys.mvvmfx.internal.SideEffect; +import de.saxsys.mvvmfx.utils.mapping.accessorfunctions.ListGetter; +import de.saxsys.mvvmfx.utils.mapping.accessorfunctions.ListSetter; +import javafx.beans.property.ListProperty; +import javafx.beans.property.Property; +import javafx.collections.FXCollections; +import javafx.collections.ListChangeListener; +import javafx.collections.ObservableList; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.function.Supplier; + +/** + * An implementation of {@link PropertyField} that is used when the field of the model class is a {@link List} and + * is not a JavaFX ListProperty but is following the old Java-Beans standard, i.e. there is getter and setter + * method for the field. + * + * @param + * @param + * the type of the list elements. + */ +class BeanListPropertyField, R extends Property> + implements PropertyField { + + private final ListGetter getter; + private final ListSetter setter; + + private List defaultValue; + private final ListProperty targetProperty; + + public BeanListPropertyField(SideEffect updateFunction, ListGetter getter, ListSetter setter, Supplier> propertySupplier) { + this(updateFunction, getter, setter, propertySupplier, Collections.emptyList()); + } + + public BeanListPropertyField(SideEffect updateFunction, ListGetter getter, ListSetter setter, Supplier> propertySupplier, List defaultValue) { + this.defaultValue = defaultValue; + this.getter = getter; + this.setter = setter; + this.targetProperty = propertySupplier.get(); + this.targetProperty.setValue(FXCollections.observableArrayList()); + + this.targetProperty.addListener((ListChangeListener) change -> updateFunction.call()); + } + + @Override + public void commit(M wrappedObject) { + setter.accept(wrappedObject, targetProperty.getValue()); + } + + @Override + public void reload(M wrappedObject) { + targetProperty.setAll(getter.apply(wrappedObject)); + } + + @Override + public void resetToDefault() { + targetProperty.setAll(defaultValue); + } + + @Override + public void updateDefault(final M wrappedObject) { + defaultValue = new ArrayList<>(getter.apply(wrappedObject)); + } + + @Override + public R getProperty() { + return (R) targetProperty; + } + + @Override + public boolean isDifferent(M wrappedObject) { + final List modelValue = getter.apply(wrappedObject); + final List wrapperValue = targetProperty; + + return !Objects.equals(modelValue, wrapperValue); + } +} diff --git a/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/BeanPropertyField.java b/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/BeanPropertyField.java new file mode 100644 index 000000000..537ade8ad --- /dev/null +++ b/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/BeanPropertyField.java @@ -0,0 +1,72 @@ +package de.saxsys.mvvmfx.utils.mapping; + +import de.saxsys.mvvmfx.internal.SideEffect; +import javafx.beans.property.Property; + +import java.util.Objects; +import java.util.function.BiConsumer; +import java.util.function.Function; +import java.util.function.Supplier; + +/** + * An implementation of {@link PropertyField} that is used when the fields of the model class are not JavaFX + * Properties but are following the old Java-Beans standard, i.e. there are getter and setter method for each field. + * + * @param + */ +class BeanPropertyField> implements PropertyField { + + private final R targetProperty; + private T defaultValue; + + private final Function getter; + private final BiConsumer setter; + + public BeanPropertyField(SideEffect updateFunction, Function getter, + BiConsumer setter, Supplier propertySupplier) { + this(updateFunction, getter, setter, null, propertySupplier); + } + + public BeanPropertyField(SideEffect updateFunction, Function getter, + BiConsumer setter, T defaultValue, Supplier propertySupplier) { + this.defaultValue = defaultValue; + this.getter = getter; + this.setter = setter; + this.targetProperty = propertySupplier.get(); + + this.targetProperty.addListener((observable, oldValue, newValue) -> updateFunction.call()); + } + + @Override + public void commit(M wrappedObject) { + setter.accept(wrappedObject, targetProperty.getValue()); + } + + @Override + public void reload(M wrappedObject) { + targetProperty.setValue(getter.apply(wrappedObject)); + } + + @Override + public void resetToDefault() { + targetProperty.setValue(defaultValue); + } + + @Override + public void updateDefault(final M wrappedObject) { + defaultValue = getter.apply(wrappedObject); + } + + @Override + public R getProperty() { + return targetProperty; + } + + @Override + public boolean isDifferent(M wrappedObject) { + final T modelValue = getter.apply(wrappedObject); + final T wrapperValue = targetProperty.getValue(); + + return !Objects.equals(modelValue, wrapperValue); + } +} diff --git a/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/FxListPropertyField.java b/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/FxListPropertyField.java new file mode 100644 index 000000000..47e75632e --- /dev/null +++ b/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/FxListPropertyField.java @@ -0,0 +1,78 @@ +package de.saxsys.mvvmfx.utils.mapping; + +import de.saxsys.mvvmfx.internal.SideEffect; +import de.saxsys.mvvmfx.utils.mapping.accessorfunctions.ListPropertyAccessor; +import javafx.beans.property.ListProperty; +import javafx.beans.property.Property; +import javafx.collections.FXCollections; +import javafx.collections.ListChangeListener; +import javafx.collections.ObservableList; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.function.Supplier; + +/** + * An implementation of {@link PropertyField} that is used when the field of the model class is a {@link List} and + * will be mapped to a JavaFX {@link ListProperty}. + * + * @param + * @param + * the type of the list elements. + */ +class FxListPropertyField, R extends Property> + implements PropertyField { + + private List defaultValue; + private final ListPropertyAccessor accessor; + private final ListProperty targetProperty; + + public FxListPropertyField(SideEffect updateFunction, ListPropertyAccessor accessor, Supplier> propertySupplier) { + this(updateFunction, accessor, propertySupplier, Collections.emptyList()); + } + + public FxListPropertyField(SideEffect updateFunction, ListPropertyAccessor accessor, Supplier> propertySupplier, List defaultValue) { + this.accessor = accessor; + this.defaultValue = defaultValue; + + this.targetProperty = propertySupplier.get(); + this.targetProperty.setValue(FXCollections.observableArrayList()); + + this.targetProperty.addListener((ListChangeListener) change -> updateFunction.call()); + } + + @Override + public void commit(M wrappedObject) { + accessor.apply(wrappedObject).setAll(targetProperty.getValue()); + } + + @Override + public void reload(M wrappedObject) { + targetProperty.setAll(accessor.apply(wrappedObject).getValue()); + } + + @Override + public void resetToDefault() { + targetProperty.setAll(defaultValue); + } + + @Override + public void updateDefault(final M wrappedObject) { + defaultValue = new ArrayList<>(accessor.apply(wrappedObject).getValue()); + } + + @Override + public R getProperty() { + return (R) targetProperty; + } + + @Override + public boolean isDifferent(M wrappedObject) { + final List modelValue = accessor.apply(wrappedObject).getValue(); + final List wrapperValue = targetProperty; + + return !Objects.equals(modelValue, wrapperValue); + } +} diff --git a/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/FxPropertyField.java b/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/FxPropertyField.java new file mode 100644 index 000000000..3d9fc4ae3 --- /dev/null +++ b/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/FxPropertyField.java @@ -0,0 +1,68 @@ +package de.saxsys.mvvmfx.utils.mapping; + +import de.saxsys.mvvmfx.internal.SideEffect; +import javafx.beans.property.Property; + +import java.util.Objects; +import java.util.function.Function; +import java.util.function.Supplier; + +/** + * An implementation of {@link PropertyField} that is used when the fields of the model class are JavaFX Properties + * too. + * + * @param + */ +class FxPropertyField> implements PropertyField { + + private T defaultValue; + private final Function> accessor; + private final R targetProperty; + + public FxPropertyField(SideEffect updateFunction, Function> accessor, Supplier> propertySupplier) { + this(updateFunction, accessor, null, propertySupplier); + } + + @SuppressWarnings("unchecked") + public FxPropertyField(SideEffect updateFunction, Function> accessor, T defaultValue, + Supplier> propertySupplier) { + this.accessor = accessor; + this.defaultValue = defaultValue; + this.targetProperty = (R) propertySupplier.get(); + + this.targetProperty.addListener((observable, oldValue, newValue) -> updateFunction.call()); + } + + @Override + public void commit(M wrappedObject) { + accessor.apply(wrappedObject).setValue(targetProperty.getValue()); + } + + @Override + public void reload(M wrappedObject) { + targetProperty.setValue(accessor.apply(wrappedObject).getValue()); + } + + @Override + public void resetToDefault() { + targetProperty.setValue(defaultValue); + } + + @Override + public void updateDefault(final M wrappedObject) { + defaultValue = accessor.apply(wrappedObject).getValue(); + } + + @Override + public R getProperty() { + return targetProperty; + } + + @Override + public boolean isDifferent(M wrappedObject) { + final T modelValue = accessor.apply(wrappedObject).getValue(); + final T wrapperValue = targetProperty.getValue(); + + return !Objects.equals(modelValue, wrapperValue); + } +} diff --git a/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/ModelWrapper.java b/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/ModelWrapper.java index d6b9771a9..e93fb8218 100644 --- a/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/ModelWrapper.java +++ b/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/ModelWrapper.java @@ -40,17 +40,12 @@ import de.saxsys.mvvmfx.utils.mapping.accessorfunctions.StringPropertyAccessor; import de.saxsys.mvvmfx.utils.mapping.accessorfunctions.StringSetter; -import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; -import java.util.function.BiConsumer; -import java.util.function.Function; -import java.util.function.Supplier; import eu.lestard.doc.Beta; import javafx.beans.property.BooleanProperty; @@ -73,8 +68,6 @@ import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; import javafx.collections.FXCollections; -import javafx.collections.ListChangeListener; -import javafx.collections.ObservableList; /** @@ -99,9 +92,9 @@ *
  • if we are creating a new model instance and the user clicks "reset" we want all UI fields to be reset to a * meaningful default value
  • * - * + * *

    - * + * * These requirements are quite common but there is a lot of code needed to copy between the model and the viewModel. * Additionally we have a tight coupling because every time the structure of the model changes (for example a field is * removed) we have several places in the viewModel that need to be adjusted. @@ -111,68 +104,68 @@ *

    * The model class: *

    - * + * *

      * public class Person {
      * 	private String name;
      * 	private String familyName;
      * 	private int age;
    - * 	
    + *
      * 	public String getName() {
      * 		return name;
      * 	}
    - * 	
    + *
      * 	public void setName(String name) {
      * 		this.name = name;
      * 	}
    - * 	
    + *
      * 	public String getFamilyName() {
      * 		return familyName;
      * 	}
    - * 	
    + *
      * 	public void setFamilyName(String familyName) {
      * 		this.familyName = familyName;
      * 	}
    - * 	
    + *
      * 	public int getAge() {
      * 		return age;
      * 	}
    - * 	
    + *
      * 	public void setAge(int age) {
      * 		this.age = age;
      * 	}
      * }
      * 
    - * + * * Without {@link ModelWrapper}: *

    - * + * *

      * public class PersonViewModel implements ViewModel {
    - * 	
    + *
      * 	private StringProperty name = new SimpleStringProperty();
      * 	private StringProperty familyName = new SimpleStringProperty();
      * 	private IntegerProperty age = new SimpleIntegerProperty();
    - * 	
    + *
      * 	private Person person;
    - * 	
    + *
      * 	public void init(Person person) {
      * 		this.person = person;
      * 		reloadFromModel();
      * 	}
    - * 	
    + *
      * 	public void reset() {
      * 		this.name.setValue("");
      * 		this.familyName.setValue("");
      * 		this.age.setValue(0);
      * 	}
    - * 	
    + *
      * 	public void reloadFromModel() {
      * 		this.name.setValue(person.getName());
      * 		this.familyName.setValue(person.getFamilyName());
      * 		this.age.setValue(person.getAge());
      * 	}
    - * 	
    + *
      * 	public void save() {
      * 		if (someValidation() && person != null) {
      * 			person.setName(name.getValue());
    @@ -180,61 +173,61 @@
      * 			person.setAge(age.getValue());
      * 		}
      * 	}
    - * 	
    + *
      * 	public StringProperty nameProperty() {
      * 		return name;
      * 	}
    - * 	
    + *
      * 	public StringProperty familyNameProperty() {
      * 		return familyName;
      * 	}
    - * 	
    + *
      * 	public IntegerProperty ageProperty() {
      * 		return age;
      * 	}
      * }
      * 
    - * + * * With {@link ModelWrapper}: *

    - * + * *

      *         public class PersonViewModel implements ViewModel {
      *              private ModelWrapper{@code} wrapper = new ModelWrapper{@code<>}();
    - * 
    + *
      *             public void init(Person person) {
      *                  wrapper.set(person);
      *                  wrapper.reload();
      *             }
    - * 
    + *
      *             public void reset() {
      *                 wrapper.reset();
      *             }
    - * 
    + *
      *             public void reloadFromModel(){
      *                 wrapper.reload();
      *             }
    - * 
    + *
      *             public void save() {
      *                 if (someValidation()) {
      *                     wrapper.commit();
      *                 }
      *             }
    - * 
    + *
      *             public StringProperty nameProperty(){
      *                 return wrapper.field("name", Person::getName, Person::setName, "");
      *             }
    - * 
    + *
      *             public StringProperty familyNameProperty(){
      *                 return wrapper.field("familyName", Person::getFamilyName, Person::setFamilyName, "");
      *             }
    - * 
    + *
      *             public IntegerProperty ageProperty() {
      *                 return wrapper.field("age", Person::getAge, Person::setAge, 0);
      *             }
      *         }
      * 
    - * + * * In the first example without the {@link ModelWrapper} we have several lines of code that are specific for each field * of the model. If we would add a new field to the model (for example "email") then we would have to update several * pieces of code in the ViewModel. @@ -242,308 +235,21 @@ * On the other hand in the example with the {@link ModelWrapper} there is only the definition of the Property accessors * in the bottom of the class that is specific to the fields of the Model. For each field we have only one place in the * ViewModel that would need an update when the structure of the model changes. - * - * - * + * + * + * * @param * the type of the model class. */ @Beta public class ModelWrapper { - + private final ReadOnlyBooleanWrapper dirtyFlag = new ReadOnlyBooleanWrapper(); private final ReadOnlyBooleanWrapper diffFlag = new ReadOnlyBooleanWrapper(); - /** - * This interface defines the operations that are possible for each field of a wrapped class. - * - * @param - * target type. The base type of the returned property, f.e. {@link String}. - * @param - * model type. The type of the Model class, that is wrapped by this ModelWrapper instance. - * @param - * return type. The type of the Property that is returned via {@link #getProperty()}, f.e. - * {@link StringProperty} or {@link Property}. - */ - private interface PropertyField> { - void commit(M wrappedObject); - - void reload(M wrappedObject); - - void resetToDefault(); - - void updateDefault(final M wrappedObject); - - R getProperty(); - - /** - * Determines if the value in the model object and the property field are different or not. - * - * This method is used to implement the {@link #differentProperty()} flag. - * - * @param wrappedObject - * the wrapped model object - * @return false if both the wrapped model object and the property field have the same value, - * otherwise true - */ - boolean isDifferent(M wrappedObject); - } - - /** - * An implementation of {@link PropertyField} that is used when the fields of the model class are JavaFX Properties - * too. - * - * @param - */ - private class FxPropertyField> implements PropertyField { - - private T defaultValue; - private final Function> accessor; - private final R targetProperty; - - public FxPropertyField(Function> accessor, Supplier> propertySupplier) { - this(accessor, null, propertySupplier); - } - - @SuppressWarnings("unchecked") - public FxPropertyField(Function> accessor, T defaultValue, - Supplier> propertySupplier) { - this.accessor = accessor; - this.defaultValue = defaultValue; - this.targetProperty = (R) propertySupplier.get(); - - this.targetProperty.addListener((observable, oldValue, newValue) -> propertyWasChanged()); - } - - @Override - public void commit(M wrappedObject) { - accessor.apply(wrappedObject).setValue(targetProperty.getValue()); - } - - @Override - public void reload(M wrappedObject) { - targetProperty.setValue(accessor.apply(wrappedObject).getValue()); - } - - @Override - public void resetToDefault() { - targetProperty.setValue(defaultValue); - } - - @Override - public void updateDefault(final M wrappedObject) { - defaultValue = accessor.apply(wrappedObject).getValue(); - } - - @Override - public R getProperty() { - return targetProperty; - } - - @Override - public boolean isDifferent(M wrappedObject) { - final T modelValue = accessor.apply(wrappedObject).getValue(); - final T wrapperValue = targetProperty.getValue(); - - return !Objects.equals(modelValue, wrapperValue); - } - } - - /** - * An implementation of {@link PropertyField} that is used when the fields of the model class are not JavaFX - * Properties but are following the old Java-Beans standard, i.e. there are getter and setter method for each field. - * - * @param - */ - private class BeanPropertyField> implements PropertyField { - - private final R targetProperty; - private T defaultValue; - - private final Function getter; - private final BiConsumer setter; - - public BeanPropertyField(Function getter, - BiConsumer setter, Supplier propertySupplier) { - this(getter, setter, null, propertySupplier); - } - - public BeanPropertyField(Function getter, - BiConsumer setter, T defaultValue, Supplier propertySupplier) { - this.defaultValue = defaultValue; - this.getter = getter; - this.setter = setter; - this.targetProperty = propertySupplier.get(); - - this.targetProperty.addListener((observable, oldValue, newValue) -> propertyWasChanged()); - } - - @Override - public void commit(M wrappedObject) { - setter.accept(wrappedObject, targetProperty.getValue()); - } - - @Override - public void reload(M wrappedObject) { - targetProperty.setValue(getter.apply(wrappedObject)); - } - - @Override - public void resetToDefault() { - targetProperty.setValue(defaultValue); - } - - @Override - public void updateDefault(final M wrappedObject) { - defaultValue = getter.apply(wrappedObject); - } - - @Override - public R getProperty() { - return targetProperty; - } - - @Override - public boolean isDifferent(M wrappedObject) { - final T modelValue = getter.apply(wrappedObject); - final T wrapperValue = targetProperty.getValue(); - - return !Objects.equals(modelValue, wrapperValue); - } - } - - /** - * An implementation of {@link PropertyField} that is used when the field of the model class is a {@link List} and - * will be mapped to a JavaFX {@link ListProperty}. - * - * @param - * @param - * the type of the list elements. - */ - private class FxListPropertyField, R extends Property> - implements PropertyField { - - private List defaultValue; - private final ListPropertyAccessor accessor; - private final ListProperty targetProperty; - - public FxListPropertyField(ListPropertyAccessor accessor, Supplier> propertySupplier) { - this(accessor, propertySupplier, Collections.emptyList()); - } - - public FxListPropertyField(ListPropertyAccessor accessor, Supplier> propertySupplier, List defaultValue) { - this.accessor = accessor; - this.defaultValue = defaultValue; - - this.targetProperty = propertySupplier.get(); - this.targetProperty.setValue(FXCollections.observableArrayList()); - - this.targetProperty.addListener((ListChangeListener) change -> ModelWrapper.this.propertyWasChanged()); - } - - @Override - public void commit(M wrappedObject) { - accessor.apply(wrappedObject).setAll(targetProperty.getValue()); - } - - @Override - public void reload(M wrappedObject) { - targetProperty.setAll(accessor.apply(wrappedObject).getValue()); - } - - @Override - public void resetToDefault() { - targetProperty.setAll(defaultValue); - } - - @Override - public void updateDefault(final M wrappedObject) { - defaultValue = new ArrayList<>(accessor.apply(wrappedObject).getValue()); - } - - @Override - public R getProperty() { - return (R) targetProperty; - } - - @Override - public boolean isDifferent(M wrappedObject) { - final List modelValue = accessor.apply(wrappedObject).getValue(); - final List wrapperValue = targetProperty; - - return !Objects.equals(modelValue, wrapperValue); - } - } - - /** - * An implementation of {@link PropertyField} that is used when the field of the model class is a {@link List} and - * is not a JavaFX ListProperty but is following the old Java-Beans standard, i.e. there is getter and setter - * method for the field. - * - * @param - * @param - * the type of the list elements. - */ - private class BeanListPropertyField, R extends Property> - implements PropertyField { - - private final ListGetter getter; - private final ListSetter setter; - - private List defaultValue; - private final ListProperty targetProperty; - - public BeanListPropertyField(ListGetter getter, ListSetter setter, Supplier> propertySupplier) { - this(getter, setter, propertySupplier, Collections.emptyList()); - } - - public BeanListPropertyField(ListGetter getter, ListSetter setter, Supplier> propertySupplier, List defaultValue) { - this.defaultValue = defaultValue; - this.getter = getter; - this.setter = setter; - this.targetProperty = propertySupplier.get(); - this.targetProperty.setValue(FXCollections.observableArrayList()); - - this.targetProperty.addListener((ListChangeListener) change -> propertyWasChanged()); - } - - @Override - public void commit(M wrappedObject) { - setter.accept(wrappedObject, targetProperty.getValue()); - } - - @Override - public void reload(M wrappedObject) { - targetProperty.setAll(getter.apply(wrappedObject)); - } - - @Override - public void resetToDefault() { - targetProperty.setAll(defaultValue); - } - - @Override - public void updateDefault(final M wrappedObject) { - defaultValue = new ArrayList<>(getter.apply(wrappedObject)); - } - - @Override - public R getProperty() { - return (R) targetProperty; - } - - @Override - public boolean isDifferent(M wrappedObject) { - final List modelValue = getter.apply(wrappedObject); - final List wrapperValue = targetProperty; - - return !Objects.equals(modelValue, wrapperValue); - } - } - private final Set> fields = new LinkedHashSet<>(); private final Map> identifiedFields = new HashMap<>(); - + private final ObjectProperty model; @@ -564,14 +270,14 @@ public ModelWrapper(ObjectProperty model) { /** * Create a new instance of {@link ModelWrapper} that wraps the given instance of the Model class. - * + * * @param model * the element of the model that will be wrapped. */ public ModelWrapper(M model) { this(new SimpleObjectProperty<>(model)); } - + /** * Create a new instance of {@link ModelWrapper} that is empty at the moment. You have to define the model element * that should be wrapped afterwards with the {@link #set(Object)} method. @@ -579,17 +285,17 @@ public ModelWrapper(M model) { public ModelWrapper() { this(new SimpleObjectProperty<>()); } - + /** * Define the model element that will be wrapped by this {@link ModelWrapper} instance. - * + * * @param model * the element of the model that will be wrapped. */ public void set(M model) { this.model.set(model); } - + /** * @return the wrapped model element if one was defined, otherwise null. */ @@ -623,7 +329,7 @@ public ObjectProperty modelProperty() { */ public void reset() { fields.forEach(PropertyField::resetToDefault); - + calculateDifferenceFlag(); } @@ -636,38 +342,38 @@ public void reset() { * Usage example: *
     	 * ModelWrapper{@code} wrapper = new ModelWrapper{@code<>}();
    -	 * 
    +	 *
     	 * wrapper.field(Person::getName, Person::setName, "oldDefault");
    -	 * 
    +	 *
     	 * Person p = new Person();
     	 * wrapper.set(p);
    -	 * 
    -	 * 
    +	 *
    +	 *
     	 * p.setName("Luise");
    -	 * 
    +	 *
     	 * wrapper.useCurrentValuesAsDefaults(); // now "Luise" is the default value for the name field.
    -	 *  
    -	 * 
    +	 *
    +	 *
     	 * name.set("Hugo");
     	 * wrapper.commit();
    -	 * 
    +	 *
     	 * name.get(); // Hugo
     	 * p.getName(); // Hugo
    -	 * 
    -	 * 
    +	 *
    +	 *
     	 * wrapper.reset(); // reset to the new defaults
     	 * name.get(); // Luise
    -	 * 
    +	 *
     	 * wrapper.commit(); // put values from properties to the wrapped model object
     	 * p.getName(); // Luise
    -	 *   
    -	 *      
    +	 *
    +	 *
     	 * 
    - * + * * * If no model instance is set to be wrapped by the ModelWrapper, nothing will happen when this method is invoked. * Instead the old default values will still be available. - * + * */ public void useCurrentValuesAsDefaults() { if(model.get() != null) { @@ -688,13 +394,13 @@ public void useCurrentValuesAsDefaults() { public void commit() { if (model.get() != null) { fields.forEach(field -> field.commit(model.get())); - + dirtyFlag.set(false); - + calculateDifferenceFlag(); } } - + /** * Take the current values from the wrapped model element and put them in the corresponding property fields. *

    @@ -706,7 +412,7 @@ public void commit() { public void reload() { if (model.get() != null) { fields.forEach(field -> field.reload(model.get())); - + dirtyFlag.set(false); calculateDifferenceFlag(); } @@ -732,7 +438,7 @@ private void propertyWasChanged() { dirtyFlag.set(true); calculateDifferenceFlag(); } - + private void calculateDifferenceFlag() { if (model.get() != null) { for (final PropertyField field : fields) { @@ -744,11 +450,86 @@ private void calculateDifferenceFlag() { diffFlag.set(false); } } - - - + + + private > R add(PropertyField field) { + fields.add(field); + if (model.get() != null) { + field.reload(model.get()); + } + return field.getProperty(); + } + + @SuppressWarnings("unchecked") + private > R addIdentified(String fieldName, PropertyField field) { + if (identifiedFields.containsKey(fieldName)) { + final Property property = identifiedFields.get(fieldName).getProperty(); + return (R) property; + } else { + identifiedFields.put(fieldName, field); + return add(field); + } + } + + /** + * This boolean flag indicates whether there is a difference of the data between the wrapped model object and the + * properties provided by this wrapper. + *

    + * Note the difference to {@link #dirtyProperty()}: This property will be true if the data of the + * wrapped model is different to the properties of this wrapper. If you change the data back to the initial state so + * that the data is equal again, this property will change back to false while the + * {@link #dirtyProperty()} will still be true. + * + * Simply speaking: This property indicates whether there is a difference in data between the model and the wrapper. + * The {@link #dirtyProperty()} indicates whether there was a change done. + * + * + * Note: Only those changes are observed that are done through the wrapped property fields of this wrapper. If you + * change the data of the model instance directly, this property won't turn to true. + * + * + * @return a read-only property indicating a difference between model and wrapper. + */ + public ReadOnlyBooleanProperty differentProperty() { + return diffFlag.getReadOnlyProperty(); + } + + /** + * See {@link #differentProperty()}. + */ + public boolean isDifferent() { + return diffFlag.get(); + } + + /** + * This boolean flag indicates whether there was a change to at least one wrapped property. + *

    + * Note the difference to {@link #differentProperty()}: This property will turn to true when the value + * of one of the wrapped properties is changed. It will only change back to false when either the + * {@link #commit()} or {@link #reload()} method is called. This property will stay true even if + * afterwards another change is done so that the data is equal again. In this case the {@link #differentProperty()} + * will switch back to false. + * + * Simply speaking: This property indicates whether there was a change done to the wrapped properties or not. The + * {@link #differentProperty()} indicates whether there is a difference in data at the moment. + * + * @return a read only boolean property indicating if there was a change done. + */ + public ReadOnlyBooleanProperty dirtyProperty() { + return dirtyFlag.getReadOnlyProperty(); + } + + /** + * See {@link #dirtyProperty()}. + */ + public boolean isDirty() { + return dirtyFlag.get(); + } + + + /** Field type String **/ - + /** * Add a new field of type String to this instance of the wrapper. This method is used for model elements that are * following the normal Java-Beans-standard i.e. the model fields are only available via getter and setter methods @@ -761,10 +542,10 @@ private void calculateDifferenceFlag() { * *

     	 * ModelWrapper{@code} personWrapper = new ModelWrapper{@code<>}();
    -	 * 
    +	 *
     	 * StringProperty wrappedNameProperty = personWrapper.field(person -> person.getName(), (person, value)
     	 * 	 -> person.setName(value));
    -	 * 
    +	 *
     	 * // or with a method reference
     	 * StringProperty wrappedNameProperty = personWrapper.field(Person::getName, Person::setName);
     	 *
    @@ -777,13 +558,13 @@ private void calculateDifferenceFlag() {
     	 * @param setter
     	 *            a function that sets the given value to the given model element. Typically you will use a method
     	 *            reference to the setter method of the model element.
    -	 * 			
    +	 *
     	 * @return The wrapped property instance.
     	 */
     	public StringProperty field(StringGetter getter, StringSetter setter) {
    -		return add(new BeanPropertyField<>(getter, setter, SimpleStringProperty::new));
    +		return add(new BeanPropertyField<>(this::propertyWasChanged, getter, setter, SimpleStringProperty::new));
     	}
    -	
    +
     	/**
     	 * Add a new field of type String to this instance of the wrapper. See {@link #field(StringGetter, StringSetter)}.
     	 * This method additionally has a parameter to define the default value that is used when the {@link #reset()}
    @@ -798,13 +579,14 @@ public StringProperty field(StringGetter getter, StringSetter setter) {
     	 *            reference to the setter method of the model element.
     	 * @param defaultValue
     	 *            the default value that is used when {@link #reset()} is invoked.
    -	 * 			
    +	 *
     	 * @return The wrapped property instance.
     	 */
     	public StringProperty field(StringGetter getter, StringSetter setter, String defaultValue) {
    -		return add(new BeanPropertyField<>(getter, setter, defaultValue, SimpleStringProperty::new));
    +		return add(new BeanPropertyField<>(this::propertyWasChanged, getter, setter, defaultValue,
    +				SimpleStringProperty::new));
     	}
    -	
    +
     	/**
     	 * Add a new field of type {@link String} to this instance of the wrapper. This method is used for model elements
     	 * that are following the enhanced JavaFX-Beans-standard i.e. the model fields are available as JavaFX Properties.
    @@ -815,9 +597,9 @@ public StringProperty field(StringGetter getter, StringSetter setter, Stri
     	 *
     	 * 
     	 * ModelWrapper{@code} personWrapper = new ModelWrapper{@code<>}();
    -	 * 
    +	 *
     	 * StringProperty wrappedNameProperty = personWrapper.field(person -> person.nameProperty());
    -	 * 
    +	 *
     	 * // or with a method reference
     	 * StringProperty wrappedNameProperty = personWrapper.field(Person::nameProperty);
     	 *
    @@ -826,18 +608,18 @@ public StringProperty field(StringGetter getter, StringSetter setter, Stri
     	 * @param accessor
     	 *            a function that returns the property for a given model instance. Typically you will use a method
     	 *            reference to the javafx-property accessor method.
    -	 * 			
    +	 *
     	 * @return The wrapped property instance.
     	 */
     	public StringProperty field(StringPropertyAccessor accessor) {
    -		return add(new FxPropertyField<>(accessor::apply, SimpleStringProperty::new));
    +		return add(new FxPropertyField<>(this::propertyWasChanged, accessor::apply, SimpleStringProperty::new));
     	}
    -	
    +
     	/**
     	 * Add a new field of type String to this instance of the wrapper. See {@link #field(StringGetter, StringSetter)}.
     	 * This method additionally has a parameter to define the default value that is used when the {@link #reset()}
     	 * method is used.
    -	 * 
    +	 *
     	 * @param accessor
     	 *            a function that returns the property for a given model instance. Typically you will use a method
     	 *            reference to the javafx-property accessor method.
    @@ -846,12 +628,13 @@ public StringProperty field(StringPropertyAccessor accessor) {
     	 * @return The wrapped property instance.
     	 */
     	public StringProperty field(StringPropertyAccessor accessor, String defaultValue) {
    -		return add(new FxPropertyField<>(accessor::apply, defaultValue, SimpleStringProperty::new));
    +		return add(new FxPropertyField<>(this::propertyWasChanged, accessor::apply, defaultValue,
    +				SimpleStringProperty::new));
     	}
    -	
    -	
    -	
    -	
    +
    +
    +
    +
     	/**
     	 * Add a new field of type String to this instance of the wrapper. See {@link #field(StringGetter, StringSetter)}.
     	 * This method additionally takes a string identifier as first parameter.
    @@ -869,15 +652,16 @@ public StringProperty field(StringPropertyAccessor accessor, String defaultVa
     	 * @return The wrapped property instance.
     	 */
     	public StringProperty field(String identifier, StringGetter getter, StringSetter setter) {
    -		return addIdentified(identifier, new BeanPropertyField<>(getter, setter, () -> new SimpleStringProperty(null, identifier)));
    +		return addIdentified(identifier, new BeanPropertyField<>(this::propertyWasChanged, getter, setter,
    +				() -> new SimpleStringProperty(null, identifier)));
     	}
    -	
    +
     	public StringProperty field(String identifier, StringGetter getter, StringSetter setter,
     			String defaultValue) {
    -		return addIdentified(identifier, new BeanPropertyField<>(getter, setter, defaultValue,
    +		return addIdentified(identifier, new BeanPropertyField<>(this::propertyWasChanged, getter, setter, defaultValue,
     				() -> new SimpleStringProperty(null, identifier)));
     	}
    -	
    +
     	/**
     	 * Add a new field of type String to this instance of the wrapper. See {@link #field(StringPropertyAccessor)}. This
     	 * method additionally takes a string identifier as first parameter.
    @@ -886,424 +670,374 @@ public StringProperty field(String identifier, StringGetter getter, StringSet
     	 *
     	 * @param identifier
     	 *            an identifier for the field.
    -	 * 			
    +	 *
     	 * @param accessor
     	 *            a function that returns the property for a given model instance. Typically you will use a method
     	 *            reference to the javafx-property accessor method.
     	 * @return The wrapped property instance.
     	 */
     	public StringProperty field(String identifier, StringPropertyAccessor accessor) {
    -		return addIdentified(identifier, new FxPropertyField<>(accessor, () -> new SimpleStringProperty(null, identifier)));
    +		return addIdentified(identifier, new FxPropertyField<>(this::propertyWasChanged, accessor,
    +				() -> new SimpleStringProperty(null, identifier)));
     	}
    -	
    +
     	public StringProperty field(String identifier, StringPropertyAccessor accessor, String defaultValue) {
     		return addIdentified(identifier,
    -				new FxPropertyField<>(accessor, defaultValue, () -> new SimpleStringProperty(null, identifier)));
    +				new FxPropertyField<>(this::propertyWasChanged, accessor, defaultValue,
    +						() -> new SimpleStringProperty(null, identifier)));
     	}
    -	
    +
     	/** Field type Boolean **/
    -	
    +
     	public BooleanProperty field(BooleanGetter getter, BooleanSetter setter) {
    -		return add(new BeanPropertyField<>(getter, setter, SimpleBooleanProperty::new));
    +		return add(new BeanPropertyField<>(this::propertyWasChanged, getter, setter, SimpleBooleanProperty::new));
     	}
    -	
    +
     	public BooleanProperty field(BooleanGetter getter, BooleanSetter setter, boolean defaultValue) {
    -		return add(new BeanPropertyField<>(getter, setter, defaultValue, SimpleBooleanProperty::new));
    +		return add(new BeanPropertyField<>(this::propertyWasChanged, getter, setter, defaultValue,
    +				SimpleBooleanProperty::new));
     	}
    -	
    +
     	public BooleanProperty field(BooleanPropertyAccessor accessor) {
    -		return add(new FxPropertyField<>(accessor, SimpleBooleanProperty::new));
    +		return add(new FxPropertyField<>(this::propertyWasChanged, accessor, SimpleBooleanProperty::new));
     	}
    -	
    +
     	public BooleanProperty field(BooleanPropertyAccessor accessor, boolean defaultValue) {
    -		return add(new FxPropertyField<>(accessor, defaultValue, SimpleBooleanProperty::new));
    +		return add(new FxPropertyField<>(this::propertyWasChanged, accessor, defaultValue, SimpleBooleanProperty::new));
     	}
    -	
    +
     	public BooleanProperty field(String identifier, BooleanGetter getter, BooleanSetter setter) {
    -		return addIdentified(identifier, new BeanPropertyField<>(getter, setter, () -> new SimpleBooleanProperty(null, identifier)));
    +		return addIdentified(identifier, new BeanPropertyField<>(this::propertyWasChanged, getter, setter,
    +				() -> new SimpleBooleanProperty(null, identifier)));
     	}
    -	
    +
     	public BooleanProperty field(String identifier, BooleanGetter getter, BooleanSetter setter,
     			boolean defaultValue) {
    -		return addIdentified(identifier, new BeanPropertyField<>(getter, setter, defaultValue,
    +		return addIdentified(identifier, new BeanPropertyField<>(this::propertyWasChanged, getter, setter, defaultValue,
     				() -> new SimpleBooleanProperty(null, identifier)));
     	}
    -	
    +
     	public BooleanProperty field(String identifier, BooleanPropertyAccessor accessor) {
    -		return addIdentified(identifier, new FxPropertyField<>(accessor, () -> new SimpleBooleanProperty(null, identifier)));
    +		return addIdentified(identifier, new FxPropertyField<>(this::propertyWasChanged, accessor,
    +				() -> new SimpleBooleanProperty(null, identifier)));
     	}
    -	
    +
     	public BooleanProperty field(String identifier, BooleanPropertyAccessor accessor, boolean defaultValue) {
    -		return addIdentified(identifier, new FxPropertyField<>(accessor, defaultValue, () -> new SimpleBooleanProperty(null, identifier)));
    +		return addIdentified(identifier, new FxPropertyField<>(this::propertyWasChanged, accessor, defaultValue,
    +				() -> new SimpleBooleanProperty(null, identifier)));
     	}
    -	
    -	
    -	
    +
    +
    +
     	/** Field type Double **/
    -	
    -	
    +
    +
     	public DoubleProperty field(DoubleGetter getter, DoubleSetter setter) {
    -		final ModelWrapper.BeanPropertyField beanPropertyField = new BeanPropertyField<>(
    +		return add(new BeanPropertyField<>(
    +				this::propertyWasChanged,
     				getter::apply, (m, number) -> setter.accept(m, number.doubleValue()),
    -				SimpleDoubleProperty::new);
    -		return add(beanPropertyField);
    +				SimpleDoubleProperty::new));
     	}
    -	
    +
     	public DoubleProperty field(DoubleGetter getter, DoubleSetter setter, double defaultValue) {
    -		final ModelWrapper.BeanPropertyField beanPropertyField = new BeanPropertyField<>(
    +		return add(new BeanPropertyField<>(
    +				this::propertyWasChanged,
     				getter::apply, (m, number) -> setter.accept(m, number.doubleValue()),
     				defaultValue,
    -				SimpleDoubleProperty::new);
    -		return add(beanPropertyField);
    +				SimpleDoubleProperty::new));
     	}
    -	
    +
     	public DoubleProperty field(DoublePropertyAccessor accessor) {
    -		return add(new FxPropertyField<>(accessor::apply, SimpleDoubleProperty::new));
    +		return add(new FxPropertyField<>(this::propertyWasChanged, accessor::apply, SimpleDoubleProperty::new));
     	}
    -	
    +
     	public DoubleProperty field(DoublePropertyAccessor accessor, double defaultValue) {
    -		return add(new FxPropertyField<>(accessor::apply, defaultValue, SimpleDoubleProperty::new));
    +		return add(new FxPropertyField<>(this::propertyWasChanged, accessor::apply, defaultValue,
    +				SimpleDoubleProperty::new));
     	}
    -	
    +
     	public DoubleProperty field(String identifier, DoubleGetter getter, DoubleSetter setter) {
    -		final ModelWrapper.BeanPropertyField beanPropertyField =
    -				new BeanPropertyField<>(
    -					getter::apply, (m, number) -> setter.accept(m, number.doubleValue()),
    -					() -> new SimpleDoubleProperty(null, identifier));
    -				
    -		return addIdentified(identifier, beanPropertyField);
    +
    +		return addIdentified(identifier, new BeanPropertyField<>(
    +				this::propertyWasChanged,
    +				getter::apply, (m, number) -> setter.accept(m, number.doubleValue()),
    +				() -> new SimpleDoubleProperty(null, identifier)));
     	}
    -	
    +
     	public DoubleProperty field(String identifier, DoubleGetter getter, DoubleSetter setter,
     			double defaultValue) {
    -		ModelWrapper.BeanPropertyField beanPropertyField = new BeanPropertyField<>(
    +		return addIdentified(identifier, new BeanPropertyField<>(
    +				this::propertyWasChanged,
     				getter::apply, (m, number) -> setter.accept(m, number.doubleValue()),
     				defaultValue,
    -				() -> new SimpleDoubleProperty(null, identifier));
    -		return addIdentified(identifier, beanPropertyField);
    +				() -> new SimpleDoubleProperty(null, identifier)));
     	}
    -	
    +
     	public DoubleProperty field(String identifier, DoublePropertyAccessor accessor) {
    -		return addIdentified(identifier, new FxPropertyField<>(accessor::apply, () -> new SimpleDoubleProperty(null, identifier)));
    +		return addIdentified(identifier, new FxPropertyField<>(this::propertyWasChanged, accessor::apply,
    +				() -> new SimpleDoubleProperty(null, identifier)));
     	}
    -	
    +
     	public DoubleProperty field(String identifier, DoublePropertyAccessor accessor, double defaultValue) {
     		return addIdentified(identifier,
    -				new FxPropertyField<>(accessor::apply, defaultValue, () -> new SimpleDoubleProperty(null, identifier)));
    +				new FxPropertyField<>(this::propertyWasChanged, accessor::apply, defaultValue,
    +						() -> new SimpleDoubleProperty(null, identifier)));
     	}
    -	
    -	
    -	
    -	
    +
    +
    +
    +
     	/** Field type Float **/
    -	
    +
     	public FloatProperty field(FloatGetter getter, FloatSetter setter) {
    -		final ModelWrapper.BeanPropertyField beanPropertyField = new BeanPropertyField<>(
    +		return add(new BeanPropertyField<>(
    +				this::propertyWasChanged,
     				getter::apply, (m, number) -> setter.accept(m, number.floatValue()),
    -				SimpleFloatProperty::new);
    -		return add(beanPropertyField);
    +				SimpleFloatProperty::new));
     	}
    -	
    +
     	public FloatProperty field(FloatGetter getter, FloatSetter setter, float defaultValue) {
    -		ModelWrapper.BeanPropertyField beanPropertyField = new BeanPropertyField<>(
    +		return add(new BeanPropertyField<>(
    +				this::propertyWasChanged,
     				getter::apply, (m, number) -> setter.accept(m, number.floatValue()), defaultValue,
    -				SimpleFloatProperty::new);
    -		return add(beanPropertyField);
    +				SimpleFloatProperty::new));
     	}
    -	
    +
     	public FloatProperty field(FloatPropertyAccessor accessor) {
    -		return add(new FxPropertyField<>(accessor::apply, SimpleFloatProperty::new));
    +		return add(new FxPropertyField<>(this::propertyWasChanged, accessor::apply, SimpleFloatProperty::new));
     	}
    -	
    +
     	public FloatProperty field(FloatPropertyAccessor accessor, float defaultValue) {
    -		return add(new FxPropertyField<>(accessor::apply, defaultValue, SimpleFloatProperty::new));
    +		return add(new FxPropertyField<>(this::propertyWasChanged, accessor::apply, defaultValue,
    +				SimpleFloatProperty::new));
     	}
    -	
    +
     	public FloatProperty field(String identifier, FloatGetter getter, FloatSetter setter) {
    -		ModelWrapper.BeanPropertyField beanPropertyField = new BeanPropertyField<>(
    +		return addIdentified(identifier, new BeanPropertyField<>(
    +				this::propertyWasChanged,
     				getter::apply, (m, number) -> setter.accept(m, number.floatValue()),
    -				() -> new SimpleFloatProperty(null, identifier));
    -		return addIdentified(identifier, beanPropertyField);
    +				() -> new SimpleFloatProperty(null, identifier)));
     	}
    -	
    +
     	public FloatProperty field(String identifier, FloatGetter getter, FloatSetter setter, float defaultValue) {
    -		
    -		ModelWrapper.BeanPropertyField beanPropertyField = new BeanPropertyField<>(
    +
    +		return addIdentified(identifier, new BeanPropertyField<>(
    +				this::propertyWasChanged,
     				getter::apply, (m, number) -> setter.accept(m, number.floatValue()),
     				defaultValue,
    -				() -> new SimpleFloatProperty(null, identifier));
    -		return addIdentified(identifier, beanPropertyField);
    +				() -> new SimpleFloatProperty(null, identifier)));
     	}
    -	
    +
     	public FloatProperty field(String identifier, FloatPropertyAccessor accessor) {
    -		return addIdentified(identifier, new FxPropertyField<>(accessor::apply, () -> new SimpleFloatProperty(null, identifier)));
    +		return addIdentified(identifier, new FxPropertyField<>(this::propertyWasChanged, accessor::apply,
    +				() -> new SimpleFloatProperty(null, identifier)));
     	}
    -	
    +
     	public FloatProperty field(String identifier, FloatPropertyAccessor accessor, float defaultValue) {
     		return addIdentified(identifier,
    -				new FxPropertyField<>(accessor::apply, defaultValue, () -> new SimpleFloatProperty(null, identifier)));
    +				new FxPropertyField<>(this::propertyWasChanged, accessor::apply, defaultValue,
    +						() -> new SimpleFloatProperty(null, identifier)));
     	}
    -	
    -	
    +
    +
     	/** Field type Integer **/
    -	
    -	
    +
    +
     	public IntegerProperty field(IntGetter getter, IntSetter setter) {
    -		ModelWrapper.BeanPropertyField beanPropertyField = new BeanPropertyField<>(
    +		return add(new BeanPropertyField<>(
    +				this::propertyWasChanged,
     				getter::apply, (m, number) -> setter.accept(m, number.intValue()),
    -				SimpleIntegerProperty::new);
    -		return add(beanPropertyField);
    +				SimpleIntegerProperty::new));
     	}
    -	
    +
     	public IntegerProperty field(IntGetter getter, IntSetter setter, int defaultValue) {
    -		ModelWrapper.BeanPropertyField beanPropertyField = new BeanPropertyField<>(
    +		return add(new BeanPropertyField<>(
    +				this::propertyWasChanged,
     				getter::apply, (m, number) -> setter.accept(m, number.intValue()),
     				defaultValue,
    -				SimpleIntegerProperty::new);
    -		return add(beanPropertyField);
    +				SimpleIntegerProperty::new));
     	}
    -	
    -	
    +
    +
     	public IntegerProperty field(IntPropertyAccessor accessor) {
    -		return add(new FxPropertyField<>(accessor::apply, SimpleIntegerProperty::new));
    +		return add(new FxPropertyField<>(this::propertyWasChanged, accessor::apply, SimpleIntegerProperty::new));
     	}
    -	
    +
     	public IntegerProperty field(IntPropertyAccessor accessor, int defaultValue) {
    -		return add(new FxPropertyField<>(accessor::apply, defaultValue, SimpleIntegerProperty::new));
    +		return add(new FxPropertyField<>(this::propertyWasChanged, accessor::apply, defaultValue,
    +				SimpleIntegerProperty::new));
     	}
    -	
    +
     	public IntegerProperty field(String identifier, IntGetter getter, IntSetter setter) {
    -		ModelWrapper.BeanPropertyField beanPropertyField = new BeanPropertyField<>(
    +		return addIdentified(identifier, new BeanPropertyField<>(
    +				this::propertyWasChanged,
     				getter::apply, (m, number) -> setter.accept(m, number.intValue()),
    -				() -> new SimpleIntegerProperty(null, identifier));
    -		return addIdentified(identifier, beanPropertyField);
    +				() -> new SimpleIntegerProperty(null, identifier)));
     	}
    -	
    +
     	public IntegerProperty field(String identifier, IntGetter getter, IntSetter setter, int defaultValue) {
    -		ModelWrapper.BeanPropertyField beanPropertyField = new BeanPropertyField<>(
    +		return addIdentified(identifier, new BeanPropertyField<>(
    +				this::propertyWasChanged,
     				getter::apply, (m, number) -> setter.accept(m, number.intValue()),
     				defaultValue,
    -				() -> new SimpleIntegerProperty(null, identifier));
    -		return addIdentified(identifier, beanPropertyField);
    +				() -> new SimpleIntegerProperty(null, identifier)));
     	}
    -	
    -	
    +
    +
     	public IntegerProperty field(String identifier, IntPropertyAccessor accessor) {
    -		return addIdentified(identifier, new FxPropertyField<>(accessor::apply, () -> new SimpleIntegerProperty(null, identifier)));
    +		return addIdentified(identifier, new FxPropertyField<>(this::propertyWasChanged, accessor::apply,
    +				() -> new SimpleIntegerProperty(null, identifier)));
     	}
    -	
    +
     	public IntegerProperty field(String identifier, IntPropertyAccessor accessor, int defaultValue) {
    -		return addIdentified(identifier, new FxPropertyField<>(accessor::apply, defaultValue,
    -				 () -> new SimpleIntegerProperty(null, identifier)));
    +		return addIdentified(identifier, new FxPropertyField<>(this::propertyWasChanged, accessor::apply, defaultValue,
    +				() -> new SimpleIntegerProperty(null, identifier)));
     	}
    -	
    -	
    -	
    +
    +
    +
     	/** Field type Long **/
    -	
    +
     	public LongProperty field(LongGetter getter, LongSetter setter) {
    -		ModelWrapper.BeanPropertyField beanPropertyField = new BeanPropertyField<>(
    +		return add(new BeanPropertyField<>(
    +				this::propertyWasChanged,
     				getter::apply, (m, number) -> setter.accept(m, number.longValue()),
    -				SimpleLongProperty::new);
    -		return add(beanPropertyField);
    +				SimpleLongProperty::new));
     	}
    -	
    +
     	public LongProperty field(LongGetter getter, LongSetter setter, long defaultValue) {
    -		ModelWrapper.BeanPropertyField beanPropertyField = new BeanPropertyField<>(
    +		return add(new BeanPropertyField<>(
    +				this::propertyWasChanged,
     				getter::apply, (m, number) -> setter.accept(m, number.longValue()),
     				defaultValue,
    -				SimpleLongProperty::new);
    -		return add(beanPropertyField);
    +				SimpleLongProperty::new));
     	}
    -	
    +
     	public LongProperty field(LongPropertyAccessor accessor) {
    -		return add(new FxPropertyField<>(accessor::apply, SimpleLongProperty::new));
    +		return add(new FxPropertyField<>(this::propertyWasChanged, accessor::apply, SimpleLongProperty::new));
     	}
    -	
    +
     	public LongProperty field(LongPropertyAccessor accessor, long defaultValue) {
    -		return add(new FxPropertyField<>(accessor::apply, defaultValue, SimpleLongProperty::new));
    +		return add(new FxPropertyField<>(this::propertyWasChanged, accessor::apply, defaultValue,
    +				SimpleLongProperty::new));
     	}
    -	
    -	
    +
    +
     	public LongProperty field(String identifier, LongGetter getter, LongSetter setter) {
    -		ModelWrapper.BeanPropertyField beanPropertyField = new BeanPropertyField<>(
    +		return addIdentified(identifier, new BeanPropertyField<>(
    +				this::propertyWasChanged,
     				getter::apply, (m, number) -> setter.accept(m, number.longValue()),
    -				() -> new SimpleLongProperty(null, identifier));
    -		return addIdentified(identifier, beanPropertyField);
    +				() -> new SimpleLongProperty(null, identifier)));
     	}
    -	
    +
     	public LongProperty field(String identifier, LongGetter getter, LongSetter setter, long defaultValue) {
    -		ModelWrapper.BeanPropertyField beanPropertyField = new BeanPropertyField<>(
    -				getter::apply, (m, number) -> setter.accept(m, number.longValue()),
    -				defaultValue,
    -				() -> new SimpleLongProperty(null, identifier));
     		return addIdentified(identifier,
    -				beanPropertyField);
    +				new BeanPropertyField<>(
    +						this::propertyWasChanged,
    +						getter::apply, (m, number) -> setter.accept(m, number.longValue()),
    +						defaultValue,
    +						() -> new SimpleLongProperty(null, identifier)));
     	}
    -	
    +
     	public LongProperty field(String identifier, LongPropertyAccessor accessor) {
    -		return addIdentified(identifier, new FxPropertyField<>(accessor::apply, () -> new SimpleLongProperty(null, identifier)));
    +		return addIdentified(identifier, new FxPropertyField<>(this::propertyWasChanged, accessor::apply,
    +				() -> new SimpleLongProperty(null, identifier)));
     	}
    -	
    +
     	public LongProperty field(String identifier, LongPropertyAccessor accessor, long defaultValue) {
    -		return addIdentified(identifier, new FxPropertyField<>(accessor::apply, defaultValue, () -> new SimpleLongProperty(null, identifier)));
    +		return addIdentified(identifier, new FxPropertyField<>(this::propertyWasChanged, accessor::apply, defaultValue,
    +				() -> new SimpleLongProperty(null, identifier)));
     	}
    -	
    -	
    -	
    +
    +
    +
     	/** Field type generic **/
    -	
    -	
    +
    +
     	public  ObjectProperty field(ObjectGetter getter, ObjectSetter setter) {
    -		return add(new BeanPropertyField<>(getter, setter, SimpleObjectProperty::new));
    +		return add(new BeanPropertyField<>(this::propertyWasChanged, getter, setter, SimpleObjectProperty::new));
     	}
    -	
    +
     	public  ObjectProperty field(ObjectGetter getter, ObjectSetter setter, T defaultValue) {
    -		return add(new BeanPropertyField<>(getter, setter, defaultValue, SimpleObjectProperty::new));
    +		return add(new BeanPropertyField<>(this::propertyWasChanged, getter, setter, defaultValue,
    +				SimpleObjectProperty::new));
     	}
    -	
    +
     	public  ObjectProperty field(ObjectPropertyAccessor accessor) {
    -		return add(new FxPropertyField<>(accessor, SimpleObjectProperty::new));
    +		return add(new FxPropertyField<>(this::propertyWasChanged, accessor, SimpleObjectProperty::new));
     	}
    -	
    +
     	public  ObjectProperty field(ObjectPropertyAccessor accessor, T defaultValue) {
    -		return add(new FxPropertyField<>(accessor, defaultValue, SimpleObjectProperty::new));
    +		return add(new FxPropertyField<>(this::propertyWasChanged, accessor, defaultValue, SimpleObjectProperty::new));
     	}
    -	
    -	
    +
    +
     	public  ObjectProperty field(String identifier, ObjectGetter getter, ObjectSetter setter) {
    -		return addIdentified(identifier, new BeanPropertyField<>(getter, setter, () -> new SimpleObjectProperty(null, identifier)));
    +		return addIdentified(identifier, new BeanPropertyField<>(this::propertyWasChanged, getter, setter,
    +				() -> new SimpleObjectProperty(null, identifier)));
     	}
    -	
    +
     	public  ObjectProperty field(String identifier, ObjectGetter getter, ObjectSetter setter,
     			T defaultValue) {
    -		return addIdentified(identifier, new BeanPropertyField<>(getter, setter, defaultValue,
    +		return addIdentified(identifier, new BeanPropertyField<>(this::propertyWasChanged, getter, setter, defaultValue,
     				() -> new SimpleObjectProperty(null, identifier)));
     	}
    -	
    +
     	public  ObjectProperty field(String identifier, ObjectPropertyAccessor accessor) {
    -		return addIdentified(identifier, new FxPropertyField<>(accessor, () -> new SimpleObjectProperty(null, identifier)));
    +		return addIdentified(identifier, new FxPropertyField<>(this::propertyWasChanged, accessor,
    +				() -> new SimpleObjectProperty(null, identifier)));
     	}
    -	
    +
     	public  ObjectProperty field(String identifier, ObjectPropertyAccessor accessor, T defaultValue) {
     		return addIdentified(identifier,
    -				new FxPropertyField<>(accessor, defaultValue, () -> new SimpleObjectProperty(null, identifier)));
    +				new FxPropertyField<>(this::propertyWasChanged, accessor, defaultValue,
    +						() -> new SimpleObjectProperty(null, identifier)));
     	}
    -	
    -	
    +
    +
     	/** Field type list **/
    -	
    +
     	public  ListProperty field(ListGetter getter, ListSetter setter) {
    -		return add(new BeanListPropertyField<>(getter,
    +		return add(new BeanListPropertyField<>(this::propertyWasChanged, getter,
     				(m, list) -> setter.accept(m, FXCollections.observableArrayList(list)), SimpleListProperty::new));
     	}
    -	
    +
     	public  ListProperty field(ListGetter getter, ListSetter setter, List defaultValue) {
    -		return add(new BeanListPropertyField<>(getter,
    -				(m, list) -> setter.accept(m, FXCollections.observableArrayList(list)), SimpleListProperty::new, defaultValue));
    +		return add(new BeanListPropertyField<>(this::propertyWasChanged, getter,
    +				(m, list) -> setter.accept(m, FXCollections.observableArrayList(list)), SimpleListProperty::new,
    +				defaultValue));
     	}
    -	
    +
     	public  ListProperty field(ListPropertyAccessor accessor) {
    -		return add(new FxListPropertyField<>(accessor, SimpleListProperty::new));
    +		return add(new FxListPropertyField<>(this::propertyWasChanged, accessor, SimpleListProperty::new));
     	}
    -	
    +
     	public  ListProperty field(ListPropertyAccessor accessor, List defaultValue) {
    -		return add(new FxListPropertyField<>(accessor, SimpleListProperty::new, defaultValue));
    +		return add(
    +				new FxListPropertyField<>(this::propertyWasChanged, accessor, SimpleListProperty::new, defaultValue));
     	}
    -	
    -	
    +
    +
     	public  ListProperty field(String identifier, ListGetter getter, ListSetter setter) {
    -		return addIdentified(identifier, new BeanListPropertyField<>(getter,
    -				(m, list) -> setter.accept(m, FXCollections.observableArrayList(list)), () -> new SimpleListProperty<>(null, identifier)));
    +		return addIdentified(identifier, new BeanListPropertyField<>(this::propertyWasChanged, getter,
    +				(m, list) -> setter.accept(m, FXCollections.observableArrayList(list)),
    +				() -> new SimpleListProperty<>(null, identifier)));
     	}
    -	
    +
     	public  ListProperty field(String identifier, ListGetter getter, ListSetter setter,
     			List defaultValue) {
    -		return addIdentified(identifier, new BeanListPropertyField<>(getter,
    -				(m, list) -> setter.accept(m, FXCollections.observableArrayList(list)), () -> new SimpleListProperty<>(null, identifier), defaultValue));
    +		return addIdentified(identifier, new BeanListPropertyField<>(this::propertyWasChanged, getter,
    +				(m, list) -> setter.accept(m, FXCollections.observableArrayList(list)),
    +				() -> new SimpleListProperty<>(null, identifier), defaultValue));
     	}
    -	
    +
     	public  ListProperty field(String identifier, ListPropertyAccessor accessor) {
    -		return addIdentified(identifier, new FxListPropertyField<>(accessor, () -> new SimpleListProperty<>(null, identifier)));
    +		return addIdentified(identifier, new FxListPropertyField<>(this::propertyWasChanged, accessor,
    +				() -> new SimpleListProperty<>(null, identifier)));
     	}
    -	
    +
     	public  ListProperty field(String identifier, ListPropertyAccessor accessor, List defaultValue) {
    -		return addIdentified(identifier, new FxListPropertyField<>(accessor, () -> new SimpleListProperty<>(null, identifier), defaultValue));
    -	}
    -	
    -	private > R add(PropertyField field) {
    -		fields.add(field);
    -		if (model.get() != null) {
    -			field.reload(model.get());
    -		}
    -		return field.getProperty();
    -	}
    -	
    -	@SuppressWarnings("unchecked")
    -	private > R addIdentified(String fieldName, PropertyField field) {
    -		if (identifiedFields.containsKey(fieldName)) {
    -			final Property property = identifiedFields.get(fieldName).getProperty();
    -			return (R) property;
    -		} else {
    -			identifiedFields.put(fieldName, field);
    -			return add(field);
    -		}
    -	}
    -	
    -	/**
    -	 * This boolean flag indicates whether there is a difference of the data between the wrapped model object and the
    -	 * properties provided by this wrapper.
    -	 * 

    - * Note the difference to {@link #dirtyProperty()}: This property will be true if the data of the - * wrapped model is different to the properties of this wrapper. If you change the data back to the initial state so - * that the data is equal again, this property will change back to false while the - * {@link #dirtyProperty()} will still be true. - * - * Simply speaking: This property indicates whether there is a difference in data between the model and the wrapper. - * The {@link #dirtyProperty()} indicates whether there was a change done. - * - * - * Note: Only those changes are observed that are done through the wrapped property fields of this wrapper. If you - * change the data of the model instance directly, this property won't turn to true. - * - * - * @return a read-only property indicating a difference between model and wrapper. - */ - public ReadOnlyBooleanProperty differentProperty() { - return diffFlag.getReadOnlyProperty(); - } - - /** - * See {@link #differentProperty()}. - */ - public boolean isDifferent() { - return diffFlag.get(); - } - - /** - * This boolean flag indicates whether there was a change to at least one wrapped property. - *

    - * Note the difference to {@link #differentProperty()}: This property will turn to true when the value - * of one of the wrapped properties is changed. It will only change back to false when either the - * {@link #commit()} or {@link #reload()} method is called. This property will stay true even if - * afterwards another change is done so that the data is equal again. In this case the {@link #differentProperty()} - * will switch back to false. - * - * Simply speaking: This property indicates whether there was a change done to the wrapped properties or not. The - * {@link #differentProperty()} indicates whether there is a difference in data at the moment. - * - * @return a read only boolean property indicating if there was a change done. - */ - public ReadOnlyBooleanProperty dirtyProperty() { - return dirtyFlag.getReadOnlyProperty(); - } - - /** - * See {@link #dirtyProperty()}. - */ - public boolean isDirty() { - return dirtyFlag.get(); + return addIdentified(identifier, new FxListPropertyField<>(this::propertyWasChanged, accessor, + () -> new SimpleListProperty<>(null, identifier), defaultValue)); } - - } diff --git a/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/PropertyField.java b/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/PropertyField.java new file mode 100644 index 000000000..a53e41dc9 --- /dev/null +++ b/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/PropertyField.java @@ -0,0 +1,39 @@ +package de.saxsys.mvvmfx.utils.mapping; + +import javafx.beans.property.Property; +import javafx.beans.property.StringProperty; + +/** + * This interface defines the operations that are possible for each field of a wrapped class. + * + * @param + * target type. The base type of the returned property, f.e. {@link String}. + * @param + * model type. The type of the Model class, that is wrapped by this ModelWrapper instance. + * @param + * return type. The type of the Property that is returned via {@link #getProperty()}, f.e. + * {@link StringProperty} or {@link Property }. + */ +interface PropertyField> { + void commit(M wrappedObject); + + void reload(M wrappedObject); + + void resetToDefault(); + + void updateDefault(final M wrappedObject); + + R getProperty(); + + /** + * Determines if the value in the model object and the property field are different or not. + * + * This method is used to implement the {@link ModelWrapper#differentProperty()} flag. + * + * @param wrappedObject + * the wrapped model object + * @return false if both the wrapped model object and the property field have the same value, + * otherwise true + */ + boolean isDifferent(M wrappedObject); +} diff --git a/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/mapping/ModelWrapperTest.java b/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/mapping/ModelWrapperTest.java index 45a6d674b..fff6b416c 100644 --- a/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/mapping/ModelWrapperTest.java +++ b/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/mapping/ModelWrapperTest.java @@ -602,39 +602,40 @@ public void defaultValuesCanBeUpdatedToCurrentValues(){ assertThat(nicknames.get()).containsExactly("myname"); } - @Test - public void valuesShouldBeUpdatedWhenModelInstanceChanges() { - final Person person1 = new Person(); - person1.setName("horst"); - person1.setAge(32); - person1.setNicknames(Arrays.asList("captain")); - final Person person2 = new Person(); - person2.setName("dieter"); - person2.setAge(42); - person2.setNicknames(Arrays.asList("robin")); - - final SimpleObjectProperty modelProp = new SimpleObjectProperty<>(person1); - - final ModelWrapper cut = new ModelWrapper<>(modelProp); - - final StringProperty nameField = cut.field(Person::getName, Person::setName, person1.getName()); - final IntegerProperty ageField = cut.field(Person::getAge, Person::setAge, person1.getAge()); - final ListProperty nicknames = cut.field(Person::getNicknames, Person::setNicknames, person1.getNicknames()); - - assertThat(nameField.get()).isEqualTo(person1.getName()); - assertThat(ageField.get()).isEqualTo(person1.getAge()); - assertThat(nicknames.get()).containsExactlyElementsOf(person1.getNicknames()); - - modelProp.set(person2); - assertThat(nameField.get()).isEqualTo(person2.getName()); - assertThat(ageField.get()).isEqualTo(person2.getAge()); - assertThat(nicknames.get()).containsExactlyElementsOf(person2.getNicknames()); - - cut.reset(); - assertThat(nameField.get()).isEqualTo(person1.getName()); - assertThat(ageField.get()).isEqualTo(person1.getAge()); - assertThat(nicknames.get()).containsExactlyElementsOf(person1.getNicknames()); - } + @Test + public void valuesShouldBeUpdatedWhenModelInstanceChanges() { + final Person person1 = new Person(); + person1.setName("horst"); + person1.setAge(32); + person1.setNicknames(Arrays.asList("captain")); + final Person person2 = new Person(); + person2.setName("dieter"); + person2.setAge(42); + person2.setNicknames(Arrays.asList("robin")); + + final SimpleObjectProperty modelProp = new SimpleObjectProperty<>(person1); + + final ModelWrapper cut = new ModelWrapper<>(modelProp); + + final StringProperty nameField = cut.field(Person::getName, Person::setName, person1.getName()); + final IntegerProperty ageField = cut.field(Person::getAge, Person::setAge, person1.getAge()); + final ListProperty nicknames = cut + .field(Person::getNicknames, Person::setNicknames, person1.getNicknames()); + + assertThat(nameField.get()).isEqualTo(person1.getName()); + assertThat(ageField.get()).isEqualTo(person1.getAge()); + assertThat(nicknames.get()).containsExactlyElementsOf(person1.getNicknames()); + + modelProp.set(person2); + assertThat(nameField.get()).isEqualTo(person2.getName()); + assertThat(ageField.get()).isEqualTo(person2.getAge()); + assertThat(nicknames.get()).containsExactlyElementsOf(person2.getNicknames()); + + cut.reset(); + assertThat(nameField.get()).isEqualTo(person1.getName()); + assertThat(ageField.get()).isEqualTo(person1.getAge()); + assertThat(nicknames.get()).containsExactlyElementsOf(person1.getNicknames()); + } @Test public void testUseCurrentValuesAsDefaultWhenModelIsNull() { From bd57e6dff7b3b5ad15de85c36a60f6b446b89cb9 Mon Sep 17 00:00:00 2001 From: Manuel Mauky Date: Wed, 14 Jun 2017 18:33:15 +0200 Subject: [PATCH 18/51] Add prototype implementation for immutable string properties --- .../utils/mapping/BeanPropertyField.java | 2 +- .../mvvmfx/utils/mapping/ModelWrapper.java | 45 +++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/BeanPropertyField.java b/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/BeanPropertyField.java index 537ade8ad..1e8301fc3 100644 --- a/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/BeanPropertyField.java +++ b/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/BeanPropertyField.java @@ -14,7 +14,7 @@ * * @param */ -class BeanPropertyField> implements PropertyField { +class BeanPropertyField> implements PropertyField { private final R targetProperty; private T defaultValue; diff --git a/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/ModelWrapper.java b/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/ModelWrapper.java index e93fb8218..3a7115c64 100644 --- a/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/ModelWrapper.java +++ b/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/ModelWrapper.java @@ -37,6 +37,7 @@ import de.saxsys.mvvmfx.utils.mapping.accessorfunctions.ObjectPropertyAccessor; import de.saxsys.mvvmfx.utils.mapping.accessorfunctions.ObjectSetter; import de.saxsys.mvvmfx.utils.mapping.accessorfunctions.StringGetter; +import de.saxsys.mvvmfx.utils.mapping.accessorfunctions.StringImmutableSetter; import de.saxsys.mvvmfx.utils.mapping.accessorfunctions.StringPropertyAccessor; import de.saxsys.mvvmfx.utils.mapping.accessorfunctions.StringSetter; @@ -250,6 +251,8 @@ public class ModelWrapper { private final Set> fields = new LinkedHashSet<>(); private final Map> identifiedFields = new HashMap<>(); + private final Set> immutableFields = new LinkedHashSet<>(); + private final ObjectProperty model; @@ -395,6 +398,11 @@ public void commit() { if (model.get() != null) { fields.forEach(field -> field.commit(model.get())); + for (ImmutablePropertyField immutableField : immutableFields) { + M newModel = immutableField.commitImmutable(model.get()); + model.setValue(newModel); + } + dirtyFlag.set(false); calculateDifferenceFlag(); @@ -471,6 +479,26 @@ private > R addIdentified(String fieldName, PropertyFie } } + private > R addImmutable(ImmutablePropertyField field) { + immutableFields.add(field); + + if(model.get() != null) { + field.reload(model.get()); + } + + return field.getProperty(); + } + + private > R addIdentifiedImmutable(String fieldName, ImmutablePropertyField field) { + if (identifiedFields.containsKey(fieldName)) { + final Property property = identifiedFields.get(fieldName).getProperty(); + return (R) property; + } else { + identifiedFields.put(fieldName, field); + return addImmutable(field); + } + } + /** * This boolean flag indicates whether there is a difference of the data between the wrapped model object and the * properties provided by this wrapper. @@ -565,6 +593,11 @@ public StringProperty field(StringGetter getter, StringSetter setter) { return add(new BeanPropertyField<>(this::propertyWasChanged, getter, setter, SimpleStringProperty::new)); } + + public StringProperty immutableField(StringGetter getter, StringImmutableSetter setter){ + return addImmutable(new ImmutablePropertyField<>(this::propertyWasChanged, getter, setter, SimpleStringProperty::new)); + } + /** * Add a new field of type String to this instance of the wrapper. See {@link #field(StringGetter, StringSetter)}. * This method additionally has a parameter to define the default value that is used when the {@link #reset()} @@ -587,6 +620,10 @@ public StringProperty field(StringGetter getter, StringSetter setter, Stri SimpleStringProperty::new)); } + public StringProperty immutableField(StringGetter getter, StringImmutableSetter setter, String defaultValue){ + return addImmutable(new ImmutablePropertyField<>(this::propertyWasChanged, getter, setter, defaultValue, SimpleStringProperty::new)); + } + /** * Add a new field of type {@link String} to this instance of the wrapper. This method is used for model elements * that are following the enhanced JavaFX-Beans-standard i.e. the model fields are available as JavaFX Properties. @@ -662,6 +699,14 @@ public StringProperty field(String identifier, StringGetter getter, StringSet () -> new SimpleStringProperty(null, identifier))); } + public StringProperty immutableField(String identifier, StringGetter getter, StringImmutableSetter setter){ + return addIdentifiedImmutable(identifier, new ImmutablePropertyField<>(this::propertyWasChanged, getter, setter, SimpleStringProperty::new)); + } + + public StringProperty immutableField(String identifier, StringGetter getter, StringImmutableSetter setter, String defaultValue){ + return addIdentifiedImmutable(identifier, new ImmutablePropertyField<>(this::propertyWasChanged, getter, setter, defaultValue, SimpleStringProperty::new)); + } + /** * Add a new field of type String to this instance of the wrapper. See {@link #field(StringPropertyAccessor)}. This * method additionally takes a string identifier as first parameter. From 237fdc6d243a8949478d60c4833af5e77563bf11 Mon Sep 17 00:00:00 2001 From: Manuel Mauky Date: Wed, 14 Jun 2017 18:40:26 +0200 Subject: [PATCH 19/51] Fix #494. AddContactDialogView shouldn't be a singleton --- .../contacts/ui/addcontact/AddContactDialogView.java | 3 --- .../examples/contacts/ui/toolbar/ToolbarViewModel.java | 7 ++++++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/ui/addcontact/AddContactDialogView.java b/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/ui/addcontact/AddContactDialogView.java index 3246d7f5e..3bd3021ef 100644 --- a/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/ui/addcontact/AddContactDialogView.java +++ b/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/ui/addcontact/AddContactDialogView.java @@ -1,12 +1,9 @@ package de.saxsys.mvvmfx.examples.contacts.ui.addcontact; -import javax.inject.Singleton; - import de.saxsys.mvvmfx.FxmlView; import de.saxsys.mvvmfx.InjectViewModel; import javafx.stage.Stage; -@Singleton public class AddContactDialogView implements FxmlView { @InjectViewModel diff --git a/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/ui/toolbar/ToolbarViewModel.java b/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/ui/toolbar/ToolbarViewModel.java index fbba55514..f22d99d98 100644 --- a/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/ui/toolbar/ToolbarViewModel.java +++ b/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/ui/toolbar/ToolbarViewModel.java @@ -4,12 +4,17 @@ import de.saxsys.mvvmfx.ViewModel; import de.saxsys.mvvmfx.examples.contacts.ui.scopes.ContactDialogScope; +import javax.enterprise.inject.Instance; +import javax.inject.Inject; import java.util.Collections; import java.util.List; public class ToolbarViewModel implements ViewModel { + @Inject + private Instance scopeInstance; + public List getScopesForAddDialog() { - return Collections.singletonList(new ContactDialogScope()); + return Collections.singletonList(scopeInstance.get()); } } From 1af8a45c60df1d3007ed73c70897ddf3025c1cc0 Mon Sep 17 00:00:00 2001 From: Manuel Mauky Date: Wed, 14 Jun 2017 18:48:35 +0200 Subject: [PATCH 20/51] encapsulate CountrySelector implementation by introducing an interface --- .../contacts/model/CountrySelector.java | 247 +----------------- .../contacts/model/DataFxCountrySelector.java | 221 ++++++++++++++++ .../model/ISO3166_2_CountryEntity.java | 27 ++ .../contacts/model/ISO3166_2_EntryEntity.java | 19 ++ .../model/ISO3166_2_SubsetEntity.java | 22 ++ .../ui/addressform/AddressFormViewModel.java | 1 - .../model/AbstractCountrySelectorTest.java | 109 ++++++++ .../model/CountrySelectorIntegrationTest.java | 162 ------------ .../DataFxCountrySelectorIntegrationTest.java | 74 ++++++ 9 files changed, 483 insertions(+), 399 deletions(-) create mode 100644 examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/DataFxCountrySelector.java create mode 100644 examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/ISO3166_2_CountryEntity.java create mode 100644 examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/ISO3166_2_EntryEntity.java create mode 100644 examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/ISO3166_2_SubsetEntity.java create mode 100644 examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/AbstractCountrySelectorTest.java delete mode 100644 examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/CountrySelectorIntegrationTest.java create mode 100644 examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/DataFxCountrySelectorIntegrationTest.java diff --git a/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/CountrySelector.java b/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/CountrySelector.java index 751522c79..23d373c70 100644 --- a/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/CountrySelector.java +++ b/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/CountrySelector.java @@ -1,257 +1,32 @@ package de.saxsys.mvvmfx.examples.contacts.model; import javafx.beans.property.ReadOnlyBooleanProperty; -import javafx.beans.property.ReadOnlyBooleanWrapper; import javafx.beans.property.ReadOnlyStringProperty; -import javafx.beans.property.ReadOnlyStringWrapper; -import javafx.collections.FXCollections; import javafx.collections.ObservableList; -import javafx.concurrent.Worker; -import org.datafx.provider.ListDataProvider; -import org.datafx.reader.DataReader; -import org.datafx.reader.InputStreamDataReader; -import org.datafx.reader.converter.InputStreamConverter; -import org.datafx.reader.converter.XmlConverter; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.xml.bind.annotation.*; -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; /** - * This class is used to encapsulate the process of loading available countries - * and there subdivisions (if available). + * Implementations of this interface are used to encapsulate the process of loading available countries + * and their subdivisions (if available). * - * This class is meant to be a stateful wrapper around the existing countries. + * Instances are meant to be a stateful wrapper around the existing countries. * You should create an instance of this class, call the {@link #init()} method * and then bind the UI to the provided observable lists ( * {@link #availableCountries()} and {@link #subdivisions()}). * - * To choose a country have to use the {@link #setCountry(Country)} method. This + * To choose a country use the {@link #setCountry(Country)} method. This * will lead to a change of the {@link #subdivisions()} list. - * - * - * At the moment this class used two XML files ({@link #ISO_3166_LOCATION} and - * {@link #ISO_3166_2_LOCATION}) that contain information about countries, - * country-codes and subdivisions according to ISO 3166 and ISO 3166-2. - * - * The loading process is implemented with the DataFX framework. */ -public class CountrySelector { - - private static final Logger LOG = LoggerFactory.getLogger(CountrySelector.class); - - public static final String ISO_3166_LOCATION = "/countries/iso_3166.xml"; - public static final String ISO_3166_2_LOCATION = "/countries/iso_3166_2.xml"; - private ObservableList countries = FXCollections.observableArrayList(); - private ObservableList subdivisions = FXCollections.observableArrayList(); - - private ReadOnlyStringWrapper subdivisionLabel = new ReadOnlyStringWrapper(); - - private ReadOnlyBooleanWrapper inProgress = new ReadOnlyBooleanWrapper(false); - - private Map> countryCodeSubdivisionMap = new HashMap<>(); - private Map countryCodeSubdivisionNameMap = new HashMap<>(); - - /** - * This method triggers the loading of the available countries and - * subdivisions. - */ - public void init() { - inProgress.set(true); - loadCountries(); - } - - /** - * Set the currently selected country. This will lead to an update of the - * {@link #subdivisions()} observable list and the - * {@link #subdivisionLabel()}. - * - * @param country the country that will be selected or null if - * no country is selected. - */ - public void setCountry(Country country) { - if (country == null) { - subdivisionLabel.set(null); - subdivisions.clear(); - return; - } - - subdivisionLabel.set(countryCodeSubdivisionNameMap.get(country)); - - subdivisions.clear(); - if (countryCodeSubdivisionMap.containsKey(country)) { - subdivisions.addAll(countryCodeSubdivisionMap.get(country)); - } - } - - /** - * Load all countries from the XML file source with DataFX. - */ - void loadCountries() { - InputStream iso3166Resource = this.getClass().getResourceAsStream(ISO_3166_LOCATION); - if (iso3166Resource == null) { - throw new IllegalStateException("Can't find the list of countries! Expected location was:" - + ISO_3166_LOCATION); - } - - XmlConverter countryConverter = new XmlConverter<>("iso_3166_entry", Country.class); - - try { - DataReader dataSource = new InputStreamSource<>( iso3166Resource, countryConverter ); - - ListDataProvider listDataProvider = new ListDataProvider<>(dataSource); - - listDataProvider.setResultObservableList(countries); - - Worker> worker = listDataProvider.retrieve(); - // when the countries are loaded we start the loading of the subdivisions. - worker.stateProperty().addListener(obs -> { - if (worker.getState() == Worker.State.SUCCEEDED) { - loadSubdivisions(); - } - }); - } catch (IOException e) { - LOG.error("A problem was detected while loading the XML file with the available countries.", e); - } - } - - static class InputStreamSource extends InputStreamDataReader { - public InputStreamSource(InputStream is, InputStreamConverter converter) throws IOException { - super(converter); - setInputStream(is); - } - } - - /** - * Load all subdivisions from the XML file source with DataFX. - */ - void loadSubdivisions() { - - InputStream iso3166_2Resource = this.getClass().getResourceAsStream(ISO_3166_2_LOCATION); - - if (iso3166_2Resource == null) { - throw new IllegalStateException("Can't find the list of subdivisions! Expected location was:" - + ISO_3166_2_LOCATION); - } - - XmlConverter converter = new XmlConverter<>("iso_3166_country", - ISO3166_2_CountryEntity.class); - - ObservableList subdivisionsEntities = FXCollections.observableArrayList(); - - try { - - DataReader dataSource = - new InputStreamSource<>( iso3166_2Resource, converter ); - - ListDataProvider listDataProvider = new ListDataProvider<>(dataSource); - - listDataProvider.setResultObservableList(subdivisionsEntities); - - Worker> worker = listDataProvider.retrieve(); - worker.stateProperty().addListener(obs -> { - if (worker.getState() == Worker.State.SUCCEEDED) { - - subdivisionsEntities.forEach(entity -> { - if (entity.subsets != null && !entity.subsets.isEmpty()) { - - Country country = findCountryByCode(entity.code); - - if (!countryCodeSubdivisionMap.containsKey(country)) { - countryCodeSubdivisionMap.put(country, new ArrayList<>()); - } - - List subdivisionList = countryCodeSubdivisionMap.get(country); - - entity.subsets.get(0).entryList.forEach(entry -> { - subdivisionList.add(new Subdivision(entry.name, entry.code, country)); - }); - - countryCodeSubdivisionNameMap.put(country, entity.subsets.get(0).subdivisionType); - } - }); - - inProgress.set(false); - } - }); - } catch (IOException e) { - LOG.error("A problem was detected while loading the XML file with the available subdivisions.", e); - } - - } - - private Country findCountryByCode(String code) { - return countries.stream().filter(country -> country.getCountryCode().equals(code)).findFirst().orElse(null); - } - - /** - * XML entity class. These classes represent the structure of the XML files - * to be loaded. - */ - @XmlRootElement(name = "iso_3166_subset") - @XmlAccessorType(XmlAccessType.FIELD) - static class ISO3166_2_EntryEntity { - - @XmlAttribute(name = "code") - public String code; - @XmlAttribute(name = "name") - public String name; - } - - /** - * XML entity class. These classes represent the structure of the XML files - * to be loaded. - */ - @XmlRootElement(name = "iso_3166_subset") - @XmlAccessorType(XmlAccessType.FIELD) - static class ISO3166_2_SubsetEntity { - - @XmlElement(name = "iso_3166_2_entry") - public List entryList; - - @XmlAttribute(name = "type") - public String subdivisionType; - } - - /** - * XML entity class. These classes represent the structure of the XML files - * to be loaded. - */ - @XmlRootElement(name = "iso_3166_country") - @XmlAccessorType(XmlAccessType.FIELD) - static class ISO3166_2_CountryEntity { - - @XmlAttribute(name = "code") - public String code; +public interface CountrySelector { - @XmlElement(name = "iso_3166_subset") - public List subsets; + void init(); - @Override - public String toString() { - return "CountryEntity " + code; - } - } + void setCountry(Country country); - public ObservableList availableCountries() { - return countries; - } + ObservableList availableCountries(); - public ReadOnlyStringProperty subdivisionLabel() { - return subdivisionLabel.getReadOnlyProperty(); - } + ReadOnlyStringProperty subdivisionLabel(); - public ObservableList subdivisions() { - return FXCollections.unmodifiableObservableList(subdivisions); - } + ObservableList subdivisions(); - public ReadOnlyBooleanProperty inProgressProperty() { - return inProgress.getReadOnlyProperty(); - } + ReadOnlyBooleanProperty inProgressProperty(); } diff --git a/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/DataFxCountrySelector.java b/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/DataFxCountrySelector.java new file mode 100644 index 000000000..83acffdde --- /dev/null +++ b/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/DataFxCountrySelector.java @@ -0,0 +1,221 @@ +package de.saxsys.mvvmfx.examples.contacts.model; + +import javafx.beans.property.ReadOnlyBooleanProperty; +import javafx.beans.property.ReadOnlyBooleanWrapper; +import javafx.beans.property.ReadOnlyStringProperty; +import javafx.beans.property.ReadOnlyStringWrapper; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.concurrent.Worker; +import org.datafx.provider.ListDataProvider; +import org.datafx.reader.DataReader; +import org.datafx.reader.InputStreamDataReader; +import org.datafx.reader.converter.InputStreamConverter; +import org.datafx.reader.converter.XmlConverter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.annotation.PostConstruct; +import javax.enterprise.inject.Alternative; +import javax.inject.Singleton; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * This class is used to encapsulate the process of loading available countries + * and their subdivisions (if available). + * + * This class is meant to be a stateful wrapper around the existing countries. + * You should create an instance of this class, call the {@link #init()} method + * and then bind the UI to the provided observable lists ( + * {@link #availableCountries()} and {@link #subdivisions()}). + * + * To choose a country use the {@link #setCountry(Country)} method. This + * will lead to a change of the {@link #subdivisions()} list. + * + * + * At the moment this class uses two XML files ({@link #ISO_3166_LOCATION} and + * {@link #ISO_3166_2_LOCATION}) that contain information about countries, + * country-codes and subdivisions according to ISO 3166 and ISO 3166-2. + * + * The loading process is implemented with the DataFX framework. + */ +@Singleton +@Alternative +public class DataFxCountrySelector implements CountrySelector { + + private static final Logger LOG = LoggerFactory.getLogger(DataFxCountrySelector.class); + + public static final String ISO_3166_LOCATION = "/countries/iso_3166.xml"; + public static final String ISO_3166_2_LOCATION = "/countries/iso_3166_2.xml"; + private ObservableList countries = FXCollections.observableArrayList(); + private ObservableList subdivisions = FXCollections.observableArrayList(); + + private ReadOnlyStringWrapper subdivisionLabel = new ReadOnlyStringWrapper(); + + private ReadOnlyBooleanWrapper inProgress = new ReadOnlyBooleanWrapper(false); + + private Map> countryCodeSubdivisionMap = new HashMap<>(); + private Map countryCodeSubdivisionNameMap = new HashMap<>(); + + /** + * This method triggers the loading of the available countries and + * subdivisions. + */ + @PostConstruct + @Override + public void init() { + inProgress.set(true); + loadCountries(); + } + + /** + * Set the currently selected country. This will lead to an update of the + * {@link #subdivisions()} observable list and the + * {@link #subdivisionLabel()}. + * + * @param country the country that will be selected or null if + * no country is selected. + */ + @Override + public void setCountry(Country country) { + if (country == null) { + subdivisionLabel.set(null); + subdivisions.clear(); + return; + } + + subdivisionLabel.set(countryCodeSubdivisionNameMap.get(country)); + + subdivisions.clear(); + if (countryCodeSubdivisionMap.containsKey(country)) { + subdivisions.addAll(countryCodeSubdivisionMap.get(country)); + } + } + + /** + * Load all countries from the XML file source with DataFX. + */ + void loadCountries() { + InputStream iso3166Resource = this.getClass().getResourceAsStream(ISO_3166_LOCATION); + if (iso3166Resource == null) { + throw new IllegalStateException("Can't find the list of countries! Expected location was:" + + ISO_3166_LOCATION); + } + + XmlConverter countryConverter = new XmlConverter<>("iso_3166_entry", Country.class); + + try { + DataReader dataSource = new InputStreamSource<>( iso3166Resource, countryConverter ); + + ListDataProvider listDataProvider = new ListDataProvider<>(dataSource); + + listDataProvider.setResultObservableList(countries); + + Worker> worker = listDataProvider.retrieve(); + // when the countries are loaded we start the loading of the subdivisions. + worker.stateProperty().addListener(obs -> { + if (worker.getState() == Worker.State.SUCCEEDED) { + loadSubdivisions(); + } + }); + } catch (IOException e) { + LOG.error("A problem was detected while loading the XML file with the available countries.", e); + } + } + + static class InputStreamSource extends InputStreamDataReader { + public InputStreamSource(InputStream is, InputStreamConverter converter) throws IOException { + super(converter); + setInputStream(is); + } + } + + /** + * Load all subdivisions from the XML file source with DataFX. + */ + void loadSubdivisions() { + + InputStream iso3166_2Resource = this.getClass().getResourceAsStream(ISO_3166_2_LOCATION); + + if (iso3166_2Resource == null) { + throw new IllegalStateException("Can't find the list of subdivisions! Expected location was:" + + ISO_3166_2_LOCATION); + } + + XmlConverter converter = new XmlConverter<>("iso_3166_country", + ISO3166_2_CountryEntity.class); + + ObservableList subdivisionsEntities = FXCollections.observableArrayList(); + + try { + + DataReader dataSource = + new InputStreamSource<>( iso3166_2Resource, converter ); + + ListDataProvider listDataProvider = new ListDataProvider<>(dataSource); + + listDataProvider.setResultObservableList(subdivisionsEntities); + + Worker> worker = listDataProvider.retrieve(); + worker.stateProperty().addListener(obs -> { + if (worker.getState() == Worker.State.SUCCEEDED) { + + subdivisionsEntities.forEach(entity -> { + if (entity.subsets != null && !entity.subsets.isEmpty()) { + + Country country = findCountryByCode(entity.code); + + if (!countryCodeSubdivisionMap.containsKey(country)) { + countryCodeSubdivisionMap.put(country, new ArrayList<>()); + } + + List subdivisionList = countryCodeSubdivisionMap.get(country); + + entity.subsets.forEach(subset -> { + subset.entryList.forEach(entry -> { + subdivisionList.add(new Subdivision(entry.name, entry.code, country)); + }); + }); + + countryCodeSubdivisionNameMap.put(country, entity.subsets.get(0).subdivisionType); + } + }); + + inProgress.set(false); + } + }); + } catch (IOException e) { + LOG.error("A problem was detected while loading the XML file with the available subdivisions.", e); + } + + } + + private Country findCountryByCode(String code) { + return countries.stream().filter(country -> country.getCountryCode().equals(code)).findFirst().orElse(null); + } + + @Override + public ObservableList availableCountries() { + return countries; + } + + @Override + public ReadOnlyStringProperty subdivisionLabel() { + return subdivisionLabel.getReadOnlyProperty(); + } + + @Override + public ObservableList subdivisions() { + return FXCollections.unmodifiableObservableList(subdivisions); + } + + @Override + public ReadOnlyBooleanProperty inProgressProperty() { + return inProgress.getReadOnlyProperty(); + } +} diff --git a/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/ISO3166_2_CountryEntity.java b/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/ISO3166_2_CountryEntity.java new file mode 100644 index 000000000..1ebea21fb --- /dev/null +++ b/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/ISO3166_2_CountryEntity.java @@ -0,0 +1,27 @@ +package de.saxsys.mvvmfx.examples.contacts.model; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import java.util.List; + +/** + * XML entity class. These classes represent the structure of the XML files + * to be loaded. + */ +@XmlRootElement(name = "iso_3166_country") +@XmlAccessorType(XmlAccessType.FIELD) class ISO3166_2_CountryEntity { + + @XmlAttribute(name = "code") + public String code; + + @XmlElement(name = "iso_3166_subset") + public List subsets; + + @Override + public String toString() { + return "CountryEntity " + code; + } +} diff --git a/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/ISO3166_2_EntryEntity.java b/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/ISO3166_2_EntryEntity.java new file mode 100644 index 000000000..4c0d88929 --- /dev/null +++ b/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/ISO3166_2_EntryEntity.java @@ -0,0 +1,19 @@ +package de.saxsys.mvvmfx.examples.contacts.model; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; + +/** + * XML entity class. These classes represent the structure of the XML files + * to be loaded. + */ +@XmlRootElement(name = "iso_3166_subset") +@XmlAccessorType(XmlAccessType.FIELD) class ISO3166_2_EntryEntity { + + @XmlAttribute(name = "code") + public String code; + @XmlAttribute(name = "name") + public String name; +} diff --git a/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/ISO3166_2_SubsetEntity.java b/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/ISO3166_2_SubsetEntity.java new file mode 100644 index 000000000..83ee67d45 --- /dev/null +++ b/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/ISO3166_2_SubsetEntity.java @@ -0,0 +1,22 @@ +package de.saxsys.mvvmfx.examples.contacts.model; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import java.util.List; + +/** + * XML entity class. These classes represent the structure of the XML files + * to be loaded. + */ +@XmlRootElement(name = "iso_3166_subset") +@XmlAccessorType(XmlAccessType.FIELD) class ISO3166_2_SubsetEntity { + + @XmlElement(name = "iso_3166_2_entry") + public List entryList; + + @XmlAttribute(name = "type") + public String subdivisionType; +} diff --git a/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/ui/addressform/AddressFormViewModel.java b/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/ui/addressform/AddressFormViewModel.java index 52a54151d..78265eeff 100644 --- a/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/ui/addressform/AddressFormViewModel.java +++ b/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/ui/addressform/AddressFormViewModel.java @@ -90,7 +90,6 @@ public void initialize() { }); loadingInProgress.bind(countrySelector.inProgressProperty()); - countrySelector.init(); initSubdivisionLabel(); initCountryList(); diff --git a/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/AbstractCountrySelectorTest.java b/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/AbstractCountrySelectorTest.java new file mode 100644 index 000000000..bd553cec2 --- /dev/null +++ b/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/AbstractCountrySelectorTest.java @@ -0,0 +1,109 @@ +package de.saxsys.mvvmfx.examples.contacts.model; + +import javafx.application.Platform; +import org.assertj.core.api.Assertions; +import org.junit.Test; + +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.stream.Collectors; + +import static eu.lestard.assertj.javafx.api.Assertions.assertThat; + +public abstract class AbstractCountrySelectorTest { + + protected abstract CountrySelector getCountrySelector(); + + @Test + public void testLoadSubdivisions() throws Exception { + CountrySelector countrySelector = getCountrySelector(); + runBlocked(countrySelector::init); + + assertThat(countrySelector.subdivisionLabel()).hasNullValue(); + Assertions.assertThat(countrySelector.subdivisions()).isEmpty(); + + countrySelector.setCountry(new Country("Germany", "DE")); + + assertThat(countrySelector.subdivisionLabel()).hasValue("State"); + Assertions.assertThat(countrySelector.subdivisions()).hasSize(16); + + countrySelector.setCountry(null); + + assertThat(countrySelector.subdivisionLabel()).hasNullValue(); + Assertions.assertThat(countrySelector.subdivisions()).isEmpty(); + + } + + @Test + public void testLoadCountries() throws InterruptedException, ExecutionException, TimeoutException { + CountrySelector countrySelector = getCountrySelector(); + + runBlocked(countrySelector::init); + + Assertions.assertThat(countrySelector.availableCountries()).hasSize(3); + Assertions.assertThat(getCountryNames(countrySelector.availableCountries())).contains("Germany", "Austria", "Switzerland"); + + Assertions.assertThat(countrySelector.subdivisions()).isEmpty(); + assertThat(countrySelector.subdivisionLabel()).hasNullValue(); + + Country germany = getCountryByName(countrySelector.availableCountries(), "Germany"); + + Assertions.assertThat(germany).isNotNull(); + + countrySelector.setCountry(germany); + + Assertions.assertThat(countrySelector.subdivisions()).hasSize(16); + Assertions + .assertThat(getSubdivisionNames(countrySelector.subdivisions())).contains("Sachsen", "Bayern", "Hessen"); // only + // test + // some + // examples. + assertThat(countrySelector.subdivisionLabel()).hasValue("State"); + + Country switzerland = getCountryByName(countrySelector.availableCountries(), "Switzerland"); + + countrySelector.setCountry(switzerland); + + Assertions.assertThat(countrySelector.subdivisions()).hasSize(26); + Assertions.assertThat(getSubdivisionNames(countrySelector.subdivisions())).contains("Zürich", "Jura", "Bern"); + assertThat(countrySelector.subdivisionLabel()).hasValue("Canton"); + + countrySelector.setCountry(null); + + Assertions.assertThat(countrySelector.subdivisions()).isEmpty(); + assertThat(countrySelector.subdivisionLabel()).hasNullValue(); + } + + protected void runBlocked(Runnable function) { + CompletableFuture blocker = new CompletableFuture<>(); + + getCountrySelector().inProgressProperty().addListener((obs, oldV, newV) -> { + if (!newV) { + blocker.complete(true); + } + }); + + Platform.runLater(function); + + try { + blocker.get(5, TimeUnit.SECONDS); + } catch (Exception e) { + e.printStackTrace(); + } + } + + protected Country getCountryByName(List countries, String name) { + return countries.stream().filter(country -> country.getName().equals(name)).findFirst().orElse(null); + } + + protected List getSubdivisionNames(List subdivisions) { + return subdivisions.stream().map(Subdivision::getName).collect(Collectors.toList()); + } + + protected List getCountryNames(List countries) { + return countries.stream().map(Country::getName).collect(Collectors.toList()); + } +} diff --git a/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/CountrySelectorIntegrationTest.java b/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/CountrySelectorIntegrationTest.java deleted file mode 100644 index 69d283b36..000000000 --- a/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/CountrySelectorIntegrationTest.java +++ /dev/null @@ -1,162 +0,0 @@ -package de.saxsys.mvvmfx.examples.contacts.model; - -import de.saxsys.mvvmfx.testingutils.jfxrunner.JfxRunner; -import javafx.application.Platform; -import org.datafx.reader.converter.XmlConverter; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.InputStream; -import java.util.List; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.stream.Collectors; - -import static org.assertj.core.api.Assertions.assertThat; -import static eu.lestard.assertj.javafx.api.Assertions.assertThat; - -@RunWith(JfxRunner.class) -public class CountrySelectorIntegrationTest { - - private CountrySelector countrySelector; - - @Before - public void setup() { - countrySelector = new CountrySelector(); - } - - @Test - public void testXmlConverterForCountry() throws FileNotFoundException { - XmlConverter converter = new XmlConverter<>("iso_3166_entry", Country.class); - - InputStream iso_3166_xml = this.getClass().getResourceAsStream("/countries/iso_3166.xml"); - - assertThat(iso_3166_xml).isNotNull(); - - converter.initialize(iso_3166_xml); - - Country country = converter.get(); - assertThat(country).isNotNull(); - } - - @Test - public void testXmlConverterForSubdivision() throws Exception { - XmlConverter converter = new XmlConverter<>("iso_3166_country", - CountrySelector.ISO3166_2_CountryEntity.class); - - InputStream iso_3166_2_xml = this.getClass().getResourceAsStream("/countries/iso_3166_2.xml"); - - assertThat(iso_3166_2_xml).isNotNull(); - - converter.initialize(iso_3166_2_xml); - - CountrySelector.ISO3166_2_CountryEntity entity = converter.get(); - - assertThat(entity).isNotNull(); - assertThat(entity.code).isNotNull().isEqualTo("DE"); - - assertThat(entity.subsets).isNotNull().hasSize(1); - assertThat(entity.subsets.get(0).subdivisionType).isEqualTo("State"); - - List entryList = entity.subsets.get(0).entryList; - - assertThat(entryList).isNotNull().hasSize(16); - - CountrySelector.ISO3166_2_EntryEntity entry = entryList.get(0); - - assertThat(entry.code).isEqualTo("DE-BW"); - assertThat(entry.name).isEqualTo("Baden-Württemberg"); - } - - @Test - public void testLoadSubdivisions() throws Exception { - runBlocked(countrySelector::init); - - assertThat(countrySelector.subdivisionLabel()).hasNullValue(); - assertThat(countrySelector.subdivisions()).isEmpty(); - - countrySelector.setCountry(new Country("Germany", "DE")); - - assertThat(countrySelector.subdivisionLabel()).hasValue("State"); - assertThat(countrySelector.subdivisions()).hasSize(16); - - countrySelector.setCountry(null); - - assertThat(countrySelector.subdivisionLabel()).hasNullValue(); - assertThat(countrySelector.subdivisions()).isEmpty(); - - } - - @Test - public void testLoadCountries() throws InterruptedException, ExecutionException, TimeoutException { - runBlocked(countrySelector::init); - - assertThat(countrySelector.availableCountries()).hasSize(3); - assertThat(getCountryNames(countrySelector.availableCountries())).contains("Germany", "Austria", "Switzerland"); - - assertThat(countrySelector.subdivisions()).isEmpty(); - assertThat(countrySelector.subdivisionLabel()).hasNullValue(); - - Country germany = getCountryByName(countrySelector.availableCountries(), "Germany"); - - assertThat(germany).isNotNull(); - - countrySelector.setCountry(germany); - - assertThat(countrySelector.subdivisions()).hasSize(16); - assertThat(getSubdivisionNames(countrySelector.subdivisions())).contains("Sachsen", "Bayern", "Hessen"); // only - // test - // some - // examples. - assertThat(countrySelector.subdivisionLabel()).hasValue("State"); - - Country switzerland = getCountryByName(countrySelector.availableCountries(), "Switzerland"); - - countrySelector.setCountry(switzerland); - - assertThat(countrySelector.subdivisions()).hasSize(26); - assertThat(getSubdivisionNames(countrySelector.subdivisions())).contains("Zürich", "Jura", "Bern"); - assertThat(countrySelector.subdivisionLabel()).hasValue("Canton"); - - countrySelector.setCountry(null); - - assertThat(countrySelector.subdivisions()).isEmpty(); - assertThat(countrySelector.subdivisionLabel()).hasNullValue(); - } - - private void runBlocked(Runnable function) { - CompletableFuture blocker = new CompletableFuture<>(); - - countrySelector.inProgressProperty().addListener((obs, oldV, newV) -> { - if (!newV) { - blocker.complete(true); - } - }); - - Platform.runLater(function); - - try { - blocker.get(5, TimeUnit.SECONDS); - } catch (Exception e) { - e.printStackTrace(); - } - } - - private Country getCountryByName(List countries, String name) { - return countries.stream().filter(country -> country.getName().equals(name)).findFirst().orElse(null); - } - - private List getSubdivisionNames(List subdivisions) { - return subdivisions.stream().map(Subdivision::getName).collect(Collectors.toList()); - } - - private List getCountryNames(List countries) { - return countries.stream().map(Country::getName).collect(Collectors.toList()); - } - -} diff --git a/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/DataFxCountrySelectorIntegrationTest.java b/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/DataFxCountrySelectorIntegrationTest.java new file mode 100644 index 000000000..f4500c06d --- /dev/null +++ b/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/DataFxCountrySelectorIntegrationTest.java @@ -0,0 +1,74 @@ +package de.saxsys.mvvmfx.examples.contacts.model; + +import de.saxsys.mvvmfx.testingutils.jfxrunner.JfxRunner; +import org.datafx.reader.converter.XmlConverter; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +@RunWith(JfxRunner.class) +public class DataFxCountrySelectorIntegrationTest extends AbstractCountrySelectorTest { + + private CountrySelector countrySelector; + + + @Override + protected CountrySelector getCountrySelector() { + return countrySelector; + } + + @Before + public void setup() { + countrySelector = new DataFxCountrySelector(); + } + + @Test + public void testXmlConverterForCountry() throws FileNotFoundException { + XmlConverter converter = new XmlConverter<>("iso_3166_entry", Country.class); + + InputStream iso_3166_xml = this.getClass().getResourceAsStream("/countries/iso_3166.xml"); + + assertThat(iso_3166_xml).isNotNull(); + + converter.initialize(iso_3166_xml); + + Country country = converter.get(); + assertThat(country).isNotNull(); + } + + @Test + public void testXmlConverterForSubdivision() throws Exception { + XmlConverter converter = new XmlConverter<>("iso_3166_country", + ISO3166_2_CountryEntity.class); + + InputStream iso_3166_2_xml = this.getClass().getResourceAsStream("/countries/iso_3166_2.xml"); + + assertThat(iso_3166_2_xml).isNotNull(); + + converter.initialize(iso_3166_2_xml); + + ISO3166_2_CountryEntity entity = converter.get(); + + assertThat(entity).isNotNull(); + assertThat(entity.code).isNotNull().isEqualTo("DE"); + + assertThat(entity.subsets).isNotNull().hasSize(1); + assertThat(entity.subsets.get(0).subdivisionType).isEqualTo("State"); + + List entryList = entity.subsets.get(0).entryList; + + assertThat(entryList).isNotNull().hasSize(16); + + ISO3166_2_EntryEntity entry = entryList.get(0); + + assertThat(entry.code).isEqualTo("DE-BW"); + assertThat(entry.name).isEqualTo("Baden-Württemberg"); + } + +} From 4d8c49fa44b96d1fe8ff03623793b32e05382543 Mon Sep 17 00:00:00 2001 From: Manuel Mauky Date: Wed, 14 Jun 2017 18:48:58 +0200 Subject: [PATCH 21/51] Add a CountrySelector implementation that uses DOM --- .../contacts/model/DomCountrySelector.java | 163 ++++++++++++++++++ .../DomCountrySelectorIntegrationTest.java | 22 +++ 2 files changed, 185 insertions(+) create mode 100644 examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/DomCountrySelector.java create mode 100644 examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/DomCountrySelectorIntegrationTest.java diff --git a/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/DomCountrySelector.java b/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/DomCountrySelector.java new file mode 100644 index 000000000..21412e73f --- /dev/null +++ b/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/DomCountrySelector.java @@ -0,0 +1,163 @@ +package de.saxsys.mvvmfx.examples.contacts.model; + +import javafx.beans.property.ReadOnlyBooleanProperty; +import javafx.beans.property.ReadOnlyBooleanWrapper; +import javafx.beans.property.ReadOnlyStringProperty; +import javafx.beans.property.ReadOnlyStringWrapper; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; + +import javax.annotation.PostConstruct; +import javax.inject.Singleton; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Singleton +public class DomCountrySelector implements CountrySelector { + private static final Logger LOG = LoggerFactory.getLogger(DomCountrySelector.class); + + public static final String ISO_3166_LOCATION = "/countries/iso_3166.xml"; + public static final String ISO_3166_2_LOCATION = "/countries/iso_3166_2.xml"; + + private ObservableList countries = FXCollections.observableArrayList(); + private ObservableList subdivisions = FXCollections.observableArrayList(); + + private ReadOnlyStringWrapper subdivisionLabel = new ReadOnlyStringWrapper(); + + private ReadOnlyBooleanWrapper inProgress = new ReadOnlyBooleanWrapper(false); + + private Map> countryCodeSubdivisionMap = new HashMap<>(); + private Map countryCodeSubdivisionNameMap = new HashMap<>(); + + @Override + @PostConstruct + public void init() { + inProgress.setValue(true); + loadCountries(); + } + + private void loadCountries() { + try { + + DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); + DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); + + loadCountriesFromXml(documentBuilder); + loadSubdivisionsFromXml(documentBuilder); + + } catch (Exception e) { + LOG.error("Cannot load Countries from XML file", e); + } + inProgress.setValue(false); + } + + private void loadSubdivisionsFromXml(DocumentBuilder documentBuilder) throws SAXException, IOException { + Document subdivisionsDocument = documentBuilder.parse(this.getClass().getResourceAsStream(ISO_3166_2_LOCATION)); + subdivisionsDocument.getDocumentElement().normalize(); + + NodeList countryNodes = subdivisionsDocument.getElementsByTagName("iso_3166_country"); + for(int countryIndex=0 ; countryIndex()); + } + + List subdivisionList = countryCodeSubdivisionMap.get(country); + + if(country != null) { + NodeList subsetNodes = ((Element)countryNode).getElementsByTagName("iso_3166_subset"); + + for(int subsetIndex=0 ; subsetIndex < subsetNodes.getLength() ; subsetIndex++) { + Node subsetNode = subsetNodes.item(subsetIndex); + + String subsetType = subsetNode.getAttributes().getNamedItem("type").getNodeValue(); + + countryCodeSubdivisionNameMap.put(country, subsetType); + + NodeList entryNodes = ((Element) subsetNode).getElementsByTagName("iso_3166_2_entry"); + + for(int entryIndex=0 ; entryIndex country.getCountryCode().equals(code)).findFirst().orElse(null); + } + + + @Override + public void setCountry(Country country) { + if (country == null) { + subdivisionLabel.set(null); + subdivisions.clear(); + return; + } + + subdivisionLabel.set(countryCodeSubdivisionNameMap.get(country)); + + subdivisions.clear(); + if (countryCodeSubdivisionMap.containsKey(country)) { + subdivisions.addAll(countryCodeSubdivisionMap.get(country)); + } + } + + @Override + public ObservableList availableCountries() { + return countries; + } + + @Override + public ReadOnlyStringProperty subdivisionLabel() { + return subdivisionLabel.getReadOnlyProperty(); + } + + @Override + public ObservableList subdivisions() { + return FXCollections.unmodifiableObservableList(subdivisions); + } + + @Override + public ReadOnlyBooleanProperty inProgressProperty() { + return inProgress.getReadOnlyProperty(); + } +} diff --git a/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/DomCountrySelectorIntegrationTest.java b/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/DomCountrySelectorIntegrationTest.java new file mode 100644 index 000000000..dee281887 --- /dev/null +++ b/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/DomCountrySelectorIntegrationTest.java @@ -0,0 +1,22 @@ +package de.saxsys.mvvmfx.examples.contacts.model; + +import de.saxsys.mvvmfx.testingutils.jfxrunner.JfxRunner; +import org.junit.Before; +import org.junit.runner.RunWith; + +@RunWith(JfxRunner.class) +public class DomCountrySelectorIntegrationTest extends AbstractCountrySelectorTest { + + private CountrySelector countrySelector; + + @Before + public void setup(){ + countrySelector = new DomCountrySelector(); + } + + + @Override + protected CountrySelector getCountrySelector() { + return countrySelector; + } +} From 509f1cb5d08a10377fe0420e4090ff307cfafdf3 Mon Sep 17 00:00:00 2001 From: Manuel Mauky Date: Wed, 14 Jun 2017 18:41:22 +0200 Subject: [PATCH 22/51] Add missing StringImmutableSetter class to fix build breaker --- .../utils/mapping/ImmutablePropertyField.java | 74 +++++++++++++++++++ .../StringImmutableSetter.java | 10 +++ 2 files changed, 84 insertions(+) create mode 100644 mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/ImmutablePropertyField.java create mode 100644 mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/accessorfunctions/StringImmutableSetter.java diff --git a/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/ImmutablePropertyField.java b/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/ImmutablePropertyField.java new file mode 100644 index 000000000..d93006b7a --- /dev/null +++ b/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/ImmutablePropertyField.java @@ -0,0 +1,74 @@ +package de.saxsys.mvvmfx.utils.mapping; + +import de.saxsys.mvvmfx.internal.SideEffect; +import javafx.beans.property.Property; + +import java.util.Objects; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.function.Supplier; + +public class ImmutablePropertyField> implements PropertyField { + + private T defaultValue; + private final R targetProperty; + + private final Function getter; + private final BiFunction immutableSetter; + + + public ImmutablePropertyField(SideEffect updateFunction, Function getter, + BiFunction immutableSetter, Supplier propertySupplier) { + this(updateFunction, getter, immutableSetter, null, propertySupplier); + } + + public ImmutablePropertyField(SideEffect updateFunction, Function getter, + BiFunction immutableSetter, T defaultValue, Supplier propertySupplier){ + this.getter = getter; + this.immutableSetter = immutableSetter; + this.defaultValue = defaultValue; + + this.targetProperty = propertySupplier.get(); + this.targetProperty.addListener(((observable, oldValue, newValue) -> updateFunction.call())); + } + + + + @Override + public void commit(M wrappedObject) { + } + + public M commitImmutable(M wrappedObject) { + return immutableSetter.apply(wrappedObject, targetProperty.getValue()); + } + + + @Override + public void reload(M wrappedObject) { + targetProperty.setValue(getter.apply(wrappedObject)); + } + + + @Override + public void resetToDefault() { + targetProperty.setValue(defaultValue); + } + + @Override + public void updateDefault(M wrappedObject) { + this.defaultValue = getter.apply(wrappedObject); + } + + @Override + public R getProperty() { + return targetProperty; + } + + @Override + public boolean isDifferent(M wrappedObject) { + final T modelValue = getter.apply(wrappedObject); + final T wrapperValue = targetProperty.getValue(); + + return !Objects.equals(modelValue, wrapperValue); + } +} diff --git a/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/accessorfunctions/StringImmutableSetter.java b/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/accessorfunctions/StringImmutableSetter.java new file mode 100644 index 000000000..a9e3c98e6 --- /dev/null +++ b/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/accessorfunctions/StringImmutableSetter.java @@ -0,0 +1,10 @@ +package de.saxsys.mvvmfx.utils.mapping.accessorfunctions; + +import java.util.function.BiFunction; + +@FunctionalInterface +public interface StringImmutableSetter extends BiFunction { + + @Override + M apply(M model, String newValue); +} From 8aacab37cb515d24ff8c17921a1f4b686a721980 Mon Sep 17 00:00:00 2001 From: gbalderas Date: Fri, 16 Jun 2017 15:46:04 +0200 Subject: [PATCH 23/51] Add a CountrySelector using JAXB --- .../examples/contacts/model/Countries.java | 20 +++ .../contacts/model/ISO3166_2_Entries.java | 16 ++ .../contacts/model/JAXBCountrySelector.java | 148 ++++++++++++++++++ .../JAXBCountrySelectorIntegrationTest.java | 21 +++ 4 files changed, 205 insertions(+) create mode 100644 examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/Countries.java create mode 100644 examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/ISO3166_2_Entries.java create mode 100644 examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/JAXBCountrySelector.java create mode 100644 examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/JAXBCountrySelectorIntegrationTest.java diff --git a/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/Countries.java b/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/Countries.java new file mode 100644 index 000000000..09f6ed731 --- /dev/null +++ b/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/Countries.java @@ -0,0 +1,20 @@ +package de.saxsys.mvvmfx.examples.contacts.model; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import java.util.ArrayList; + +@XmlRootElement(name = "iso_3166_entries") +@XmlAccessorType(XmlAccessType.FIELD) +public class Countries { + + @XmlElement(name = "iso_3166_entry") + private ArrayList countries; + + public ArrayList getCountries() { + return countries; + } + +} diff --git a/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/ISO3166_2_Entries.java b/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/ISO3166_2_Entries.java new file mode 100644 index 000000000..d2ae1ab29 --- /dev/null +++ b/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/ISO3166_2_Entries.java @@ -0,0 +1,16 @@ +package de.saxsys.mvvmfx.examples.contacts.model; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import java.util.ArrayList; + +@XmlRootElement(name = "iso_3166_2_entries") +@XmlAccessorType(XmlAccessType.FIELD) +public class ISO3166_2_Entries { + + @XmlElement(name = "iso_3166_country") + public ArrayList countryEntities; + +} diff --git a/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/JAXBCountrySelector.java b/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/JAXBCountrySelector.java new file mode 100644 index 000000000..bbc62dadc --- /dev/null +++ b/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/JAXBCountrySelector.java @@ -0,0 +1,148 @@ +package de.saxsys.mvvmfx.examples.contacts.model; + +import javafx.beans.property.ReadOnlyBooleanProperty; +import javafx.beans.property.ReadOnlyBooleanWrapper; +import javafx.beans.property.ReadOnlyStringProperty; +import javafx.beans.property.ReadOnlyStringWrapper; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import kotlin.reflect.jvm.internal.impl.javax.inject.Singleton; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.annotation.PostConstruct; +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@Singleton +public class JAXBCountrySelector implements CountrySelector { + private static final Logger LOG = LoggerFactory.getLogger(JAXBCountrySelector.class); + + public static final String ISO_3166_LOCATION = "/countries/iso_3166.xml"; + public static final String ISO_3166_2_LOCATION = "/countries/iso_3166_2.xml"; + + private ObservableList countries = FXCollections.observableArrayList(); + private ObservableList subdivisions = FXCollections.observableArrayList(); + + private ReadOnlyStringWrapper subdivisionLabel = new ReadOnlyStringWrapper(); + + private ReadOnlyBooleanWrapper inProgress = new ReadOnlyBooleanWrapper(false); + + private Map> countryCodeSubdivisionMap = new HashMap<>(); + private Map countryCodeSubdivisionNameMap = new HashMap<>(); + + public static void main(String[] args) { + JAXBCountrySelector jaxbCountrySelector = new JAXBCountrySelector(); + jaxbCountrySelector.init(); + } + + @Override + @PostConstruct + public void init() { + loadCountries(); + } + + private void loadCountries() { + inProgress.setValue(true); + + try { + loadCountriesFromXml(); + loadSubdivisionsFromXml(); + + } catch (JAXBException e) { + LOG.error("Cannot load Countries from XML file", e); + } + + inProgress.set(false); + } + + private void loadCountriesFromXml() throws JAXBException { + InputStream iso_3166 = this.getClass().getResourceAsStream(ISO_3166_LOCATION); + + JAXBContext context = JAXBContext.newInstance(Countries.class); + Countries countries = (Countries) context.createUnmarshaller().unmarshal(iso_3166); + this.countries.addAll(countries.getCountries()); + + } + + private void loadSubdivisionsFromXml() throws JAXBException { + JAXBContext jaxbContext = JAXBContext.newInstance(ISO3166_2_Entries.class); + InputStream iso_3166_2 = this.getClass().getResourceAsStream(ISO_3166_2_LOCATION); + ISO3166_2_Entries iso3166_2_entries = (ISO3166_2_Entries) jaxbContext.createUnmarshaller() + .unmarshal(iso_3166_2); + + iso3166_2_entries.countryEntities.stream() + .filter(entity -> entity.subsets != null && !entity.subsets.isEmpty()) + .forEach(entity -> { + Country country = findCountryByCode(entity.code); + if (!countryCodeSubdivisionMap.containsKey(country)) { + countryCodeSubdivisionMap.put(country, new ArrayList<>()); + } + + List subdivisionList = countryCodeSubdivisionMap.get(country); + + entity.subsets.stream() + .flatMap(subset -> subset.entryList.stream()) + .map(entry -> new Subdivision(entry.name, entry.code, country)) + .forEach(subdivisionList::add); + + + String subdivisionName = entity.subsets.stream() + .map(subset -> subset.subdivisionType) + .collect(Collectors.joining("/")); + + + countryCodeSubdivisionNameMap.put(country, subdivisionName); + }); + + } + + private Country findCountryByCode(String code) { + return countries.stream() + .filter(country -> country.getCountryCode().equals(code)) + .findFirst() + .orElse(null); + } + + @Override + public void setCountry(Country country) { + if (country == null) { + subdivisionLabel.set(null); + subdivisions.clear(); + return; + } + + subdivisionLabel.set(countryCodeSubdivisionNameMap.get(country)); + + subdivisions.clear(); + if (countryCodeSubdivisionMap.containsKey(country)) { + subdivisions.addAll(countryCodeSubdivisionMap.get(country)); + } + } + + @Override + public ObservableList availableCountries() { + return countries; + } + + @Override + public ReadOnlyStringProperty subdivisionLabel() { + return subdivisionLabel.getReadOnlyProperty(); + } + + @Override + public ObservableList subdivisions() { + return FXCollections.unmodifiableObservableList(subdivisions); + } + + @Override + public ReadOnlyBooleanProperty inProgressProperty() { + return inProgress.getReadOnlyProperty(); + } +} diff --git a/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/JAXBCountrySelectorIntegrationTest.java b/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/JAXBCountrySelectorIntegrationTest.java new file mode 100644 index 000000000..9475cecb9 --- /dev/null +++ b/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/JAXBCountrySelectorIntegrationTest.java @@ -0,0 +1,21 @@ +package de.saxsys.mvvmfx.examples.contacts.model; + +import de.saxsys.mvvmfx.testingutils.jfxrunner.JfxRunner; +import org.junit.Before; +import org.junit.runner.RunWith; + +@RunWith(JfxRunner.class) +public class JAXBCountrySelectorIntegrationTest extends AbstractCountrySelectorTest { + + private CountrySelector countrySelector; + + @Before + public void setup() { + countrySelector = new JAXBCountrySelector(); + } + + @Override + protected CountrySelector getCountrySelector() { + return countrySelector; + } +} From 15074377ba2d47ea83a3c8d12a90a7ef9c38b476 Mon Sep 17 00:00:00 2001 From: gbalderas Date: Fri, 16 Jun 2017 15:54:06 +0200 Subject: [PATCH 24/51] Update Subdivisions String --- .../contacts/model/DataFxCountrySelector.java | 7 +- .../contacts/model/DomCountrySelector.java | 26 +++-- .../main/resources/countries/iso_3166_2.xml | 5 +- .../model/AbstractCountrySelectorTest.java | 10 +- .../src/test/resources/countries/iso_3166.xml | 11 ++ .../test/resources/countries/iso_3166_2.xml | 102 ++++++++++++++++++ 6 files changed, 147 insertions(+), 14 deletions(-) diff --git a/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/DataFxCountrySelector.java b/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/DataFxCountrySelector.java index 83acffdde..d67703866 100644 --- a/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/DataFxCountrySelector.java +++ b/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/DataFxCountrySelector.java @@ -24,6 +24,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; /** * This class is used to encapsulate the process of loading available countries @@ -182,7 +183,11 @@ void loadSubdivisions() { }); }); - countryCodeSubdivisionNameMap.put(country, entity.subsets.get(0).subdivisionType); + String subdivisionName = entity.subsets.stream() + .map(subset -> subset.subdivisionType) + .collect(Collectors.joining("/")); + + countryCodeSubdivisionNameMap.put(country, subdivisionName); } }); diff --git a/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/DomCountrySelector.java b/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/DomCountrySelector.java index 21412e73f..84689b176 100644 --- a/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/DomCountrySelector.java +++ b/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/DomCountrySelector.java @@ -23,6 +23,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; @Singleton public class DomCountrySelector implements CountrySelector { @@ -68,7 +69,7 @@ private void loadSubdivisionsFromXml(DocumentBuilder documentBuilder) throws SAX subdivisionsDocument.getDocumentElement().normalize(); NodeList countryNodes = subdivisionsDocument.getElementsByTagName("iso_3166_country"); - for(int countryIndex=0 ; countryIndex subdivisionList = countryCodeSubdivisionMap.get(country); - if(country != null) { - NodeList subsetNodes = ((Element)countryNode).getElementsByTagName("iso_3166_subset"); + if (country != null) { + NodeList subsetNodes = ((Element) countryNode).getElementsByTagName("iso_3166_subset"); - for(int subsetIndex=0 ; subsetIndex < subsetNodes.getLength() ; subsetIndex++) { - Node subsetNode = subsetNodes.item(subsetIndex); + List subdivisionNames = new ArrayList<>(); - String subsetType = subsetNode.getAttributes().getNamedItem("type").getNodeValue(); + for (int subsetIndex = 0; subsetIndex < subsetNodes.getLength(); subsetIndex++) { + Node subsetNode = subsetNodes.item(subsetIndex); - countryCodeSubdivisionNameMap.put(country, subsetType); + String subsetType = subsetNode.getAttributes().getNamedItem("type").getNodeValue(); + subdivisionNames.add(subsetType); + NodeList entryNodes = ((Element) subsetNode).getElementsByTagName("iso_3166_2_entry"); - for(int entryIndex=0 ; entryIndex - - + + + diff --git a/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/AbstractCountrySelectorTest.java b/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/AbstractCountrySelectorTest.java index bd553cec2..965b0cedc 100644 --- a/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/AbstractCountrySelectorTest.java +++ b/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/AbstractCountrySelectorTest.java @@ -30,6 +30,12 @@ public void testLoadSubdivisions() throws Exception { assertThat(countrySelector.subdivisionLabel()).hasValue("State"); Assertions.assertThat(countrySelector.subdivisions()).hasSize(16); + countrySelector.setCountry(new Country("Australia", "AU")); + assertThat(countrySelector.subdivisionLabel()).hasValue("State/Territory"); + + countrySelector.setCountry(new Country("China", "CN")); + assertThat(countrySelector.subdivisionLabel()).hasValue("Municipality/Province/Autonomous region/Special administrative region"); + countrySelector.setCountry(null); assertThat(countrySelector.subdivisionLabel()).hasNullValue(); @@ -43,8 +49,8 @@ public void testLoadCountries() throws InterruptedException, ExecutionException, runBlocked(countrySelector::init); - Assertions.assertThat(countrySelector.availableCountries()).hasSize(3); - Assertions.assertThat(getCountryNames(countrySelector.availableCountries())).contains("Germany", "Austria", "Switzerland"); + Assertions.assertThat(countrySelector.availableCountries()).hasSize(5); + Assertions.assertThat(getCountryNames(countrySelector.availableCountries())).contains("Germany", "Austria", "Switzerland", "Australia", "China"); Assertions.assertThat(countrySelector.subdivisions()).isEmpty(); assertThat(countrySelector.subdivisionLabel()).hasNullValue(); diff --git a/examples/contacts-example/src/test/resources/countries/iso_3166.xml b/examples/contacts-example/src/test/resources/countries/iso_3166.xml index 7e68fb37b..13ef04a48 100644 --- a/examples/contacts-example/src/test/resources/countries/iso_3166.xml +++ b/examples/contacts-example/src/test/resources/countries/iso_3166.xml @@ -41,4 +41,15 @@ numeric_code="756" name="Switzerland" official_name="Swiss Confederation"/> + + \ No newline at end of file diff --git a/examples/contacts-example/src/test/resources/countries/iso_3166_2.xml b/examples/contacts-example/src/test/resources/countries/iso_3166_2.xml index 7e4f5ba66..03a454dea 100644 --- a/examples/contacts-example/src/test/resources/countries/iso_3166_2.xml +++ b/examples/contacts-example/src/test/resources/countries/iso_3166_2.xml @@ -138,4 +138,106 @@ code="CH-ZH" name="Zürich"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From a9ac19d4c808b84ff241266c6763c16bd5f4842c Mon Sep 17 00:00:00 2001 From: Gerardo Balderas Date: Thu, 22 Jun 2017 12:30:38 +0200 Subject: [PATCH 25/51] fix Singleton import --- .../mvvmfx/examples/contacts/model/JAXBCountrySelector.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/JAXBCountrySelector.java b/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/JAXBCountrySelector.java index bbc62dadc..4fbb8943d 100644 --- a/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/JAXBCountrySelector.java +++ b/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/JAXBCountrySelector.java @@ -6,11 +6,11 @@ import javafx.beans.property.ReadOnlyStringWrapper; import javafx.collections.FXCollections; import javafx.collections.ObservableList; -import kotlin.reflect.jvm.internal.impl.javax.inject.Singleton; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.annotation.PostConstruct; +import javax.inject.Singleton; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import java.io.InputStream; From 32f46a3e57e12eb2f03ca595e39cc6e6d0bb272a Mon Sep 17 00:00:00 2001 From: Manuel Mauky Date: Mon, 26 Jun 2017 15:49:16 +0200 Subject: [PATCH 26/51] Move country loading code into subpackage --- .../contacts/model/{ => countries}/CountrySelector.java | 4 +++- .../model/{ => countries}/DataFxCountrySelector.java | 4 +++- .../contacts/model/{ => countries}/DomCountrySelector.java | 6 +++++- .../model/{ => countries}/ISO3166_2_CountryEntity.java | 2 +- .../contacts/model/{ => countries}/ISO3166_2_Entries.java | 2 +- .../model/{ => countries}/ISO3166_2_EntryEntity.java | 2 +- .../model/{ => countries}/ISO3166_2_SubsetEntity.java | 2 +- .../contacts/model/{ => countries}/JAXBCountrySelector.java | 5 ++++- .../contacts/ui/addressform/AddressFormViewModel.java | 2 +- .../model/{ => countries}/AbstractCountrySelectorTest.java | 5 ++++- .../DataFxCountrySelectorIntegrationTest.java | 3 ++- .../{ => countries}/DomCountrySelectorIntegrationTest.java | 2 +- .../{ => countries}/JAXBCountrySelectorIntegrationTest.java | 5 ++++- .../contacts/ui/addressform/AddressFormViewModelTest.java | 2 +- 14 files changed, 32 insertions(+), 14 deletions(-) rename examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/{ => countries}/CountrySelector.java (84%) rename examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/{ => countries}/DataFxCountrySelector.java (97%) rename examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/{ => countries}/DomCountrySelector.java (96%) rename examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/{ => countries}/ISO3166_2_CountryEntity.java (92%) rename examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/{ => countries}/ISO3166_2_Entries.java (88%) rename examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/{ => countries}/ISO3166_2_EntryEntity.java (89%) rename examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/{ => countries}/ISO3166_2_SubsetEntity.java (91%) rename examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/{ => countries}/JAXBCountrySelector.java (94%) rename examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/{ => countries}/AbstractCountrySelectorTest.java (94%) rename examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/{ => countries}/DataFxCountrySelectorIntegrationTest.java (94%) rename examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/{ => countries}/DomCountrySelectorIntegrationTest.java (88%) rename examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/{ => countries}/JAXBCountrySelectorIntegrationTest.java (61%) diff --git a/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/CountrySelector.java b/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/countries/CountrySelector.java similarity index 84% rename from examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/CountrySelector.java rename to examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/countries/CountrySelector.java index 23d373c70..f572ea867 100644 --- a/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/CountrySelector.java +++ b/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/countries/CountrySelector.java @@ -1,5 +1,7 @@ -package de.saxsys.mvvmfx.examples.contacts.model; +package de.saxsys.mvvmfx.examples.contacts.model.countries; +import de.saxsys.mvvmfx.examples.contacts.model.Country; +import de.saxsys.mvvmfx.examples.contacts.model.Subdivision; import javafx.beans.property.ReadOnlyBooleanProperty; import javafx.beans.property.ReadOnlyStringProperty; import javafx.collections.ObservableList; diff --git a/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/DataFxCountrySelector.java b/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/countries/DataFxCountrySelector.java similarity index 97% rename from examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/DataFxCountrySelector.java rename to examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/countries/DataFxCountrySelector.java index d67703866..6011d7816 100644 --- a/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/DataFxCountrySelector.java +++ b/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/countries/DataFxCountrySelector.java @@ -1,5 +1,7 @@ -package de.saxsys.mvvmfx.examples.contacts.model; +package de.saxsys.mvvmfx.examples.contacts.model.countries; +import de.saxsys.mvvmfx.examples.contacts.model.Country; +import de.saxsys.mvvmfx.examples.contacts.model.Subdivision; import javafx.beans.property.ReadOnlyBooleanProperty; import javafx.beans.property.ReadOnlyBooleanWrapper; import javafx.beans.property.ReadOnlyStringProperty; diff --git a/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/DomCountrySelector.java b/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/countries/DomCountrySelector.java similarity index 96% rename from examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/DomCountrySelector.java rename to examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/countries/DomCountrySelector.java index 84689b176..1ffc1aa02 100644 --- a/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/DomCountrySelector.java +++ b/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/countries/DomCountrySelector.java @@ -1,5 +1,7 @@ -package de.saxsys.mvvmfx.examples.contacts.model; +package de.saxsys.mvvmfx.examples.contacts.model.countries; +import de.saxsys.mvvmfx.examples.contacts.model.Country; +import de.saxsys.mvvmfx.examples.contacts.model.Subdivision; import javafx.beans.property.ReadOnlyBooleanProperty; import javafx.beans.property.ReadOnlyBooleanWrapper; import javafx.beans.property.ReadOnlyStringProperty; @@ -15,6 +17,7 @@ import org.xml.sax.SAXException; import javax.annotation.PostConstruct; +import javax.enterprise.inject.Alternative; import javax.inject.Singleton; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; @@ -26,6 +29,7 @@ import java.util.stream.Collectors; @Singleton +@Alternative public class DomCountrySelector implements CountrySelector { private static final Logger LOG = LoggerFactory.getLogger(DomCountrySelector.class); diff --git a/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/ISO3166_2_CountryEntity.java b/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/countries/ISO3166_2_CountryEntity.java similarity index 92% rename from examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/ISO3166_2_CountryEntity.java rename to examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/countries/ISO3166_2_CountryEntity.java index 1ebea21fb..6f7ab6696 100644 --- a/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/ISO3166_2_CountryEntity.java +++ b/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/countries/ISO3166_2_CountryEntity.java @@ -1,4 +1,4 @@ -package de.saxsys.mvvmfx.examples.contacts.model; +package de.saxsys.mvvmfx.examples.contacts.model.countries; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; diff --git a/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/ISO3166_2_Entries.java b/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/countries/ISO3166_2_Entries.java similarity index 88% rename from examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/ISO3166_2_Entries.java rename to examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/countries/ISO3166_2_Entries.java index d2ae1ab29..f1da7d865 100644 --- a/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/ISO3166_2_Entries.java +++ b/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/countries/ISO3166_2_Entries.java @@ -1,4 +1,4 @@ -package de.saxsys.mvvmfx.examples.contacts.model; +package de.saxsys.mvvmfx.examples.contacts.model.countries; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; diff --git a/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/ISO3166_2_EntryEntity.java b/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/countries/ISO3166_2_EntryEntity.java similarity index 89% rename from examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/ISO3166_2_EntryEntity.java rename to examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/countries/ISO3166_2_EntryEntity.java index 4c0d88929..66b93b7f2 100644 --- a/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/ISO3166_2_EntryEntity.java +++ b/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/countries/ISO3166_2_EntryEntity.java @@ -1,4 +1,4 @@ -package de.saxsys.mvvmfx.examples.contacts.model; +package de.saxsys.mvvmfx.examples.contacts.model.countries; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; diff --git a/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/ISO3166_2_SubsetEntity.java b/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/countries/ISO3166_2_SubsetEntity.java similarity index 91% rename from examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/ISO3166_2_SubsetEntity.java rename to examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/countries/ISO3166_2_SubsetEntity.java index 83ee67d45..553746597 100644 --- a/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/ISO3166_2_SubsetEntity.java +++ b/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/countries/ISO3166_2_SubsetEntity.java @@ -1,4 +1,4 @@ -package de.saxsys.mvvmfx.examples.contacts.model; +package de.saxsys.mvvmfx.examples.contacts.model.countries; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; diff --git a/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/JAXBCountrySelector.java b/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/countries/JAXBCountrySelector.java similarity index 94% rename from examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/JAXBCountrySelector.java rename to examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/countries/JAXBCountrySelector.java index 4fbb8943d..e2f1e2693 100644 --- a/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/JAXBCountrySelector.java +++ b/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/model/countries/JAXBCountrySelector.java @@ -1,5 +1,8 @@ -package de.saxsys.mvvmfx.examples.contacts.model; +package de.saxsys.mvvmfx.examples.contacts.model.countries; +import de.saxsys.mvvmfx.examples.contacts.model.Countries; +import de.saxsys.mvvmfx.examples.contacts.model.Country; +import de.saxsys.mvvmfx.examples.contacts.model.Subdivision; import javafx.beans.property.ReadOnlyBooleanProperty; import javafx.beans.property.ReadOnlyBooleanWrapper; import javafx.beans.property.ReadOnlyStringProperty; diff --git a/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/ui/addressform/AddressFormViewModel.java b/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/ui/addressform/AddressFormViewModel.java index 78265eeff..72d2f3464 100644 --- a/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/ui/addressform/AddressFormViewModel.java +++ b/examples/contacts-example/src/main/java/de/saxsys/mvvmfx/examples/contacts/ui/addressform/AddressFormViewModel.java @@ -10,7 +10,7 @@ import de.saxsys.mvvmfx.examples.contacts.model.Address; import de.saxsys.mvvmfx.examples.contacts.model.Contact; import de.saxsys.mvvmfx.examples.contacts.model.Country; -import de.saxsys.mvvmfx.examples.contacts.model.CountrySelector; +import de.saxsys.mvvmfx.examples.contacts.model.countries.CountrySelector; import de.saxsys.mvvmfx.examples.contacts.model.Subdivision; import de.saxsys.mvvmfx.examples.contacts.ui.scopes.ContactDialogScope; import de.saxsys.mvvmfx.utils.itemlist.ItemList; diff --git a/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/AbstractCountrySelectorTest.java b/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/countries/AbstractCountrySelectorTest.java similarity index 94% rename from examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/AbstractCountrySelectorTest.java rename to examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/countries/AbstractCountrySelectorTest.java index 965b0cedc..90e89f9e5 100644 --- a/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/AbstractCountrySelectorTest.java +++ b/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/countries/AbstractCountrySelectorTest.java @@ -1,5 +1,8 @@ -package de.saxsys.mvvmfx.examples.contacts.model; +package de.saxsys.mvvmfx.examples.contacts.model.countries; +import de.saxsys.mvvmfx.examples.contacts.model.Country; +import de.saxsys.mvvmfx.examples.contacts.model.Subdivision; +import de.saxsys.mvvmfx.examples.contacts.model.countries.CountrySelector; import javafx.application.Platform; import org.assertj.core.api.Assertions; import org.junit.Test; diff --git a/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/DataFxCountrySelectorIntegrationTest.java b/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/countries/DataFxCountrySelectorIntegrationTest.java similarity index 94% rename from examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/DataFxCountrySelectorIntegrationTest.java rename to examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/countries/DataFxCountrySelectorIntegrationTest.java index f4500c06d..6ae671b9c 100644 --- a/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/DataFxCountrySelectorIntegrationTest.java +++ b/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/countries/DataFxCountrySelectorIntegrationTest.java @@ -1,5 +1,6 @@ -package de.saxsys.mvvmfx.examples.contacts.model; +package de.saxsys.mvvmfx.examples.contacts.model.countries; +import de.saxsys.mvvmfx.examples.contacts.model.Country; import de.saxsys.mvvmfx.testingutils.jfxrunner.JfxRunner; import org.datafx.reader.converter.XmlConverter; import org.junit.Before; diff --git a/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/DomCountrySelectorIntegrationTest.java b/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/countries/DomCountrySelectorIntegrationTest.java similarity index 88% rename from examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/DomCountrySelectorIntegrationTest.java rename to examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/countries/DomCountrySelectorIntegrationTest.java index dee281887..e117171d2 100644 --- a/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/DomCountrySelectorIntegrationTest.java +++ b/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/countries/DomCountrySelectorIntegrationTest.java @@ -1,4 +1,4 @@ -package de.saxsys.mvvmfx.examples.contacts.model; +package de.saxsys.mvvmfx.examples.contacts.model.countries; import de.saxsys.mvvmfx.testingutils.jfxrunner.JfxRunner; import org.junit.Before; diff --git a/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/JAXBCountrySelectorIntegrationTest.java b/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/countries/JAXBCountrySelectorIntegrationTest.java similarity index 61% rename from examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/JAXBCountrySelectorIntegrationTest.java rename to examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/countries/JAXBCountrySelectorIntegrationTest.java index 9475cecb9..60132168a 100644 --- a/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/JAXBCountrySelectorIntegrationTest.java +++ b/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/countries/JAXBCountrySelectorIntegrationTest.java @@ -1,5 +1,8 @@ -package de.saxsys.mvvmfx.examples.contacts.model; +package de.saxsys.mvvmfx.examples.contacts.model.countries; +import de.saxsys.mvvmfx.examples.contacts.model.countries.AbstractCountrySelectorTest; +import de.saxsys.mvvmfx.examples.contacts.model.countries.CountrySelector; +import de.saxsys.mvvmfx.examples.contacts.model.countries.JAXBCountrySelector; import de.saxsys.mvvmfx.testingutils.jfxrunner.JfxRunner; import org.junit.Before; import org.junit.runner.RunWith; diff --git a/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/ui/addressform/AddressFormViewModelTest.java b/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/ui/addressform/AddressFormViewModelTest.java index 06d8ea6d1..dc836333d 100644 --- a/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/ui/addressform/AddressFormViewModelTest.java +++ b/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/ui/addressform/AddressFormViewModelTest.java @@ -1,7 +1,7 @@ package de.saxsys.mvvmfx.examples.contacts.ui.addressform; import de.saxsys.mvvmfx.examples.contacts.model.Country; -import de.saxsys.mvvmfx.examples.contacts.model.CountrySelector; +import de.saxsys.mvvmfx.examples.contacts.model.countries.CountrySelector; import de.saxsys.mvvmfx.examples.contacts.model.Subdivision; import de.saxsys.mvvmfx.examples.contacts.ui.scopes.ContactDialogScope; import javafx.beans.property.SimpleBooleanProperty; From 8d08945fc00f6d6958618844d39a05d5647d2cd8 Mon Sep 17 00:00:00 2001 From: Manuel Mauky Date: Wed, 2 Aug 2017 15:03:25 +0200 Subject: [PATCH 27/51] Add missing immutable field methods. Add tests --- .../mapping/ImmutableBeanPropertyField.java | 76 +++ .../mapping/ImmutableListPropertyField.java | 81 ++++ .../utils/mapping/ImmutablePropertyField.java | 69 +-- .../mvvmfx/utils/mapping/ModelWrapper.java | 443 ++++++++++++++++-- .../BooleanImmutableSetter.java | 25 + .../DoubleImmutableSetter.java | 25 + .../FloatImmutableSetter.java | 25 + .../accessorfunctions/IntImmutableSetter.java | 24 + .../ListImmutableSetter.java | 26 + .../LongImmutableSetter.java | 26 + .../ObjectImmutableSetter.java | 25 + .../StringImmutableSetter.java | 15 + .../utils/mapping/ModelWrapperTest.java | 342 +++++++++++++- .../mvvmfx/utils/mapping/PersonImmutable.java | 70 +++ 14 files changed, 1144 insertions(+), 128 deletions(-) create mode 100644 mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/ImmutableBeanPropertyField.java create mode 100644 mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/ImmutableListPropertyField.java create mode 100644 mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/accessorfunctions/BooleanImmutableSetter.java create mode 100644 mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/accessorfunctions/DoubleImmutableSetter.java create mode 100644 mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/accessorfunctions/FloatImmutableSetter.java create mode 100644 mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/accessorfunctions/IntImmutableSetter.java create mode 100644 mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/accessorfunctions/ListImmutableSetter.java create mode 100644 mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/accessorfunctions/LongImmutableSetter.java create mode 100644 mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/accessorfunctions/ObjectImmutableSetter.java create mode 100644 mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/mapping/PersonImmutable.java diff --git a/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/ImmutableBeanPropertyField.java b/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/ImmutableBeanPropertyField.java new file mode 100644 index 000000000..2d0844a02 --- /dev/null +++ b/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/ImmutableBeanPropertyField.java @@ -0,0 +1,76 @@ +package de.saxsys.mvvmfx.utils.mapping; + +import de.saxsys.mvvmfx.internal.SideEffect; +import javafx.beans.property.Property; + +import java.util.Objects; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.function.Supplier; + +public class ImmutableBeanPropertyField> implements ImmutablePropertyField { + + private T defaultValue; + private final R targetProperty; + + private final Function getter; + private final BiFunction immutableSetter; + + + public ImmutableBeanPropertyField(SideEffect updateFunction, Function getter, + BiFunction immutableSetter, Supplier propertySupplier) { + this(updateFunction, getter, immutableSetter, null, propertySupplier); + } + + public ImmutableBeanPropertyField(SideEffect updateFunction, Function getter, + BiFunction immutableSetter, T defaultValue, Supplier propertySupplier){ + this.getter = getter; + this.immutableSetter = immutableSetter; + this.defaultValue = defaultValue; + + this.targetProperty = propertySupplier.get(); + this.targetProperty.addListener(((observable, oldValue, newValue) -> updateFunction.call())); + } + + + + @Override + public void commit(M wrappedObject) { + // commit is not supported because the model instance is immutable. + } + + @Override + public M commitImmutable(M wrappedObject) { + return immutableSetter.apply(wrappedObject, targetProperty.getValue()); + } + + + @Override + public void reload(M wrappedObject) { + targetProperty.setValue(getter.apply(wrappedObject)); + } + + + @Override + public void resetToDefault() { + targetProperty.setValue(defaultValue); + } + + @Override + public void updateDefault(M wrappedObject) { + this.defaultValue = getter.apply(wrappedObject); + } + + @Override + public R getProperty() { + return targetProperty; + } + + @Override + public boolean isDifferent(M wrappedObject) { + final T modelValue = getter.apply(wrappedObject); + final T wrapperValue = targetProperty.getValue(); + + return !Objects.equals(modelValue, wrapperValue); + } +} diff --git a/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/ImmutableListPropertyField.java b/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/ImmutableListPropertyField.java new file mode 100644 index 000000000..64cfb4c7a --- /dev/null +++ b/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/ImmutableListPropertyField.java @@ -0,0 +1,81 @@ +package de.saxsys.mvvmfx.utils.mapping; + +import de.saxsys.mvvmfx.internal.SideEffect; +import de.saxsys.mvvmfx.utils.mapping.accessorfunctions.ListGetter; +import de.saxsys.mvvmfx.utils.mapping.accessorfunctions.ListImmutableSetter; +import javafx.beans.property.ListProperty; +import javafx.beans.property.Property; +import javafx.collections.FXCollections; +import javafx.collections.ListChangeListener; +import javafx.collections.ObservableList; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.function.Supplier; + +public class ImmutableListPropertyField, R extends Property> + implements ImmutablePropertyField { + + private final ListGetter getter; + private final ListImmutableSetter immutableSetter; + + private List defaultValue; + private final ListProperty targetProperty; + + public ImmutableListPropertyField( + SideEffect updateFunction, + ListGetter getter, ListImmutableSetter immutableSetter, + Supplier> propertySupplier) { + this(updateFunction, getter, immutableSetter, propertySupplier, Collections.emptyList()); + } + + public ImmutableListPropertyField(SideEffect updateFunction, ListGetter getter, ListImmutableSetter immutableSetter, Supplier> propertySupplier, List defaultValue) { + this.defaultValue = defaultValue; + this.getter = getter; + this.immutableSetter = immutableSetter; + this.targetProperty = propertySupplier.get(); + this.targetProperty.setValue(FXCollections.observableArrayList()); + + this.targetProperty.addListener((ListChangeListener) change -> updateFunction.call()); + } + + @Override + public void commit(M wrappedObject) { + // commit is not supported because the model instance is immutable. + } + + @Override + public M commitImmutable(M wrappedObject) { + return immutableSetter.apply(wrappedObject, targetProperty.getValue()); + } + + @Override + public void reload(M wrappedObject) { + targetProperty.setAll(getter.apply(wrappedObject)); + } + + @Override + public void resetToDefault() { + targetProperty.setAll(defaultValue); + } + + @Override + public void updateDefault(M wrappedObject) { + defaultValue = new ArrayList<>(getter.apply(wrappedObject)); + } + + @Override + public R getProperty() { + return (R) targetProperty; + } + + @Override + public boolean isDifferent(M wrappedObject) { + final List modelValue = getter.apply(wrappedObject); + final List wrapperValue = targetProperty; + + return !Objects.equals(modelValue, wrapperValue); + } +} diff --git a/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/ImmutablePropertyField.java b/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/ImmutablePropertyField.java index d93006b7a..a420d1590 100644 --- a/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/ImmutablePropertyField.java +++ b/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/ImmutablePropertyField.java @@ -1,74 +1,9 @@ package de.saxsys.mvvmfx.utils.mapping; -import de.saxsys.mvvmfx.internal.SideEffect; import javafx.beans.property.Property; -import java.util.Objects; -import java.util.function.BiFunction; -import java.util.function.Function; -import java.util.function.Supplier; +public interface ImmutablePropertyField> extends PropertyField { -public class ImmutablePropertyField> implements PropertyField { + M commitImmutable(M wrappedObject); - private T defaultValue; - private final R targetProperty; - - private final Function getter; - private final BiFunction immutableSetter; - - - public ImmutablePropertyField(SideEffect updateFunction, Function getter, - BiFunction immutableSetter, Supplier propertySupplier) { - this(updateFunction, getter, immutableSetter, null, propertySupplier); - } - - public ImmutablePropertyField(SideEffect updateFunction, Function getter, - BiFunction immutableSetter, T defaultValue, Supplier propertySupplier){ - this.getter = getter; - this.immutableSetter = immutableSetter; - this.defaultValue = defaultValue; - - this.targetProperty = propertySupplier.get(); - this.targetProperty.addListener(((observable, oldValue, newValue) -> updateFunction.call())); - } - - - - @Override - public void commit(M wrappedObject) { - } - - public M commitImmutable(M wrappedObject) { - return immutableSetter.apply(wrappedObject, targetProperty.getValue()); - } - - - @Override - public void reload(M wrappedObject) { - targetProperty.setValue(getter.apply(wrappedObject)); - } - - - @Override - public void resetToDefault() { - targetProperty.setValue(defaultValue); - } - - @Override - public void updateDefault(M wrappedObject) { - this.defaultValue = getter.apply(wrappedObject); - } - - @Override - public R getProperty() { - return targetProperty; - } - - @Override - public boolean isDifferent(M wrappedObject) { - final T modelValue = getter.apply(wrappedObject); - final T wrapperValue = targetProperty.getValue(); - - return !Objects.equals(modelValue, wrapperValue); - } } diff --git a/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/ModelWrapper.java b/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/ModelWrapper.java index 3a7115c64..d67660a8b 100644 --- a/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/ModelWrapper.java +++ b/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/ModelWrapper.java @@ -16,24 +16,31 @@ package de.saxsys.mvvmfx.utils.mapping; import de.saxsys.mvvmfx.utils.mapping.accessorfunctions.BooleanGetter; +import de.saxsys.mvvmfx.utils.mapping.accessorfunctions.BooleanImmutableSetter; import de.saxsys.mvvmfx.utils.mapping.accessorfunctions.BooleanPropertyAccessor; import de.saxsys.mvvmfx.utils.mapping.accessorfunctions.BooleanSetter; import de.saxsys.mvvmfx.utils.mapping.accessorfunctions.DoubleGetter; +import de.saxsys.mvvmfx.utils.mapping.accessorfunctions.DoubleImmutableSetter; import de.saxsys.mvvmfx.utils.mapping.accessorfunctions.DoublePropertyAccessor; import de.saxsys.mvvmfx.utils.mapping.accessorfunctions.DoubleSetter; import de.saxsys.mvvmfx.utils.mapping.accessorfunctions.FloatGetter; +import de.saxsys.mvvmfx.utils.mapping.accessorfunctions.FloatImmutableSetter; import de.saxsys.mvvmfx.utils.mapping.accessorfunctions.FloatPropertyAccessor; import de.saxsys.mvvmfx.utils.mapping.accessorfunctions.FloatSetter; import de.saxsys.mvvmfx.utils.mapping.accessorfunctions.IntGetter; +import de.saxsys.mvvmfx.utils.mapping.accessorfunctions.IntImmutableSetter; import de.saxsys.mvvmfx.utils.mapping.accessorfunctions.IntPropertyAccessor; import de.saxsys.mvvmfx.utils.mapping.accessorfunctions.IntSetter; import de.saxsys.mvvmfx.utils.mapping.accessorfunctions.ListGetter; +import de.saxsys.mvvmfx.utils.mapping.accessorfunctions.ListImmutableSetter; import de.saxsys.mvvmfx.utils.mapping.accessorfunctions.ListPropertyAccessor; import de.saxsys.mvvmfx.utils.mapping.accessorfunctions.ListSetter; import de.saxsys.mvvmfx.utils.mapping.accessorfunctions.LongGetter; +import de.saxsys.mvvmfx.utils.mapping.accessorfunctions.LongImmutableSetter; import de.saxsys.mvvmfx.utils.mapping.accessorfunctions.LongPropertyAccessor; import de.saxsys.mvvmfx.utils.mapping.accessorfunctions.LongSetter; import de.saxsys.mvvmfx.utils.mapping.accessorfunctions.ObjectGetter; +import de.saxsys.mvvmfx.utils.mapping.accessorfunctions.ObjectImmutableSetter; import de.saxsys.mvvmfx.utils.mapping.accessorfunctions.ObjectPropertyAccessor; import de.saxsys.mvvmfx.utils.mapping.accessorfunctions.ObjectSetter; import de.saxsys.mvvmfx.utils.mapping.accessorfunctions.StringGetter; @@ -255,6 +262,16 @@ public class ModelWrapper { private final ObjectProperty model; + /** + * This flag is needed to support immutable fields. Without immutables when {@link #commit()} is invoked, + * the fields of the model instance are changed. With immutables however on commit the instance of the model itself is + * replaced. By default when the model instance is changed a {@link #reload()} is executed. + * This is ok when the user changes the model instance. It is not ok when we replace the model instance because of immutable fields. + * For this reason we need to distinguish between a change of the model instance due to a commit with immutable fields# + * and when the user changes the model instance. Therefore durring commit this flag will switch to true + * to indicate that we are currently executing a commit. + */ + private boolean inCommitPhase = false; /** * Create a new instance of {@link ModelWrapper} that wraps the instance of the Model class wrapped by the property. @@ -267,7 +284,13 @@ public ModelWrapper(ObjectProperty model) { this.model = model; reload(); this.model.addListener((observable, oldValue, newValue) -> { - reload(); + /* + * Only reload the values from the new model instance when it was changed by the user and not when it was changed + * during the commit phase. + */ + if(!inCommitPhase) { + reload(); + } }); } @@ -332,6 +355,7 @@ public ObjectProperty modelProperty() { */ public void reset() { fields.forEach(PropertyField::resetToDefault); + immutableFields.forEach(PropertyField::resetToDefault); calculateDifferenceFlag(); } @@ -379,9 +403,14 @@ public void reset() { * */ public void useCurrentValuesAsDefaults() { - if(model.get() != null) { + M wrappedModelInstance = model.get(); + if(wrappedModelInstance != null) { for (final PropertyField field : fields) { - field.updateDefault(model.get()); + field.updateDefault(wrappedModelInstance); + } + + for (final ImmutablePropertyField field : immutableFields) { + field.updateDefault(wrappedModelInstance); } } } @@ -396,13 +425,27 @@ public void useCurrentValuesAsDefaults() { */ public void commit() { if (model.get() != null) { + + inCommitPhase = true; + fields.forEach(field -> field.commit(model.get())); - for (ImmutablePropertyField immutableField : immutableFields) { - M newModel = immutableField.commitImmutable(model.get()); - model.setValue(newModel); + if(! immutableFields.isEmpty()) { + + M tmp = model.get(); + + for (ImmutablePropertyField immutableField : immutableFields) { + tmp = immutableField.commitImmutable(tmp); + } + + model.set(tmp); + } + inCommitPhase = false; + + + dirtyFlag.set(false); calculateDifferenceFlag(); @@ -418,8 +461,11 @@ public void commit() { * defined property fields. */ public void reload() { - if (model.get() != null) { - fields.forEach(field -> field.reload(model.get())); + M wrappedModelInstance = model.get(); + if (wrappedModelInstance != null) { + fields.forEach(field -> field.reload(wrappedModelInstance)); + + immutableFields.forEach(field -> field.reload(wrappedModelInstance)); dirtyFlag.set(false); calculateDifferenceFlag(); @@ -448,14 +494,24 @@ private void propertyWasChanged() { } private void calculateDifferenceFlag() { - if (model.get() != null) { + M wrappedModelInstance = model.get(); + + if (wrappedModelInstance != null) { for (final PropertyField field : fields) { - if (field.isDifferent(model.get())) { - diffFlag.set(true); - return; - } - } - diffFlag.set(false); + if (field.isDifferent(wrappedModelInstance)) { + diffFlag.set(true); + return; + } + } + + for (final ImmutablePropertyField field : immutableFields) { + if(field.isDifferent(wrappedModelInstance)) { + diffFlag.set(true); + return; + } + } + + diffFlag.set(false); } } @@ -537,6 +593,7 @@ public boolean isDifferent() { * {@link #commit()} or {@link #reload()} method is called. This property will stay true even if * afterwards another change is done so that the data is equal again. In this case the {@link #differentProperty()} * will switch back to false. + *

    * * Simply speaking: This property indicates whether there was a change done to the wrapped properties or not. The * {@link #differentProperty()} indicates whether there is a difference in data at the moment. @@ -556,7 +613,7 @@ public boolean isDirty() { - /** Field type String **/ + /* Field type String */ /** * Add a new field of type String to this instance of the wrapper. This method is used for model elements that are @@ -593,9 +650,43 @@ public StringProperty field(StringGetter getter, StringSetter setter) { return add(new BeanPropertyField<>(this::propertyWasChanged, getter, setter, SimpleStringProperty::new)); } - - public StringProperty immutableField(StringGetter getter, StringImmutableSetter setter){ - return addImmutable(new ImmutablePropertyField<>(this::propertyWasChanged, getter, setter, SimpleStringProperty::new)); + /** + * Add a new immutable field of type String to this instance of the wrapper. This method is used for immutable + * model elements that have getters to get values for it's fields but not setters. + * Instead, immutables have methods that take a new value for a field and return a new cloned instance of the + * model element with only this field updated to the new value. The old model instance isn't changed. + * + *

    + * + * Example: + *

    + * + *

    +	 * ModelWrapper{@code} personWrapper = new ModelWrapper{@code<>}();
    +	 *
    +	 * StringProperty wrappedNameProperty = personWrapper.field(person -> person.getName(), (person, value)
    +	 * 	 -> {
    +	 * 	     Person clone = person.withName(value);
    +	 * 	     return clone;
    +	 * 	 });
    +	 *
    +	 * // or with a method reference
    +	 * StringProperty wrappedNameProperty = personWrapper.field(Person::getName, Person::withName);
    +	 *
    +	 * 
    + * + * + * @param getter + * a function that returns the current value of the field for a given model element. Typically you will + * use a method reference to the getter method of the model element. + * @param immutableSetter + * a function that returns a clone of this the given model element that has the given value set. Typically you will use a method + * reference to the immutable setter method of the model element. + * + * @return The wrapped property instance. + */ + public StringProperty immutableField(StringGetter getter, StringImmutableSetter immutableSetter){ + return addImmutable(new ImmutableBeanPropertyField<>(this::propertyWasChanged, getter, immutableSetter, SimpleStringProperty::new)); } /** @@ -620,8 +711,25 @@ public StringProperty field(StringGetter getter, StringSetter setter, Stri SimpleStringProperty::new)); } - public StringProperty immutableField(StringGetter getter, StringImmutableSetter setter, String defaultValue){ - return addImmutable(new ImmutablePropertyField<>(this::propertyWasChanged, getter, setter, defaultValue, SimpleStringProperty::new)); + /** + * Ad a new immutable field of type String to this instance of the wrapper. See {@link #immutableField(StringGetter, StringImmutableSetter)}. + * This method additionally has a parameter to define the default value that is used when the {@link #reset()} + * method is used. + * + * + * @param getter + * a function that returns the current value of the field for a given model element. Typically you will + * use a method reference to the getter method of the model element. + * @param immutableSetter + * a function that returns a clone of this the given model element that has the given value set. Typically you will use a method + * reference to the immutable setter method of the model element. + * @param defaultValue + * the default value that is used when {@link #reset()} is invoked. + * + * @return The wrapped property instance. + */ + public StringProperty immutableField(StringGetter getter, StringImmutableSetter immutableSetter, String defaultValue){ + return addImmutable(new ImmutableBeanPropertyField<>(this::propertyWasChanged, getter, immutableSetter, defaultValue, SimpleStringProperty::new)); } /** @@ -649,7 +757,7 @@ public StringProperty immutableField(StringGetter getter, StringImmutableSett * @return The wrapped property instance. */ public StringProperty field(StringPropertyAccessor accessor) { - return add(new FxPropertyField<>(this::propertyWasChanged, accessor::apply, SimpleStringProperty::new)); + return add(new FxPropertyField<>(this::propertyWasChanged, accessor, SimpleStringProperty::new)); } /** @@ -665,7 +773,7 @@ public StringProperty field(StringPropertyAccessor accessor) { * @return The wrapped property instance. */ public StringProperty field(StringPropertyAccessor accessor, String defaultValue) { - return add(new FxPropertyField<>(this::propertyWasChanged, accessor::apply, defaultValue, + return add(new FxPropertyField<>(this::propertyWasChanged, accessor, defaultValue, SimpleStringProperty::new)); } @@ -675,7 +783,7 @@ public StringProperty field(StringPropertyAccessor accessor, String defaultVa /** * Add a new field of type String to this instance of the wrapper. See {@link #field(StringGetter, StringSetter)}. * This method additionally takes a string identifier as first parameter. - * + *

    * This identifier is used to return the same property instance even when the method is invoked multiple times. * * @param identifier @@ -699,12 +807,28 @@ public StringProperty field(String identifier, StringGetter getter, StringSet () -> new SimpleStringProperty(null, identifier))); } - public StringProperty immutableField(String identifier, StringGetter getter, StringImmutableSetter setter){ - return addIdentifiedImmutable(identifier, new ImmutablePropertyField<>(this::propertyWasChanged, getter, setter, SimpleStringProperty::new)); + /** + * Add a new immutable field of type String to this instance of the wrapper. See {@link #immutableField(StringGetter, StringImmutableSetter}). + * This method additionally takes a string identifier as first parameter. + *

    + * This identifier is used to return the same property instance even when the method is invoked multiple times. + * + * @param identifier + * an identifier for the field. + * @param getter + * a function that returns the current value of the field for a given model element. Typically you will + * use a method reference to the getter method of the model element. + * @param immutableSetter + * a function that returns a clone of this the given model element that has the given value set. Typically you will use a method + * reference to the immutable setter method of the model element. + * @return The wrapped property instance. + */ + public StringProperty immutableField(String identifier, StringGetter getter, StringImmutableSetter immutableSetter){ + return addIdentifiedImmutable(identifier, new ImmutableBeanPropertyField<>(this::propertyWasChanged, getter, immutableSetter, () -> new SimpleStringProperty(null, identifier))); } - public StringProperty immutableField(String identifier, StringGetter getter, StringImmutableSetter setter, String defaultValue){ - return addIdentifiedImmutable(identifier, new ImmutablePropertyField<>(this::propertyWasChanged, getter, setter, defaultValue, SimpleStringProperty::new)); + public StringProperty immutableField(String identifier, StringGetter getter, StringImmutableSetter immutableSetter, String defaultValue){ + return addIdentifiedImmutable(identifier, new ImmutableBeanPropertyField<>(this::propertyWasChanged, getter, immutableSetter, defaultValue, () -> new SimpleStringProperty(null, identifier))); } /** @@ -732,17 +856,25 @@ public StringProperty field(String identifier, StringPropertyAccessor accesso () -> new SimpleStringProperty(null, identifier))); } - /** Field type Boolean **/ + /* Field type Boolean */ public BooleanProperty field(BooleanGetter getter, BooleanSetter setter) { return add(new BeanPropertyField<>(this::propertyWasChanged, getter, setter, SimpleBooleanProperty::new)); } + public BooleanProperty immutableField(BooleanGetter getter, BooleanImmutableSetter immutableSetter){ + return addImmutable(new ImmutableBeanPropertyField<>(this::propertyWasChanged, getter, immutableSetter, SimpleBooleanProperty::new)); + } + public BooleanProperty field(BooleanGetter getter, BooleanSetter setter, boolean defaultValue) { return add(new BeanPropertyField<>(this::propertyWasChanged, getter, setter, defaultValue, SimpleBooleanProperty::new)); } + public BooleanProperty immutableField(BooleanGetter getter, BooleanImmutableSetter immutableSetter, boolean defaultValue){ + return addImmutable(new ImmutableBeanPropertyField<>(this::propertyWasChanged, getter, immutableSetter, defaultValue, SimpleBooleanProperty::new)); + } + public BooleanProperty field(BooleanPropertyAccessor accessor) { return add(new FxPropertyField<>(this::propertyWasChanged, accessor, SimpleBooleanProperty::new)); } @@ -762,6 +894,14 @@ public BooleanProperty field(String identifier, BooleanGetter getter, Boolean () -> new SimpleBooleanProperty(null, identifier))); } + public BooleanProperty immutableField(String identifier, BooleanGetter getter, BooleanImmutableSetter immutableSetter){ + return addIdentifiedImmutable(identifier, new ImmutableBeanPropertyField<>(this::propertyWasChanged, getter, immutableSetter, () -> new SimpleBooleanProperty(null, identifier))); + } + + public BooleanProperty immutableField(String identifier, BooleanGetter getter, BooleanImmutableSetter immutableSetter, boolean defaultValue){ + return addIdentifiedImmutable(identifier, new ImmutableBeanPropertyField<>(this::propertyWasChanged, getter, immutableSetter, defaultValue, () -> new SimpleBooleanProperty(null, identifier))); + } + public BooleanProperty field(String identifier, BooleanPropertyAccessor accessor) { return addIdentified(identifier, new FxPropertyField<>(this::propertyWasChanged, accessor, () -> new SimpleBooleanProperty(null, identifier))); @@ -774,7 +914,7 @@ public BooleanProperty field(String identifier, BooleanPropertyAccessor acces - /** Field type Double **/ + /* Field type Double */ public DoubleProperty field(DoubleGetter getter, DoubleSetter setter) { @@ -784,6 +924,16 @@ public DoubleProperty field(DoubleGetter getter, DoubleSetter setter) { SimpleDoubleProperty::new)); } + + public DoubleProperty immutableField(DoubleGetter getter, DoubleImmutableSetter immutableSetter){ + return addImmutable(new ImmutableBeanPropertyField<>( + this::propertyWasChanged, + getter::apply, + (m, number) -> immutableSetter.apply(m, number.doubleValue()), + SimpleDoubleProperty::new + )); + } + public DoubleProperty field(DoubleGetter getter, DoubleSetter setter, double defaultValue) { return add(new BeanPropertyField<>( this::propertyWasChanged, @@ -792,6 +942,15 @@ public DoubleProperty field(DoubleGetter getter, DoubleSetter setter, doub SimpleDoubleProperty::new)); } + public DoubleProperty immutableField(DoubleGetter getter, DoubleImmutableSetter immutableSetter, double defaultValue){ + return addImmutable(new ImmutableBeanPropertyField<>( + this::propertyWasChanged, + getter::apply, + (m, number) -> immutableSetter.apply(m, number.doubleValue()), + defaultValue, + SimpleDoubleProperty::new)); + } + public DoubleProperty field(DoublePropertyAccessor accessor) { return add(new FxPropertyField<>(this::propertyWasChanged, accessor::apply, SimpleDoubleProperty::new)); } @@ -805,7 +964,8 @@ public DoubleProperty field(String identifier, DoubleGetter getter, DoubleSet return addIdentified(identifier, new BeanPropertyField<>( this::propertyWasChanged, - getter::apply, (m, number) -> setter.accept(m, number.doubleValue()), + getter::apply, + (m, number) -> setter.accept(m, number.doubleValue()), () -> new SimpleDoubleProperty(null, identifier))); } @@ -813,13 +973,34 @@ public DoubleProperty field(String identifier, DoubleGetter getter, DoubleSet double defaultValue) { return addIdentified(identifier, new BeanPropertyField<>( this::propertyWasChanged, - getter::apply, (m, number) -> setter.accept(m, number.doubleValue()), + getter::apply, + (m, number) -> setter.accept(m, number.doubleValue()), + defaultValue, + () -> new SimpleDoubleProperty(null, identifier))); + } + + + public DoubleProperty immutableField(String identifier, DoubleGetter getter, DoubleImmutableSetter immutableSetter){ + return addIdentifiedImmutable(identifier, new ImmutableBeanPropertyField<>( + this::propertyWasChanged, + getter::apply, + (m, number) -> immutableSetter.apply(m, number.doubleValue()), + () -> new SimpleDoubleProperty(null, identifier))); + } + + public DoubleProperty immutableField(String identifier, DoubleGetter getter, DoubleImmutableSetter immutableSetter, double defaultValue){ + return addIdentifiedImmutable(identifier, new ImmutableBeanPropertyField<>( + this::propertyWasChanged, + getter::apply, + (m, number) -> immutableSetter.apply(m, number.doubleValue()), defaultValue, () -> new SimpleDoubleProperty(null, identifier))); } public DoubleProperty field(String identifier, DoublePropertyAccessor accessor) { - return addIdentified(identifier, new FxPropertyField<>(this::propertyWasChanged, accessor::apply, + return addIdentified(identifier, new FxPropertyField<>( + this::propertyWasChanged, + accessor::apply, () -> new SimpleDoubleProperty(null, identifier))); } @@ -832,7 +1013,7 @@ public DoubleProperty field(String identifier, DoublePropertyAccessor accesso - /** Field type Float **/ + /* Field type Float */ public FloatProperty field(FloatGetter getter, FloatSetter setter) { return add(new BeanPropertyField<>( @@ -841,6 +1022,15 @@ public FloatProperty field(FloatGetter getter, FloatSetter setter) { SimpleFloatProperty::new)); } + public FloatProperty immutableField(FloatGetter getter, FloatImmutableSetter immutableSetter){ + return addImmutable(new ImmutableBeanPropertyField<>( + this::propertyWasChanged, + getter::apply, + (m, number) -> immutableSetter.apply(m, number.floatValue()), + SimpleFloatProperty::new + )); + } + public FloatProperty field(FloatGetter getter, FloatSetter setter, float defaultValue) { return add(new BeanPropertyField<>( this::propertyWasChanged, @@ -848,6 +1038,15 @@ public FloatProperty field(FloatGetter getter, FloatSetter setter, float d SimpleFloatProperty::new)); } + public FloatProperty immutableField(FloatGetter getter, FloatImmutableSetter immutableSetter, float defaultValue){ + return addImmutable(new ImmutableBeanPropertyField<>( + this::propertyWasChanged, + getter::apply, + (m, number) -> immutableSetter.apply(m, number.floatValue()), + defaultValue, + SimpleFloatProperty::new)); + } + public FloatProperty field(FloatPropertyAccessor accessor) { return add(new FxPropertyField<>(this::propertyWasChanged, accessor::apply, SimpleFloatProperty::new)); } @@ -873,6 +1072,24 @@ public FloatProperty field(String identifier, FloatGetter getter, FloatSetter () -> new SimpleFloatProperty(null, identifier))); } + + public FloatProperty immutableField(String identifier, FloatGetter getter, FloatImmutableSetter immutableSetter){ + return addIdentifiedImmutable(identifier, new ImmutableBeanPropertyField<>( + this::propertyWasChanged, + getter::apply, + (m, number) -> immutableSetter.apply(m, number.floatValue()), + () -> new SimpleFloatProperty(null, identifier))); + } + + public FloatProperty immutableField(String identifier, FloatGetter getter, FloatImmutableSetter immutableSetter, float defaultValue){ + return addIdentifiedImmutable(identifier, new ImmutableBeanPropertyField<>( + this::propertyWasChanged, + getter::apply, + (m, number) -> immutableSetter.apply(m, number.floatValue()), + defaultValue, + () -> new SimpleFloatProperty(null, identifier))); + } + public FloatProperty field(String identifier, FloatPropertyAccessor accessor) { return addIdentified(identifier, new FxPropertyField<>(this::propertyWasChanged, accessor::apply, () -> new SimpleFloatProperty(null, identifier))); @@ -885,7 +1102,7 @@ public FloatProperty field(String identifier, FloatPropertyAccessor accessor, } - /** Field type Integer **/ + /* Field type Integer */ public IntegerProperty field(IntGetter getter, IntSetter setter) { @@ -895,6 +1112,16 @@ public IntegerProperty field(IntGetter getter, IntSetter setter) { SimpleIntegerProperty::new)); } + + public IntegerProperty immutableField(IntGetter getter, IntImmutableSetter immutableSetter){ + return addImmutable(new ImmutableBeanPropertyField<>( + this::propertyWasChanged, + getter::apply, + (m, number) -> immutableSetter.apply(m, number.intValue()), + SimpleIntegerProperty::new + )); + } + public IntegerProperty field(IntGetter getter, IntSetter setter, int defaultValue) { return add(new BeanPropertyField<>( this::propertyWasChanged, @@ -904,6 +1131,16 @@ public IntegerProperty field(IntGetter getter, IntSetter setter, int defau } + public IntegerProperty immutableField(IntGetter getter, IntImmutableSetter immutableSetter, int defaultValue){ + return addImmutable(new ImmutableBeanPropertyField<>( + this::propertyWasChanged, + getter::apply, + (m, number) -> immutableSetter.apply(m, number.intValue()), + defaultValue, + SimpleIntegerProperty::new)); + } + + public IntegerProperty field(IntPropertyAccessor accessor) { return add(new FxPropertyField<>(this::propertyWasChanged, accessor::apply, SimpleIntegerProperty::new)); } @@ -929,6 +1166,24 @@ public IntegerProperty field(String identifier, IntGetter getter, IntSetter getter, IntImmutableSetter immutableSetter){ + return addIdentifiedImmutable(identifier, new ImmutableBeanPropertyField<>( + this::propertyWasChanged, + getter::apply, + (m, number) -> immutableSetter.apply(m, number.intValue()), + () -> new SimpleIntegerProperty(null, identifier))); + } + + public IntegerProperty immutableField(String identifier, IntGetter getter, IntImmutableSetter immutableSetter, int defaultValue){ + return addIdentifiedImmutable(identifier, new ImmutableBeanPropertyField<>( + this::propertyWasChanged, + getter::apply, + (m, number) -> immutableSetter.apply(m, number.intValue()), + defaultValue, + () -> new SimpleIntegerProperty(null, identifier))); + } + public IntegerProperty field(String identifier, IntPropertyAccessor accessor) { return addIdentified(identifier, new FxPropertyField<>(this::propertyWasChanged, accessor::apply, () -> new SimpleIntegerProperty(null, identifier))); @@ -941,7 +1196,7 @@ public IntegerProperty field(String identifier, IntPropertyAccessor accessor, - /** Field type Long **/ + /* Field type Long */ public LongProperty field(LongGetter getter, LongSetter setter) { return add(new BeanPropertyField<>( @@ -950,6 +1205,15 @@ public LongProperty field(LongGetter getter, LongSetter setter) { SimpleLongProperty::new)); } + public LongProperty immutableField(LongGetter getter, LongImmutableSetter immutableSetter){ + return addImmutable(new ImmutableBeanPropertyField<>( + this::propertyWasChanged, + getter::apply, + (m, number) -> immutableSetter.apply(m, number.longValue()), + SimpleLongProperty::new + )); + } + public LongProperty field(LongGetter getter, LongSetter setter, long defaultValue) { return add(new BeanPropertyField<>( this::propertyWasChanged, @@ -958,6 +1222,15 @@ public LongProperty field(LongGetter getter, LongSetter setter, long defau SimpleLongProperty::new)); } + public LongProperty immutableField(LongGetter getter, LongImmutableSetter immutableSetter, long defaultValue){ + return addImmutable(new ImmutableBeanPropertyField<>( + this::propertyWasChanged, + getter::apply, + (m, number) -> immutableSetter.apply(m, number.longValue()), + defaultValue, + SimpleLongProperty::new)); + } + public LongProperty field(LongPropertyAccessor accessor) { return add(new FxPropertyField<>(this::propertyWasChanged, accessor::apply, SimpleLongProperty::new)); } @@ -984,6 +1257,25 @@ public LongProperty field(String identifier, LongGetter getter, LongSetter () -> new SimpleLongProperty(null, identifier))); } + + public LongProperty immutableField(String identifier, LongGetter getter, LongImmutableSetter immutableSetter){ + return addIdentifiedImmutable(identifier, new ImmutableBeanPropertyField<>( + this::propertyWasChanged, + getter::apply, + (m, number) -> immutableSetter.apply(m, number.longValue()), + () -> new SimpleLongProperty(null, identifier))); + } + + public LongProperty immutableField(String identifier, LongGetter getter, LongImmutableSetter immutableSetter, long defaultValue){ + return addIdentifiedImmutable(identifier, new ImmutableBeanPropertyField<>( + this::propertyWasChanged, + getter::apply, + (m, number) -> immutableSetter.apply(m, number.longValue()), + defaultValue, + () -> new SimpleLongProperty(null, identifier))); + } + + public LongProperty field(String identifier, LongPropertyAccessor accessor) { return addIdentified(identifier, new FxPropertyField<>(this::propertyWasChanged, accessor::apply, () -> new SimpleLongProperty(null, identifier))); @@ -996,18 +1288,37 @@ public LongProperty field(String identifier, LongPropertyAccessor accessor, l - /** Field type generic **/ + /* Field type generic */ public ObjectProperty field(ObjectGetter getter, ObjectSetter setter) { return add(new BeanPropertyField<>(this::propertyWasChanged, getter, setter, SimpleObjectProperty::new)); } + + public ObjectProperty immutableField(ObjectGetter getter, ObjectImmutableSetter immutableSetter){ + return addImmutable(new ImmutableBeanPropertyField<>( + this::propertyWasChanged, + getter, + immutableSetter, + SimpleObjectProperty::new + )); + } + public ObjectProperty field(ObjectGetter getter, ObjectSetter setter, T defaultValue) { return add(new BeanPropertyField<>(this::propertyWasChanged, getter, setter, defaultValue, SimpleObjectProperty::new)); } + public ObjectProperty immutableField(ObjectGetter getter, ObjectImmutableSetter immutableSetter, T defaultValue){ + return addImmutable(new ImmutableBeanPropertyField<>( + this::propertyWasChanged, + getter, + immutableSetter, + defaultValue, + SimpleObjectProperty::new)); + } + public ObjectProperty field(ObjectPropertyAccessor accessor) { return add(new FxPropertyField<>(this::propertyWasChanged, accessor, SimpleObjectProperty::new)); } @@ -1028,6 +1339,25 @@ public ObjectProperty field(String identifier, ObjectGetter getter, () -> new SimpleObjectProperty(null, identifier))); } + + + public ObjectProperty immutableField(String identifier, ObjectGetter getter, ObjectImmutableSetter immutableSetter){ + return addIdentifiedImmutable(identifier, new ImmutableBeanPropertyField<>( + this::propertyWasChanged, + getter, + immutableSetter, + () -> new SimpleObjectProperty(null, identifier))); + } + + public ObjectProperty immutableField(String identifier, ObjectGetter getter, ObjectImmutableSetter immutableSetter, T defaultValue){ + return addIdentifiedImmutable(identifier, new ImmutableBeanPropertyField<>( + this::propertyWasChanged, + getter, + immutableSetter, + defaultValue, + () -> new SimpleObjectProperty(null, identifier))); + } + public ObjectProperty field(String identifier, ObjectPropertyAccessor accessor) { return addIdentified(identifier, new FxPropertyField<>(this::propertyWasChanged, accessor, () -> new SimpleObjectProperty(null, identifier))); @@ -1040,19 +1370,39 @@ public ObjectProperty field(String identifier, ObjectPropertyAccessor ListProperty field(ListGetter getter, ListSetter setter) { return add(new BeanListPropertyField<>(this::propertyWasChanged, getter, (m, list) -> setter.accept(m, FXCollections.observableArrayList(list)), SimpleListProperty::new)); } + + public ListProperty immutableField(ListGetter getter, ListImmutableSetter immutableSetter){ + return addImmutable(new ImmutableListPropertyField<>( + this::propertyWasChanged, + getter, + (m, list) -> immutableSetter.apply(m, FXCollections.observableArrayList(list)), + SimpleListProperty::new + )); + } + public ListProperty field(ListGetter getter, ListSetter setter, List defaultValue) { return add(new BeanListPropertyField<>(this::propertyWasChanged, getter, (m, list) -> setter.accept(m, FXCollections.observableArrayList(list)), SimpleListProperty::new, defaultValue)); } + + public ListProperty immutableField(ListGetter getter, ListImmutableSetter immutableSetter, List defaultValue){ + return addImmutable(new ImmutableListPropertyField<>( + this::propertyWasChanged, + getter, + (m, list) -> immutableSetter.apply(m, FXCollections.observableArrayList(list)), + SimpleListProperty::new, + defaultValue)); + } + public ListProperty field(ListPropertyAccessor accessor) { return add(new FxListPropertyField<>(this::propertyWasChanged, accessor, SimpleListProperty::new)); } @@ -1076,6 +1426,23 @@ public ListProperty field(String identifier, ListGetter getter, Lis () -> new SimpleListProperty<>(null, identifier), defaultValue)); } + public ListProperty immutableField(String identifier, ListGetter getter, ListImmutableSetter immutableSetter){ + return addIdentifiedImmutable(identifier, new ImmutableListPropertyField<>( + this::propertyWasChanged, + getter, + (m, list) -> immutableSetter.apply(m, FXCollections.observableArrayList(list)), + () -> new SimpleListProperty<>(null, identifier))); + } + + public ListProperty immutableField(String identifier, ListGetter getter, ListImmutableSetter immutableSetter, List defaultValue){ + return addIdentifiedImmutable(identifier, new ImmutableListPropertyField<>( + this::propertyWasChanged, + getter, + (m, list) -> immutableSetter.apply(m, FXCollections.observableArrayList(list)), + () -> new SimpleListProperty<>(null, identifier), + defaultValue)); + } + public ListProperty field(String identifier, ListPropertyAccessor accessor) { return addIdentified(identifier, new FxListPropertyField<>(this::propertyWasChanged, accessor, () -> new SimpleListProperty<>(null, identifier))); diff --git a/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/accessorfunctions/BooleanImmutableSetter.java b/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/accessorfunctions/BooleanImmutableSetter.java new file mode 100644 index 000000000..aa1c3feb6 --- /dev/null +++ b/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/accessorfunctions/BooleanImmutableSetter.java @@ -0,0 +1,25 @@ +package de.saxsys.mvvmfx.utils.mapping.accessorfunctions; + +import java.util.function.BiFunction; + +/** + * A functional interface to define an immutable "setter" method of type {@link Boolean}. + * As the model element is immutable this method is not a real "setter". + * Instead it returns a new immutable copy of the original model element that has the + * specified field updated to the new value. + * + * @param + * the generic type of the model. + */ +@FunctionalInterface +public interface BooleanImmutableSetter extends BiFunction { + + /** + * @param model + * the model instance. + * @param newValue + * the new value to be set. + */ + @Override + M apply(M model, Boolean newValue); +} diff --git a/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/accessorfunctions/DoubleImmutableSetter.java b/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/accessorfunctions/DoubleImmutableSetter.java new file mode 100644 index 000000000..c4403858d --- /dev/null +++ b/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/accessorfunctions/DoubleImmutableSetter.java @@ -0,0 +1,25 @@ +package de.saxsys.mvvmfx.utils.mapping.accessorfunctions; + +import java.util.function.BiFunction; + +/** + * A functional interface to define an immutable "setter" method of type {@link Double}. + * As the model element is immutable this method is not a real "setter". + * Instead it returns a new immutable copy of the original model element that has the + * specified field updated to the new value. + * + * @param + * the generic type of the model. + */ +@FunctionalInterface +public interface DoubleImmutableSetter extends BiFunction { + + /** + * @param model + * the model instance. + * @param newValue + * the new value to be set. + */ + @Override + M apply(M model, Double newValue); +} diff --git a/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/accessorfunctions/FloatImmutableSetter.java b/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/accessorfunctions/FloatImmutableSetter.java new file mode 100644 index 000000000..4cefc3e4a --- /dev/null +++ b/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/accessorfunctions/FloatImmutableSetter.java @@ -0,0 +1,25 @@ +package de.saxsys.mvvmfx.utils.mapping.accessorfunctions; + +import java.util.function.BiFunction; + +/** + * A functional interface to define an immutable "setter" method of type {@link Float}. + * As the model element is immutable this method is not a real "setter". + * Instead it returns a new immutable copy of the original model element that has the + * specified field updated to the new value. + * + * @param + * the generic type of the model. + */ +@FunctionalInterface +public interface FloatImmutableSetter extends BiFunction { + + /** + * @param model + * the model instance. + * @param newValue + * the new value to be set. + */ + @Override + M apply(M model, Float newValue); +} diff --git a/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/accessorfunctions/IntImmutableSetter.java b/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/accessorfunctions/IntImmutableSetter.java new file mode 100644 index 000000000..c5faca659 --- /dev/null +++ b/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/accessorfunctions/IntImmutableSetter.java @@ -0,0 +1,24 @@ +package de.saxsys.mvvmfx.utils.mapping.accessorfunctions; + +import java.util.function.BiFunction; + +/** + * A functional interface to define an immutable "setter" method of type {@link Integer}. + * As the model element is immutable this method is not a real "setter". + * Instead it returns a new immutable copy of the original model element that has the + * specified field updated to the new value. + * + * @param + * the generic type of the model. + */ +@FunctionalInterface +public interface IntImmutableSetter extends BiFunction { + /** + * @param model + * the model instance. + * @param newValue + * the new value to be set. + */ + @Override + M apply(M model, Integer newValue); +} diff --git a/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/accessorfunctions/ListImmutableSetter.java b/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/accessorfunctions/ListImmutableSetter.java new file mode 100644 index 000000000..4a7fa6b4a --- /dev/null +++ b/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/accessorfunctions/ListImmutableSetter.java @@ -0,0 +1,26 @@ +package de.saxsys.mvvmfx.utils.mapping.accessorfunctions; + +import java.util.List; +import java.util.function.BiFunction; + +/** + * A functional interface to define an immutable "setter" method of type {@link List}. + * As the model element is immutable this method is not a real "setter". + * Instead it returns a new immutable copy of the original model element that has the + * specified field updated to the new value. + * + * @param + * the generic type of the model. + */ +@FunctionalInterface +public interface ListImmutableSetter extends BiFunction, M> { + + /** + * @param model + * the model instance. + * @param newElements + * the new elements of this list field. + */ + @Override + M apply(M model, List newElements); +} diff --git a/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/accessorfunctions/LongImmutableSetter.java b/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/accessorfunctions/LongImmutableSetter.java new file mode 100644 index 000000000..767b8498b --- /dev/null +++ b/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/accessorfunctions/LongImmutableSetter.java @@ -0,0 +1,26 @@ +package de.saxsys.mvvmfx.utils.mapping.accessorfunctions; + +import java.util.function.BiFunction; + +/** + * A functional interface to define an immutable "setter" method of type {@link Long}. + * As the model element is immutable this method is not a real "setter". + * Instead it returns a new immutable copy of the original model element that has the + * specified field updated to the new value. + * + * @param + * the generic type of the model. + */ +@FunctionalInterface +public interface LongImmutableSetter extends BiFunction { + + /** + * @param model + * the model instance. + * @param newValue + * the new value to be set. + */ + @Override + M apply(M model, Long newValue); +} + diff --git a/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/accessorfunctions/ObjectImmutableSetter.java b/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/accessorfunctions/ObjectImmutableSetter.java new file mode 100644 index 000000000..71e664dbb --- /dev/null +++ b/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/accessorfunctions/ObjectImmutableSetter.java @@ -0,0 +1,25 @@ +package de.saxsys.mvvmfx.utils.mapping.accessorfunctions; + +import java.util.function.BiFunction; + +/** + * A functional interface to define an immutable "setter" method of a generic type. + * As the model element is immutable this method is not a real "setter". + * Instead it returns a new immutable copy of the original model element that has the + * specified field updated to the new value. + * + * @param the generic type of the model. + * @param the generic type of the field. + */ +@FunctionalInterface +public interface ObjectImmutableSetter extends BiFunction { + + /** + * @param model + * the model instance. + * @param newValue + * the new value to be set. + */ + @Override + M apply(M model, T newValue); +} diff --git a/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/accessorfunctions/StringImmutableSetter.java b/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/accessorfunctions/StringImmutableSetter.java index a9e3c98e6..670295cbd 100644 --- a/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/accessorfunctions/StringImmutableSetter.java +++ b/mvvmfx/src/main/java/de/saxsys/mvvmfx/utils/mapping/accessorfunctions/StringImmutableSetter.java @@ -2,9 +2,24 @@ import java.util.function.BiFunction; +/** + * A functional interface to define an immutable "setter" method of type {@link String}. + * As the model element is immutable this method is not a real "setter". + * Instead it returns a new immutable copy of the original model element that has the + * specified field updated to the new value. + * + * @param + * the generic type of the model. + */ @FunctionalInterface public interface StringImmutableSetter extends BiFunction { + /** + * @param model + * the model instance. + * @param newValue + * the new value to be set. + */ @Override M apply(M model, String newValue); } diff --git a/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/mapping/ModelWrapperTest.java b/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/mapping/ModelWrapperTest.java index fff6b416c..253ff19b6 100644 --- a/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/mapping/ModelWrapperTest.java +++ b/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/mapping/ModelWrapperTest.java @@ -119,7 +119,7 @@ public void testWithGetterAndSetter() { assertThat(nameProperty.getValue()).isEqualTo(null); assertThat(ageProperty.getValue()).isEqualTo(0); - assertThat(nicknamesProperty.getValue().size()).isEqualTo(0); + assertThat(nicknamesProperty.getValue()).isEmpty(); // the wrapped object has still the values from the last commit. assertThat(person.getName()).isEqualTo("hugo"); @@ -254,6 +254,76 @@ public void testWithJavaFXPropertiesField() { assertThat(otherPerson.getAge()).isEqualTo(24); } + + @Test + public void testWithImmutables() { + + PersonImmutable person1 = PersonImmutable.create() + .withName("horst") + .withAge(32) + .withNicknames(Collections.singletonList("captain")); + + ModelWrapper personWrapper = new ModelWrapper<>(person1); + + final StringProperty nameProperty = personWrapper + .immutableField(PersonImmutable::getName, PersonImmutable::withName); + final IntegerProperty ageProperty = personWrapper.immutableField(PersonImmutable::getAge, + PersonImmutable::withAge); + final ListProperty nicknamesProperty = personWrapper.immutableField(PersonImmutable::getNicknames, PersonImmutable::withNicknames); + + assertThat(nameProperty.getValue()).isEqualTo("horst"); + assertThat(ageProperty.getValue()).isEqualTo(32); + assertThat(nicknamesProperty.getValue()).containsOnly("captain"); + + nameProperty.setValue("hugo"); + ageProperty.setValue(33); + nicknamesProperty.add("player"); + + personWrapper.commit(); + + // old person has still the same old values. + assertThat(person1.getName()).isEqualTo("horst"); + assertThat(person1.getAge()).isEqualTo(32); + assertThat(person1.getNicknames()).containsOnly("captain"); + + + PersonImmutable person2 = personWrapper.get(); + + assertThat(person2).isNotEqualTo(person1); + assertThat(person2.getName()).isEqualTo("hugo"); + assertThat(person2.getAge()).isEqualTo(33); + assertThat(person2.getNicknames()).containsOnly("captain", "player"); + + nameProperty.setValue("luise"); + ageProperty.setValue(33); + nicknamesProperty.setValue(FXCollections.observableArrayList("student")); + + personWrapper.reset(); + + assertThat(nameProperty.getValue()).isEqualTo(null); + assertThat(ageProperty.getValue()).isEqualTo(0); + assertThat(nicknamesProperty.getValue()).isEmpty(); + + personWrapper.reload(); + // now the properties have the values from the wrapped object + assertThat(nameProperty.getValue()).isEqualTo("hugo"); + assertThat(ageProperty.getValue()).isEqualTo(33); + assertThat(nicknamesProperty.get()).containsOnly("captain", "player"); + + + PersonImmutable person3 = person1.withName("gisela") + .withAge(23) + .withNicknames(Collections.singletonList("referee")); + + personWrapper.set(person3); + personWrapper.reload(); + + assertThat(nameProperty.getValue()).isEqualTo("gisela"); + assertThat(ageProperty.getValue()).isEqualTo(23); + assertThat(nicknamesProperty.getValue()).containsOnly("referee"); + } + + @Test public void testIdentifiedFields() { Person person = new Person(); @@ -287,6 +357,36 @@ public void testIdentifiedFields() { } + @Test + public void testIdentifiedFieldsWithImmutables() { + + PersonImmutable person1 = PersonImmutable.create() + .withName("horst") + .withAge(32) + .withNicknames(Collections.singletonList("captain")); + + ModelWrapper personWrapper = new ModelWrapper<>(person1); + + final StringProperty nameProperty = personWrapper + .immutableField("name", PersonImmutable::getName, PersonImmutable::withName); + final IntegerProperty ageProperty = personWrapper.immutableField("age", PersonImmutable::getAge, PersonImmutable::withAge); + final ListProperty nicknamesProperty = personWrapper.immutableField("nicknames", PersonImmutable::getNicknames, PersonImmutable::withNicknames); + + + final StringProperty nameProperty2 = personWrapper + .immutableField("name", PersonImmutable::getName, PersonImmutable::withName); + final IntegerProperty ageProperty2 = personWrapper.immutableField("age", PersonImmutable::getAge, PersonImmutable::withAge); + final ListProperty nicknamesProperty2 = personWrapper.immutableField("nicknames", PersonImmutable::getNicknames, PersonImmutable::withNicknames); + + assertThat(nameProperty).isSameAs(nameProperty2); + assertThat(ageProperty).isSameAs(ageProperty2); + assertThat(nicknamesProperty).isSameAs(nicknamesProperty2); + + assertThat(nameProperty.getName()).isEqualTo("name"); + assertThat(ageProperty.getName()).isEqualTo("age"); + assertThat(nicknamesProperty.getName()).isEqualTo("nicknames"); + } + @Test public void testDirtyFlag() { @@ -410,6 +510,70 @@ public void testDirtyFlagWithFxProperties() { assertThat(personWrapper.isDirty()).isFalse(); } + @Test + public void testDirtyFlagWithImmutables() { + + PersonImmutable person = PersonImmutable.create() + .withName("horst") + .withAge(32) + .withNicknames(Collections.singletonList("captain")); + + ModelWrapper personWrapper = new ModelWrapper<>(person); + + assertThat(personWrapper.isDirty()).isFalse(); + + final StringProperty name = personWrapper + .immutableField(PersonImmutable::getName, PersonImmutable::withName); + final IntegerProperty age = personWrapper.immutableField(PersonImmutable::getAge, + PersonImmutable::withAge); + final ListProperty nicknames = personWrapper.immutableField(PersonImmutable::getNicknames, PersonImmutable::withNicknames); + + name.set("hugo"); + + assertThat(personWrapper.isDirty()).isTrue(); + + personWrapper.commit(); + assertThat(personWrapper.isDirty()).isFalse(); + + age.set(33); + assertThat(personWrapper.isDirty()).isTrue(); + + age.set(32); + assertThat(personWrapper.isDirty()).isTrue(); // dirty is still true + + personWrapper.reload(); + assertThat(personWrapper.isDirty()).isFalse(); + + + nicknames.add("player"); + assertThat(personWrapper.isDirty()).isTrue(); + + nicknames.remove("player"); + assertThat(personWrapper.isDirty()).isTrue(); // dirty is still true + + personWrapper.commit(); + assertThat(personWrapper.isDirty()).isFalse(); + + name.set("hans"); + assertThat(personWrapper.isDirty()).isTrue(); + + personWrapper.reset(); + assertThat(personWrapper.isDirty()).isTrue(); + + + personWrapper.reload(); + assertThat(personWrapper.isDirty()).isFalse(); + + nicknames.set(FXCollections.observableArrayList("player")); + assertThat(personWrapper.isDirty()).isTrue(); + + personWrapper.reset(); + assertThat(personWrapper.isDirty()).isTrue(); + + personWrapper.reload(); + assertThat(personWrapper.isDirty()).isFalse(); + } + @Test public void testDifferentFlag() { Person person = new Person(); @@ -567,6 +731,64 @@ public void testDifferentFlagWithFxProperties() { assertThat(personWrapper.isDifferent()).isTrue(); } + @Test + public void testDifferentFlagWithImmutables() { + PersonImmutable person = PersonImmutable.create() + .withName("horst") + .withAge(32) + .withNicknames(Collections.singletonList("captain")); + + ModelWrapper personWrapper = new ModelWrapper<>(person); + + assertThat(personWrapper.isDirty()).isFalse(); + + final StringProperty name = personWrapper + .immutableField(PersonImmutable::getName, PersonImmutable::withName); + final IntegerProperty age = personWrapper.immutableField(PersonImmutable::getAge, + PersonImmutable::withAge); + final ListProperty nicknames = personWrapper.immutableField(PersonImmutable::getNicknames, PersonImmutable::withNicknames); + + + + name.set("hugo"); + assertThat(personWrapper.isDifferent()).isTrue(); + + personWrapper.commit(); + assertThat(personWrapper.isDifferent()).isFalse(); + + + age.set(33); + assertThat(personWrapper.isDifferent()).isTrue(); + + age.set(32); + assertThat(personWrapper.isDifferent()).isFalse(); + + + nicknames.remove("captain"); + assertThat(personWrapper.isDifferent()).isTrue(); + + nicknames.add("captain"); + assertThat(personWrapper.isDifferent()).isFalse(); + + nicknames.add("player"); + assertThat(personWrapper.isDifferent()).isTrue(); + + nicknames.remove("player"); + assertThat(personWrapper.isDifferent()).isFalse(); + + nicknames.setValue(FXCollections.observableArrayList("spectator")); + assertThat(personWrapper.isDifferent()).isTrue(); + + personWrapper.reload(); + assertThat(personWrapper.isDifferent()).isFalse(); + + nicknames.add("captain"); // duplicate captain + assertThat(personWrapper.isDifferent()).isTrue(); + + nicknames.add("player"); + assertThat(personWrapper.isDifferent()).isTrue(); + } + @Test public void defaultValuesCanBeUpdatedToCurrentValues(){ final Person person = new Person(); @@ -574,34 +796,71 @@ public void defaultValuesCanBeUpdatedToCurrentValues(){ person.setAge(32); person.setNicknames(Arrays.asList("captain")); - final ModelWrapper cut = new ModelWrapper<>(person); + final ModelWrapper personWrapper = new ModelWrapper<>(person); - final StringProperty nameField = cut.field(Person::getName, Person::setName, person.getName()); - nameField.set("test"); - cut.commit(); - cut.useCurrentValuesAsDefaults(); - cut.reset(); + final StringProperty name = personWrapper.field(Person::getName, Person::setName, person.getName()); + name.set("test"); + personWrapper.commit(); + personWrapper.useCurrentValuesAsDefaults(); + personWrapper.reset(); assertThat(person.getName()).isEqualTo("test"); - assertThat(nameField.get()).isEqualTo("test"); + assertThat(name.get()).isEqualTo("test"); - final IntegerProperty ageField = cut.field(Person::getAge, Person::setAge, person.getAge()); - ageField.set(42); - cut.commit(); - cut.useCurrentValuesAsDefaults(); - cut.reset(); + final IntegerProperty age = personWrapper.field(Person::getAge, Person::setAge, person.getAge()); + age.set(42); + personWrapper.commit(); + personWrapper.useCurrentValuesAsDefaults(); + personWrapper.reset(); assertThat(person.getAge()).isEqualTo(42); - assertThat(ageField.get()).isEqualTo(42); + assertThat(age.get()).isEqualTo(42); - final ListProperty nicknames = cut.field(Person::getNicknames, Person::setNicknames, person.getNicknames()); + final ListProperty nicknames = personWrapper.field(Person::getNicknames, Person::setNicknames, person.getNicknames()); nicknames.add("myname"); nicknames.remove("captain"); - cut.commit(); - cut.useCurrentValuesAsDefaults(); - cut.reset(); + personWrapper.commit(); + personWrapper.useCurrentValuesAsDefaults(); + personWrapper.reset(); assertThat(person.getNicknames()).containsExactly("myname"); assertThat(nicknames.get()).containsExactly("myname"); } + @Test + public void defaultValuesCanBeUpdatedToCurrentValuesWithImmutables(){ + PersonImmutable person = PersonImmutable.create() + .withName("Luise") + .withAge(32) + .withNicknames(Collections.singletonList("captain")); + + ModelWrapper personWrapper = new ModelWrapper<>(person); + + assertThat(personWrapper.isDirty()).isFalse(); + + final StringProperty name = personWrapper + .immutableField(PersonImmutable::getName, PersonImmutable::withName); + final IntegerProperty age = personWrapper.immutableField(PersonImmutable::getAge, + PersonImmutable::withAge); + final ListProperty nicknames = personWrapper.immutableField(PersonImmutable::getNicknames, PersonImmutable::withNicknames); + + name.set("test"); + personWrapper.commit(); + personWrapper.useCurrentValuesAsDefaults(); + personWrapper.reset(); + assertThat(name.get()).isEqualTo("test"); + + age.set(42); + personWrapper.commit(); + personWrapper.useCurrentValuesAsDefaults(); + personWrapper.reset(); + assertThat(age.get()).isEqualTo(42); + + nicknames.add("myname"); + nicknames.remove("captain"); + personWrapper.commit(); + personWrapper.useCurrentValuesAsDefaults(); + personWrapper.reset(); + assertThat(nicknames.get()).containsExactly("myname"); + } + @Test public void valuesShouldBeUpdatedWhenModelInstanceChanges() { final Person person1 = new Person(); @@ -615,11 +874,11 @@ public void valuesShouldBeUpdatedWhenModelInstanceChanges() { final SimpleObjectProperty modelProp = new SimpleObjectProperty<>(person1); - final ModelWrapper cut = new ModelWrapper<>(modelProp); + final ModelWrapper personWrapper = new ModelWrapper<>(modelProp); - final StringProperty nameField = cut.field(Person::getName, Person::setName, person1.getName()); - final IntegerProperty ageField = cut.field(Person::getAge, Person::setAge, person1.getAge()); - final ListProperty nicknames = cut + final StringProperty nameField = personWrapper.field(Person::getName, Person::setName, person1.getName()); + final IntegerProperty ageField = personWrapper.field(Person::getAge, Person::setAge, person1.getAge()); + final ListProperty nicknames = personWrapper .field(Person::getNicknames, Person::setNicknames, person1.getNicknames()); assertThat(nameField.get()).isEqualTo(person1.getName()); @@ -631,7 +890,44 @@ public void valuesShouldBeUpdatedWhenModelInstanceChanges() { assertThat(ageField.get()).isEqualTo(person2.getAge()); assertThat(nicknames.get()).containsExactlyElementsOf(person2.getNicknames()); - cut.reset(); + personWrapper.reset(); + assertThat(nameField.get()).isEqualTo(person1.getName()); + assertThat(ageField.get()).isEqualTo(person1.getAge()); + assertThat(nicknames.get()).containsExactlyElementsOf(person1.getNicknames()); + } + + @Test + public void valuesShouldBeUpdatedWhenModelInstanceChangesWithImmutables() { + PersonImmutable person1 = PersonImmutable.create() + .withName("horst") + .withAge(32) + .withNicknames(Collections.singletonList("captain")); + + PersonImmutable person2 = PersonImmutable.create() + .withName("dieter") + .withAge(42) + .withNicknames(Collections.singletonList("robin")); + + final SimpleObjectProperty modelProp = new SimpleObjectProperty<>(person1); + + ModelWrapper personWrapper = new ModelWrapper<>(modelProp); + + final StringProperty nameField = personWrapper + .immutableField(PersonImmutable::getName, PersonImmutable::withName, person1.getName()); + final IntegerProperty ageField = personWrapper.immutableField(PersonImmutable::getAge, + PersonImmutable::withAge, person1.getAge()); + final ListProperty nicknames = personWrapper.immutableField(PersonImmutable::getNicknames, PersonImmutable::withNicknames, person1.getNicknames()); + + assertThat(nameField.get()).isEqualTo(person1.getName()); + assertThat(ageField.get()).isEqualTo(person1.getAge()); + assertThat(nicknames.get()).containsExactlyElementsOf(person1.getNicknames()); + + modelProp.set(person2); + assertThat(nameField.get()).isEqualTo(person2.getName()); + assertThat(ageField.get()).isEqualTo(person2.getAge()); + assertThat(nicknames.get()).containsExactlyElementsOf(person2.getNicknames()); + + personWrapper.reset(); assertThat(nameField.get()).isEqualTo(person1.getName()); assertThat(ageField.get()).isEqualTo(person1.getAge()); assertThat(nicknames.get()).containsExactlyElementsOf(person1.getNicknames()); diff --git a/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/mapping/PersonImmutable.java b/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/mapping/PersonImmutable.java new file mode 100644 index 000000000..12aeee92d --- /dev/null +++ b/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/mapping/PersonImmutable.java @@ -0,0 +1,70 @@ +package de.saxsys.mvvmfx.utils.mapping; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class PersonImmutable { + private final String name; + private final int age; + private final List nicknames = new ArrayList<>(); + + public static PersonImmutable create(){ + return new PersonImmutable("", 0, Collections.emptyList()); + } + + private PersonImmutable(String name, int age, List nicknames) { + this.name = name; + this.age = age; + this.nicknames.addAll(nicknames); + } + + public String getName() { + return name; + } + + public PersonImmutable withName(String name) { + return new PersonImmutable(name, this.age, this.nicknames); + } + + public int getAge() { + return age; + } + + public PersonImmutable withAge(int age) { + return new PersonImmutable(this.name, age, this.nicknames); + } + + public List getNicknames() { + return Collections.unmodifiableList(nicknames); + } + + public PersonImmutable withNicknames(List nicknames) { + return new PersonImmutable(this.name, this.age, nicknames); + } + + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + + PersonImmutable that = (PersonImmutable) o; + + if (age != that.age) + return false; + if (name != null ? !name.equals(that.name) : that.name != null) + return false; + return nicknames.equals(that.nicknames); + } + + @Override + public int hashCode() { + int result = name != null ? name.hashCode() : 0; + result = 31 * result + age; + result = 31 * result + nicknames.hashCode(); + return result; + } +} From 348450d7d74bb3f49de631495b56a89cd3432df3 Mon Sep 17 00:00:00 2001 From: gbalderas Date: Tue, 22 Aug 2017 17:09:42 +0200 Subject: [PATCH 28/51] add JUnit5 dependency --- mvvmfx/pom.xml | 5 +++++ pom.xml | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/mvvmfx/pom.xml b/mvvmfx/pom.xml index f7ea64ed5..c72c52ef7 100644 --- a/mvvmfx/pom.xml +++ b/mvvmfx/pom.xml @@ -56,6 +56,11 @@ junit test + + org.junit.jupiter + junit-jupiter-api + test + org.mockito mockito-all diff --git a/pom.xml b/pom.xml index 3b203d435..747ad4bb5 100644 --- a/pom.xml +++ b/pom.xml @@ -183,6 +183,11 @@ junit 4.12 + + org.junit.jupiter + junit-jupiter-api + 5.0.0-RC2 + org.mockito mockito-all From 7fe18377333e9e67d234d5d25dcb146be701f626 Mon Sep 17 00:00:00 2001 From: gbalderas Date: Tue, 22 Aug 2017 17:11:07 +0200 Subject: [PATCH 29/51] add replacement for JfxRunner --- mvvmfx-testing-utils/pom.xml | 5 +++++ .../mvvmfx/testingutils/FxTestingUtils.java | 21 +++++++++++++++++-- .../testingutils/JfxToolkitExtension.java | 11 ++++++++++ 3 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 mvvmfx-testing-utils/src/main/java/de/saxsys/mvvmfx/testingutils/JfxToolkitExtension.java diff --git a/mvvmfx-testing-utils/pom.xml b/mvvmfx-testing-utils/pom.xml index 24f1348cd..fa2e99fa6 100644 --- a/mvvmfx-testing-utils/pom.xml +++ b/mvvmfx-testing-utils/pom.xml @@ -27,6 +27,11 @@ junit compile + + org.junit.jupiter + junit-jupiter-api + compile + org.mockito mockito-all diff --git a/mvvmfx-testing-utils/src/main/java/de/saxsys/mvvmfx/testingutils/FxTestingUtils.java b/mvvmfx-testing-utils/src/main/java/de/saxsys/mvvmfx/testingutils/FxTestingUtils.java index 84101b7fa..f54ddcb6f 100644 --- a/mvvmfx-testing-utils/src/main/java/de/saxsys/mvvmfx/testingutils/FxTestingUtils.java +++ b/mvvmfx-testing-utils/src/main/java/de/saxsys/mvvmfx/testingutils/FxTestingUtils.java @@ -10,10 +10,15 @@ public class FxTestingUtils { - public static void waitForUiThread(long timeout) { + public static void runInFXThread(Runnable code){ + runInFXThread(code, 1000); + } + + public static void runInFXThread(Runnable code, long timeout){ CompletableFuture future = new CompletableFuture<>(); Platform.runLater(() -> { + code.run(); future.complete(null); }); @@ -24,7 +29,19 @@ public static void waitForUiThread(long timeout) { } } + /** + * This method is used to wait until the UI thread has done all work that was queued via + * {@link Platform#runLater(Runnable)}. + */ + public static void waitForUiThread(long timeout) { + runInFXThread(() -> {}, timeout); + } + + /** + * This method is used to wait until the UI thread has done all work that was queued via + * {@link Platform#runLater(Runnable)}. + */ public static void waitForUiThread() { - waitForUiThread(0); + waitForUiThread(1000); } } diff --git a/mvvmfx-testing-utils/src/main/java/de/saxsys/mvvmfx/testingutils/JfxToolkitExtension.java b/mvvmfx-testing-utils/src/main/java/de/saxsys/mvvmfx/testingutils/JfxToolkitExtension.java new file mode 100644 index 000000000..ff0f32a63 --- /dev/null +++ b/mvvmfx-testing-utils/src/main/java/de/saxsys/mvvmfx/testingutils/JfxToolkitExtension.java @@ -0,0 +1,11 @@ +package de.saxsys.mvvmfx.testingutils; + +import javafx.embed.swing.JFXPanel; +import org.junit.jupiter.api.extension.BeforeAllCallback; +import org.junit.jupiter.api.extension.ExtensionContext; + +public class JfxToolkitExtension implements BeforeAllCallback { + @Override public void beforeAll(ExtensionContext extensionContext) throws Exception { + new JFXPanel(); + } +} From 50e1850b975f6e59434700ca6b4741e3834cd1b0 Mon Sep 17 00:00:00 2001 From: gbalderas Date: Tue, 22 Aug 2017 17:25:17 +0200 Subject: [PATCH 30/51] migrate mvvmfx-core tests to JUnit5 --- mvvmfx/pom.xml | 5 - .../java/FxmlViewinDefaultPackageTest.java | 9 +- .../viewloader/DependencyInjectorTest.java | 8 +- .../viewloader/FluentViewLoader_API_Test.java | 8 +- .../FluentViewLoader_FxmlView_Test.java | 22 +- .../FluentViewLoader_JavaView_Test.java | 26 +- .../FluentViewLoader_ResourceBundle_Test.java | 18 +- .../viewloader/MockableViewLoaderTest.java | 2 +- .../ResourceBundleInjectorTest.java | 51 ++- .../viewloader/ResourceBundleManagerTest.java | 6 +- .../ViewLoaderReflectionUtilsTest.java | 9 +- .../viewloader/lifecycle/LifecycleTest.java | 430 +++++++++--------- .../global/GlobalResourceBundleTest.java | 16 +- .../included/IncludedViewsTest.java | 16 +- .../mvvmfx/scopes/context/ContextTest.java | 2 +- .../scopes/example1/Example1ScopesTest.java | 71 +-- .../scopes/example2/Example2ScopesTest.java | 6 +- .../mvvmfx/scopes/example3/Example3Test.java | 6 +- .../scopes/example4/views/Example4Test.java | 14 +- .../mvvmfx/scopes/example5/Example5Test.java | 4 +- .../commands/CommandsWithoutUiThreadTest.java | 2 +- .../utils/commands/CompositeCommandTest.java | 20 +- .../utils/commands/DelegateCommandTest.java | 22 +- .../mvvmfx/utils/itemlist/ItemListTest.java | 58 +-- .../itemlist/ListTransformationTest.java | 2 +- .../itemlist/SelectableItemListTest.java | 60 +-- .../mapping/FieldMethodOverloadingTest.java | 6 +- .../utils/mapping/ModelWrapperTest.java | 2 +- .../ConcurrentModificationBugTest.java | 2 +- .../DefaultNotificationCenterTest.java | 19 +- .../notifications/MemoryLeakGlobalTest.java | 6 +- .../NotificationTestHelperTest.java | 6 +- .../ResetNotificationCenterTest.java | 10 +- .../viewmodel/MemoryLeakOnViewModelTest.java | 30 +- .../viewmodel/ViewModelTest.java | 40 +- .../ViewModelWithoutUiThreadTest.java | 2 +- .../viewmodel/WeakNotificationsTest.java | 9 +- 37 files changed, 508 insertions(+), 517 deletions(-) diff --git a/mvvmfx/pom.xml b/mvvmfx/pom.xml index c72c52ef7..1ed563cbe 100644 --- a/mvvmfx/pom.xml +++ b/mvvmfx/pom.xml @@ -51,11 +51,6 @@ mvvmfx-testing-utils test - - junit - junit - test - org.junit.jupiter junit-jupiter-api diff --git a/mvvmfx/src/test/java/FxmlViewinDefaultPackageTest.java b/mvvmfx/src/test/java/FxmlViewinDefaultPackageTest.java index 18d851e3a..44c82eb4f 100644 --- a/mvvmfx/src/test/java/FxmlViewinDefaultPackageTest.java +++ b/mvvmfx/src/test/java/FxmlViewinDefaultPackageTest.java @@ -16,9 +16,10 @@ import de.saxsys.mvvmfx.FluentViewLoader; import de.saxsys.mvvmfx.ViewTuple; import de.saxsys.mvvmfx.internal.viewloader.example.TestViewModel; -import de.saxsys.mvvmfx.testingutils.jfxrunner.JfxRunner; -import org.junit.Test; -import org.junit.runner.RunWith; +import de.saxsys.mvvmfx.testingutils.JfxToolkitExtension; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + import static org.assertj.core.api.Assertions.assertThat; @@ -27,7 +28,7 @@ * * A FxmlView located in the default package couldn't be loaded because a NullPointerException was thrown. */ -@RunWith(JfxRunner.class) +@ExtendWith(JfxToolkitExtension.class) public class FxmlViewinDefaultPackageTest { diff --git a/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/DependencyInjectorTest.java b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/DependencyInjectorTest.java index 87260abdd..23703e12e 100644 --- a/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/DependencyInjectorTest.java +++ b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/DependencyInjectorTest.java @@ -20,12 +20,12 @@ import javafx.util.Callback; import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.mockito.Mockito; /** - * This test verifies the behaviour of the {@link de.saxsys.mvvmfx.internal.viewloader.DependencyInjector}. + * This test verifies the behaviour of the {@link DependencyInjector}. */ public class DependencyInjectorTest { @@ -57,7 +57,7 @@ private ExampleWithPrivateConstructor() { } } - @Before + @BeforeEach public void setup() { injector = new DependencyInjector(); } diff --git a/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/FluentViewLoader_API_Test.java b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/FluentViewLoader_API_Test.java index c424de914..991cba526 100644 --- a/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/FluentViewLoader_API_Test.java +++ b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/FluentViewLoader_API_Test.java @@ -27,8 +27,8 @@ import de.saxsys.mvvmfx.ViewTuple; import javafx.scene.layout.VBox; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import de.saxsys.mvvmfx.internal.viewloader.example.TestFxmlView; import de.saxsys.mvvmfx.internal.viewloader.example.TestJavaView; @@ -36,7 +36,7 @@ /** - * This test verifies the API of the {@link de.saxsys.mvvmfx.FluentViewLoader}. The functionality of loading Views is + * This test verifies the API of the {@link FluentViewLoader}. The functionality of loading Views is * not part of this test as it is already tested in other tests for the ViewLoader itself. */ public class FluentViewLoader_API_Test { @@ -44,7 +44,7 @@ public class FluentViewLoader_API_Test { private ResourceBundle resourceBundle; - @Before + @BeforeEach public void setup() throws IOException { resourceBundle = new PropertyResourceBundle(new StringReader("")); } diff --git a/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/FluentViewLoader_FxmlView_Test.java b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/FluentViewLoader_FxmlView_Test.java index a25acae3b..a58cf4488 100644 --- a/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/FluentViewLoader_FxmlView_Test.java +++ b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/FluentViewLoader_FxmlView_Test.java @@ -21,12 +21,13 @@ import de.saxsys.mvvmfx.ViewTuple; import de.saxsys.mvvmfx.internal.viewloader.example.*; import de.saxsys.mvvmfx.testingutils.ExceptionUtils; -import de.saxsys.mvvmfx.testingutils.jfxrunner.JfxRunner; +import de.saxsys.mvvmfx.testingutils.JfxToolkitExtension; import javafx.fxml.LoadException; import javafx.scene.layout.VBox; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import java.io.IOException; import java.io.StringReader; @@ -42,12 +43,12 @@ * * @author manuel.mauky */ -@RunWith(JfxRunner.class) +@ExtendWith(JfxToolkitExtension.class) public class FluentViewLoader_FxmlView_Test { private ResourceBundle resourceBundle; - @Before + @BeforeEach public void setup() throws Exception { resourceBundle = new PropertyResourceBundle(new StringReader("")); } @@ -233,10 +234,13 @@ public void testUseExistingCodeBehindFailWhenControllerIsDefinedInFXML() { } } - @Test(expected = RuntimeException.class) + @Test public void testLoadFailNoSuchFxmlFile() { - ViewTuple viewTuple = FluentViewLoader.fxmlView(InvalidFxmlTestView.class) - .load(); + Assertions.assertThrows(RuntimeException.class, () -> { + ViewTuple viewTuple = FluentViewLoader + .fxmlView(InvalidFxmlTestView.class) + .load(); + }); } /** diff --git a/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/FluentViewLoader_JavaView_Test.java b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/FluentViewLoader_JavaView_Test.java index 93bff07c3..77e67becb 100644 --- a/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/FluentViewLoader_JavaView_Test.java +++ b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/FluentViewLoader_JavaView_Test.java @@ -24,9 +24,9 @@ import de.saxsys.mvvmfx.testingutils.ExceptionUtils; import javafx.fxml.Initializable; import javafx.scene.layout.VBox; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.io.StringReader; import java.lang.reflect.Constructor; @@ -41,32 +41,32 @@ /** - * This test verifies that loading views of type {@link de.saxsys.mvvmfx.JavaView} works correctly. + * This test verifies that loading views of type {@link JavaView} works correctly. * * This includes the handling of initialization and injection of the ViewModel and the resourceBundle. * * The injection and initialization is similar to that of FXML files. It can be done explicit by the use of the - * {@link javafx.fxml.Initializable} interface or implicit by using the naming conventions of the + * {@link Initializable} interface or implicit by using the naming conventions of the * {@link javafx.fxml.FXMLLoader}. * * This naming conventions are: *

      * - *
    • a public field of type {@link java.util.ResourceBundle} named "resources" gets the current ResourceBundle + *
    • a public field of type {@link ResourceBundle} named "resources" gets the current ResourceBundle * injected.
    • *
    • a public no-arg method named "initialize" is called after the injection of other resources is finished.
    • * *
    * * The third convention of the FXMLLoader to inject the path of the FXML file to a public field of type - * {@link java.net.URL} named "location" is NOT done by mvvmfx because it doesn't make sense for Java written Views (as + * {@link URL} named "location" is NOT done by mvvmfx because it doesn't make sense for Java written Views (as * there is no FXML file at all). */ public class FluentViewLoader_JavaView_Test { private ResourceBundle resourceBundle; - @Before + @BeforeEach public void before() throws Exception { resourceBundle = new PropertyResourceBundle(new StringReader("")); @@ -90,14 +90,14 @@ public void before() throws Exception { }); } - @After + @AfterEach public void after() { MvvmFX.setCustomDependencyInjector(null); } /** - * Verify that the loaded {@link de.saxsys.mvvmfx.ViewTuple} contains all expected references. + * Verify that the loaded {@link ViewTuple} contains all expected references. */ @Test public void testViewTuple() { @@ -402,7 +402,7 @@ void initialize() throws Exception { /** - * When the {@link javafx.fxml.Initializable} interface is implemented, the implicit initialize method may not be + * When the {@link Initializable} interface is implemented, the implicit initialize method may not be * called. */ @Test @@ -497,7 +497,7 @@ public ResourceBundle getResources() { /** * The naming conventions say that the field for the resourceBundle may be named "resources". The injection is still - * working when the type of the field is not {@link java.util.ResourceBundle}. + * working when the type of the field is not {@link ResourceBundle}. */ @Test public void testResourcesFieldHasOtherTypeAndIsStillInjected() { @@ -548,7 +548,7 @@ public void initialize(URL location, ResourceBundle resources) { } /** - * When the {@link javafx.fxml.Initializable} interface is implemented, no implicit injection should be done. + * When the {@link Initializable} interface is implemented, no implicit injection should be done. */ @Test public void testResourceBundleIsNotInjectedImplicitWhenInitializeableIsImplemented() { diff --git a/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/FluentViewLoader_ResourceBundle_Test.java b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/FluentViewLoader_ResourceBundle_Test.java index 00983d8f0..d9446e248 100644 --- a/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/FluentViewLoader_ResourceBundle_Test.java +++ b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/FluentViewLoader_ResourceBundle_Test.java @@ -29,18 +29,20 @@ import de.saxsys.mvvmfx.resourcebundle.global.TestView; import de.saxsys.mvvmfx.resourcebundle.global.TestViewModel; import de.saxsys.mvvmfx.testingutils.ExceptionUtils; +import de.saxsys.mvvmfx.testingutils.JfxToolkitExtension; import de.saxsys.mvvmfx.testingutils.jfxrunner.JfxRunner; import javafx.scene.layout.VBox; -import org.junit.After; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import de.saxsys.mvvmfx.FluentViewLoader; import de.saxsys.mvvmfx.InjectResourceBundle; import de.saxsys.mvvmfx.JavaView; import de.saxsys.mvvmfx.ViewTuple; +import org.junit.jupiter.api.extension.ExtendWith; import org.junit.runner.RunWith; /** @@ -49,14 +51,14 @@ * A resourceBundle can be injected into the View with default behaviour of JavaFX. Additionally the user can use the * mvvmfx annotation {@link InjectResourceBundle} to inject the resourceBundle in the View and in the ViewModel. */ -@RunWith(JfxRunner.class) +@ExtendWith(JfxToolkitExtension.class) public class FluentViewLoader_ResourceBundle_Test { private ResourceBundle resourceBundle; private ResourceBundle globalResourceBundle; - @Before + @BeforeEach public void setup() throws Exception { resourceBundle = new ListResourceBundle() { @Override @@ -82,7 +84,7 @@ protected Object[][] getContents() { MvvmFX.setGlobalResourceBundle(null); } - @After + @AfterEach public void tearDown() { MvvmFX.setGlobalResourceBundle(null); } @@ -210,7 +212,7 @@ public void success_java_injectionWithExistingViewModel() { * but no resourceBundle was provided at loading time. Therefore an exception is thrown. */ @Test - // @Ignore("until fixed. See issue #435") + // @Disabled("until fixed. See issue #435") public void fail_noResourceBundleGivenForViewAndViewModel() { MvvmFX.setGlobalResourceBundle(null); diff --git a/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/MockableViewLoaderTest.java b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/MockableViewLoaderTest.java index f15cce5c7..db29ff3e8 100644 --- a/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/MockableViewLoaderTest.java +++ b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/MockableViewLoaderTest.java @@ -16,7 +16,7 @@ package de.saxsys.mvvmfx.internal.viewloader; import de.saxsys.mvvmfx.ViewTuple; -import org.junit.Test; +import org.junit.jupiter.api.Test; import de.saxsys.mvvmfx.FluentViewLoader; import de.saxsys.mvvmfx.internal.viewloader.example.TestFxmlView; diff --git a/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/ResourceBundleInjectorTest.java b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/ResourceBundleInjectorTest.java index f2be52263..f51a1f198 100644 --- a/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/ResourceBundleInjectorTest.java +++ b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/ResourceBundleInjectorTest.java @@ -21,8 +21,9 @@ import java.util.PropertyResourceBundle; import java.util.ResourceBundle; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import de.saxsys.mvvmfx.InjectResourceBundle; @@ -33,7 +34,7 @@ public class ResourceBundleInjectorTest { private ResourceBundle resourceBundle; - @Before + @BeforeEach public void setup() throws Exception { resourceBundle = new PropertyResourceBundle(new StringReader("")); } @@ -66,7 +67,7 @@ class Example { assertThat(example.resourceBundle).isEqualTo(resourceBundle); } - @Test(expected = IllegalStateException.class) + @Test public void fail_wrongType() { class Example { @InjectResourceBundle @@ -74,8 +75,10 @@ class Example { } Example example = new Example(); - - ResourceBundleInjector.injectResourceBundle(example, resourceBundle); + + Assertions.assertThrows(IllegalStateException.class, () -> { + ResourceBundleInjector.injectResourceBundle(example, resourceBundle); + }); } @Test @@ -91,7 +94,7 @@ class Example { assertThat(example.resourceBundle).isNull(); } - @Test(expected = IllegalStateException.class) + @Test public void fail_annotationIsPresentButNoResourceBundleProvided() { class Example { @InjectResourceBundle @@ -99,8 +102,10 @@ class Example { } Example example = new Example(); - - ResourceBundleInjector.injectResourceBundle(example, ResourceBundleManager.EMPTY_RESOURCE_BUNDLE); + + Assertions.assertThrows(IllegalStateException.class, () -> { + ResourceBundleInjector.injectResourceBundle(example, ResourceBundleManager.EMPTY_RESOURCE_BUNDLE); + }); } /** @@ -123,7 +128,7 @@ class Example { * If the annotation is present, even when the type of the field is wrong, an exception has to be thrown when no * resourceBundle was provided. */ - @Test(expected = IllegalStateException.class) + @Test public void fail_wrongTypeAndNoResourceBundleProvided() { class Example { @InjectResourceBundle @@ -131,8 +136,10 @@ class Example { } Example example = new Example(); - - ResourceBundleInjector.injectResourceBundle(example, ResourceBundleManager.EMPTY_RESOURCE_BUNDLE); + + Assertions.assertThrows(IllegalStateException.class, () -> { + ResourceBundleInjector.injectResourceBundle(example, ResourceBundleManager.EMPTY_RESOURCE_BUNDLE); + }); } @@ -173,7 +180,7 @@ class Example { /** * When the type of the field is wrong, an exception should be thrown even if the optional attribute is set to true. */ - @Test(expected = IllegalStateException.class) + @Test public void fail_optionalIsTrueButWrongType() { class Example { @InjectResourceBundle(optional = true) @@ -181,8 +188,10 @@ class Example { } Example example = new Example(); - - ResourceBundleInjector.injectResourceBundle(example, resourceBundle); + + Assertions.assertThrows(IllegalStateException.class, () -> { + ResourceBundleInjector.injectResourceBundle(example, resourceBundle); + }); } /** @@ -200,9 +209,9 @@ class Example { } Example example = new Example(); - + ResourceBundleInjector.injectResourceBundle(example, resourceBundle); - + assertThat(example.resourceBundle).isEqualTo(resourceBundle); assertThat(example.resourceBundleToo).isEqualTo(resourceBundle); } @@ -211,7 +220,7 @@ class Example { * When multiple fields are available, an exception is thrown when at least one of the fields has a wrong type. This * is true even if one of the fields is correct. */ - @Test(expected = IllegalStateException.class) + @Test public void fail_multipleResourceBundleFieldsOneHasWrongType() { class Example { @InjectResourceBundle @@ -222,8 +231,10 @@ class Example { } Example example = new Example(); - - ResourceBundleInjector.injectResourceBundle(example, resourceBundle); + + Assertions.assertThrows(IllegalStateException.class, () -> { + ResourceBundleInjector.injectResourceBundle(example, resourceBundle); + }); } } diff --git a/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/ResourceBundleManagerTest.java b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/ResourceBundleManagerTest.java index f6dfed8bc..178131f5b 100644 --- a/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/ResourceBundleManagerTest.java +++ b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/ResourceBundleManagerTest.java @@ -15,8 +15,8 @@ ******************************************************************************/ package de.saxsys.mvvmfx.internal.viewloader; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.util.ListResourceBundle; import java.util.MissingResourceException; @@ -46,7 +46,7 @@ public class ResourceBundleManagerTest { private ResourceBundle global; private ResourceBundle other; - @Before + @BeforeEach public void setup(){ manager = new ResourceBundleManager(); diff --git a/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/ViewLoaderReflectionUtilsTest.java b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/ViewLoaderReflectionUtilsTest.java index cd660e897..655566f44 100644 --- a/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/ViewLoaderReflectionUtilsTest.java +++ b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/ViewLoaderReflectionUtilsTest.java @@ -18,7 +18,8 @@ import de.saxsys.mvvmfx.ViewModel; import de.saxsys.mvvmfx.internal.viewloader.example.TestViewModel; import de.saxsys.mvvmfx.internal.viewloader.example.TestViewModelWithDoubleInjection; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -55,11 +56,13 @@ class TestView implements View { assertThat(viewModel).isNull(); } - @Test(expected = IllegalStateException.class) + @Test public void testDoubleInjection() { class TestView implements View {} ViewModel viewModel = ViewLoaderReflectionUtils.createViewModel(new TestView()); - ViewLoaderReflectionUtils.initializeViewModel(viewModel); + Assertions.assertThrows(IllegalStateException.class, () -> { + ViewLoaderReflectionUtils.initializeViewModel(viewModel); + }); } } diff --git a/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/lifecycle/LifecycleTest.java b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/lifecycle/LifecycleTest.java index 1cd03af62..7eebe7c57 100644 --- a/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/lifecycle/LifecycleTest.java +++ b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/lifecycle/LifecycleTest.java @@ -15,20 +15,20 @@ import de.saxsys.mvvmfx.internal.viewloader.lifecycle.example_notification.LifecycleNotificationViewModel; import de.saxsys.mvvmfx.internal.viewloader.lifecycle.example_notification_without_lifecycle.NotificationWithoutLifecycleView; import de.saxsys.mvvmfx.internal.viewloader.lifecycle.example_notification_without_lifecycle.NotificationWithoutLifecycleViewModel; +import de.saxsys.mvvmfx.testingutils.FxTestingUtils; import de.saxsys.mvvmfx.testingutils.GCVerifier; -import de.saxsys.mvvmfx.testingutils.jfxrunner.JfxRunner; -import de.saxsys.mvvmfx.testingutils.jfxrunner.TestInJfxThread; +import de.saxsys.mvvmfx.testingutils.JfxToolkitExtension; import de.saxsys.mvvmfx.utils.notifications.DefaultNotificationCenter; import de.saxsys.mvvmfx.utils.notifications.NotificationCenterFactory; import javafx.scene.Scene; import javafx.scene.layout.VBox; import javafx.stage.Stage; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import static org.assertj.core.api.Assertions.assertThat; -@RunWith(JfxRunner.class) +@ExtendWith(JfxToolkitExtension.class) public class LifecycleTest { /** @@ -42,68 +42,70 @@ public class LifecycleTest { * of the lifecycle. */ @Test - @TestInJfxThread public void testLifecycleWithSubViewsWithoutGC() { - LifecycleTestRootViewModel.onViewAddedCalled = 0; - LifecycleTestRootViewModel.onViewRemovedCalled = 0; - LifecycleTestSub1ViewModel.onViewAddedCalled = 0; - LifecycleTestSub1ViewModel.onViewRemovedCalled = 0; - LifecycleTestSub2ViewModel.onViewAddedCalled = 0; - LifecycleTestSub2ViewModel.onViewRemovedCalled = 0; - - ViewTuple viewTuple = FluentViewLoader.fxmlView(LifecycleTestRootView.class).load(); - - // the root view is not directly added to the Scene but encapsulated in - // another container - VBox subContainer = new VBox(); - subContainer.getChildren().add(viewTuple.getView()); - - VBox container = new VBox(); - - Stage stage = new Stage(); - Scene scene = new Scene(container); - stage.setScene(scene); - - - // before adding to scene - assertThat(LifecycleTestRootViewModel.onViewAddedCalled).isEqualTo(0); - assertThat(LifecycleTestRootViewModel.onViewRemovedCalled).isEqualTo(0); - assertThat(LifecycleTestSub1ViewModel.onViewAddedCalled).isEqualTo(0); - assertThat(LifecycleTestSub1ViewModel.onViewRemovedCalled).isEqualTo(0); - assertThat(LifecycleTestSub2ViewModel.onViewAddedCalled).isEqualTo(0); - assertThat(LifecycleTestSub2ViewModel.onViewRemovedCalled).isEqualTo(0); - - // add rootView to container - container.getChildren().add(subContainer); - - assertThat(LifecycleTestRootViewModel.onViewAddedCalled).isEqualTo(1); - assertThat(LifecycleTestRootViewModel.onViewRemovedCalled).isEqualTo(0); - assertThat(LifecycleTestSub1ViewModel.onViewAddedCalled).isEqualTo(1); - assertThat(LifecycleTestSub1ViewModel.onViewRemovedCalled).isEqualTo(0); - assertThat(LifecycleTestSub2ViewModel.onViewAddedCalled).isEqualTo(1); - assertThat(LifecycleTestSub2ViewModel.onViewRemovedCalled).isEqualTo(0); - - // remove from container - container.getChildren().clear(); - - assertThat(LifecycleTestRootViewModel.onViewAddedCalled).isEqualTo(1); - assertThat(LifecycleTestRootViewModel.onViewRemovedCalled).isEqualTo(1); - assertThat(LifecycleTestSub1ViewModel.onViewAddedCalled).isEqualTo(1); - assertThat(LifecycleTestSub1ViewModel.onViewRemovedCalled).isEqualTo(1); - assertThat(LifecycleTestSub2ViewModel.onViewAddedCalled).isEqualTo(1); - assertThat(LifecycleTestSub2ViewModel.onViewRemovedCalled).isEqualTo(1); - - - // add again to container - container.getChildren().add(subContainer); - - // the lifecycle methods are invoked again - assertThat(LifecycleTestRootViewModel.onViewAddedCalled).isEqualTo(2); - assertThat(LifecycleTestRootViewModel.onViewRemovedCalled).isEqualTo(1); - assertThat(LifecycleTestSub1ViewModel.onViewAddedCalled).isEqualTo(2); - assertThat(LifecycleTestSub1ViewModel.onViewRemovedCalled).isEqualTo(1); - assertThat(LifecycleTestSub2ViewModel.onViewAddedCalled).isEqualTo(2); - assertThat(LifecycleTestSub2ViewModel.onViewRemovedCalled).isEqualTo(1); + FxTestingUtils.runInFXThread(() -> { + LifecycleTestRootViewModel.onViewAddedCalled = 0; + LifecycleTestRootViewModel.onViewRemovedCalled = 0; + LifecycleTestSub1ViewModel.onViewAddedCalled = 0; + LifecycleTestSub1ViewModel.onViewRemovedCalled = 0; + LifecycleTestSub2ViewModel.onViewAddedCalled = 0; + LifecycleTestSub2ViewModel.onViewRemovedCalled = 0; + + ViewTuple viewTuple = FluentViewLoader + .fxmlView(LifecycleTestRootView.class).load(); + + // the root view is not directly added to the Scene but encapsulated in + // another container + VBox subContainer = new VBox(); + subContainer.getChildren().add(viewTuple.getView()); + + VBox container = new VBox(); + + Stage stage = new Stage(); + Scene scene = new Scene(container); + stage.setScene(scene); + + + // before adding to scene + assertThat(LifecycleTestRootViewModel.onViewAddedCalled).isEqualTo(0); + assertThat(LifecycleTestRootViewModel.onViewRemovedCalled).isEqualTo(0); + assertThat(LifecycleTestSub1ViewModel.onViewAddedCalled).isEqualTo(0); + assertThat(LifecycleTestSub1ViewModel.onViewRemovedCalled).isEqualTo(0); + assertThat(LifecycleTestSub2ViewModel.onViewAddedCalled).isEqualTo(0); + assertThat(LifecycleTestSub2ViewModel.onViewRemovedCalled).isEqualTo(0); + + // add rootView to container + container.getChildren().add(subContainer); + + assertThat(LifecycleTestRootViewModel.onViewAddedCalled).isEqualTo(1); + assertThat(LifecycleTestRootViewModel.onViewRemovedCalled).isEqualTo(0); + assertThat(LifecycleTestSub1ViewModel.onViewAddedCalled).isEqualTo(1); + assertThat(LifecycleTestSub1ViewModel.onViewRemovedCalled).isEqualTo(0); + assertThat(LifecycleTestSub2ViewModel.onViewAddedCalled).isEqualTo(1); + assertThat(LifecycleTestSub2ViewModel.onViewRemovedCalled).isEqualTo(0); + + // remove from container + container.getChildren().clear(); + + assertThat(LifecycleTestRootViewModel.onViewAddedCalled).isEqualTo(1); + assertThat(LifecycleTestRootViewModel.onViewRemovedCalled).isEqualTo(1); + assertThat(LifecycleTestSub1ViewModel.onViewAddedCalled).isEqualTo(1); + assertThat(LifecycleTestSub1ViewModel.onViewRemovedCalled).isEqualTo(1); + assertThat(LifecycleTestSub2ViewModel.onViewAddedCalled).isEqualTo(1); + assertThat(LifecycleTestSub2ViewModel.onViewRemovedCalled).isEqualTo(1); + + + // add again to container + container.getChildren().add(subContainer); + + // the lifecycle methods are invoked again + assertThat(LifecycleTestRootViewModel.onViewAddedCalled).isEqualTo(2); + assertThat(LifecycleTestRootViewModel.onViewRemovedCalled).isEqualTo(1); + assertThat(LifecycleTestSub1ViewModel.onViewAddedCalled).isEqualTo(2); + assertThat(LifecycleTestSub1ViewModel.onViewRemovedCalled).isEqualTo(1); + assertThat(LifecycleTestSub2ViewModel.onViewAddedCalled).isEqualTo(2); + assertThat(LifecycleTestSub2ViewModel.onViewRemovedCalled).isEqualTo(1); + }); } @@ -120,89 +122,91 @@ public void testLifecycleWithSubViewsWithoutGC() { * when the view is added to the scene a second time. */ @Test - @TestInJfxThread public void testLifecycleWithSubViewsWithGC() { - LifecycleTestRootViewModel.onViewAddedCalled = 0; - LifecycleTestRootViewModel.onViewRemovedCalled = 0; - LifecycleTestSub1ViewModel.onViewAddedCalled = 0; - LifecycleTestSub1ViewModel.onViewRemovedCalled = 0; - LifecycleTestSub2ViewModel.onViewAddedCalled = 0; - LifecycleTestSub2ViewModel.onViewRemovedCalled = 0; - - ViewTuple viewTuple = FluentViewLoader.fxmlView(LifecycleTestRootView.class).load(); - - // GC is performed, however as we still have a reference to the viewTuple, - // nothing will be collected yet. - GCVerifier.forceGC(); - - // the root view is not directly added to the Scene. Instead it is encapsulated in - // another container - VBox subContainer = new VBox(); - subContainer.getChildren().add(viewTuple.getView()); - - VBox container = new VBox(); - - Stage stage = new Stage(); - Scene scene = new Scene(container); - stage.setScene(scene); - - - // now we clear the viewTuple reference ... - viewTuple = null; - - // and perform GC a second time. - // This time, both the ViewModel and the CodeBehind would be collected - // if the framework hadn't prevented it. - GCVerifier.forceGC(); - - // before adding to scene - assertThat(LifecycleTestRootViewModel.onViewAddedCalled).isEqualTo(0); - assertThat(LifecycleTestRootViewModel.onViewRemovedCalled).isEqualTo(0); - assertThat(LifecycleTestSub1ViewModel.onViewAddedCalled).isEqualTo(0); - assertThat(LifecycleTestSub1ViewModel.onViewRemovedCalled).isEqualTo(0); - assertThat(LifecycleTestSub2ViewModel.onViewAddedCalled).isEqualTo(0); - assertThat(LifecycleTestSub2ViewModel.onViewRemovedCalled).isEqualTo(0); - - // add rootView to container - container.getChildren().add(subContainer); - - // onViewAdded is invoked for all viewModels - assertThat(LifecycleTestRootViewModel.onViewAddedCalled).isEqualTo(1); - assertThat(LifecycleTestRootViewModel.onViewRemovedCalled).isEqualTo(0); - assertThat(LifecycleTestSub1ViewModel.onViewAddedCalled).isEqualTo(1); - assertThat(LifecycleTestSub1ViewModel.onViewRemovedCalled).isEqualTo(0); - assertThat(LifecycleTestSub2ViewModel.onViewAddedCalled).isEqualTo(1); - assertThat(LifecycleTestSub2ViewModel.onViewRemovedCalled).isEqualTo(0); - - GCVerifier.forceGC(); - - // remove from container - container.getChildren().clear(); - - GCVerifier.forceGC(); - - // onViewRemoved is invoked on all ViewModels - assertThat(LifecycleTestRootViewModel.onViewAddedCalled).isEqualTo(1); - assertThat(LifecycleTestRootViewModel.onViewRemovedCalled).isEqualTo(1); - assertThat(LifecycleTestSub1ViewModel.onViewAddedCalled).isEqualTo(1); - assertThat(LifecycleTestSub1ViewModel.onViewRemovedCalled).isEqualTo(1); - assertThat(LifecycleTestSub2ViewModel.onViewAddedCalled).isEqualTo(1); - assertThat(LifecycleTestSub2ViewModel.onViewRemovedCalled).isEqualTo(1); - - // Here the guarantee of the framework ends. This time the viewModels - // will be collected - GCVerifier.forceGC(); - - // add again to container - container.getChildren().add(subContainer); - - // no methods are invoked. - assertThat(LifecycleTestRootViewModel.onViewAddedCalled).isEqualTo(1); - assertThat(LifecycleTestRootViewModel.onViewRemovedCalled).isEqualTo(1); - assertThat(LifecycleTestSub1ViewModel.onViewAddedCalled).isEqualTo(1); - assertThat(LifecycleTestSub1ViewModel.onViewRemovedCalled).isEqualTo(1); - assertThat(LifecycleTestSub2ViewModel.onViewAddedCalled).isEqualTo(1); - assertThat(LifecycleTestSub2ViewModel.onViewRemovedCalled).isEqualTo(1); + FxTestingUtils.runInFXThread(() -> { + LifecycleTestRootViewModel.onViewAddedCalled = 0; + LifecycleTestRootViewModel.onViewRemovedCalled = 0; + LifecycleTestSub1ViewModel.onViewAddedCalled = 0; + LifecycleTestSub1ViewModel.onViewRemovedCalled = 0; + LifecycleTestSub2ViewModel.onViewAddedCalled = 0; + LifecycleTestSub2ViewModel.onViewRemovedCalled = 0; + + ViewTuple viewTuple = FluentViewLoader + .fxmlView(LifecycleTestRootView.class).load(); + + // GC is performed, however as we still have a reference to the viewTuple, + // nothing will be collected yet. + GCVerifier.forceGC(); + + // the root view is not directly added to the Scene. Instead it is encapsulated in + // another container + VBox subContainer = new VBox(); + subContainer.getChildren().add(viewTuple.getView()); + + VBox container = new VBox(); + + Stage stage = new Stage(); + Scene scene = new Scene(container); + stage.setScene(scene); + + + // now we clear the viewTuple reference ... + viewTuple = null; + + // and perform GC a second time. + // This time, both the ViewModel and the CodeBehind would be collected + // if the framework hadn't prevented it. + GCVerifier.forceGC(); + + // before adding to scene + assertThat(LifecycleTestRootViewModel.onViewAddedCalled).isEqualTo(0); + assertThat(LifecycleTestRootViewModel.onViewRemovedCalled).isEqualTo(0); + assertThat(LifecycleTestSub1ViewModel.onViewAddedCalled).isEqualTo(0); + assertThat(LifecycleTestSub1ViewModel.onViewRemovedCalled).isEqualTo(0); + assertThat(LifecycleTestSub2ViewModel.onViewAddedCalled).isEqualTo(0); + assertThat(LifecycleTestSub2ViewModel.onViewRemovedCalled).isEqualTo(0); + + // add rootView to container + container.getChildren().add(subContainer); + + // onViewAdded is invoked for all viewModels + assertThat(LifecycleTestRootViewModel.onViewAddedCalled).isEqualTo(1); + assertThat(LifecycleTestRootViewModel.onViewRemovedCalled).isEqualTo(0); + assertThat(LifecycleTestSub1ViewModel.onViewAddedCalled).isEqualTo(1); + assertThat(LifecycleTestSub1ViewModel.onViewRemovedCalled).isEqualTo(0); + assertThat(LifecycleTestSub2ViewModel.onViewAddedCalled).isEqualTo(1); + assertThat(LifecycleTestSub2ViewModel.onViewRemovedCalled).isEqualTo(0); + + GCVerifier.forceGC(); + + // remove from container + container.getChildren().clear(); + + GCVerifier.forceGC(); + + // onViewRemoved is invoked on all ViewModels + assertThat(LifecycleTestRootViewModel.onViewAddedCalled).isEqualTo(1); + assertThat(LifecycleTestRootViewModel.onViewRemovedCalled).isEqualTo(1); + assertThat(LifecycleTestSub1ViewModel.onViewAddedCalled).isEqualTo(1); + assertThat(LifecycleTestSub1ViewModel.onViewRemovedCalled).isEqualTo(1); + assertThat(LifecycleTestSub2ViewModel.onViewAddedCalled).isEqualTo(1); + assertThat(LifecycleTestSub2ViewModel.onViewRemovedCalled).isEqualTo(1); + + // Here the guarantee of the framework ends. This time the viewModels + // will be collected + GCVerifier.forceGC(); + + // add again to container + container.getChildren().add(subContainer); + + // no methods are invoked. + assertThat(LifecycleTestRootViewModel.onViewAddedCalled).isEqualTo(1); + assertThat(LifecycleTestRootViewModel.onViewRemovedCalled).isEqualTo(1); + assertThat(LifecycleTestSub1ViewModel.onViewAddedCalled).isEqualTo(1); + assertThat(LifecycleTestSub1ViewModel.onViewRemovedCalled).isEqualTo(1); + assertThat(LifecycleTestSub2ViewModel.onViewAddedCalled).isEqualTo(1); + assertThat(LifecycleTestSub2ViewModel.onViewRemovedCalled).isEqualTo(1); + }); } @@ -217,42 +221,44 @@ public void testLifecycleWithSubViewsWithGC() { * prevents Garbage collection. */ @Test - @TestInJfxThread public void testGarbageCollectionFailed() { - NotificationCenterFactory.setNotificationCenter(new DefaultNotificationCenter()); + FxTestingUtils.runInFXThread(() -> { + NotificationCenterFactory.setNotificationCenter(new DefaultNotificationCenter()); - ViewTuple viewTuple = FluentViewLoader.fxmlView(NotificationWithoutLifecycleView.class).load(); + ViewTuple viewTuple = FluentViewLoader + .fxmlView(NotificationWithoutLifecycleView.class).load(); - VBox container = new VBox(); + VBox container = new VBox(); - Stage stage = new Stage(); - Scene scene = new Scene(container); - stage.setScene(scene); + Stage stage = new Stage(); + Scene scene = new Scene(container); + stage.setScene(scene); - GCVerifier vmVerifier = GCVerifier.create(viewTuple.getViewModel()); + GCVerifier vmVerifier = GCVerifier.create(viewTuple.getViewModel()); - assertThat(vmVerifier.isAvailableForGC()).isFalse(); + assertThat(vmVerifier.isAvailableForGC()).isFalse(); - container.getChildren().add(viewTuple.getView()); + container.getChildren().add(viewTuple.getView()); - viewTuple = null; + viewTuple = null; - // The ViewModel has a listener subscribed so it isn't available for GC - assertThat(vmVerifier.isAvailableForGC()).isFalse(); + // The ViewModel has a listener subscribed so it isn't available for GC + assertThat(vmVerifier.isAvailableForGC()).isFalse(); - container.getChildren().clear(); + container.getChildren().clear(); - // even after the view isn't used anymore, the ViewModel still can't be garbage collected - // because it is still registered in the notification center - assertThat(vmVerifier.isAvailableForGC()).isFalse(); + // even after the view isn't used anymore, the ViewModel still can't be garbage collected + // because it is still registered in the notification center + assertThat(vmVerifier.isAvailableForGC()).isFalse(); - // only if we replace the notification center ... - NotificationCenterFactory.setNotificationCenter(new DefaultNotificationCenter()); + // only if we replace the notification center ... + NotificationCenterFactory.setNotificationCenter(new DefaultNotificationCenter()); - // the viewModel can be garbage collected. Of cause in practice this isn't a suitable solution - assertThat(vmVerifier.isAvailableForGC()).isTrue(); + // the viewModel can be garbage collected. Of cause in practice this isn't a suitable solution + assertThat(vmVerifier.isAvailableForGC()).isTrue(); + }); } /** @@ -266,39 +272,41 @@ public void testGarbageCollectionFailed() { * Therefore the ViewModel is available for garbage collection afterwards. */ @Test - @TestInJfxThread public void testGarbageCollection() { - NotificationCenterFactory.setNotificationCenter(new DefaultNotificationCenter()); + FxTestingUtils.runInFXThread(() -> { + NotificationCenterFactory.setNotificationCenter(new DefaultNotificationCenter()); - ViewTuple viewTuple = FluentViewLoader.fxmlView(LifecycleNotificationView.class).load(); + ViewTuple viewTuple = FluentViewLoader + .fxmlView(LifecycleNotificationView.class).load(); - VBox container = new VBox(); + VBox container = new VBox(); - Stage stage = new Stage(); - Scene scene = new Scene(container); - stage.setScene(scene); + Stage stage = new Stage(); + Scene scene = new Scene(container); + stage.setScene(scene); - GCVerifier vmVerifier = GCVerifier.create(viewTuple.getViewModel()); + GCVerifier vmVerifier = GCVerifier.create(viewTuple.getViewModel()); - assertThat(vmVerifier.isAvailableForGC()).isFalse(); + assertThat(vmVerifier.isAvailableForGC()).isFalse(); - container.getChildren().add(viewTuple.getView()); + container.getChildren().add(viewTuple.getView()); - viewTuple = null; + viewTuple = null; - // The ViewModel has a listener subscribed so it isn't available for GC now - assertThat(vmVerifier.isAvailableForGC()).isFalse(); + // The ViewModel has a listener subscribed so it isn't available for GC now + assertThat(vmVerifier.isAvailableForGC()).isFalse(); - // this triggeres the lifecycle method which is used to deregister the listener - container.getChildren().clear(); + // this triggeres the lifecycle method which is used to deregister the listener + container.getChildren().clear(); - // therefore the ViewModel can now be garbage collected. - assertThat(vmVerifier.isAvailableForGC()).isTrue(); + // therefore the ViewModel can now be garbage collected. + assertThat(vmVerifier.isAvailableForGC()).isTrue(); - // cleanup notification center to not infer with other tests - NotificationCenterFactory.setNotificationCenter(new DefaultNotificationCenter()); + // cleanup notification center to not infer with other tests + NotificationCenterFactory.setNotificationCenter(new DefaultNotificationCenter()); + }); } @@ -321,40 +329,42 @@ public void testGarbageCollection() { * In the previous implementation this resulted in only the first method was invoked. */ @Test - @TestInJfxThread public void testGcBetweenLifecycleMethods() { - LifecycleGCTestRootViewModel.onViewRemovedCalled = 0; - LifecycleGCTestSub1ViewModel.onViewRemovedCalled = 0; - LifecycleGCTestSub2ViewModel.onViewRemovedCalled = 0; + FxTestingUtils.runInFXThread(() -> { + LifecycleGCTestRootViewModel.onViewRemovedCalled = 0; + LifecycleGCTestSub1ViewModel.onViewRemovedCalled = 0; + LifecycleGCTestSub2ViewModel.onViewRemovedCalled = 0; - ViewTuple viewTuple = FluentViewLoader.fxmlView(LifecycleGCTestRootView.class).load(); + ViewTuple viewTuple = FluentViewLoader + .fxmlView(LifecycleGCTestRootView.class).load(); - VBox subContainer = new VBox(); - subContainer.getChildren().add(viewTuple.getView()); + VBox subContainer = new VBox(); + subContainer.getChildren().add(viewTuple.getView()); - VBox container = new VBox(); + VBox container = new VBox(); - Stage stage = new Stage(); - Scene scene = new Scene(container); - stage.setScene(scene); + Stage stage = new Stage(); + Scene scene = new Scene(container); + stage.setScene(scene); - viewTuple = null; + viewTuple = null; - GCVerifier.forceGC(); + GCVerifier.forceGC(); - assertThat(LifecycleGCTestRootViewModel.onViewRemovedCalled).isEqualTo(0); - assertThat(LifecycleGCTestSub1ViewModel.onViewRemovedCalled).isEqualTo(0); - assertThat(LifecycleGCTestSub2ViewModel.onViewRemovedCalled).isEqualTo(0); + assertThat(LifecycleGCTestRootViewModel.onViewRemovedCalled).isEqualTo(0); + assertThat(LifecycleGCTestSub1ViewModel.onViewRemovedCalled).isEqualTo(0); + assertThat(LifecycleGCTestSub2ViewModel.onViewRemovedCalled).isEqualTo(0); - container.getChildren().add(subContainer); + container.getChildren().add(subContainer); - GCVerifier.forceGC(); + GCVerifier.forceGC(); - container.getChildren().remove(subContainer); + container.getChildren().remove(subContainer); - assertThat(LifecycleGCTestRootViewModel.onViewRemovedCalled).isEqualTo(1); - assertThat(LifecycleGCTestSub1ViewModel.onViewRemovedCalled).isEqualTo(1); - assertThat(LifecycleGCTestSub2ViewModel.onViewRemovedCalled).isEqualTo(1); + assertThat(LifecycleGCTestRootViewModel.onViewRemovedCalled).isEqualTo(1); + assertThat(LifecycleGCTestSub1ViewModel.onViewRemovedCalled).isEqualTo(1); + assertThat(LifecycleGCTestSub2ViewModel.onViewRemovedCalled).isEqualTo(1); + }); } } diff --git a/mvvmfx/src/test/java/de/saxsys/mvvmfx/resourcebundle/global/GlobalResourceBundleTest.java b/mvvmfx/src/test/java/de/saxsys/mvvmfx/resourcebundle/global/GlobalResourceBundleTest.java index db848414e..11af3e656 100644 --- a/mvvmfx/src/test/java/de/saxsys/mvvmfx/resourcebundle/global/GlobalResourceBundleTest.java +++ b/mvvmfx/src/test/java/de/saxsys/mvvmfx/resourcebundle/global/GlobalResourceBundleTest.java @@ -18,11 +18,11 @@ import de.saxsys.mvvmfx.FluentViewLoader; import de.saxsys.mvvmfx.MvvmFX; import de.saxsys.mvvmfx.ViewTuple; -import de.saxsys.mvvmfx.testingutils.jfxrunner.JfxRunner; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import de.saxsys.mvvmfx.testingutils.JfxToolkitExtension; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import java.util.ResourceBundle; @@ -33,21 +33,21 @@ * * @author manuel.mauky */ -@RunWith(JfxRunner.class) +@ExtendWith(JfxToolkitExtension.class) public class GlobalResourceBundleTest { private ResourceBundle global; private ResourceBundle other; - @Before + @BeforeEach public void setup(){ MvvmFX.setGlobalResourceBundle(null); global = ResourceBundle.getBundle(this.getClass().getPackage().getName() + ".global"); other = ResourceBundle.getBundle(this.getClass().getPackage().getName() + ".other"); } - @After + @AfterEach public void tearDown() { MvvmFX.setGlobalResourceBundle(null); } diff --git a/mvvmfx/src/test/java/de/saxsys/mvvmfx/resourcebundle/included/IncludedViewsTest.java b/mvvmfx/src/test/java/de/saxsys/mvvmfx/resourcebundle/included/IncludedViewsTest.java index 5837b23ce..b3a782991 100644 --- a/mvvmfx/src/test/java/de/saxsys/mvvmfx/resourcebundle/included/IncludedViewsTest.java +++ b/mvvmfx/src/test/java/de/saxsys/mvvmfx/resourcebundle/included/IncludedViewsTest.java @@ -18,11 +18,11 @@ import de.saxsys.mvvmfx.FluentViewLoader; import de.saxsys.mvvmfx.MvvmFX; import de.saxsys.mvvmfx.ViewTuple; -import de.saxsys.mvvmfx.testingutils.jfxrunner.JfxRunner; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import de.saxsys.mvvmfx.testingutils.JfxToolkitExtension; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import java.util.ResourceBundle; @@ -33,21 +33,21 @@ * * @author manuel.mauky */ -@RunWith(JfxRunner.class) +@ExtendWith(JfxToolkitExtension.class) public class IncludedViewsTest { private ResourceBundle root; private ResourceBundle included; - @Before + @BeforeEach public void setup(){ MvvmFX.setGlobalResourceBundle(null); root = ResourceBundle.getBundle(this.getClass().getPackage().getName() + ".root"); included = ResourceBundle.getBundle(this.getClass().getPackage().getName() + ".included"); } - @After + @AfterEach public void tearDown() { MvvmFX.setGlobalResourceBundle(null); } diff --git a/mvvmfx/src/test/java/de/saxsys/mvvmfx/scopes/context/ContextTest.java b/mvvmfx/src/test/java/de/saxsys/mvvmfx/scopes/context/ContextTest.java index c3a329119..7a8ad880b 100644 --- a/mvvmfx/src/test/java/de/saxsys/mvvmfx/scopes/context/ContextTest.java +++ b/mvvmfx/src/test/java/de/saxsys/mvvmfx/scopes/context/ContextTest.java @@ -3,7 +3,7 @@ import de.saxsys.mvvmfx.FluentViewLoader; import de.saxsys.mvvmfx.scopes.context.views.ScopedFxmlView; import de.saxsys.mvvmfx.scopes.context.views.ScopedFxmlViewModel; -import org.junit.Test; +import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; diff --git a/mvvmfx/src/test/java/de/saxsys/mvvmfx/scopes/example1/Example1ScopesTest.java b/mvvmfx/src/test/java/de/saxsys/mvvmfx/scopes/example1/Example1ScopesTest.java index 58485ec77..3b113277f 100644 --- a/mvvmfx/src/test/java/de/saxsys/mvvmfx/scopes/example1/Example1ScopesTest.java +++ b/mvvmfx/src/test/java/de/saxsys/mvvmfx/scopes/example1/Example1ScopesTest.java @@ -2,8 +2,9 @@ import de.saxsys.mvvmfx.FluentViewLoader; import de.saxsys.mvvmfx.scopes.example1.views.*; -import org.junit.Assert; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.omg.SendingContext.RunTime; public class Example1ScopesTest { @@ -51,64 +52,66 @@ public void testFxmlScopedView() throws Exception { ScopedViewModelG viewModel_B_E_G = parentView.subviewBController.subviewEController.subviewGController.viewModel; - Assert.assertNotNull(viewModel_A_E); - Assert.assertNotNull(viewModel_A_E_F); - Assert.assertNotNull(viewModel_A_E_G); - Assert.assertNotNull(viewModel_B_E); - Assert.assertNotNull(viewModel_B_E_F); - Assert.assertNotNull(viewModel_B_E_G); + Assertions.assertNotNull(viewModel_A_E); + Assertions.assertNotNull(viewModel_A_E_F); + Assertions.assertNotNull(viewModel_A_E_G); + Assertions.assertNotNull(viewModel_B_E); + Assertions.assertNotNull(viewModel_B_E_F); + Assertions.assertNotNull(viewModel_B_E_G); - Assert.assertNotEquals(viewModel_A_E.testScope3, viewModel_B_E.testScope3); + Assertions.assertNotEquals(viewModel_A_E.testScope3, viewModel_B_E.testScope3); - Assert.assertEquals(viewModel_A_E.testScope3, viewModel_A_E_F.testScope3); - Assert.assertEquals(viewModel_A_E.testScope3, viewModel_A_E_G.testScope3); + Assertions.assertEquals(viewModel_A_E.testScope3, viewModel_A_E_F.testScope3); + Assertions.assertEquals(viewModel_A_E.testScope3, viewModel_A_E_G.testScope3); - Assert.assertEquals(viewModel_B_E.testScope3, viewModel_B_E_F.testScope3); - Assert.assertEquals(viewModel_B_E.testScope3, viewModel_B_E_G.testScope3); + Assertions.assertEquals(viewModel_B_E.testScope3, viewModel_B_E_F.testScope3); + Assertions.assertEquals(viewModel_B_E.testScope3, viewModel_B_E_G.testScope3); verifyScopes(viewModelA, viewModelB, viewModelCinA, viewModelCinB, viewModelDinA, viewModelDinB); } - @Test(expected = Exception.class) + @Test public void testErrorWhenNoScopeProviderFound() { final ScopesFxmlParentView parentView = FluentViewLoader.fxmlView(ScopesFxmlParentView.class) .load() .getCodeBehind(); - parentView.subviewAController.subviewCController.loadWrongScopedView(); + Assertions.assertThrows(Exception.class, () ->{ + parentView.subviewAController.subviewCController.loadWrongScopedView(); + }); } private void verifyScopes(ScopedViewModelA viewModelA, ScopedViewModelB viewModelB, ScopedViewModelC viewModelCinA, ScopedViewModelC viewModelCinB, ScopedViewModelD viewModelDinA, ScopedViewModelD viewModelDinB) { - Assert.assertNotNull(viewModelA); - Assert.assertNotNull(viewModelB); - Assert.assertNotNull(viewModelCinA); - Assert.assertNotNull(viewModelCinB); - Assert.assertNotNull(viewModelDinA); - Assert.assertNotNull(viewModelDinB); + Assertions.assertNotNull(viewModelA); + Assertions.assertNotNull(viewModelB); + Assertions.assertNotNull(viewModelCinA); + Assertions.assertNotNull(viewModelCinB); + Assertions.assertNotNull(viewModelDinA); + Assertions.assertNotNull(viewModelDinB); - Assert.assertNotNull(viewModelA.injectedScope1); - Assert.assertNotNull(viewModelB.injectedScope1); - Assert.assertNotNull(viewModelCinA.injectedScope1); - Assert.assertNotNull(viewModelCinB.injectedScope1); - Assert.assertNotNull(viewModelDinA.injectedScope1); - Assert.assertNotNull(viewModelDinA.injectedScope2); - Assert.assertNotNull(viewModelDinB.injectedScope1); - Assert.assertNotNull(viewModelDinB.injectedScope2); + Assertions.assertNotNull(viewModelA.injectedScope1); + Assertions.assertNotNull(viewModelB.injectedScope1); + Assertions.assertNotNull(viewModelCinA.injectedScope1); + Assertions.assertNotNull(viewModelCinB.injectedScope1); + Assertions.assertNotNull(viewModelDinA.injectedScope1); + Assertions.assertNotNull(viewModelDinA.injectedScope2); + Assertions.assertNotNull(viewModelDinB.injectedScope1); + Assertions.assertNotNull(viewModelDinB.injectedScope2); - Assert.assertNotEquals(viewModelA.injectedScope1, viewModelB.injectedScope1); + Assertions.assertNotEquals(viewModelA.injectedScope1, viewModelB.injectedScope1); - Assert.assertEquals(viewModelA.injectedScope1, viewModelCinA.injectedScope1); - Assert.assertEquals(viewModelA.injectedScope1, viewModelDinA.injectedScope1); + Assertions.assertEquals(viewModelA.injectedScope1, viewModelCinA.injectedScope1); + Assertions.assertEquals(viewModelA.injectedScope1, viewModelDinA.injectedScope1); - Assert.assertEquals(viewModelB.injectedScope1, viewModelCinB.injectedScope1); - Assert.assertEquals(viewModelB.injectedScope1, viewModelDinB.injectedScope1); + Assertions.assertEquals(viewModelB.injectedScope1, viewModelCinB.injectedScope1); + Assertions.assertEquals(viewModelB.injectedScope1, viewModelDinB.injectedScope1); } diff --git a/mvvmfx/src/test/java/de/saxsys/mvvmfx/scopes/example2/Example2ScopesTest.java b/mvvmfx/src/test/java/de/saxsys/mvvmfx/scopes/example2/Example2ScopesTest.java index 7f62ee51c..0ddaa17ce 100644 --- a/mvvmfx/src/test/java/de/saxsys/mvvmfx/scopes/example2/Example2ScopesTest.java +++ b/mvvmfx/src/test/java/de/saxsys/mvvmfx/scopes/example2/Example2ScopesTest.java @@ -6,14 +6,14 @@ import de.saxsys.mvvmfx.scopes.example2.views.ScopedViewB; import de.saxsys.mvvmfx.scopes.example2.views.ScopedViewC; import de.saxsys.mvvmfx.scopes.example2.views.ScopedViewModelA; -import org.junit.Ignore; -import org.junit.Test; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.fail; -@Ignore +@Disabled public class Example2ScopesTest { @Test diff --git a/mvvmfx/src/test/java/de/saxsys/mvvmfx/scopes/example3/Example3Test.java b/mvvmfx/src/test/java/de/saxsys/mvvmfx/scopes/example3/Example3Test.java index e1373e799..b63c640a3 100644 --- a/mvvmfx/src/test/java/de/saxsys/mvvmfx/scopes/example3/Example3Test.java +++ b/mvvmfx/src/test/java/de/saxsys/mvvmfx/scopes/example3/Example3Test.java @@ -4,10 +4,10 @@ import de.saxsys.mvvmfx.ViewTuple; import de.saxsys.mvvmfx.scopes.example3.views.MainView; import de.saxsys.mvvmfx.scopes.example3.views.MainViewModel; -import org.junit.Ignore; -import org.junit.Test; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; -@Ignore("Ignore until fixed") +@Disabled("Ignore until fixed") public class Example3Test { @Test diff --git a/mvvmfx/src/test/java/de/saxsys/mvvmfx/scopes/example4/views/Example4Test.java b/mvvmfx/src/test/java/de/saxsys/mvvmfx/scopes/example4/views/Example4Test.java index 3c2fdf750..9d3ceb976 100644 --- a/mvvmfx/src/test/java/de/saxsys/mvvmfx/scopes/example4/views/Example4Test.java +++ b/mvvmfx/src/test/java/de/saxsys/mvvmfx/scopes/example4/views/Example4Test.java @@ -4,22 +4,16 @@ import de.saxsys.mvvmfx.FluentViewLoader; import de.saxsys.mvvmfx.ViewTuple; import de.saxsys.mvvmfx.testingutils.FxTestingUtils; -import de.saxsys.mvvmfx.testingutils.jfxrunner.JfxRunner; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; +import de.saxsys.mvvmfx.testingutils.JfxToolkitExtension; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import static org.assertj.core.api.Assertions.assertThat; -@RunWith(JfxRunner.class) +@ExtendWith(JfxToolkitExtension.class) public class Example4Test { - // Rule to get exceptions from the JavaFX Thread into the JUnit thread - @Rule - public CatchAllExceptionsRule catchAllExceptionsRule = new CatchAllExceptionsRule(); - - @Test public void test() { diff --git a/mvvmfx/src/test/java/de/saxsys/mvvmfx/scopes/example5/Example5Test.java b/mvvmfx/src/test/java/de/saxsys/mvvmfx/scopes/example5/Example5Test.java index 8d0410153..f816f9e2b 100644 --- a/mvvmfx/src/test/java/de/saxsys/mvvmfx/scopes/example5/Example5Test.java +++ b/mvvmfx/src/test/java/de/saxsys/mvvmfx/scopes/example5/Example5Test.java @@ -3,12 +3,10 @@ import de.saxsys.mvvmfx.FluentViewLoader; import de.saxsys.mvvmfx.ViewTuple; import de.saxsys.mvvmfx.testingutils.jfxrunner.JfxRunner; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; -@RunWith(JfxRunner.class) public class Example5Test { @Test diff --git a/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/commands/CommandsWithoutUiThreadTest.java b/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/commands/CommandsWithoutUiThreadTest.java index bb5755802..ad6b403ff 100644 --- a/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/commands/CommandsWithoutUiThreadTest.java +++ b/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/commands/CommandsWithoutUiThreadTest.java @@ -4,7 +4,7 @@ import javafx.beans.property.IntegerProperty; import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleIntegerProperty; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.function.Supplier; diff --git a/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/commands/CompositeCommandTest.java b/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/commands/CompositeCommandTest.java index 7d04c1d20..9c87285ec 100644 --- a/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/commands/CompositeCommandTest.java +++ b/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/commands/CompositeCommandTest.java @@ -23,27 +23,23 @@ import java.util.concurrent.TimeUnit; import com.cedarsoft.test.utils.CatchAllExceptionsRule; -import de.saxsys.mvvmfx.testingutils.jfxrunner.JfxRunner; +import de.saxsys.mvvmfx.testingutils.JfxToolkitExtension; import javafx.application.Platform; import javafx.beans.property.BooleanProperty; import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import de.saxsys.mvvmfx.testingutils.GCVerifier; +import org.junit.jupiter.api.extension.ExtendWith; -@RunWith(JfxRunner.class) +@ExtendWith(JfxToolkitExtension.class) public class CompositeCommandTest { - // Rule to get exceptions from the JavaFX Thread into the JUnit thread - @Rule - public CatchAllExceptionsRule catchAllExceptionsRule = new CatchAllExceptionsRule(); private BooleanProperty condition1; @@ -53,7 +49,7 @@ public class CompositeCommandTest { private BooleanProperty called2; private DelegateCommand delegateCommand2; - @Before + @BeforeEach public void init() { condition1 = new SimpleBooleanProperty(true); called1 = new SimpleBooleanProperty(); @@ -170,7 +166,7 @@ public void allCommandsAreUnregistered() throws Exception { compositeCommand.unregister(delegateCommand2); } - @Ignore("unstable test. Needs to be fixed. see bug #260") + @Disabled("unstable test. Needs to be fixed. see bug #260") @Test public void longRunningAsyncComposite() throws Exception { diff --git a/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/commands/DelegateCommandTest.java b/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/commands/DelegateCommandTest.java index 63db62463..9a67cf140 100644 --- a/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/commands/DelegateCommandTest.java +++ b/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/commands/DelegateCommandTest.java @@ -27,9 +27,9 @@ import java.util.function.BiConsumer; import java.util.function.Supplier; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; +import de.saxsys.mvvmfx.testingutils.JfxToolkitExtension; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; import com.cedarsoft.test.utils.CatchAllExceptionsRule; @@ -41,15 +41,13 @@ import javafx.beans.value.ChangeListener; import javafx.concurrent.Service; import javafx.concurrent.Task; +import org.junit.jupiter.api.extension.ExtendWith; -@RunWith(JfxRunner.class) +@ExtendWith(JfxToolkitExtension.class) public class DelegateCommandTest { - - // Rule to get exceptions from the JavaFX Thread into the JUnit thread - @Rule - public CatchAllExceptionsRule catchAllExceptionsRule = new CatchAllExceptionsRule(); + @Test public void executable() { @@ -154,7 +152,7 @@ protected void action() { .hasMessage(exceptionReason); } - @Test(expected = RuntimeException.class) + @Test public void commandNotExecutable() { BooleanProperty condition = new SimpleBooleanProperty(false); @@ -163,8 +161,10 @@ public void commandNotExecutable() { protected void action() { } }, condition); - - delegateCommand.execute(); + + Assertions.assertThrows(RuntimeException.class, () -> { + delegateCommand.execute(); + }); } @Test diff --git a/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/itemlist/ItemListTest.java b/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/itemlist/ItemListTest.java index e1337c2bf..3e07ba127 100644 --- a/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/itemlist/ItemListTest.java +++ b/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/itemlist/ItemListTest.java @@ -19,9 +19,9 @@ import javafx.collections.ListChangeListener; import javafx.collections.ObservableList; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.util.Collection; import java.util.List; @@ -52,7 +52,7 @@ public class ItemListTest { /** * Prepares the test. */ - @Before + @BeforeEach public void init() { // Create the items in the model @@ -77,7 +77,7 @@ public String apply(Person object) { */ @Test public void mapFromModelToString() { - Assert.assertEquals(PREFIX + PERSON3_NAME, itemList + Assertions.assertEquals(PREFIX + PERSON3_NAME, itemList .stringListProperty().get(2)); } @@ -86,11 +86,11 @@ public void mapFromModelToString() { */ @Test public void addItemToItemList() { - Assert.assertEquals(3, itemList.stringListProperty().size()); - Assert.assertEquals(3, listWithModelObjects.size()); + Assertions.assertEquals(3, itemList.stringListProperty().size()); + Assertions.assertEquals(3, listWithModelObjects.size()); listWithModelObjects.add(new Person("addedPerson")); - Assert.assertEquals(4, itemList.stringListProperty().size()); - Assert.assertEquals(4, listWithModelObjects.size()); + Assertions.assertEquals(4, itemList.stringListProperty().size()); + Assertions.assertEquals(4, listWithModelObjects.size()); } /** @@ -98,56 +98,56 @@ public void addItemToItemList() { */ @Test public void removeItemFromItemList() { - Assert.assertEquals(3, itemList.stringListProperty().size()); - Assert.assertEquals(3, listWithModelObjects.size()); + Assertions.assertEquals(3, itemList.stringListProperty().size()); + Assertions.assertEquals(3, listWithModelObjects.size()); listWithModelObjects.remove(0); - Assert.assertEquals(2, itemList.stringListProperty().size()); - Assert.assertEquals(2, listWithModelObjects.size()); + Assertions.assertEquals(2, itemList.stringListProperty().size()); + Assertions.assertEquals(2, listWithModelObjects.size()); } @Test public void removeMultipleItemsFromItemList() { listWithModelObjects.removeAll(person1, person2); - Assert.assertEquals(1, listWithModelObjects.size()); - Assert.assertEquals(1, itemList.stringListProperty().size()); - Assert.assertEquals(person3, listWithModelObjects.get(0)); - Assert.assertEquals(PREFIX + PERSON3_NAME, itemList + Assertions.assertEquals(1, listWithModelObjects.size()); + Assertions.assertEquals(1, itemList.stringListProperty().size()); + Assertions.assertEquals(person3, listWithModelObjects.get(0)); + Assertions.assertEquals(PREFIX + PERSON3_NAME, itemList .stringListProperty().get(0)); } @Test public void removeAllItemsFromItemList() { listWithModelObjects.clear(); - Assert.assertEquals(0, listWithModelObjects.size()); - Assert.assertEquals(0, itemList.stringListProperty().size()); + Assertions.assertEquals(0, listWithModelObjects.size()); + Assertions.assertEquals(0, itemList.stringListProperty().size()); } @Test public void addItemToItemListAtIndex() { listWithModelObjects.add(1, new Person("addedPerson")); - Assert.assertEquals(4, itemList.stringListProperty().size()); - Assert.assertEquals(4, listWithModelObjects.size()); - Assert.assertEquals(PREFIX + "addedPerson", itemList + Assertions.assertEquals(4, itemList.stringListProperty().size()); + Assertions.assertEquals(4, listWithModelObjects.size()); + Assertions.assertEquals(PREFIX + "addedPerson", itemList .stringListProperty().get(1)); } @Test public void addMultipleItemsToItemList() { listWithModelObjects.addAll(new Person("added1"), new Person("added2")); - Assert.assertEquals(5, listWithModelObjects.size()); - Assert.assertEquals(5, itemList.stringListProperty().size()); - Assert.assertEquals(PREFIX + "added1", itemList.stringListProperty() + Assertions.assertEquals(5, listWithModelObjects.size()); + Assertions.assertEquals(5, itemList.stringListProperty().size()); + Assertions.assertEquals(PREFIX + "added1", itemList.stringListProperty() .get(3)); - Assert.assertEquals(PREFIX + "added2", itemList.stringListProperty() + Assertions.assertEquals(PREFIX + "added2", itemList.stringListProperty() .get(4)); } @Test public void replaceItemInItemListAtIndex() { listWithModelObjects.set(1, new Person("replacedPerson")); - Assert.assertEquals(3, listWithModelObjects.size()); - Assert.assertEquals(3, itemList.stringListProperty().size()); - Assert.assertEquals(PREFIX + "replacedPerson", itemList + Assertions.assertEquals(3, listWithModelObjects.size()); + Assertions.assertEquals(3, itemList.stringListProperty().size()); + Assertions.assertEquals(PREFIX + "replacedPerson", itemList .stringListProperty().get(1)); } diff --git a/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/itemlist/ListTransformationTest.java b/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/itemlist/ListTransformationTest.java index ebc1cc039..98781d89f 100644 --- a/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/itemlist/ListTransformationTest.java +++ b/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/itemlist/ListTransformationTest.java @@ -1,6 +1,6 @@ package de.saxsys.mvvmfx.utils.itemlist; -import org.junit.Test; +import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; diff --git a/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/itemlist/SelectableItemListTest.java b/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/itemlist/SelectableItemListTest.java index ad2b04f2d..17df22b92 100644 --- a/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/itemlist/SelectableItemListTest.java +++ b/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/itemlist/SelectableItemListTest.java @@ -19,9 +19,9 @@ import javafx.collections.FXCollections; import javafx.collections.ObservableList; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; /** * Tests for {@link SelectableItemList}. @@ -41,7 +41,7 @@ public class SelectableItemListTest { /** * Prepares the test. */ - @Before + @BeforeEach public void init() { // Create the items in the model @@ -68,8 +68,8 @@ public String apply(Person object) { */ @Test public void checkStartState() { - Assert.assertEquals(-1, selectableItemList.getSelectedIndex()); - Assert.assertEquals(null, selectableItemList.getSelectedItem()); + Assertions.assertEquals(-1, selectableItemList.getSelectedIndex()); + Assertions.assertEquals(null, selectableItemList.getSelectedItem()); } /** @@ -79,7 +79,7 @@ public void checkStartState() { @Test public void setSelectedItemByIndex() { selectableItemList.select(1); - Assert.assertEquals(listWithModelObjects.get(1), selectableItemList + Assertions.assertEquals(listWithModelObjects.get(1), selectableItemList .selectedItemProperty().get()); } @@ -90,7 +90,7 @@ public void setSelectedItemByIndex() { @Test public void setSelectedIndexByItem() { selectableItemList.select(person3); - Assert.assertEquals(2, selectableItemList.getSelectedIndex()); + Assertions.assertEquals(2, selectableItemList.getSelectedIndex()); } /** @@ -99,14 +99,14 @@ public void setSelectedIndexByItem() { @Test public void setSelectedIndexWithInvalidItem() { selectableItemList.select(person1); - Assert.assertEquals(0, selectableItemList.getSelectedIndex()); - Assert.assertEquals(person1, selectableItemList.getSelectedItem()); + Assertions.assertEquals(0, selectableItemList.getSelectedIndex()); + Assertions.assertEquals(person1, selectableItemList.getSelectedItem()); selectableItemList.select(new Person("Roflcopter")); - Assert.assertEquals(0, selectableItemList.getSelectedIndex()); - Assert.assertEquals(person1, selectableItemList.getSelectedItem()); + Assertions.assertEquals(0, selectableItemList.getSelectedIndex()); + Assertions.assertEquals(person1, selectableItemList.getSelectedItem()); selectableItemList.select(person2); - Assert.assertEquals(1, selectableItemList.getSelectedIndex()); - Assert.assertEquals(person2, selectableItemList.getSelectedItem()); + Assertions.assertEquals(1, selectableItemList.getSelectedIndex()); + Assertions.assertEquals(person2, selectableItemList.getSelectedItem()); } /** @@ -115,44 +115,44 @@ public void setSelectedIndexWithInvalidItem() { @Test public void setSelectedItemWithInvalidIndex() { selectableItemList.select(person1); - Assert.assertEquals(0, selectableItemList.getSelectedIndex()); - Assert.assertEquals(person1, selectableItemList.getSelectedItem()); + Assertions.assertEquals(0, selectableItemList.getSelectedIndex()); + Assertions.assertEquals(person1, selectableItemList.getSelectedItem()); selectableItemList.select(100); - Assert.assertEquals(0, selectableItemList.getSelectedIndex()); - Assert.assertEquals(person1, selectableItemList.getSelectedItem()); + Assertions.assertEquals(0, selectableItemList.getSelectedIndex()); + Assertions.assertEquals(person1, selectableItemList.getSelectedItem()); } @Test public void unselectBySettingSelectedItemToNull() { selectableItemList.select(person2); - Assert.assertEquals(1, selectableItemList.getSelectedIndex()); - Assert.assertEquals(person2, selectableItemList.selectedItemProperty() + Assertions.assertEquals(1, selectableItemList.getSelectedIndex()); + Assertions.assertEquals(person2, selectableItemList.selectedItemProperty() .get()); selectableItemList.select(null); - Assert.assertEquals(-1, selectableItemList.getSelectedIndex()); - Assert.assertNull(selectableItemList.selectedItemProperty().get()); + Assertions.assertEquals(-1, selectableItemList.getSelectedIndex()); + Assertions.assertNull(selectableItemList.selectedItemProperty().get()); } @Test public void unselectBySettingSelectedIndexToMinus1() { selectableItemList.select(person2); - Assert.assertEquals(1, selectableItemList.getSelectedIndex()); - Assert.assertEquals(person2, selectableItemList.selectedItemProperty() + Assertions.assertEquals(1, selectableItemList.getSelectedIndex()); + Assertions.assertEquals(person2, selectableItemList.selectedItemProperty() .get()); selectableItemList.select(-1); - Assert.assertEquals(-1, selectableItemList.getSelectedIndex()); - Assert.assertNull(selectableItemList.selectedItemProperty().get()); + Assertions.assertEquals(-1, selectableItemList.getSelectedIndex()); + Assertions.assertNull(selectableItemList.selectedItemProperty().get()); } @Test public void unselectByClearSelection() { selectableItemList.select(person2); - Assert.assertEquals(1, selectableItemList.getSelectedIndex()); - Assert.assertEquals(person2, selectableItemList.selectedItemProperty() + Assertions.assertEquals(1, selectableItemList.getSelectedIndex()); + Assertions.assertEquals(person2, selectableItemList.selectedItemProperty() .get()); selectableItemList.clearSelection(); - Assert.assertEquals(-1, selectableItemList.getSelectedIndex()); - Assert.assertNull(selectableItemList.selectedItemProperty().get()); + Assertions.assertEquals(-1, selectableItemList.getSelectedIndex()); + Assertions.assertNull(selectableItemList.selectedItemProperty().get()); } } diff --git a/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/mapping/FieldMethodOverloadingTest.java b/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/mapping/FieldMethodOverloadingTest.java index 06db12d7e..c630a9c22 100644 --- a/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/mapping/FieldMethodOverloadingTest.java +++ b/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/mapping/FieldMethodOverloadingTest.java @@ -24,8 +24,8 @@ import javafx.beans.property.ObjectProperty; import javafx.beans.property.Property; import javafx.beans.property.StringProperty; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.Collections; @@ -59,7 +59,7 @@ public class FieldMethodOverloadingTest { private ModelWrapper wrapper; - @Before + @BeforeEach public void setup() { wrapper = new ModelWrapper<>(); } diff --git a/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/mapping/ModelWrapperTest.java b/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/mapping/ModelWrapperTest.java index 45a6d674b..459c42392 100644 --- a/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/mapping/ModelWrapperTest.java +++ b/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/mapping/ModelWrapperTest.java @@ -20,7 +20,7 @@ import javafx.beans.property.SimpleObjectProperty; import javafx.beans.property.StringProperty; import javafx.collections.FXCollections; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.Collections; diff --git a/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/notifications/ConcurrentModificationBugTest.java b/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/notifications/ConcurrentModificationBugTest.java index 63ef70a7d..0448756c7 100644 --- a/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/notifications/ConcurrentModificationBugTest.java +++ b/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/notifications/ConcurrentModificationBugTest.java @@ -15,7 +15,7 @@ ******************************************************************************/ package de.saxsys.mvvmfx.utils.notifications; -import org.junit.Test; +import org.junit.jupiter.api.Test; /** diff --git a/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/notifications/DefaultNotificationCenterTest.java b/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/notifications/DefaultNotificationCenterTest.java index 552f2e0cf..6152d830a 100644 --- a/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/notifications/DefaultNotificationCenterTest.java +++ b/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/notifications/DefaultNotificationCenterTest.java @@ -17,11 +17,12 @@ package de.saxsys.mvvmfx.utils.notifications; import de.saxsys.mvvmfx.ViewModel; -import de.saxsys.mvvmfx.testingutils.jfxrunner.JfxRunner; +import de.saxsys.mvvmfx.testingutils.JfxToolkitExtension; import javafx.application.Platform; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mockito; import java.util.concurrent.CompletableFuture; @@ -31,7 +32,7 @@ import static org.assertj.core.api.Assertions.assertThat; -@RunWith(JfxRunner.class) +@ExtendWith(JfxToolkitExtension.class) public class DefaultNotificationCenterTest { private static final String TEST_NOTIFICATION = "test_notification"; @@ -44,7 +45,7 @@ public class DefaultNotificationCenterTest { NotificationObserver observer2; NotificationObserver observer3; - @Before + @BeforeEach public void init() { observer1 = Mockito.mock(NotificationObserver.class); observer2 = Mockito.mock(NotificationObserver.class); @@ -178,8 +179,10 @@ public void observerForViewModelIsCalledFromUiThread() throws InterruptedExcepti assertThat(wasCalledOnUiThread).isTrue(); } - @Test(expected = IllegalArgumentException.class) + @Test public void subscribeWithNullObserver() { - defaultCenter.subscribe(TEST_NOTIFICATION,null); + Assertions.assertThrows(IllegalArgumentException.class, () -> { + defaultCenter.subscribe(TEST_NOTIFICATION, null); + }); } } diff --git a/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/notifications/MemoryLeakGlobalTest.java b/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/notifications/MemoryLeakGlobalTest.java index 415dba909..01b2f993f 100644 --- a/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/notifications/MemoryLeakGlobalTest.java +++ b/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/notifications/MemoryLeakGlobalTest.java @@ -1,8 +1,8 @@ package de.saxsys.mvvmfx.utils.notifications; import de.saxsys.mvvmfx.testingutils.GCVerifier; -import org.junit.After; -import org.junit.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; import java.util.concurrent.atomic.AtomicInteger; @@ -16,7 +16,7 @@ public class MemoryLeakGlobalTest { - @After + @AfterEach public void tearDown() { NotificationCenterFactory.setNotificationCenter(new DefaultNotificationCenter()); } diff --git a/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/notifications/NotificationTestHelperTest.java b/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/notifications/NotificationTestHelperTest.java index 751644db4..125c63aae 100644 --- a/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/notifications/NotificationTestHelperTest.java +++ b/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/notifications/NotificationTestHelperTest.java @@ -17,8 +17,8 @@ import de.saxsys.mvvmfx.ViewModel; import javafx.util.Pair; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.util.stream.Stream; @@ -34,7 +34,7 @@ public class MyViewModel implements ViewModel { private MyViewModel viewModel; - @Before + @BeforeEach public void setUp() throws Exception { viewModel = new MyViewModel(); } diff --git a/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/notifications/ResetNotificationCenterTest.java b/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/notifications/ResetNotificationCenterTest.java index 235dd9165..53f7a91ee 100644 --- a/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/notifications/ResetNotificationCenterTest.java +++ b/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/notifications/ResetNotificationCenterTest.java @@ -15,9 +15,9 @@ ******************************************************************************/ package de.saxsys.mvvmfx.utils.notifications; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.util.concurrent.atomic.AtomicBoolean; @@ -40,7 +40,7 @@ public class ResetNotificationCenterTest { private static final String MY_MESSAGE = "myMessage"; private AtomicBoolean wasCalled = new AtomicBoolean(false); - @Before + @BeforeEach public void setup() { NotificationCenter notificationCenter = NotificationCenterFactory.getNotificationCenter(); @@ -59,7 +59,7 @@ public void setup() { * one of the test cases would fail. * By replacing the instance we assure that each test uses a fresh notification center. */ - @After + @AfterEach public void tearDown() { NotificationCenterFactory.setNotificationCenter(new DefaultNotificationCenter()); diff --git a/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/notifications/viewmodel/MemoryLeakOnViewModelTest.java b/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/notifications/viewmodel/MemoryLeakOnViewModelTest.java index 8af5ccaf7..83d216005 100644 --- a/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/notifications/viewmodel/MemoryLeakOnViewModelTest.java +++ b/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/notifications/viewmodel/MemoryLeakOnViewModelTest.java @@ -1,12 +1,13 @@ package de.saxsys.mvvmfx.utils.notifications.viewmodel; import de.saxsys.mvvmfx.ViewModel; +import de.saxsys.mvvmfx.testingutils.FxTestingUtils; import de.saxsys.mvvmfx.testingutils.GCVerifier; -import de.saxsys.mvvmfx.testingutils.jfxrunner.JfxRunner; +import de.saxsys.mvvmfx.testingutils.JfxToolkitExtension; import de.saxsys.mvvmfx.utils.notifications.NotificationObserver; import javafx.application.Platform; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; @@ -21,7 +22,7 @@ * This test verifies that the communication between View and ViewModel * via notifications doesn't introduce memory leaks. */ -@RunWith(JfxRunner.class) +@ExtendWith(JfxToolkitExtension.class) public class MemoryLeakOnViewModelTest { @@ -43,7 +44,7 @@ public void testViewModelWithViewCommunication() { viewModel.actionThatPublishes(); - waitForUiThread(); + FxTestingUtils.waitForUiThread(); assertThat(view.counter.get()).isEqualTo(1); @@ -79,7 +80,7 @@ public void testViewModelCommunication() { vm.publish("test"); - waitForUiThread(); + FxTestingUtils.waitForUiThread(); assertThat(counter.get()).isEqualTo(1); @@ -115,7 +116,7 @@ public void testStaticObserverDoesntCreateMemoryLeak() { vm.publish("test"); - waitForUiThread(); + FxTestingUtils.waitForUiThread(); assertThat(StaticObserver.counter.get()).isEqualTo(1); @@ -139,19 +140,4 @@ public void receivedNotification(String key, Object... payload) { } } - - /** - * This method is used to wait until the UI thread has done all work that was queued via - * {@link Platform#runLater(Runnable)}. - */ - private void waitForUiThread() { - CompletableFuture future = new CompletableFuture<>(); - Platform.runLater(() -> future.complete(null)); - try { - future.get(1l, TimeUnit.SECONDS); - } catch (InterruptedException | ExecutionException | TimeoutException e) { - throw new IllegalStateException(e); - } - } - } diff --git a/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/notifications/viewmodel/ViewModelTest.java b/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/notifications/viewmodel/ViewModelTest.java index b425c421b..a1ca91321 100644 --- a/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/notifications/viewmodel/ViewModelTest.java +++ b/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/notifications/viewmodel/ViewModelTest.java @@ -17,15 +17,16 @@ import de.saxsys.mvvmfx.MvvmFX; import de.saxsys.mvvmfx.ViewModel; -import de.saxsys.mvvmfx.testingutils.jfxrunner.JfxRunner; +import de.saxsys.mvvmfx.testingutils.FxTestingUtils; +import de.saxsys.mvvmfx.testingutils.JfxToolkitExtension; import de.saxsys.mvvmfx.utils.notifications.DefaultNotificationCenter; import de.saxsys.mvvmfx.utils.notifications.DefaultNotificationCenterTest; import de.saxsys.mvvmfx.utils.notifications.NotificationCenterFactory; import de.saxsys.mvvmfx.utils.notifications.NotificationObserver; import javafx.application.Platform; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mockito; import java.util.concurrent.CompletableFuture; @@ -36,7 +37,7 @@ /** * This test verifies the communication via notifications between the View and ViewModel. */ -@RunWith(JfxRunner.class) +@ExtendWith(JfxToolkitExtension.class) public class ViewModelTest { private static final String TEST_NOTIFICATION = "test_notification"; @@ -47,7 +48,7 @@ public class ViewModelTest { DummyNotificationObserver observer2; DummyNotificationObserver observer3; - @Before + @BeforeEach public void init() { observer1 = Mockito.mock(DummyNotificationObserver.class); observer2 = Mockito.mock(DummyNotificationObserver.class); @@ -63,7 +64,7 @@ public void observerFromOutsideDoesNotReceiveNotifications() { MvvmFX.getNotificationCenter().subscribe(TEST_NOTIFICATION, observer1); viewModel.publish(TEST_NOTIFICATION); - waitForUiThread(); + FxTestingUtils.waitForUiThread(); Mockito.verify(observer1, Mockito.never()).receivedNotification(TEST_NOTIFICATION); } @@ -72,7 +73,7 @@ public void addObserverAndPublish() throws Exception { viewModel.subscribe(TEST_NOTIFICATION, observer1); viewModel.publish(TEST_NOTIFICATION, OBJECT_ARRAY_FOR_NOTIFICATION); - waitForUiThread(); + FxTestingUtils.waitForUiThread(); Mockito.verify(observer1).receivedNotification(TEST_NOTIFICATION, OBJECT_ARRAY_FOR_NOTIFICATION); } @@ -82,14 +83,14 @@ public void addAndRemoveObserverAndPublish() throws Exception { viewModel.unsubscribe(observer1); viewModel.publish(TEST_NOTIFICATION); - waitForUiThread(); + FxTestingUtils.waitForUiThread(); Mockito.verify(observer1, Mockito.never()).receivedNotification(TEST_NOTIFICATION); viewModel.subscribe(TEST_NOTIFICATION, observer1); viewModel.unsubscribe(TEST_NOTIFICATION, observer1); viewModel.publish(TEST_NOTIFICATION); - waitForUiThread(); + FxTestingUtils.waitForUiThread(); Mockito.verify(observer1, Mockito.never()).receivedNotification(TEST_NOTIFICATION); } @@ -100,7 +101,7 @@ public void addMultipleObserverAndPublish() throws Exception { viewModel.subscribe(TEST_NOTIFICATION, observer3); viewModel.publish(TEST_NOTIFICATION, OBJECT_ARRAY_FOR_NOTIFICATION); - waitForUiThread(); + FxTestingUtils.waitForUiThread(); Mockito.verify(observer1).receivedNotification(TEST_NOTIFICATION, OBJECT_ARRAY_FOR_NOTIFICATION); Mockito.verify(observer2).receivedNotification(TEST_NOTIFICATION, OBJECT_ARRAY_FOR_NOTIFICATION); @@ -116,7 +117,7 @@ public void addMultipleObserverAndRemoveOneAndPublish() throws Exception { viewModel.unsubscribe(observer1); viewModel.publish(TEST_NOTIFICATION, OBJECT_ARRAY_FOR_NOTIFICATION); - waitForUiThread(); + FxTestingUtils.waitForUiThread(); Mockito.verify(observer1, Mockito.never()).receivedNotification(TEST_NOTIFICATION, OBJECT_ARRAY_FOR_NOTIFICATION); @@ -136,20 +137,7 @@ public void removeObserverThatWasNotRegisteredYet() { viewModel.unsubscribe(TEST_NOTIFICATION, observer1); } - - /** - * This method is used to wait until the UI thread has done all work that was queued via - * {@link Platform#runLater(Runnable)}. - */ - private void waitForUiThread() { - CompletableFuture future = new CompletableFuture<>(); - Platform.runLater(() -> future.complete(null)); - try { - future.get(1l, TimeUnit.SECONDS); - } catch (InterruptedException | ExecutionException | TimeoutException e) { - throw new IllegalStateException(e); - } - } + private class DummyNotificationObserver implements NotificationObserver { @Override diff --git a/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/notifications/viewmodel/ViewModelWithoutUiThreadTest.java b/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/notifications/viewmodel/ViewModelWithoutUiThreadTest.java index 52385d1e9..b5e751fa9 100644 --- a/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/notifications/viewmodel/ViewModelWithoutUiThreadTest.java +++ b/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/notifications/viewmodel/ViewModelWithoutUiThreadTest.java @@ -16,7 +16,7 @@ package de.saxsys.mvvmfx.utils.notifications.viewmodel; import de.saxsys.mvvmfx.ViewModel; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; diff --git a/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/notifications/viewmodel/WeakNotificationsTest.java b/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/notifications/viewmodel/WeakNotificationsTest.java index deb36f076..dcb5907dd 100644 --- a/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/notifications/viewmodel/WeakNotificationsTest.java +++ b/mvvmfx/src/test/java/de/saxsys/mvvmfx/utils/notifications/viewmodel/WeakNotificationsTest.java @@ -1,10 +1,8 @@ package de.saxsys.mvvmfx.utils.notifications.viewmodel; -import de.saxsys.mvvmfx.testingutils.jfxrunner.JfxRunner; import de.saxsys.mvvmfx.utils.notifications.*; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.mockito.Mockito; /** @@ -12,7 +10,6 @@ * To do this most test cases of {@link DefaultNotificationCenterTest} * are reproduced with the weak variant of notifications. */ -@RunWith(JfxRunner.class) public class WeakNotificationsTest { @@ -26,7 +23,7 @@ public class WeakNotificationsTest { NotificationObserver observer2; NotificationObserver observer3; - @Before + @BeforeEach public void init() { observer1 = Mockito.mock(NotificationObserver.class); observer2 = Mockito.mock(NotificationObserver.class); From 69f75bd5c8dcde0d4167f69ab141fbc71de9fc32 Mon Sep 17 00:00:00 2001 From: gbalderas Date: Tue, 22 Aug 2017 17:26:53 +0200 Subject: [PATCH 31/51] migrate mvvmfx-cdi test to JUnit5 --- mvvmfx-cdi/pom.xml | 4 ++-- .../test/java/de/saxsys/mvvmfx/cdi/it/IntegrationTest.java | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/mvvmfx-cdi/pom.xml b/mvvmfx-cdi/pom.xml index 41f9d9a1b..fde5743ee 100644 --- a/mvvmfx-cdi/pom.xml +++ b/mvvmfx-cdi/pom.xml @@ -75,8 +75,8 @@ test - junit - junit + org.junit.jupiter + junit-jupiter-api test diff --git a/mvvmfx-cdi/src/test/java/de/saxsys/mvvmfx/cdi/it/IntegrationTest.java b/mvvmfx-cdi/src/test/java/de/saxsys/mvvmfx/cdi/it/IntegrationTest.java index 205a360ce..b6d3395e5 100644 --- a/mvvmfx-cdi/src/test/java/de/saxsys/mvvmfx/cdi/it/IntegrationTest.java +++ b/mvvmfx-cdi/src/test/java/de/saxsys/mvvmfx/cdi/it/IntegrationTest.java @@ -3,12 +3,12 @@ import static org.assertj.core.api.Assertions.assertThat; import javafx.application.Application; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; public class IntegrationTest { - @Before + @BeforeEach public void setup() { MyApp.wasPostConstructCalled = false; MyApp.wasPreDestroyCalled = false; From d0d0caba928b536540b8ca1d6ad923a4f3b6cbd8 Mon Sep 17 00:00:00 2001 From: gbalderas Date: Tue, 22 Aug 2017 17:30:49 +0200 Subject: [PATCH 32/51] migrate mvvmfx-guice tests to JUnit5 --- mvvmfx-guice/pom.xml | 4 ++-- .../de/saxsys/mvvmfx/guice/DuplicateInjectionBugTest.java | 2 +- .../mvvmfx/guice/interceptiontest/InterceptorTest.java | 2 +- .../java/de/saxsys/mvvmfx/guice/it/IntegrationTest.java | 6 +++--- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/mvvmfx-guice/pom.xml b/mvvmfx-guice/pom.xml index fa5dc8864..18a8c7fa1 100644 --- a/mvvmfx-guice/pom.xml +++ b/mvvmfx-guice/pom.xml @@ -63,8 +63,8 @@ test - junit - junit + org.junit.jupiter + junit-jupiter-api test diff --git a/mvvmfx-guice/src/test/java/de/saxsys/mvvmfx/guice/DuplicateInjectionBugTest.java b/mvvmfx-guice/src/test/java/de/saxsys/mvvmfx/guice/DuplicateInjectionBugTest.java index 4c4703ad2..f35bed8bc 100644 --- a/mvvmfx-guice/src/test/java/de/saxsys/mvvmfx/guice/DuplicateInjectionBugTest.java +++ b/mvvmfx-guice/src/test/java/de/saxsys/mvvmfx/guice/DuplicateInjectionBugTest.java @@ -7,7 +7,7 @@ import javax.inject.Inject; -import org.junit.Test; +import org.junit.jupiter.api.Test; /** * This test is used to reproduce a bug in the mvvmfx-guice module. A class that is injected into the application is diff --git a/mvvmfx-guice/src/test/java/de/saxsys/mvvmfx/guice/interceptiontest/InterceptorTest.java b/mvvmfx-guice/src/test/java/de/saxsys/mvvmfx/guice/interceptiontest/InterceptorTest.java index af1e31f23..b1171ae7f 100644 --- a/mvvmfx-guice/src/test/java/de/saxsys/mvvmfx/guice/interceptiontest/InterceptorTest.java +++ b/mvvmfx-guice/src/test/java/de/saxsys/mvvmfx/guice/interceptiontest/InterceptorTest.java @@ -1,7 +1,7 @@ package de.saxsys.mvvmfx.guice.interceptiontest; -import org.junit.Test; +import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; diff --git a/mvvmfx-guice/src/test/java/de/saxsys/mvvmfx/guice/it/IntegrationTest.java b/mvvmfx-guice/src/test/java/de/saxsys/mvvmfx/guice/it/IntegrationTest.java index ef42fb349..340af78f8 100644 --- a/mvvmfx-guice/src/test/java/de/saxsys/mvvmfx/guice/it/IntegrationTest.java +++ b/mvvmfx-guice/src/test/java/de/saxsys/mvvmfx/guice/it/IntegrationTest.java @@ -1,14 +1,14 @@ package de.saxsys.mvvmfx.guice.it; import de.saxsys.mvvmfx.guice.internal.GuiceInjector; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; public class IntegrationTest { - @Before + @BeforeEach public void setup() { MyApp.wasInitCalled = false; MyApp.wasStopCalled = false; From 48e9b661d72c331574e375bb23ea0bad14d04761 Mon Sep 17 00:00:00 2001 From: gbalderas Date: Tue, 22 Aug 2017 17:34:17 +0200 Subject: [PATCH 33/51] migrate GCVerifierTest to JUnit5 --- .../test/java/de/saxsys/mvvmfx/testingutils/GCVerifierTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mvvmfx-testing-utils/src/test/java/de/saxsys/mvvmfx/testingutils/GCVerifierTest.java b/mvvmfx-testing-utils/src/test/java/de/saxsys/mvvmfx/testingutils/GCVerifierTest.java index d3ee95fd0..5a298c934 100644 --- a/mvvmfx-testing-utils/src/test/java/de/saxsys/mvvmfx/testingutils/GCVerifierTest.java +++ b/mvvmfx-testing-utils/src/test/java/de/saxsys/mvvmfx/testingutils/GCVerifierTest.java @@ -1,6 +1,6 @@ package de.saxsys.mvvmfx.testingutils; -import org.junit.Test; +import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.fail; From bed0f2b7b9166b82dc0930666a9420b5819f9743 Mon Sep 17 00:00:00 2001 From: gbalderas Date: Tue, 22 Aug 2017 17:36:03 +0200 Subject: [PATCH 34/51] migrate mvvmfx-utils tests to JUnit5 --- mvvmfx-utils/pom.xml | 4 ++-- .../mvvmfx/utils/listener/ListenerManagerTest.java | 6 +++--- .../utils/sizebinding/BindSizeToControlTest.java | 6 +++--- .../utils/sizebinding/BindSizeToImageViewTest.java | 6 +++--- .../utils/sizebinding/BindSizeToRectangleTest.java | 6 +++--- .../mvvmfx/utils/sizebinding/BindSizeToRegionTest.java | 6 +++--- .../utils/sizebinding/SizeBindingsBuilderTestBase.java | 10 +++++----- .../mvvmfx/utils/sizebinding/UnbindHeightTest.java | 6 +++--- .../mvvmfx/utils/sizebinding/UnbindSizeTest.java | 6 +++--- .../mvvmfx/utils/sizebinding/UnbindWidthTest.java | 6 +++--- 10 files changed, 31 insertions(+), 31 deletions(-) diff --git a/mvvmfx-utils/pom.xml b/mvvmfx-utils/pom.xml index 3a597e38b..a7a6c4de9 100644 --- a/mvvmfx-utils/pom.xml +++ b/mvvmfx-utils/pom.xml @@ -30,8 +30,8 @@ - junit - junit + org.junit.jupiter + junit-jupiter-api test diff --git a/mvvmfx-utils/src/test/java/de/saxsys/mvvmfx/utils/listener/ListenerManagerTest.java b/mvvmfx-utils/src/test/java/de/saxsys/mvvmfx/utils/listener/ListenerManagerTest.java index 8a542641c..9e190a4c2 100644 --- a/mvvmfx-utils/src/test/java/de/saxsys/mvvmfx/utils/listener/ListenerManagerTest.java +++ b/mvvmfx-utils/src/test/java/de/saxsys/mvvmfx/utils/listener/ListenerManagerTest.java @@ -25,8 +25,8 @@ import javafx.beans.value.ObservableValue; import javafx.collections.FXCollections; import javafx.collections.ListChangeListener; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import static org.mockito.Matchers.any; import static org.mockito.Mockito.*; @@ -53,7 +53,7 @@ public class ListenerManagerTest { private ListenerManager manager; - @Before + @BeforeEach public void setup() { manager = new ListenerManager(); simpleListProperty.set(FXCollections. observableArrayList()); diff --git a/mvvmfx-utils/src/test/java/de/saxsys/mvvmfx/utils/sizebinding/BindSizeToControlTest.java b/mvvmfx-utils/src/test/java/de/saxsys/mvvmfx/utils/sizebinding/BindSizeToControlTest.java index 59eb84a45..c4184e322 100644 --- a/mvvmfx-utils/src/test/java/de/saxsys/mvvmfx/utils/sizebinding/BindSizeToControlTest.java +++ b/mvvmfx-utils/src/test/java/de/saxsys/mvvmfx/utils/sizebinding/BindSizeToControlTest.java @@ -20,15 +20,15 @@ import javafx.scene.control.Control; import javafx.scene.control.ScrollPane; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; public class BindSizeToControlTest extends SizeBindingsBuilderTestBase { private Control toControl; - @Before + @BeforeEach public void setUp() { toControl = new ScrollPane(); } diff --git a/mvvmfx-utils/src/test/java/de/saxsys/mvvmfx/utils/sizebinding/BindSizeToImageViewTest.java b/mvvmfx-utils/src/test/java/de/saxsys/mvvmfx/utils/sizebinding/BindSizeToImageViewTest.java index 983da976c..6560dcb84 100644 --- a/mvvmfx-utils/src/test/java/de/saxsys/mvvmfx/utils/sizebinding/BindSizeToImageViewTest.java +++ b/mvvmfx-utils/src/test/java/de/saxsys/mvvmfx/utils/sizebinding/BindSizeToImageViewTest.java @@ -19,14 +19,14 @@ import javafx.scene.image.ImageView; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; public class BindSizeToImageViewTest extends SizeBindingsBuilderTestBase { private ImageView toImageView; - @Before + @BeforeEach public void setUp() { toImageView = new ImageView(); } diff --git a/mvvmfx-utils/src/test/java/de/saxsys/mvvmfx/utils/sizebinding/BindSizeToRectangleTest.java b/mvvmfx-utils/src/test/java/de/saxsys/mvvmfx/utils/sizebinding/BindSizeToRectangleTest.java index 3745fd323..3519a0f5e 100644 --- a/mvvmfx-utils/src/test/java/de/saxsys/mvvmfx/utils/sizebinding/BindSizeToRectangleTest.java +++ b/mvvmfx-utils/src/test/java/de/saxsys/mvvmfx/utils/sizebinding/BindSizeToRectangleTest.java @@ -19,8 +19,8 @@ import javafx.scene.shape.Rectangle; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; public class BindSizeToRectangleTest extends SizeBindingsBuilderTestBase { @@ -30,7 +30,7 @@ public class BindSizeToRectangleTest extends SizeBindingsBuilderTestBase { /** * Create elements which will bind to each other. */ - @Before + @BeforeEach public void setUp() { toRectangle = new Rectangle(); } diff --git a/mvvmfx-utils/src/test/java/de/saxsys/mvvmfx/utils/sizebinding/BindSizeToRegionTest.java b/mvvmfx-utils/src/test/java/de/saxsys/mvvmfx/utils/sizebinding/BindSizeToRegionTest.java index 0983f9789..6beedd19b 100644 --- a/mvvmfx-utils/src/test/java/de/saxsys/mvvmfx/utils/sizebinding/BindSizeToRegionTest.java +++ b/mvvmfx-utils/src/test/java/de/saxsys/mvvmfx/utils/sizebinding/BindSizeToRegionTest.java @@ -19,15 +19,15 @@ import javafx.scene.layout.Region; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; public class BindSizeToRegionTest extends SizeBindingsBuilderTestBase { private Region toRegion; - @Before + @BeforeEach public void setUp() { toRegion = new Region(); } diff --git a/mvvmfx-utils/src/test/java/de/saxsys/mvvmfx/utils/sizebinding/SizeBindingsBuilderTestBase.java b/mvvmfx-utils/src/test/java/de/saxsys/mvvmfx/utils/sizebinding/SizeBindingsBuilderTestBase.java index eafd8ad5a..a852c0ccb 100644 --- a/mvvmfx-utils/src/test/java/de/saxsys/mvvmfx/utils/sizebinding/SizeBindingsBuilderTestBase.java +++ b/mvvmfx-utils/src/test/java/de/saxsys/mvvmfx/utils/sizebinding/SizeBindingsBuilderTestBase.java @@ -17,7 +17,7 @@ import static org.assertj.core.api.Assertions.assertThat; -import de.saxsys.mvvmfx.testingutils.jfxrunner.JfxRunner; +import de.saxsys.mvvmfx.testingutils.JfxToolkitExtension; import javafx.beans.property.ReadOnlyDoubleWrapper; import javafx.scene.control.Control; import javafx.scene.control.ScrollPane; @@ -25,12 +25,12 @@ import javafx.scene.layout.Region; import javafx.scene.shape.Rectangle; -import org.junit.Before; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.internal.util.reflection.Whitebox; -@RunWith(JfxRunner.class) +@ExtendWith(JfxToolkitExtension.class) public abstract class SizeBindingsBuilderTestBase { protected static final double SIZEVAL = 100d; @@ -43,7 +43,7 @@ public abstract class SizeBindingsBuilderTestBase { protected ImageView fromImageView; - @Before + @BeforeEach public void setup() { fromRegion = new Region(); mockSize(fromRegion); diff --git a/mvvmfx-utils/src/test/java/de/saxsys/mvvmfx/utils/sizebinding/UnbindHeightTest.java b/mvvmfx-utils/src/test/java/de/saxsys/mvvmfx/utils/sizebinding/UnbindHeightTest.java index 2eaf2cc82..91f896ab6 100644 --- a/mvvmfx-utils/src/test/java/de/saxsys/mvvmfx/utils/sizebinding/UnbindHeightTest.java +++ b/mvvmfx-utils/src/test/java/de/saxsys/mvvmfx/utils/sizebinding/UnbindHeightTest.java @@ -24,8 +24,8 @@ import javafx.scene.layout.Region; import javafx.scene.shape.Rectangle; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; public class UnbindHeightTest extends SizeBindingsBuilderTestBase { @@ -35,7 +35,7 @@ public class UnbindHeightTest extends SizeBindingsBuilderTestBase { private Region targetRegion; - @Before + @BeforeEach public void setUp() { targetImageView = new ImageView(); targetControl = new ScrollPane(); diff --git a/mvvmfx-utils/src/test/java/de/saxsys/mvvmfx/utils/sizebinding/UnbindSizeTest.java b/mvvmfx-utils/src/test/java/de/saxsys/mvvmfx/utils/sizebinding/UnbindSizeTest.java index d60cd0700..eaffe5e13 100644 --- a/mvvmfx-utils/src/test/java/de/saxsys/mvvmfx/utils/sizebinding/UnbindSizeTest.java +++ b/mvvmfx-utils/src/test/java/de/saxsys/mvvmfx/utils/sizebinding/UnbindSizeTest.java @@ -20,8 +20,8 @@ import javafx.scene.image.ImageView; import javafx.scene.layout.Region; import javafx.scene.shape.Rectangle; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import static de.saxsys.mvvmfx.utils.sizebinding.SizeBindingsBuilder.bindSize; import static de.saxsys.mvvmfx.utils.sizebinding.SizeBindingsBuilder.unbindSize; @@ -35,7 +35,7 @@ public class UnbindSizeTest extends SizeBindingsBuilderTestBase { private Region targetRegion; - @Before + @BeforeEach public void setUp() { targetImageView = new ImageView(); targetControl = new ScrollPane(); diff --git a/mvvmfx-utils/src/test/java/de/saxsys/mvvmfx/utils/sizebinding/UnbindWidthTest.java b/mvvmfx-utils/src/test/java/de/saxsys/mvvmfx/utils/sizebinding/UnbindWidthTest.java index 2fa7c3edd..a6c015717 100644 --- a/mvvmfx-utils/src/test/java/de/saxsys/mvvmfx/utils/sizebinding/UnbindWidthTest.java +++ b/mvvmfx-utils/src/test/java/de/saxsys/mvvmfx/utils/sizebinding/UnbindWidthTest.java @@ -24,8 +24,8 @@ import javafx.scene.layout.Region; import javafx.scene.shape.Rectangle; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; public class UnbindWidthTest extends SizeBindingsBuilderTestBase { @@ -37,7 +37,7 @@ public class UnbindWidthTest extends SizeBindingsBuilderTestBase { private Region targetRegion; - @Before + @BeforeEach public void setUp() { targetImageView = new ImageView(); targetControl = new ScrollPane(); From 942ec98f54000e6b64c1aa7be2230b9651022e09 Mon Sep 17 00:00:00 2001 From: gbalderas Date: Tue, 22 Aug 2017 17:38:26 +0200 Subject: [PATCH 35/51] migrate mvvmfx-validation tests to JUnit5 --- mvvmfx-validation/pom.xml | 4 ++-- .../mvvmfx/utils/validation/CompositeValidatorTest.java | 6 +++--- .../utils/validation/CustomValidationMessageTest.java | 2 +- .../mvvmfx/utils/validation/FunctionBasedValidatorTest.java | 2 +- .../mvvmfx/utils/validation/HighestMessageBugTest.java | 6 +++--- .../saxsys/mvvmfx/utils/validation/ObservableRulesTest.java | 2 +- .../utils/validation/ObservableRulesValidatorTest.java | 6 +++--- .../mvvmfx/utils/validation/ValidationMessageTest.java | 2 +- .../mvvmfx/utils/validation/ValidationStatusTest.java | 2 +- 9 files changed, 16 insertions(+), 16 deletions(-) diff --git a/mvvmfx-validation/pom.xml b/mvvmfx-validation/pom.xml index 5478accd1..9527c7d76 100644 --- a/mvvmfx-validation/pom.xml +++ b/mvvmfx-validation/pom.xml @@ -49,8 +49,8 @@ test - junit - junit + org.junit.jupiter + junit-jupiter-api test diff --git a/mvvmfx-validation/src/test/java/de/saxsys/mvvmfx/utils/validation/CompositeValidatorTest.java b/mvvmfx-validation/src/test/java/de/saxsys/mvvmfx/utils/validation/CompositeValidatorTest.java index ac533bd5f..6ad27b75c 100644 --- a/mvvmfx-validation/src/test/java/de/saxsys/mvvmfx/utils/validation/CompositeValidatorTest.java +++ b/mvvmfx-validation/src/test/java/de/saxsys/mvvmfx/utils/validation/CompositeValidatorTest.java @@ -19,8 +19,8 @@ import javafx.beans.binding.Bindings; import javafx.beans.binding.IntegerBinding; import javafx.beans.property.*; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.util.List; import java.util.function.Predicate; @@ -41,7 +41,7 @@ public class CompositeValidatorTest { private ObservableRuleBasedValidator validator1; private ObservableRuleBasedValidator validator2; - @Before + @BeforeEach public void setup() { validator1 = new ObservableRuleBasedValidator(); validator1.addRule(valid1, ValidationMessage.error("error1")); diff --git a/mvvmfx-validation/src/test/java/de/saxsys/mvvmfx/utils/validation/CustomValidationMessageTest.java b/mvvmfx-validation/src/test/java/de/saxsys/mvvmfx/utils/validation/CustomValidationMessageTest.java index 58bda73f7..2a2cb7d0a 100644 --- a/mvvmfx-validation/src/test/java/de/saxsys/mvvmfx/utils/validation/CustomValidationMessageTest.java +++ b/mvvmfx-validation/src/test/java/de/saxsys/mvvmfx/utils/validation/CustomValidationMessageTest.java @@ -3,7 +3,7 @@ import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; import javafx.collections.ObservableList; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.function.Predicate; diff --git a/mvvmfx-validation/src/test/java/de/saxsys/mvvmfx/utils/validation/FunctionBasedValidatorTest.java b/mvvmfx-validation/src/test/java/de/saxsys/mvvmfx/utils/validation/FunctionBasedValidatorTest.java index 17fbc3a0e..57ade2205 100644 --- a/mvvmfx-validation/src/test/java/de/saxsys/mvvmfx/utils/validation/FunctionBasedValidatorTest.java +++ b/mvvmfx-validation/src/test/java/de/saxsys/mvvmfx/utils/validation/FunctionBasedValidatorTest.java @@ -17,7 +17,7 @@ import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.function.Function; import java.util.function.Predicate; diff --git a/mvvmfx-validation/src/test/java/de/saxsys/mvvmfx/utils/validation/HighestMessageBugTest.java b/mvvmfx-validation/src/test/java/de/saxsys/mvvmfx/utils/validation/HighestMessageBugTest.java index 6b97518d1..cd0825483 100644 --- a/mvvmfx-validation/src/test/java/de/saxsys/mvvmfx/utils/validation/HighestMessageBugTest.java +++ b/mvvmfx-validation/src/test/java/de/saxsys/mvvmfx/utils/validation/HighestMessageBugTest.java @@ -24,8 +24,8 @@ import javafx.beans.property.StringProperty; import javafx.collections.ListChangeListener; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; /** * This test case reproduces the bug #264 @@ -40,7 +40,7 @@ public class HighestMessageBugTest { private ValidationStatus validationStatus; - @Before + @BeforeEach public void setUp() throws Exception { value = new SimpleStringProperty(""); validator = new FunctionBasedValidator<>(value, v -> v != null, ValidationMessage.error("error")); diff --git a/mvvmfx-validation/src/test/java/de/saxsys/mvvmfx/utils/validation/ObservableRulesTest.java b/mvvmfx-validation/src/test/java/de/saxsys/mvvmfx/utils/validation/ObservableRulesTest.java index 758f6589f..11ef17931 100644 --- a/mvvmfx-validation/src/test/java/de/saxsys/mvvmfx/utils/validation/ObservableRulesTest.java +++ b/mvvmfx-validation/src/test/java/de/saxsys/mvvmfx/utils/validation/ObservableRulesTest.java @@ -18,7 +18,7 @@ import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; import javafx.beans.value.ObservableBooleanValue; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.regex.Pattern; diff --git a/mvvmfx-validation/src/test/java/de/saxsys/mvvmfx/utils/validation/ObservableRulesValidatorTest.java b/mvvmfx-validation/src/test/java/de/saxsys/mvvmfx/utils/validation/ObservableRulesValidatorTest.java index 8f617f5e7..ac3e86286 100644 --- a/mvvmfx-validation/src/test/java/de/saxsys/mvvmfx/utils/validation/ObservableRulesValidatorTest.java +++ b/mvvmfx-validation/src/test/java/de/saxsys/mvvmfx/utils/validation/ObservableRulesValidatorTest.java @@ -17,8 +17,8 @@ import de.saxsys.mvvmfx.testingutils.GCVerifier; import javafx.beans.property.*; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.util.regex.Pattern; @@ -33,7 +33,7 @@ public class ObservableRulesValidatorTest { private ObservableRuleBasedValidator validator; - @Before + @BeforeEach public void setup() { rule1 = new SimpleBooleanProperty(); rule2 = new SimpleBooleanProperty(); diff --git a/mvvmfx-validation/src/test/java/de/saxsys/mvvmfx/utils/validation/ValidationMessageTest.java b/mvvmfx-validation/src/test/java/de/saxsys/mvvmfx/utils/validation/ValidationMessageTest.java index d0e7abd12..b6d5ccbb0 100644 --- a/mvvmfx-validation/src/test/java/de/saxsys/mvvmfx/utils/validation/ValidationMessageTest.java +++ b/mvvmfx-validation/src/test/java/de/saxsys/mvvmfx/utils/validation/ValidationMessageTest.java @@ -17,7 +17,7 @@ import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; -import org.junit.Test; +import org.junit.jupiter.api.Test; public class ValidationMessageTest { diff --git a/mvvmfx-validation/src/test/java/de/saxsys/mvvmfx/utils/validation/ValidationStatusTest.java b/mvvmfx-validation/src/test/java/de/saxsys/mvvmfx/utils/validation/ValidationStatusTest.java index 3c74b55cf..f654e5f1a 100644 --- a/mvvmfx-validation/src/test/java/de/saxsys/mvvmfx/utils/validation/ValidationStatusTest.java +++ b/mvvmfx-validation/src/test/java/de/saxsys/mvvmfx/utils/validation/ValidationStatusTest.java @@ -15,7 +15,7 @@ ******************************************************************************/ package de.saxsys.mvvmfx.utils.validation; -import org.junit.Test; +import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.fail; From 2359699380dbabf5be65672e9c7dfc833b4008e7 Mon Sep 17 00:00:00 2001 From: gbalderas Date: Tue, 22 Aug 2017 17:54:57 +0200 Subject: [PATCH 36/51] migrate examples tests to JUnit5 --- examples/books-example/pom.xml | 6 +++++- .../examples/books/MainViewModelTest.java | 6 +++--- .../books/backend/LibraryServiceMockTest.java | 6 +++--- examples/contacts-example/pom.xml | 4 ++-- .../mvvmfx/examples/contacts/AppTestFxIT.java | 10 +++++----- ....java => CountrySelectorInterfaceTest.java} | 18 +++++++++--------- .../DataFxCountrySelectorIntegrationTest.java | 17 ++++++++--------- .../DomCountrySelectorIntegrationTest.java | 15 +++++++-------- .../JAXBCountrySelectorIntegrationTest.java | 18 +++++++----------- .../validation/BirthdayValidatorTest.java | 6 +++--- .../validation/EmailAddressValidatorTest.java | 6 +++--- .../validation/PhoneNumberValidatorTest.java | 6 +++--- .../contacts/ui/about/AboutViewModelTest.java | 6 +++--- .../addressform/AddressFormViewModelTest.java | 6 +++--- .../ContactDialogViewModelTest.java | 6 +++--- .../ContactFormViewModelTest.java | 6 +++--- .../ui/detail/DetailViewModelTest.java | 6 +++--- .../EditContactDialogViewModelTest.java | 4 ++-- .../ui/master/MasterTableViewModelTest.java | 2 +- .../ui/master/MasterViewModelTest.java | 16 ++++++++-------- examples/mini-examples/fx-root-example/pom.xml | 4 ++++ .../fx_root_example/LabeledTextFieldTest.java | 6 +++--- examples/mini-examples/welcome-example/pom.xml | 4 ++-- .../personlogin/PersonLoginViewModelTest.java | 4 ++-- .../PersonWelcomeViewModelTest.java | 8 ++++---- 25 files changed, 99 insertions(+), 97 deletions(-) rename examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/countries/{AbstractCountrySelectorTest.java => CountrySelectorInterfaceTest.java} (86%) diff --git a/examples/books-example/pom.xml b/examples/books-example/pom.xml index 3ab531752..fb4100c4e 100644 --- a/examples/books-example/pom.xml +++ b/examples/books-example/pom.xml @@ -88,7 +88,11 @@ junit test - + + org.junit.jupiter + junit-jupiter-api + test + org.assertj assertj-core diff --git a/examples/books-example/src/test/java/de/saxsys/mvvmfx/examples/books/MainViewModelTest.java b/examples/books-example/src/test/java/de/saxsys/mvvmfx/examples/books/MainViewModelTest.java index 267f0f0a0..48ccf479c 100644 --- a/examples/books-example/src/test/java/de/saxsys/mvvmfx/examples/books/MainViewModelTest.java +++ b/examples/books-example/src/test/java/de/saxsys/mvvmfx/examples/books/MainViewModelTest.java @@ -3,8 +3,8 @@ import de.saxsys.mvvmfx.examples.books.backend.Error; import de.saxsys.mvvmfx.examples.books.backend.Book; import de.saxsys.mvvmfx.examples.books.backend.LibraryService; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.Collections; @@ -19,7 +19,7 @@ public class MainViewModelTest { private MainViewModel viewModel; private LibraryService libraryService; - @Before + @BeforeEach public void setup() { libraryService = mock(LibraryService.class); diff --git a/examples/books-example/src/test/java/de/saxsys/mvvmfx/examples/books/backend/LibraryServiceMockTest.java b/examples/books-example/src/test/java/de/saxsys/mvvmfx/examples/books/backend/LibraryServiceMockTest.java index 77d59fd05..4b926f4a3 100644 --- a/examples/books-example/src/test/java/de/saxsys/mvvmfx/examples/books/backend/LibraryServiceMockTest.java +++ b/examples/books-example/src/test/java/de/saxsys/mvvmfx/examples/books/backend/LibraryServiceMockTest.java @@ -1,7 +1,7 @@ package de.saxsys.mvvmfx.examples.books.backend; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.util.List; @@ -18,7 +18,7 @@ public class LibraryServiceMockTest { private Book theMetamorphosis; - @Before + @BeforeEach public void setup() { libraryService = new LibraryServiceMockImpl(); diff --git a/examples/contacts-example/pom.xml b/examples/contacts-example/pom.xml index 8cccfe23d..af99c72fd 100644 --- a/examples/contacts-example/pom.xml +++ b/examples/contacts-example/pom.xml @@ -93,8 +93,8 @@ - junit - junit + org.junit.jupiter + junit-jupiter-api test diff --git a/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/AppTestFxIT.java b/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/AppTestFxIT.java index f48d27b03..d1e8ef65f 100644 --- a/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/AppTestFxIT.java +++ b/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/AppTestFxIT.java @@ -1,19 +1,19 @@ package de.saxsys.mvvmfx.examples.contacts; import de.saxsys.mvvmfx.examples.contacts.App; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.BeforeEach; import org.testfx.api.FxRobot; import org.testfx.api.FxToolkit; import static org.testfx.api.FxAssert.verifyThat; import static org.testfx.matcher.control.TableViewMatchers.hasTableCell; -@Ignore +@Disabled public class AppTestFxIT extends FxRobot { - @Before + @BeforeEach public void setupApp() throws Exception { FxToolkit.registerPrimaryStage(); diff --git a/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/countries/AbstractCountrySelectorTest.java b/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/countries/CountrySelectorInterfaceTest.java similarity index 86% rename from examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/countries/AbstractCountrySelectorTest.java rename to examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/countries/CountrySelectorInterfaceTest.java index 90e89f9e5..aedd0e232 100644 --- a/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/countries/AbstractCountrySelectorTest.java +++ b/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/countries/CountrySelectorInterfaceTest.java @@ -5,7 +5,7 @@ import de.saxsys.mvvmfx.examples.contacts.model.countries.CountrySelector; import javafx.application.Platform; import org.assertj.core.api.Assertions; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.List; import java.util.concurrent.CompletableFuture; @@ -16,12 +16,12 @@ import static eu.lestard.assertj.javafx.api.Assertions.assertThat; -public abstract class AbstractCountrySelectorTest { +public interface CountrySelectorInterfaceTest { - protected abstract CountrySelector getCountrySelector(); + CountrySelector getCountrySelector(); @Test - public void testLoadSubdivisions() throws Exception { + default void testLoadSubdivisions() throws Exception { CountrySelector countrySelector = getCountrySelector(); runBlocked(countrySelector::init); @@ -47,7 +47,7 @@ public void testLoadSubdivisions() throws Exception { } @Test - public void testLoadCountries() throws InterruptedException, ExecutionException, TimeoutException { + default void testLoadCountries() throws InterruptedException, ExecutionException, TimeoutException { CountrySelector countrySelector = getCountrySelector(); runBlocked(countrySelector::init); @@ -86,7 +86,7 @@ public void testLoadCountries() throws InterruptedException, ExecutionException, assertThat(countrySelector.subdivisionLabel()).hasNullValue(); } - protected void runBlocked(Runnable function) { + default void runBlocked(Runnable function) { CompletableFuture blocker = new CompletableFuture<>(); getCountrySelector().inProgressProperty().addListener((obs, oldV, newV) -> { @@ -104,15 +104,15 @@ protected void runBlocked(Runnable function) { } } - protected Country getCountryByName(List countries, String name) { + default Country getCountryByName(List countries, String name) { return countries.stream().filter(country -> country.getName().equals(name)).findFirst().orElse(null); } - protected List getSubdivisionNames(List subdivisions) { + default List getSubdivisionNames(List subdivisions) { return subdivisions.stream().map(Subdivision::getName).collect(Collectors.toList()); } - protected List getCountryNames(List countries) { + default List getCountryNames(List countries) { return countries.stream().map(Country::getName).collect(Collectors.toList()); } } diff --git a/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/countries/DataFxCountrySelectorIntegrationTest.java b/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/countries/DataFxCountrySelectorIntegrationTest.java index 6ae671b9c..860173a30 100644 --- a/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/countries/DataFxCountrySelectorIntegrationTest.java +++ b/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/countries/DataFxCountrySelectorIntegrationTest.java @@ -1,11 +1,11 @@ package de.saxsys.mvvmfx.examples.contacts.model.countries; import de.saxsys.mvvmfx.examples.contacts.model.Country; -import de.saxsys.mvvmfx.testingutils.jfxrunner.JfxRunner; +import de.saxsys.mvvmfx.testingutils.JfxToolkitExtension; import org.datafx.reader.converter.XmlConverter; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import java.io.FileNotFoundException; import java.io.InputStream; @@ -13,18 +13,17 @@ import static org.assertj.core.api.Assertions.assertThat; -@RunWith(JfxRunner.class) -public class DataFxCountrySelectorIntegrationTest extends AbstractCountrySelectorTest { +@ExtendWith(JfxToolkitExtension.class) +public class DataFxCountrySelectorIntegrationTest implements CountrySelectorInterfaceTest { private CountrySelector countrySelector; - @Override - protected CountrySelector getCountrySelector() { + @Override public CountrySelector getCountrySelector() { return countrySelector; } - @Before + @BeforeEach public void setup() { countrySelector = new DataFxCountrySelector(); } diff --git a/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/countries/DomCountrySelectorIntegrationTest.java b/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/countries/DomCountrySelectorIntegrationTest.java index e117171d2..ee2c415e6 100644 --- a/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/countries/DomCountrySelectorIntegrationTest.java +++ b/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/countries/DomCountrySelectorIntegrationTest.java @@ -1,22 +1,21 @@ package de.saxsys.mvvmfx.examples.contacts.model.countries; -import de.saxsys.mvvmfx.testingutils.jfxrunner.JfxRunner; -import org.junit.Before; -import org.junit.runner.RunWith; +import de.saxsys.mvvmfx.testingutils.JfxToolkitExtension; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.ExtendWith; -@RunWith(JfxRunner.class) -public class DomCountrySelectorIntegrationTest extends AbstractCountrySelectorTest { +@ExtendWith(JfxToolkitExtension.class) +public class DomCountrySelectorIntegrationTest implements CountrySelectorInterfaceTest { private CountrySelector countrySelector; - @Before + @BeforeEach public void setup(){ countrySelector = new DomCountrySelector(); } - @Override - protected CountrySelector getCountrySelector() { + @Override public CountrySelector getCountrySelector() { return countrySelector; } } diff --git a/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/countries/JAXBCountrySelectorIntegrationTest.java b/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/countries/JAXBCountrySelectorIntegrationTest.java index 60132168a..9e2816239 100644 --- a/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/countries/JAXBCountrySelectorIntegrationTest.java +++ b/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/countries/JAXBCountrySelectorIntegrationTest.java @@ -1,24 +1,20 @@ package de.saxsys.mvvmfx.examples.contacts.model.countries; -import de.saxsys.mvvmfx.examples.contacts.model.countries.AbstractCountrySelectorTest; -import de.saxsys.mvvmfx.examples.contacts.model.countries.CountrySelector; -import de.saxsys.mvvmfx.examples.contacts.model.countries.JAXBCountrySelector; -import de.saxsys.mvvmfx.testingutils.jfxrunner.JfxRunner; -import org.junit.Before; -import org.junit.runner.RunWith; +import de.saxsys.mvvmfx.testingutils.JfxToolkitExtension; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.ExtendWith; -@RunWith(JfxRunner.class) -public class JAXBCountrySelectorIntegrationTest extends AbstractCountrySelectorTest { +@ExtendWith(JfxToolkitExtension.class) +public class JAXBCountrySelectorIntegrationTest implements CountrySelectorInterfaceTest { private CountrySelector countrySelector; - @Before + @BeforeEach public void setup() { countrySelector = new JAXBCountrySelector(); } - @Override - protected CountrySelector getCountrySelector() { + @Override public CountrySelector getCountrySelector() { return countrySelector; } } diff --git a/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/validation/BirthdayValidatorTest.java b/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/validation/BirthdayValidatorTest.java index 6c7f48358..ef906572a 100644 --- a/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/validation/BirthdayValidatorTest.java +++ b/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/validation/BirthdayValidatorTest.java @@ -13,8 +13,8 @@ import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleObjectProperty; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import de.saxsys.mvvmfx.examples.contacts.util.CentralClock; import de.saxsys.mvvmfx.utils.validation.ValidationStatus; @@ -24,7 +24,7 @@ public class BirthdayValidatorTest { private ValidationStatus result; private ObjectProperty value = new SimpleObjectProperty<>(); - @Before + @BeforeEach public void setup() { ZonedDateTime now = ZonedDateTime .of(LocalDate.of(2014, Month.JANUARY, 1), LocalTime.of(0, 0), ZoneId.systemDefault()); diff --git a/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/validation/EmailAddressValidatorTest.java b/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/validation/EmailAddressValidatorTest.java index dd3b7fbcf..34fada138 100644 --- a/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/validation/EmailAddressValidatorTest.java +++ b/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/validation/EmailAddressValidatorTest.java @@ -7,8 +7,8 @@ import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import de.saxsys.mvvmfx.utils.validation.ValidationStatus; @@ -17,7 +17,7 @@ public class EmailAddressValidatorTest { private ValidationStatus result; private StringProperty value = new SimpleStringProperty(); - @Before + @BeforeEach public void setup() { Validator validator = new EmailValidator(value); diff --git a/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/validation/PhoneNumberValidatorTest.java b/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/validation/PhoneNumberValidatorTest.java index cbb2e2242..deb4ed61f 100644 --- a/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/validation/PhoneNumberValidatorTest.java +++ b/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/model/validation/PhoneNumberValidatorTest.java @@ -7,8 +7,8 @@ import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import de.saxsys.mvvmfx.utils.validation.ValidationStatus; @@ -17,7 +17,7 @@ public class PhoneNumberValidatorTest { private ValidationStatus result; private StringProperty value = new SimpleStringProperty(); - @Before + @BeforeEach public void setup() { Validator validator = new PhoneValidator(value, "error message"); diff --git a/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/ui/about/AboutViewModelTest.java b/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/ui/about/AboutViewModelTest.java index b6a65472d..b32ace847 100644 --- a/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/ui/about/AboutViewModelTest.java +++ b/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/ui/about/AboutViewModelTest.java @@ -9,8 +9,8 @@ import javafx.beans.property.ReadOnlyStringProperty; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; public class AboutViewModelTest { @@ -25,7 +25,7 @@ public class AboutViewModelTest { private Consumer onLinkClickedHandler; @SuppressWarnings("unchecked") - @Before + @BeforeEach public void setup() { viewModel = new AboutViewModel(); diff --git a/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/ui/addressform/AddressFormViewModelTest.java b/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/ui/addressform/AddressFormViewModelTest.java index dc836333d..a8992e161 100644 --- a/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/ui/addressform/AddressFormViewModelTest.java +++ b/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/ui/addressform/AddressFormViewModelTest.java @@ -9,8 +9,8 @@ import javafx.beans.property.StringProperty; import javafx.collections.FXCollections; import javafx.collections.ObservableList; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.util.ListResourceBundle; import java.util.ResourceBundle; @@ -39,7 +39,7 @@ public class AddressFormViewModelTest { private ContactDialogScope scope; - @Before + @BeforeEach public void setup() { availableCountries.addAll(germany, austria); diff --git a/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/ui/contactdialog/ContactDialogViewModelTest.java b/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/ui/contactdialog/ContactDialogViewModelTest.java index 268dd6562..2bb9d18cc 100644 --- a/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/ui/contactdialog/ContactDialogViewModelTest.java +++ b/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/ui/contactdialog/ContactDialogViewModelTest.java @@ -2,8 +2,8 @@ import de.saxsys.mvvmfx.examples.contacts.ui.scopes.ContactDialogScope; import javafx.beans.property.BooleanProperty; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import static eu.lestard.assertj.javafx.api.Assertions.assertThat; @@ -16,7 +16,7 @@ public class ContactDialogViewModelTest { private BooleanProperty contactFormValid; private BooleanProperty addressFormValid; - @Before + @BeforeEach public void setup() { scope = new ContactDialogScope(); contactFormValid = scope.contactFormValidProperty(); diff --git a/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/ui/contactdialog/ContactFormViewModelTest.java b/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/ui/contactdialog/ContactFormViewModelTest.java index 6107d71c3..e27b7d1ab 100644 --- a/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/ui/contactdialog/ContactFormViewModelTest.java +++ b/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/ui/contactdialog/ContactFormViewModelTest.java @@ -2,8 +2,8 @@ import de.saxsys.mvvmfx.examples.contacts.ui.contactform.ContactFormViewModel; import de.saxsys.mvvmfx.testingutils.GCVerifier; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import static eu.lestard.assertj.javafx.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat; @@ -12,7 +12,7 @@ public class ContactFormViewModelTest { private ContactFormViewModel viewModel; - @Before + @BeforeEach public void setup() { viewModel = new ContactFormViewModel(); } diff --git a/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/ui/detail/DetailViewModelTest.java b/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/ui/detail/DetailViewModelTest.java index bad440ab4..a25f3f3ba 100644 --- a/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/ui/detail/DetailViewModelTest.java +++ b/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/ui/detail/DetailViewModelTest.java @@ -7,8 +7,8 @@ import java.time.LocalDate; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import de.saxsys.mvvmfx.examples.contacts.model.Contact; import de.saxsys.mvvmfx.examples.contacts.model.Repository; @@ -26,7 +26,7 @@ public class DetailViewModelTest { private Repository repository; - @Before + @BeforeEach public void setup() { MasterDetailScope masterViewModelMock = mock(MasterDetailScope.class); diff --git a/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/ui/editcontact/EditContactDialogViewModelTest.java b/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/ui/editcontact/EditContactDialogViewModelTest.java index d93ce97b7..285ed9e00 100644 --- a/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/ui/editcontact/EditContactDialogViewModelTest.java +++ b/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/ui/editcontact/EditContactDialogViewModelTest.java @@ -7,7 +7,7 @@ import java.util.ListResourceBundle; import java.util.ResourceBundle; -import org.junit.Before; +import org.junit.jupiter.api.BeforeEach; import de.saxsys.mvvmfx.examples.contacts.model.Repository; import de.saxsys.mvvmfx.examples.contacts.ui.contactdialog.ContactDialogViewModel; @@ -25,7 +25,7 @@ public class EditContactDialogViewModelTest { private ContactDialogScope scope; - @Before + @BeforeEach public void setup() { scope = new ContactDialogScope(); diff --git a/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/ui/master/MasterTableViewModelTest.java b/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/ui/master/MasterTableViewModelTest.java index f74bab81a..46bdc3625 100644 --- a/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/ui/master/MasterTableViewModelTest.java +++ b/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/ui/master/MasterTableViewModelTest.java @@ -8,7 +8,7 @@ import java.time.ZoneId; import java.time.ZonedDateTime; -import org.junit.Test; +import org.junit.jupiter.api.Test; import de.saxsys.mvvmfx.examples.contacts.model.Contact; import de.saxsys.mvvmfx.examples.contacts.util.CentralClock; diff --git a/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/ui/master/MasterViewModelTest.java b/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/ui/master/MasterViewModelTest.java index 6ab0f089c..329b18636 100644 --- a/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/ui/master/MasterViewModelTest.java +++ b/examples/contacts-example/src/test/java/de/saxsys/mvvmfx/examples/contacts/ui/master/MasterViewModelTest.java @@ -11,8 +11,8 @@ import java.util.function.Consumer; import java.util.stream.Collectors; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import de.saxsys.mvvmfx.examples.contacts.events.ContactsUpdatedEvent; import de.saxsys.mvvmfx.examples.contacts.model.Contact; @@ -34,7 +34,7 @@ public class MasterViewModelTest { private Consumer onSelectConsumer; - @Before + @BeforeEach public void setup() { repository = new InmemoryRepository(); viewModel = new MasterViewModel(); @@ -143,12 +143,12 @@ public void testUpdateContactListNoSelectionWhenSelectedItemIsRemoved() { * TableView. * * The TableView doesn't directly show instances of - * {@link de.saxsys.mvvmfx.examples.contacts.model.Contact} but instead + * {@link Contact} but instead * contains instances of - * {@link de.saxsys.mvvmfx.examples.contacts.ui.master.MasterTableViewModel}. + * {@link MasterTableViewModel}. * * Every - * {@link de.saxsys.mvvmfx.examples.contacts.ui.master.MasterTableViewModel} + * {@link MasterTableViewModel} * has an ID attribute corresponding to the ID of the contact that is shown. * This method extracts these IDs and returns them as List. This way we can * verify what Contacts are shown in the Table. @@ -160,8 +160,8 @@ private List getContactIdsInTable() { /** * Returns the - * {@link de.saxsys.mvvmfx.examples.contacts.ui.master.MasterTableViewModel} - * for the given {@link de.saxsys.mvvmfx.examples.contacts.model.Contact} + * {@link MasterTableViewModel} + * for the given {@link Contact} * from the contact list. */ private MasterTableViewModel findTableViewModelForContact(Contact contact) { diff --git a/examples/mini-examples/fx-root-example/pom.xml b/examples/mini-examples/fx-root-example/pom.xml index 47b123a2f..c295139ee 100644 --- a/examples/mini-examples/fx-root-example/pom.xml +++ b/examples/mini-examples/fx-root-example/pom.xml @@ -40,6 +40,10 @@ junit test + + org.junit.jupiter + junit-jupiter-api + org.loadui diff --git a/examples/mini-examples/fx-root-example/src/test/java/de/saxsys/mvvmfx/examples/fx_root_example/LabeledTextFieldTest.java b/examples/mini-examples/fx-root-example/src/test/java/de/saxsys/mvvmfx/examples/fx_root_example/LabeledTextFieldTest.java index 5edbb2217..6654ae188 100644 --- a/examples/mini-examples/fx-root-example/src/test/java/de/saxsys/mvvmfx/examples/fx_root_example/LabeledTextFieldTest.java +++ b/examples/mini-examples/fx-root-example/src/test/java/de/saxsys/mvvmfx/examples/fx_root_example/LabeledTextFieldTest.java @@ -2,14 +2,14 @@ import static eu.lestard.assertj.javafx.api.Assertions.assertThat; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; public class LabeledTextFieldTest { private LabeledTextFieldViewModel viewModel; - @Before + @BeforeEach public void setup() { viewModel = new LabeledTextFieldViewModel(); } diff --git a/examples/mini-examples/welcome-example/pom.xml b/examples/mini-examples/welcome-example/pom.xml index db7ddf4d0..e94737685 100644 --- a/examples/mini-examples/welcome-example/pom.xml +++ b/examples/mini-examples/welcome-example/pom.xml @@ -40,8 +40,8 @@ - junit - junit + org.junit.jupiter + junit-jupiter-api test diff --git a/examples/mini-examples/welcome-example/src/test/java/de/saxsys/mvvmfx/viewmodel/personlogin/PersonLoginViewModelTest.java b/examples/mini-examples/welcome-example/src/test/java/de/saxsys/mvvmfx/viewmodel/personlogin/PersonLoginViewModelTest.java index fbce7d537..83b7332a3 100644 --- a/examples/mini-examples/welcome-example/src/test/java/de/saxsys/mvvmfx/viewmodel/personlogin/PersonLoginViewModelTest.java +++ b/examples/mini-examples/welcome-example/src/test/java/de/saxsys/mvvmfx/viewmodel/personlogin/PersonLoginViewModelTest.java @@ -1,10 +1,10 @@ package de.saxsys.mvvmfx.viewmodel.personlogin; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import javafx.collections.ObservableList; -import org.junit.Test; +import org.junit.jupiter.api.Test; import de.saxsys.mvvmfx.examples.welcome.model.Repository; import de.saxsys.mvvmfx.examples.welcome.viewmodel.personlogin.PersonLoginViewModel; diff --git a/examples/mini-examples/welcome-example/src/test/java/de/saxsys/mvvmfx/viewmodel/personwelcome/PersonWelcomeViewModelTest.java b/examples/mini-examples/welcome-example/src/test/java/de/saxsys/mvvmfx/viewmodel/personwelcome/PersonWelcomeViewModelTest.java index 0728f1ba2..9f671c9cd 100644 --- a/examples/mini-examples/welcome-example/src/test/java/de/saxsys/mvvmfx/viewmodel/personwelcome/PersonWelcomeViewModelTest.java +++ b/examples/mini-examples/welcome-example/src/test/java/de/saxsys/mvvmfx/viewmodel/personwelcome/PersonWelcomeViewModelTest.java @@ -1,9 +1,9 @@ package de.saxsys.mvvmfx.viewmodel.personwelcome; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import de.saxsys.mvvmfx.examples.welcome.model.Gender; import de.saxsys.mvvmfx.examples.welcome.model.Person; @@ -15,7 +15,7 @@ public class PersonWelcomeViewModelTest { private Repository repository; private PersonWelcomeViewModel personWelcomeViewModel; - @Before + @BeforeEach public void setup() { // TODO: this should be mocked repository = new Repository(); From 958fb83b21eaead0683fc6654dccea41ffa393f8 Mon Sep 17 00:00:00 2001 From: gbalderas Date: Wed, 23 Aug 2017 13:09:51 +0200 Subject: [PATCH 37/51] update maven surefire plugin to work with JUnit5 --- mvvmfx-cdi/pom.xml | 2 +- mvvmfx-guice/pom.xml | 2 +- pom.xml | 42 +++++++++++++++++++++++++++++++++++++++++- 3 files changed, 43 insertions(+), 3 deletions(-) diff --git a/mvvmfx-cdi/pom.xml b/mvvmfx-cdi/pom.xml index fde5743ee..9c162ea6c 100644 --- a/mvvmfx-cdi/pom.xml +++ b/mvvmfx-cdi/pom.xml @@ -34,7 +34,7 @@ maven-surefire-plugin - 2.18.1 + 2.19.1 always diff --git a/mvvmfx-guice/pom.xml b/mvvmfx-guice/pom.xml index 18a8c7fa1..3083ed10a 100644 --- a/mvvmfx-guice/pom.xml +++ b/mvvmfx-guice/pom.xml @@ -33,7 +33,7 @@ maven-surefire-plugin - 2.18.1 + 2.19.1 always diff --git a/pom.xml b/pom.xml index 747ad4bb5..5cadfd63d 100644 --- a/pom.xml +++ b/pom.xml @@ -275,6 +275,29 @@ maven-gpg-plugin 1.1 + + org.apache.maven.plugins + maven-surefire-plugin + + 2.19.1 + + + org.junit.platform + junit-platform-surefire-provider + 1.0.0-RC2 + + + org.junit.jupiter + junit-jupiter-engine + 5.0.0-RC2 + + + org.junit.vintage + junit-vintage-engine + 4.12.0-RC2 + + + @@ -326,7 +349,7 @@ org.apache.maven.plugins maven-surefire-plugin - 2.16 + 2.19.1 @@ -335,6 +358,23 @@ + + + org.junit.platform + junit-platform-surefire-provider + 1.0.0-RC2 + + + org.junit.jupiter + junit-jupiter-engine + 5.0.0-RC2 + + + org.junit.vintage + junit-vintage-engine + 4.12.0-RC2 + + From 4de2afa8f0367706683df7c2bffbcea1c624edbc Mon Sep 17 00:00:00 2001 From: gbalderas Date: Fri, 25 Aug 2017 15:36:53 +0200 Subject: [PATCH 38/51] throw an exception if a viewmodel class is referenced as an fx:controller in a fxml --- .../internal/viewloader/FxmlViewLoader.java | 7 +++++++ .../FluentViewLoader_FxmlView_Test.java | 20 +++++++++++++++++++ .../TestFxmlViewModelAsController.java | 13 ++++++++++++ ...estFxmlViewModelAsControllerViewModel.java | 6 ++++++ ...mlViewModelAsControllerWithCustomPath.java | 8 ++++++++ ...elAsControllerWithCustomPathViewModel.java | 6 ++++++ .../TestFxmlViewModelAsController.fxml | 14 +++++++++++++ ...mlViewModelAsControllerWithCustomPath.fxml | 14 +++++++++++++ 8 files changed, 88 insertions(+) create mode 100644 mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewModelAsController.java create mode 100644 mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewModelAsControllerViewModel.java create mode 100644 mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewModelAsControllerWithCustomPath.java create mode 100644 mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewModelAsControllerWithCustomPathViewModel.java create mode 100644 mvvmfx/src/test/resources/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewModelAsController.fxml create mode 100644 mvvmfx/src/test/resources/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewModelAsControllerWithCustomPath.fxml diff --git a/mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/viewloader/FxmlViewLoader.java b/mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/viewloader/FxmlViewLoader.java index d84a05dc8..fbab6eb5a 100644 --- a/mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/viewloader/FxmlViewLoader.java +++ b/mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/viewloader/FxmlViewLoader.java @@ -164,6 +164,13 @@ public , ViewModelType extends Vi loader.load(); + //throw an exception if the fx:controller was of type ViewModel + Object controller = loader.getController(); + if (controller instanceof ViewModel) { + throw new IllegalStateException("A ViewModel class [" + controller.getClass().getCanonicalName() + "] was referenced in the FXML file ["+resource+"] as the fx:controller." + + " Instead a class that implements FxmlView has to be defined as the fx:controller in the FXML file."); + } + final ViewType loadedController = loader.getController(); final Parent loadedRoot = loader.getRoot(); diff --git a/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/FluentViewLoader_FxmlView_Test.java b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/FluentViewLoader_FxmlView_Test.java index a25acae3b..ef1ada970 100644 --- a/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/FluentViewLoader_FxmlView_Test.java +++ b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/FluentViewLoader_FxmlView_Test.java @@ -527,4 +527,24 @@ public void testLoadFxmlViewTupleWithCustomPath() throws IOException { assertThat(TestViewModel.instanceCounter).isEqualTo(1); assertThat(TestViewModel.wasInitialized).isTrue(); } + + @Test + public void testFxmlViewModelAsControllerException(){ + try { + ViewTuple load = FluentViewLoader + .fxmlView(TestFxmlViewModelAsController.class).load(); + } catch (IllegalStateException e){ + assertThat(e.getMessage()).contains("A ViewModel class").contains("was referenced in the FXML file").contains("as the fx:controller"); + } + } + + @Test + public void testFxmlViewModelAsControllerWithCustomPath(){ + ViewTuple load = FluentViewLoader + .fxmlView(TestFxmlViewModelAsControllerWithCustomPath.class).load(); + + //should work since the View points to another FXML through @FxmlPath + assertThat(load.getView()).isNotNull().isInstanceOf(VBox.class); + System.out.println("test"); + } } diff --git a/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewModelAsController.java b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewModelAsController.java new file mode 100644 index 000000000..c4f529dd9 --- /dev/null +++ b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewModelAsController.java @@ -0,0 +1,13 @@ +package de.saxsys.mvvmfx.internal.viewloader.example; + +import de.saxsys.mvvmfx.FxmlPath; +import de.saxsys.mvvmfx.FxmlView; +import de.saxsys.mvvmfx.InjectViewModel; +import de.saxsys.mvvmfx.internal.viewloader.View; + +public class TestFxmlViewModelAsController implements FxmlView { + + @InjectViewModel + TestFxmlViewModelAsControllerViewModel viewModel; + +} diff --git a/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewModelAsControllerViewModel.java b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewModelAsControllerViewModel.java new file mode 100644 index 000000000..b64c2b198 --- /dev/null +++ b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewModelAsControllerViewModel.java @@ -0,0 +1,6 @@ +package de.saxsys.mvvmfx.internal.viewloader.example; + +import de.saxsys.mvvmfx.ViewModel; + +public class TestFxmlViewModelAsControllerViewModel implements ViewModel { +} diff --git a/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewModelAsControllerWithCustomPath.java b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewModelAsControllerWithCustomPath.java new file mode 100644 index 000000000..ba7461b6a --- /dev/null +++ b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewModelAsControllerWithCustomPath.java @@ -0,0 +1,8 @@ +package de.saxsys.mvvmfx.internal.viewloader.example; + +import de.saxsys.mvvmfx.FxmlPath; +import de.saxsys.mvvmfx.FxmlView; + +@FxmlPath("/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewWithCustomPath.fxml") +public class TestFxmlViewModelAsControllerWithCustomPath implements FxmlView { +} diff --git a/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewModelAsControllerWithCustomPathViewModel.java b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewModelAsControllerWithCustomPathViewModel.java new file mode 100644 index 000000000..db50de7ee --- /dev/null +++ b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewModelAsControllerWithCustomPathViewModel.java @@ -0,0 +1,6 @@ +package de.saxsys.mvvmfx.internal.viewloader.example; + +import de.saxsys.mvvmfx.ViewModel; + +public class TestFxmlViewModelAsControllerWithCustomPathViewModel implements ViewModel{ +} diff --git a/mvvmfx/src/test/resources/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewModelAsController.fxml b/mvvmfx/src/test/resources/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewModelAsController.fxml new file mode 100644 index 000000000..68f5ce687 --- /dev/null +++ b/mvvmfx/src/test/resources/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewModelAsController.fxml @@ -0,0 +1,14 @@ + + + + + + + + + + + diff --git a/mvvmfx/src/test/resources/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewModelAsControllerWithCustomPath.fxml b/mvvmfx/src/test/resources/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewModelAsControllerWithCustomPath.fxml new file mode 100644 index 000000000..9c626ab24 --- /dev/null +++ b/mvvmfx/src/test/resources/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewModelAsControllerWithCustomPath.fxml @@ -0,0 +1,14 @@ + + + + + + + + + + + From 60e91e9786d8a0700c490312bb519ab8ebc64618 Mon Sep 17 00:00:00 2001 From: gbalderas Date: Fri, 25 Aug 2017 17:15:28 +0200 Subject: [PATCH 39/51] update test --- .../viewloader/FluentViewLoader_FxmlView_Test.java | 12 ++++++------ ...FxmlViewModelAsControllerWithCustomPathView.java} | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) rename mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/example/{TestFxmlViewModelAsControllerWithCustomPath.java => TestFxmlViewModelAsControllerWithCustomPathView.java} (51%) diff --git a/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/FluentViewLoader_FxmlView_Test.java b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/FluentViewLoader_FxmlView_Test.java index ef1ada970..eb9918059 100644 --- a/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/FluentViewLoader_FxmlView_Test.java +++ b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/FluentViewLoader_FxmlView_Test.java @@ -540,11 +540,11 @@ public void testFxmlViewModelAsControllerException(){ @Test public void testFxmlViewModelAsControllerWithCustomPath(){ - ViewTuple load = FluentViewLoader - .fxmlView(TestFxmlViewModelAsControllerWithCustomPath.class).load(); - - //should work since the View points to another FXML through @FxmlPath - assertThat(load.getView()).isNotNull().isInstanceOf(VBox.class); - System.out.println("test"); + try { + ViewTuple load = FluentViewLoader + .fxmlView(TestFxmlViewModelAsControllerWithCustomPathView.class).load(); + } catch (IllegalStateException e){ + assertThat(e.getMessage()).contains("A ViewModel class").contains("was referenced in the FXML file").contains("as the fx:controller"); + } } } diff --git a/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewModelAsControllerWithCustomPath.java b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewModelAsControllerWithCustomPathView.java similarity index 51% rename from mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewModelAsControllerWithCustomPath.java rename to mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewModelAsControllerWithCustomPathView.java index ba7461b6a..3ab9c1945 100644 --- a/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewModelAsControllerWithCustomPath.java +++ b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewModelAsControllerWithCustomPathView.java @@ -3,6 +3,6 @@ import de.saxsys.mvvmfx.FxmlPath; import de.saxsys.mvvmfx.FxmlView; -@FxmlPath("/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewWithCustomPath.fxml") -public class TestFxmlViewModelAsControllerWithCustomPath implements FxmlView { +@FxmlPath("/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewModelAsControllerWithCustomPath.fxml") +public class TestFxmlViewModelAsControllerWithCustomPathView implements FxmlView { } From 276303393de65902055c01567829a8a86563afd0 Mon Sep 17 00:00:00 2001 From: gbalderas Date: Tue, 29 Aug 2017 15:12:40 +0200 Subject: [PATCH 40/51] throw exception at the ControllerFactories. --- .../internal/viewloader/FxmlViewLoader.java | 26 +++++++--- .../FluentViewLoader_FxmlView_Test.java | 47 +++++++++++++++---- 2 files changed, 58 insertions(+), 15 deletions(-) diff --git a/mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/viewloader/FxmlViewLoader.java b/mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/viewloader/FxmlViewLoader.java index fbab6eb5a..838765aae 100644 --- a/mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/viewloader/FxmlViewLoader.java +++ b/mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/viewloader/FxmlViewLoader.java @@ -164,13 +164,6 @@ public , ViewModelType extends Vi loader.load(); - //throw an exception if the fx:controller was of type ViewModel - Object controller = loader.getController(); - if (controller instanceof ViewModel) { - throw new IllegalStateException("A ViewModel class [" + controller.getClass().getCanonicalName() + "] was referenced in the FXML file ["+resource+"] as the fx:controller." - + " Instead a class that implements FxmlView has to be defined as the fx:controller in the FXML file."); - } - final ViewType loadedController = loader.getController(); final Parent loadedRoot = loader.getRoot(); @@ -290,6 +283,15 @@ public DefaultControllerFactory(ResourceBundle resourceBundle, ContextImpl conte public Object call(Class type) { Object controller = DependencyInjector.getInstance().getInstanceOf(type); + //throw an exception if the fx:controller was of type ViewModel +// Object controller = loader.getController(); + if (controller instanceof ViewModel) { + throw new IllegalStateException("A ViewModel class [" + controller.getClass().getCanonicalName() + "] was referenced in the FXML file" +// + "["+resource+"]" + + " as the fx:controller." + + " Instead a class that implements FxmlView has to be defined as the fx:controller in the FXML file."); + } + if (controller instanceof View) { View codeBehind = (View) controller; @@ -399,6 +401,16 @@ public Object call(Class type) { handleInjection(codeBehind, resourceBundle, context, viewInSceneProperty); } + //throw an exception if the fx:controller was of type ViewModel + // Object controller = loader.getController(); + if (controller instanceof ViewModel) { + throw new IllegalStateException("A ViewModel class [" + controller.getClass().getCanonicalName() + "] was referenced in the FXML file" + // + "["+resource+"]" + + " as the fx:controller." + + " Instead a class that implements FxmlView has to be defined as the fx:controller in the FXML file."); + } + + return controller; } } diff --git a/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/FluentViewLoader_FxmlView_Test.java b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/FluentViewLoader_FxmlView_Test.java index eb9918059..049ba8b39 100644 --- a/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/FluentViewLoader_FxmlView_Test.java +++ b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/FluentViewLoader_FxmlView_Test.java @@ -531,20 +531,51 @@ public void testLoadFxmlViewTupleWithCustomPath() throws IOException { @Test public void testFxmlViewModelAsControllerException(){ try { - ViewTuple load = FluentViewLoader - .fxmlView(TestFxmlViewModelAsController.class).load(); - } catch (IllegalStateException e){ - assertThat(e.getMessage()).contains("A ViewModel class").contains("was referenced in the FXML file").contains("as the fx:controller"); + FluentViewLoader.fxmlView(TestFxmlViewModelAsController.class) + .load(); + } catch (RuntimeException e){ ; + assertThat(e.getCause().getCause().getMessage()) + .contains("A ViewModel class") + .contains("was referenced in the FXML file") + .contains("as the fx:controller"); + } + + //with ControllerFactoryWithCustomViewModel + try { + FluentViewLoader.fxmlView(TestFxmlViewModelAsController.class) + .viewModel(new TestFxmlViewModelAsControllerViewModel()) + .load(); + } catch (RuntimeException e){ ; + assertThat(e.getCause().getCause().getMessage()) + .contains("A ViewModel class") + .contains("was referenced in the FXML file") + .contains("as the fx:controller"); } } @Test public void testFxmlViewModelAsControllerWithCustomPath(){ try { - ViewTuple load = FluentViewLoader - .fxmlView(TestFxmlViewModelAsControllerWithCustomPathView.class).load(); - } catch (IllegalStateException e){ - assertThat(e.getMessage()).contains("A ViewModel class").contains("was referenced in the FXML file").contains("as the fx:controller"); + FluentViewLoader.fxmlView(TestFxmlViewModelAsControllerWithCustomPathView.class) + .load(); + } catch (RuntimeException e){ + assertThat(e.getCause().getCause().getMessage()) + .contains("A ViewModel class") + .contains("was referenced in the FXML file") + .contains("as the fx:controller"); } + + //with ControllerFactoryWithCustomViewModel + try { + FluentViewLoader.fxmlView(TestFxmlViewModelAsControllerWithCustomPathView.class) + .viewModel(new TestFxmlViewModelAsControllerWithCustomPathViewModel()) + .load(); + } catch (RuntimeException e){ + assertThat(e.getCause().getCause().getMessage()) + .contains("A ViewModel class") + .contains("was referenced in the FXML file") + .contains("as the fx:controller"); + } + } } From c5bc177bedde8c745d021e783e44124dd6ce704b Mon Sep 17 00:00:00 2001 From: Manuel Mauky Date: Mon, 4 Sep 2017 18:37:44 +0200 Subject: [PATCH 41/51] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index dcb96e94e..e1f56bfa2 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ Here we develop new features. This release is unstable and shouldn't be used in ### Get Help -If you need help you can use the forums on [Google Groups](https://groups.google.com/forum/#!forum/mvvmfx-dev) for asking questions and interacting with the mvvmFX developers. Additionally you can create issues, report bugs and add feature requests on the issue tracker at [github](https://github.com/sialcasa/mvvmFX/issues). +The best way to get help with mvvmFX is to either ask questions on StackOverflow using the [tag "mvvmfx"](https://stackoverflow.com/questions/tagged/mvvmfx) or to use our [Google Groups](https://groups.google.com/forum/#!forum/mvvmfx-dev) mailing list. Additionally you can create issues, report bugs and add feature requests on the issue tracker at [github](https://github.com/sialcasa/mvvmFX/issues). ### Links From e5bbfe5b85535b890469cf75bb98a8c9881f3cce Mon Sep 17 00:00:00 2001 From: Manuel Mauky Date: Mon, 4 Sep 2017 18:40:37 +0200 Subject: [PATCH 42/51] Update README.md --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index e1f56bfa2..12807877a 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,16 @@ ![image](http://www.buildpath.de/mvvm/mvvmfx.png) -__mvvm(fx)__ is an application framework which provides you necessary components to implement the [MVVM](../../wiki/MVVM "MVVM") pattern with JavaFX. +**mvvmFX** is an application framework which provides you necessary components to implement the [MVVM](../../wiki/MVVM "MVVM") pattern with JavaFX. -__MVVM__ is the enhanced version of the [Presentation Model](http://martinfowler.com/eaaDev/PresentationModel.html "Presentation Model") pattern and was created by Microsoft engineers for [WPF](http://msdn.microsoft.com/en-us/library/ms754130.aspx "WPF") . JavaFX and WPF does have similarities like Databinding and descriptive UI declaration (FXML/XAML). Because of this fact we adopt best practices of the development with the Microsoft technology. +**MVVM** is the enhanced version of the [Presentation Model](http://martinfowler.com/eaaDev/PresentationModel.html "Presentation Model") pattern and was created by Microsoft engineers for [WPF](http://msdn.microsoft.com/en-us/library/ms754130.aspx "WPF"). JavaFX and WPF does have similarities like data binding and descriptive UI declaration (FXML/XAML). Because of this fact we adopted best practices of the development with the Microsoft technology and introduced new helpers to support the development of applications with JavaFX and MVVM. [![Commercial Support](https://img.shields.io/badge/Commercial%20Support%20-by%20Saxonia%20Systems-brightgreen.svg)](http://goo.gl/forms/WVBG3SWHuL) [![Build Status](https://api.travis-ci.org/sialcasa/mvvmFX.svg?branch=develop)](https://travis-ci.org/sialcasa/mvvmFX) -###[Howto](../../wiki "Howto")### +### [Howto](../../wiki "Howto") -### Maven dependency### +### Maven dependency #### Stable Release From a7b0d37d2ab0981ac20b35d801f217122a8fa25e Mon Sep 17 00:00:00 2001 From: Manuel Mauky Date: Thu, 7 Sep 2017 16:20:13 +0200 Subject: [PATCH 43/51] Implement possibility to add custom builder factories --- .../main/java/de/saxsys/mvvmfx/MvvmFX.java | 20 +++++ .../internal/viewloader/FxmlViewLoader.java | 1 + .../viewloader/GlobalBuilderFactory.java | 64 ++++++++++++++++ .../builderfactory/BuilderFactoryTest.java | 76 +++++++++++++++++++ .../BuilderFactoryTestView.java | 17 +++++ .../BuilderFactoryTestViewModel.java | 7 ++ .../builderfactory/CustomTextField.java | 23 ++++++ .../BuilderFactoryTestView.fxml | 11 +++ 8 files changed, 219 insertions(+) create mode 100644 mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/viewloader/GlobalBuilderFactory.java create mode 100644 mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/builderfactory/BuilderFactoryTest.java create mode 100644 mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/builderfactory/BuilderFactoryTestView.java create mode 100644 mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/builderfactory/BuilderFactoryTestViewModel.java create mode 100644 mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/builderfactory/CustomTextField.java create mode 100644 mvvmfx/src/test/resources/de/saxsys/mvvmfx/internal/viewloader/builderfactory/BuilderFactoryTestView.fxml diff --git a/mvvmfx/src/main/java/de/saxsys/mvvmfx/MvvmFX.java b/mvvmfx/src/main/java/de/saxsys/mvvmfx/MvvmFX.java index c24ac6196..de8c86c4e 100644 --- a/mvvmfx/src/main/java/de/saxsys/mvvmfx/MvvmFX.java +++ b/mvvmfx/src/main/java/de/saxsys/mvvmfx/MvvmFX.java @@ -15,7 +15,9 @@ ******************************************************************************/ package de.saxsys.mvvmfx; +import de.saxsys.mvvmfx.internal.viewloader.GlobalBuilderFactory; import de.saxsys.mvvmfx.internal.viewloader.ResourceBundleManager; +import javafx.util.BuilderFactory; import javafx.util.Callback; import de.saxsys.mvvmfx.utils.notifications.NotificationCenter; @@ -65,4 +67,22 @@ public static void setCustomDependencyInjector(final Callback, Object> public static void setGlobalResourceBundle(ResourceBundle resourceBundle) { ResourceBundleManager.getInstance().setGlobalResourceBundle(resourceBundle); } + + /** + * Add a {@link BuilderFactory} to be used by mvvmFX. + *
    + * A {@link BuilderFactory} is used to enable custom controls that need special initialization to be used with FXML. + * MvvmFX can manage multiple builder factories. If you add multiple factories that can provide builders for the same type, + * the last added builder factory will be used. This way it's possible to "overwrite" a more abstract builder factory with a more specific + * factory. + *
    + * MvvmFX also takes care for handling the default {@link javafx.fxml.JavaFXBuilderFactory}. If no custom builder factory + * is able to provide a builder for a given type the default JavaFX builder factory will be used as last resort. + * This way you don't have to take care for standard JavaFX types in your builder factory. + * + * @param factory the builder factory + */ + public static void addGlobalBuilderFactory(BuilderFactory factory) { + GlobalBuilderFactory.getInstance().addBuilderFactory(factory); + } } diff --git a/mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/viewloader/FxmlViewLoader.java b/mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/viewloader/FxmlViewLoader.java index d84a05dc8..f3a2bac56 100644 --- a/mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/viewloader/FxmlViewLoader.java +++ b/mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/viewloader/FxmlViewLoader.java @@ -237,6 +237,7 @@ private FXMLLoader createFxmlLoader(String resource, ResourceBundle resourceBund fxmlLoader.setRoot(root); fxmlLoader.setResources(resourceBundle); fxmlLoader.setLocation(location); + fxmlLoader.setBuilderFactory(GlobalBuilderFactory.getInstance()); // when the user provides a viewModel but no codeBehind, we need to use // the custom controller factory. diff --git a/mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/viewloader/GlobalBuilderFactory.java b/mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/viewloader/GlobalBuilderFactory.java new file mode 100644 index 000000000..14164d398 --- /dev/null +++ b/mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/viewloader/GlobalBuilderFactory.java @@ -0,0 +1,64 @@ +package de.saxsys.mvvmfx.internal.viewloader; + +import java.util.ArrayList; +import java.util.List; +import java.util.ListIterator; + +import javafx.fxml.JavaFXBuilderFactory; +import javafx.util.Builder; +import javafx.util.BuilderFactory; + +/** + * A {@link BuilderFactory} that can manage multiple custom builder factories. + *
    + * This class is part of the internal API of mvvmFX and may be subject to changes. + * Don't use this class directly. Instead add new builder factories by using public API: + * {@link de.saxsys.mvvmfx.MvvmFX#addGlobalBuilderFactory(BuilderFactory)}. + */ +public class GlobalBuilderFactory implements BuilderFactory { + + private List factories = new ArrayList<>(); + + private static final GlobalBuilderFactory SINGLETON = new GlobalBuilderFactory(); + + private BuilderFactory defaultBuilderFactory = new JavaFXBuilderFactory(); + + private GlobalBuilderFactory() { + } + + public static GlobalBuilderFactory getInstance() { + return SINGLETON; + } + + @Override + public Builder getBuilder(Class type) { + // if no factories are defined yet, use the default factory. + // this is an optimization for performance reasons to prevent unnecessary iterator handling + // for a very common usage scenario + if(factories.isEmpty()) { + return defaultBuilderFactory.getBuilder(type); + } + + Builder builder = null; + + final ListIterator listIterator = factories.listIterator(factories.size()); + + // iterate builder list in reverse order + while(listIterator.hasPrevious() && builder == null) { + final BuilderFactory factory = listIterator.previous(); + + builder = factory.getBuilder(type); + } + + // if no custom builderFactory is suitable for this type, use the default one. + if(builder == null) { + builder = defaultBuilderFactory.getBuilder(type); + } + + return builder; + } + + public void addBuilderFactory(BuilderFactory factory) { + this.factories.add(factory); + } +} diff --git a/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/builderfactory/BuilderFactoryTest.java b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/builderfactory/BuilderFactoryTest.java new file mode 100644 index 000000000..14af75a0f --- /dev/null +++ b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/builderfactory/BuilderFactoryTest.java @@ -0,0 +1,76 @@ +package de.saxsys.mvvmfx.internal.viewloader.builderfactory; + + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; + +import java.util.ArrayList; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.internal.util.reflection.Whitebox; + +import de.saxsys.mvvmfx.FluentViewLoader; +import de.saxsys.mvvmfx.MvvmFX; +import de.saxsys.mvvmfx.internal.viewloader.GlobalBuilderFactory; +import de.saxsys.mvvmfx.testingutils.jfxrunner.JfxRunner; +import javafx.fxml.LoadException; +import javafx.util.Builder; +import javafx.util.BuilderFactory; + +@RunWith(JfxRunner.class) +public class BuilderFactoryTest { + + public static BuilderFactory customBuilderFactoryOne = type -> { + if(CustomTextField.class.isAssignableFrom(type)) { + return (Builder) () -> new CustomTextField("Test 1"); + } else { + return null; + } + }; + + public static BuilderFactory customBuilderFactoryTwo = type -> { + if(CustomTextField.class.isAssignableFrom(type)) { + return (Builder) () -> new CustomTextField("Test 2"); + } else { + return null; + } + }; + + @Before + @After + public void clearFactories() { + ArrayList factories = (ArrayList) Whitebox.getInternalState(GlobalBuilderFactory.getInstance(), "factories"); + + factories.clear(); + } + + @Test + public void testWithoutFactory() { + try { + FluentViewLoader.fxmlView(BuilderFactoryTestView.class).load(); + fail("expected loading to fail"); + } catch (Exception e) { + assertThat(e).hasCauseInstanceOf(LoadException.class); + assertThat(e).hasRootCauseInstanceOf(NoSuchMethodException.class); + } + } + + + @Test + public void testWithCustomFactory() { + MvvmFX.addGlobalBuilderFactory(customBuilderFactoryOne); + FluentViewLoader.fxmlView(BuilderFactoryTestView.class).load(); + } + + @Test + public void testWithMultipleFactories() { + MvvmFX.addGlobalBuilderFactory(customBuilderFactoryOne); + MvvmFX.addGlobalBuilderFactory(customBuilderFactoryTwo); + BuilderFactoryTestView codeBehind = FluentViewLoader.fxmlView(BuilderFactoryTestView.class).load().getCodeBehind(); + + assertThat(codeBehind.textField.getSpecial()).isEqualTo("Test 2"); + } +} diff --git a/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/builderfactory/BuilderFactoryTestView.java b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/builderfactory/BuilderFactoryTestView.java new file mode 100644 index 000000000..8c81a10de --- /dev/null +++ b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/builderfactory/BuilderFactoryTestView.java @@ -0,0 +1,17 @@ +package de.saxsys.mvvmfx.internal.viewloader.builderfactory; + +import de.saxsys.mvvmfx.FxmlView; +import de.saxsys.mvvmfx.InjectViewModel; +import javafx.fxml.FXML; + +public class BuilderFactoryTestView implements FxmlView { + + @FXML + public CustomTextField textField; + + @InjectViewModel + private BuilderFactoryTestViewModel viewModel; + + public void initialize() { + } +} diff --git a/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/builderfactory/BuilderFactoryTestViewModel.java b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/builderfactory/BuilderFactoryTestViewModel.java new file mode 100644 index 000000000..20ebfc4cb --- /dev/null +++ b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/builderfactory/BuilderFactoryTestViewModel.java @@ -0,0 +1,7 @@ +package de.saxsys.mvvmfx.internal.viewloader.builderfactory; + +import de.saxsys.mvvmfx.ViewModel; + +public class BuilderFactoryTestViewModel implements ViewModel { + +} diff --git a/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/builderfactory/CustomTextField.java b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/builderfactory/CustomTextField.java new file mode 100644 index 000000000..8865eb510 --- /dev/null +++ b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/builderfactory/CustomTextField.java @@ -0,0 +1,23 @@ +package de.saxsys.mvvmfx.internal.viewloader.builderfactory; + +import javafx.scene.control.TextField; + +/** + * A custom TextField to test builder factories + */ +public class CustomTextField extends TextField { + + private final String special; + + /** + * Due to this constructor the custom control can't be used directly in FXML + * without the builder factory. + */ + public CustomTextField(String special) { + this.special = special; + } + + public String getSpecial() { + return special; + } +} diff --git a/mvvmfx/src/test/resources/de/saxsys/mvvmfx/internal/viewloader/builderfactory/BuilderFactoryTestView.fxml b/mvvmfx/src/test/resources/de/saxsys/mvvmfx/internal/viewloader/builderfactory/BuilderFactoryTestView.fxml new file mode 100644 index 000000000..1d2813416 --- /dev/null +++ b/mvvmfx/src/test/resources/de/saxsys/mvvmfx/internal/viewloader/builderfactory/BuilderFactoryTestView.fxml @@ -0,0 +1,11 @@ + + + + + + + + + + From 5d92be424fa679b07037ca017cc71bbd3e7827f4 Mon Sep 17 00:00:00 2001 From: Manuel Mauky Date: Thu, 7 Sep 2017 18:04:52 +0200 Subject: [PATCH 44/51] Add possibility to define local builder factories for a single loading procedure --- .../de/saxsys/mvvmfx/FluentViewLoader.java | 34 ++++++++++++++++++- .../main/java/de/saxsys/mvvmfx/MvvmFX.java | 7 ++++ .../internal/viewloader/FxmlViewLoader.java | 34 +++++++++++++------ .../viewloader/GlobalBuilderFactory.java | 16 ++++++++- .../builderfactory/BuilderFactoryTest.java | 21 ++++++++++++ 5 files changed, 100 insertions(+), 12 deletions(-) diff --git a/mvvmfx/src/main/java/de/saxsys/mvvmfx/FluentViewLoader.java b/mvvmfx/src/main/java/de/saxsys/mvvmfx/FluentViewLoader.java index 9df55f7a6..058a1e99a 100644 --- a/mvvmfx/src/main/java/de/saxsys/mvvmfx/FluentViewLoader.java +++ b/mvvmfx/src/main/java/de/saxsys/mvvmfx/FluentViewLoader.java @@ -1,5 +1,6 @@ package de.saxsys.mvvmfx; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; @@ -8,6 +9,7 @@ import de.saxsys.mvvmfx.internal.viewloader.FxmlViewLoader; import de.saxsys.mvvmfx.internal.viewloader.JavaViewLoader; import de.saxsys.mvvmfx.internal.viewloader.ResourceBundleManager; +import javafx.util.BuilderFactory; /** * Fluent API for loading Views.
    @@ -181,6 +183,8 @@ public static class FxmlViewStep providedScopes; + private List builderFactories; + FxmlViewStep(Class viewType) { this.viewType = viewType; } @@ -273,6 +277,34 @@ public FxmlViewStep viewModel(ViewModelType viewModel) return this; } + /** + * This param is used to add a {@link BuilderFactory} that is used when loading the view.
    . + * MvvmFX supports multiple builder factories. There are two ways of defining builder factories: + *
      + *
    1. a local builder factory by using this method. + * In this case the builder factory is only used for this loading procedure.
    2. + *
    3. a global builder factory by using {@link MvvmFX#addGlobalBuilderFactory(BuilderFactory)}. + * This defines a global builder factory that is used for all loading procedures.
    4. + *
    + *
    + * For most use cases it's better to define a global builder factory. + * Only if you like to limit the usage of the + * builder factory to only this loading procedure then use this fluent API method instead. + * + * @param builderFactory a builder factory that is used only for this loading procedure. + * + * @return this instance of the builder step. + */ + public FxmlViewStep builderFactory(BuilderFactory builderFactory) { + if(this.builderFactories == null) { + this.builderFactories = new ArrayList<>(); + } + + this.builderFactories.add(builderFactory); + + return this; + } + /** * The final step of the Fluent API. This method loads the view based on * the given params. @@ -284,7 +316,7 @@ public ViewTuple load() { return fxmlViewLoader.loadFxmlViewTuple(viewType, ResourceBundleManager.getInstance().mergeWithGlobal(resourceBundle), codeBehind, root, viewModel, - context, providedScopes); + context, providedScopes, builderFactories); } } diff --git a/mvvmfx/src/main/java/de/saxsys/mvvmfx/MvvmFX.java b/mvvmfx/src/main/java/de/saxsys/mvvmfx/MvvmFX.java index de8c86c4e..8f710168e 100644 --- a/mvvmfx/src/main/java/de/saxsys/mvvmfx/MvvmFX.java +++ b/mvvmfx/src/main/java/de/saxsys/mvvmfx/MvvmFX.java @@ -79,6 +79,13 @@ public static void setGlobalResourceBundle(ResourceBundle resourceBundle) { * MvvmFX also takes care for handling the default {@link javafx.fxml.JavaFXBuilderFactory}. If no custom builder factory * is able to provide a builder for a given type the default JavaFX builder factory will be used as last resort. * This way you don't have to take care for standard JavaFX types in your builder factory. + *
    + * While most of the time using a global builder factory is the best approach, for some special use cases + * it's needed to define a special builder factory that is only used for a single loading procedure. + * For such use cases one can use the {@link FluentViewLoader} with the parameter + * {@link de.saxsys.mvvmfx.FluentViewLoader.FxmlViewStep#builderFactory(BuilderFactory)} instead. + * In this case the provided builder factory is again combined with the global factories (if defined). + * Builder factories provided via {@link FluentViewLoader} have a higher priority then global builder factories. * * @param factory the builder factory */ diff --git a/mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/viewloader/FxmlViewLoader.java b/mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/viewloader/FxmlViewLoader.java index f3a2bac56..45fc26824 100644 --- a/mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/viewloader/FxmlViewLoader.java +++ b/mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/viewloader/FxmlViewLoader.java @@ -26,6 +26,7 @@ import javafx.beans.value.ObservableBooleanValue; import javafx.fxml.FXMLLoader; import javafx.scene.Parent; +import javafx.util.BuilderFactory; import javafx.util.Callback; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -33,6 +34,7 @@ import java.io.IOException; import java.net.URL; import java.util.Collection; +import java.util.List; import java.util.Optional; import java.util.ResourceBundle; import java.util.function.Consumer; @@ -68,14 +70,16 @@ public class FxmlViewLoader { * the generic type of the view. * @param * the generic type of the viewModel. - * @return the loaded ViewTuple. + * @param builderFactories a list of custom builder factories. may be null + * @return the loaded ViewTuple. */ public , ViewModelType extends ViewModel> ViewTuple loadFxmlViewTuple( - Class viewType, ResourceBundle resourceBundle, ViewType codeBehind, Object root, - ViewModelType viewModel, Context context, Collection providedScopes) { + Class viewType, ResourceBundle resourceBundle, ViewType codeBehind, Object root, + ViewModelType viewModel, Context context, Collection providedScopes, + List builderFactories) { final String pathToFXML = createFxmlPath(viewType); - return loadFxmlViewTuple(pathToFXML, resourceBundle, codeBehind, root, viewModel, context, providedScopes); + return loadFxmlViewTuple(pathToFXML, resourceBundle, codeBehind, root, viewModel, context, providedScopes, builderFactories); } /** @@ -146,11 +150,13 @@ private String createFxmlPath(Class viewType) { * the generic type of the view. * @param * the generic type of the viewModel. - * @return the loaded ViewTuple. + * @param builderFactories a list of custom builder factories. may be null + * @return the loaded ViewTuple. */ public , ViewModelType extends ViewModel> ViewTuple loadFxmlViewTuple( - final String resource, ResourceBundle resourceBundle, final ViewType codeBehind, final Object root, - ViewModelType viewModel, Context parentContext, Collection providedScopes) { + final String resource, ResourceBundle resourceBundle, final ViewType codeBehind, final Object root, + ViewModelType viewModel, Context parentContext, Collection providedScopes, + List builderFactories) { try { // FIXME Woanders hin? @@ -160,7 +166,7 @@ public , ViewModelType extends Vi // for the SceneLifecycle we need to know when the view is put into the scene BooleanProperty viewInSceneProperty = new SimpleBooleanProperty(); - final FXMLLoader loader = createFxmlLoader(resource, resourceBundle, codeBehind, root, viewModel, context, viewInSceneProperty); + final FXMLLoader loader = createFxmlLoader(resource, resourceBundle, codeBehind, root, viewModel, context, viewInSceneProperty, builderFactories); loader.load(); @@ -225,7 +231,8 @@ public , ViewModelType extends Vi } private FXMLLoader createFxmlLoader(String resource, ResourceBundle resourceBundle, View codeBehind, Object root, - ViewModel viewModel, ContextImpl context, ObservableBooleanValue viewInSceneProperty) throws IOException { + ViewModel viewModel, ContextImpl context, ObservableBooleanValue viewInSceneProperty, + List builderFactories) throws IOException { // Load FXML file final URL location = FxmlViewLoader.class.getResource(resource); if (location == null) { @@ -237,7 +244,14 @@ private FXMLLoader createFxmlLoader(String resource, ResourceBundle resourceBund fxmlLoader.setRoot(root); fxmlLoader.setResources(resourceBundle); fxmlLoader.setLocation(location); - fxmlLoader.setBuilderFactory(GlobalBuilderFactory.getInstance()); + + if(builderFactories == null || builderFactories.isEmpty()) { + fxmlLoader.setBuilderFactory(GlobalBuilderFactory.getInstance()); + } else { + BuilderFactory factory = GlobalBuilderFactory.getInstance().mergeWith(builderFactories); + fxmlLoader.setBuilderFactory(factory); + } + // when the user provides a viewModel but no codeBehind, we need to use // the custom controller factory. diff --git a/mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/viewloader/GlobalBuilderFactory.java b/mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/viewloader/GlobalBuilderFactory.java index 14164d398..e32c900b8 100644 --- a/mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/viewloader/GlobalBuilderFactory.java +++ b/mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/viewloader/GlobalBuilderFactory.java @@ -11,7 +11,7 @@ /** * A {@link BuilderFactory} that can manage multiple custom builder factories. *
    - * This class is part of the internal API of mvvmFX and may be subject to changes. + * This class is part of the internal API of mvvmFX and may be subject to changes. * Don't use this class directly. Instead add new builder factories by using public API: * {@link de.saxsys.mvvmfx.MvvmFX#addGlobalBuilderFactory(BuilderFactory)}. */ @@ -58,6 +58,20 @@ public Builder getBuilder(Class type) { return builder; } + /** + * Create a new BuilderFactory instance that contains all custom factories of this instance + * combined with the list of factories passed as argument to this method. + *
    + * This instance of the builderFactory is not changed by this method. + */ + public BuilderFactory mergeWith(List factories) { + GlobalBuilderFactory factory = new GlobalBuilderFactory(); + + factory.factories.addAll(factories); + + return factory; + } + public void addBuilderFactory(BuilderFactory factory) { this.factories.add(factory); } diff --git a/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/builderfactory/BuilderFactoryTest.java b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/builderfactory/BuilderFactoryTest.java index 14af75a0f..dcde803bb 100644 --- a/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/builderfactory/BuilderFactoryTest.java +++ b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/builderfactory/BuilderFactoryTest.java @@ -73,4 +73,25 @@ public void testWithMultipleFactories() { assertThat(codeBehind.textField.getSpecial()).isEqualTo("Test 2"); } + + @Test + public void testWithBuilderAtLoadingTime() { + MvvmFX.addGlobalBuilderFactory(customBuilderFactoryOne); + + BuilderFactoryTestView codeBehind = FluentViewLoader + .fxmlView(BuilderFactoryTestView.class) + .load().getCodeBehind(); + + // loading without parameter to FluentViewLoader results into the first factory to be used. + assertThat(codeBehind.textField.getSpecial()).isEqualTo("Test 1"); + + + codeBehind = FluentViewLoader + .fxmlView(BuilderFactoryTestView.class) + .builderFactory(customBuilderFactoryTwo) + .load().getCodeBehind(); + + // passing a factory as parameter results into this factory being used instead of the global one. + assertThat(codeBehind.textField.getSpecial()).isEqualTo("Test 2"); + } } From ba6d668682041dcd37700f460cf36ca948823c76 Mon Sep 17 00:00:00 2001 From: gbalderas Date: Wed, 20 Sep 2017 16:05:46 +0200 Subject: [PATCH 45/51] update tests with ExceptionUtils --- .../internal/viewloader/FxmlViewLoader.java | 8 ++------ .../FluentViewLoader_FxmlView_Test.java | 20 +++++++++++-------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/viewloader/FxmlViewLoader.java b/mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/viewloader/FxmlViewLoader.java index 838765aae..95c845367 100644 --- a/mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/viewloader/FxmlViewLoader.java +++ b/mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/viewloader/FxmlViewLoader.java @@ -284,10 +284,8 @@ public Object call(Class type) { Object controller = DependencyInjector.getInstance().getInstanceOf(type); //throw an exception if the fx:controller was of type ViewModel -// Object controller = loader.getController(); if (controller instanceof ViewModel) { - throw new IllegalStateException("A ViewModel class [" + controller.getClass().getCanonicalName() + "] was referenced in the FXML file" -// + "["+resource+"]" + throw new IllegalStateException("A ViewModel class [" + controller.getClass().getCanonicalName() + "] was referenced in an FXML file" + " as the fx:controller." + " Instead a class that implements FxmlView has to be defined as the fx:controller in the FXML file."); } @@ -402,10 +400,8 @@ public Object call(Class type) { } //throw an exception if the fx:controller was of type ViewModel - // Object controller = loader.getController(); if (controller instanceof ViewModel) { - throw new IllegalStateException("A ViewModel class [" + controller.getClass().getCanonicalName() + "] was referenced in the FXML file" - // + "["+resource+"]" + throw new IllegalStateException("A ViewModel class [" + controller.getClass().getCanonicalName() + "] was referenced an the FXML file" + " as the fx:controller." + " Instead a class that implements FxmlView has to be defined as the fx:controller in the FXML file."); } diff --git a/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/FluentViewLoader_FxmlView_Test.java b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/FluentViewLoader_FxmlView_Test.java index 049ba8b39..d521ce58d 100644 --- a/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/FluentViewLoader_FxmlView_Test.java +++ b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/FluentViewLoader_FxmlView_Test.java @@ -533,10 +533,11 @@ public void testFxmlViewModelAsControllerException(){ try { FluentViewLoader.fxmlView(TestFxmlViewModelAsController.class) .load(); - } catch (RuntimeException e){ ; - assertThat(e.getCause().getCause().getMessage()) + } catch (RuntimeException e){ + assertThat(ExceptionUtils.getRootCause(e)).isInstanceOf(IllegalStateException.class); + assertThat(ExceptionUtils.getRootCause(e).getMessage()) .contains("A ViewModel class") - .contains("was referenced in the FXML file") + .contains("was referenced in an FXML file") .contains("as the fx:controller"); } @@ -545,10 +546,11 @@ public void testFxmlViewModelAsControllerException(){ FluentViewLoader.fxmlView(TestFxmlViewModelAsController.class) .viewModel(new TestFxmlViewModelAsControllerViewModel()) .load(); - } catch (RuntimeException e){ ; - assertThat(e.getCause().getCause().getMessage()) + } catch (RuntimeException e){ + assertThat(ExceptionUtils.getRootCause(e)).isInstanceOf(IllegalStateException.class); + assertThat(ExceptionUtils.getRootCause(e).getMessage()) .contains("A ViewModel class") - .contains("was referenced in the FXML file") + .contains("was referenced in an FXML file") .contains("as the fx:controller"); } } @@ -559,7 +561,8 @@ public void testFxmlViewModelAsControllerWithCustomPath(){ FluentViewLoader.fxmlView(TestFxmlViewModelAsControllerWithCustomPathView.class) .load(); } catch (RuntimeException e){ - assertThat(e.getCause().getCause().getMessage()) + assertThat(ExceptionUtils.getRootCause(e)).isInstanceOf(IllegalStateException.class); + assertThat(ExceptionUtils.getRootCause(e).getMessage()) .contains("A ViewModel class") .contains("was referenced in the FXML file") .contains("as the fx:controller"); @@ -571,7 +574,8 @@ public void testFxmlViewModelAsControllerWithCustomPath(){ .viewModel(new TestFxmlViewModelAsControllerWithCustomPathViewModel()) .load(); } catch (RuntimeException e){ - assertThat(e.getCause().getCause().getMessage()) + assertThat(ExceptionUtils.getRootCause(e)).isInstanceOf(IllegalStateException.class); + assertThat(ExceptionUtils.getRootCause(e).getMessage()) .contains("A ViewModel class") .contains("was referenced in the FXML file") .contains("as the fx:controller"); From 3700b348c86805da4e1f6738640469ef818df6d1 Mon Sep 17 00:00:00 2001 From: gbalderas Date: Wed, 20 Sep 2017 16:10:52 +0200 Subject: [PATCH 46/51] update exception message --- .../internal/viewloader/FxmlViewLoader.java | 2 +- .../FluentViewLoader_FxmlView_Test.java | 32 +++++++++---------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/viewloader/FxmlViewLoader.java b/mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/viewloader/FxmlViewLoader.java index 95c845367..a79434d0e 100644 --- a/mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/viewloader/FxmlViewLoader.java +++ b/mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/viewloader/FxmlViewLoader.java @@ -401,7 +401,7 @@ public Object call(Class type) { //throw an exception if the fx:controller was of type ViewModel if (controller instanceof ViewModel) { - throw new IllegalStateException("A ViewModel class [" + controller.getClass().getCanonicalName() + "] was referenced an the FXML file" + throw new IllegalStateException("A ViewModel class [" + controller.getClass().getCanonicalName() + "] was referenced in an FXML file" + " as the fx:controller." + " Instead a class that implements FxmlView has to be defined as the fx:controller in the FXML file."); } diff --git a/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/FluentViewLoader_FxmlView_Test.java b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/FluentViewLoader_FxmlView_Test.java index d521ce58d..fcc6afa15 100644 --- a/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/FluentViewLoader_FxmlView_Test.java +++ b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/FluentViewLoader_FxmlView_Test.java @@ -535,10 +535,10 @@ public void testFxmlViewModelAsControllerException(){ .load(); } catch (RuntimeException e){ assertThat(ExceptionUtils.getRootCause(e)).isInstanceOf(IllegalStateException.class); - assertThat(ExceptionUtils.getRootCause(e).getMessage()) - .contains("A ViewModel class") - .contains("was referenced in an FXML file") - .contains("as the fx:controller"); + assertThat(ExceptionUtils.getRootCause(e)) + .hasMessageContaining("A ViewModel class") + .hasMessageContaining("was referenced in an FXML file") + .hasMessageContaining("as the fx:controller"); } //with ControllerFactoryWithCustomViewModel @@ -548,10 +548,10 @@ public void testFxmlViewModelAsControllerException(){ .load(); } catch (RuntimeException e){ assertThat(ExceptionUtils.getRootCause(e)).isInstanceOf(IllegalStateException.class); - assertThat(ExceptionUtils.getRootCause(e).getMessage()) - .contains("A ViewModel class") - .contains("was referenced in an FXML file") - .contains("as the fx:controller"); + assertThat(ExceptionUtils.getRootCause(e)) + .hasMessageContaining("A ViewModel class") + .hasMessageContaining("was referenced in an FXML file") + .hasMessageContaining("as the fx:controller"); } } @@ -562,10 +562,10 @@ public void testFxmlViewModelAsControllerWithCustomPath(){ .load(); } catch (RuntimeException e){ assertThat(ExceptionUtils.getRootCause(e)).isInstanceOf(IllegalStateException.class); - assertThat(ExceptionUtils.getRootCause(e).getMessage()) - .contains("A ViewModel class") - .contains("was referenced in the FXML file") - .contains("as the fx:controller"); + assertThat(ExceptionUtils.getRootCause(e)) + .hasMessageContaining("A ViewModel class") + .hasMessageContaining("was referenced in the FXML file") + .hasMessageContaining("as the fx:controller"); } //with ControllerFactoryWithCustomViewModel @@ -575,10 +575,10 @@ public void testFxmlViewModelAsControllerWithCustomPath(){ .load(); } catch (RuntimeException e){ assertThat(ExceptionUtils.getRootCause(e)).isInstanceOf(IllegalStateException.class); - assertThat(ExceptionUtils.getRootCause(e).getMessage()) - .contains("A ViewModel class") - .contains("was referenced in the FXML file") - .contains("as the fx:controller"); + assertThat(ExceptionUtils.getRootCause(e)) + .hasMessageContaining("A ViewModel class") + .hasMessageContaining("was referenced in the FXML file") + .hasMessageContaining("as the fx:controller"); } } From 1ca39d960d8fb648f50aced89fe6ce91156e2e77 Mon Sep 17 00:00:00 2001 From: gbalderas Date: Wed, 20 Sep 2017 17:12:45 +0200 Subject: [PATCH 47/51] add fxinclude testcase --- .../FluentViewLoader_FxmlView_Test.java | 31 +++++++++++++++++-- .../TestFxmlViewModelAsControllerChild.java | 6 ++++ ...mlViewModelAsControllerChildViewModel.java | 6 ++++ .../TestFxmlViewModelAsControllerParent.java | 6 ++++ ...lViewModelAsControllerParentViewModel.java | 6 ++++ .../TestFxmlViewModelAsControllerChild.fxml | 14 +++++++++ .../TestFxmlViewModelAsControllerParent.fxml | 16 ++++++++++ 7 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewModelAsControllerChild.java create mode 100644 mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewModelAsControllerChildViewModel.java create mode 100644 mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewModelAsControllerParent.java create mode 100644 mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewModelAsControllerParentViewModel.java create mode 100644 mvvmfx/src/test/resources/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewModelAsControllerChild.fxml create mode 100644 mvvmfx/src/test/resources/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewModelAsControllerParent.fxml diff --git a/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/FluentViewLoader_FxmlView_Test.java b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/FluentViewLoader_FxmlView_Test.java index fcc6afa15..1fea4a6ce 100644 --- a/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/FluentViewLoader_FxmlView_Test.java +++ b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/FluentViewLoader_FxmlView_Test.java @@ -564,7 +564,7 @@ public void testFxmlViewModelAsControllerWithCustomPath(){ assertThat(ExceptionUtils.getRootCause(e)).isInstanceOf(IllegalStateException.class); assertThat(ExceptionUtils.getRootCause(e)) .hasMessageContaining("A ViewModel class") - .hasMessageContaining("was referenced in the FXML file") + .hasMessageContaining("was referenced in an FXML file") .hasMessageContaining("as the fx:controller"); } @@ -577,9 +577,36 @@ public void testFxmlViewModelAsControllerWithCustomPath(){ assertThat(ExceptionUtils.getRootCause(e)).isInstanceOf(IllegalStateException.class); assertThat(ExceptionUtils.getRootCause(e)) .hasMessageContaining("A ViewModel class") - .hasMessageContaining("was referenced in the FXML file") + .hasMessageContaining("was referenced in an FXML file") .hasMessageContaining("as the fx:controller"); } } + + @Test + public void testFxmlViewModelAsControllerFxInclude(){ + try { + FluentViewLoader.fxmlView(TestFxmlViewModelAsControllerParent.class) + .load(); + } catch (RuntimeException e){ + assertThat(ExceptionUtils.getRootCause(e)).isInstanceOf(IllegalStateException.class); + assertThat(ExceptionUtils.getRootCause(e)) + .hasMessageContaining("A ViewModel class") + .hasMessageContaining("was referenced in an FXML file") + .hasMessageContaining("as the fx:controller"); + } + + //with ControllerFactoryWithCustomViewModel + try { + FluentViewLoader.fxmlView(TestFxmlViewModelAsControllerParent.class) + .viewModel(new TestFxmlViewModelAsControllerParentViewModel()) + .load(); + } catch (RuntimeException e){ + assertThat(ExceptionUtils.getRootCause(e)).isInstanceOf(IllegalStateException.class); + assertThat(ExceptionUtils.getRootCause(e)) + .hasMessageContaining("A ViewModel class") + .hasMessageContaining("was referenced in an FXML file") + .hasMessageContaining("as the fx:controller"); + } + } } diff --git a/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewModelAsControllerChild.java b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewModelAsControllerChild.java new file mode 100644 index 000000000..be738bfc9 --- /dev/null +++ b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewModelAsControllerChild.java @@ -0,0 +1,6 @@ +package de.saxsys.mvvmfx.internal.viewloader.example; + +import de.saxsys.mvvmfx.FxmlView; + +public class TestFxmlViewModelAsControllerChild implements FxmlView{ +} diff --git a/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewModelAsControllerChildViewModel.java b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewModelAsControllerChildViewModel.java new file mode 100644 index 000000000..625f2e171 --- /dev/null +++ b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewModelAsControllerChildViewModel.java @@ -0,0 +1,6 @@ +package de.saxsys.mvvmfx.internal.viewloader.example; + +import de.saxsys.mvvmfx.ViewModel; + +public class TestFxmlViewModelAsControllerChildViewModel implements ViewModel{ +} diff --git a/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewModelAsControllerParent.java b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewModelAsControllerParent.java new file mode 100644 index 000000000..705a59fdb --- /dev/null +++ b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewModelAsControllerParent.java @@ -0,0 +1,6 @@ +package de.saxsys.mvvmfx.internal.viewloader.example; + +import de.saxsys.mvvmfx.FxmlView; + +public class TestFxmlViewModelAsControllerParent implements FxmlView{ +} diff --git a/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewModelAsControllerParentViewModel.java b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewModelAsControllerParentViewModel.java new file mode 100644 index 000000000..8b2aaa155 --- /dev/null +++ b/mvvmfx/src/test/java/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewModelAsControllerParentViewModel.java @@ -0,0 +1,6 @@ +package de.saxsys.mvvmfx.internal.viewloader.example; + +import de.saxsys.mvvmfx.ViewModel; + +public class TestFxmlViewModelAsControllerParentViewModel implements ViewModel{ +} diff --git a/mvvmfx/src/test/resources/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewModelAsControllerChild.fxml b/mvvmfx/src/test/resources/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewModelAsControllerChild.fxml new file mode 100644 index 000000000..711f3c2e3 --- /dev/null +++ b/mvvmfx/src/test/resources/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewModelAsControllerChild.fxml @@ -0,0 +1,14 @@ + + + + + + + + + + + diff --git a/mvvmfx/src/test/resources/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewModelAsControllerParent.fxml b/mvvmfx/src/test/resources/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewModelAsControllerParent.fxml new file mode 100644 index 000000000..75ca9f207 --- /dev/null +++ b/mvvmfx/src/test/resources/de/saxsys/mvvmfx/internal/viewloader/example/TestFxmlViewModelAsControllerParent.fxml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + From 9bce174ad1b91f6aef642bb42383d703897719df Mon Sep 17 00:00:00 2001 From: Manuel Mauky Date: Wed, 20 Sep 2017 18:49:32 +0200 Subject: [PATCH 48/51] remove unused id parameter from InjectScope annotation #512 --- mvvmfx/src/main/java/de/saxsys/mvvmfx/InjectScope.java | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/mvvmfx/src/main/java/de/saxsys/mvvmfx/InjectScope.java b/mvvmfx/src/main/java/de/saxsys/mvvmfx/InjectScope.java index 5bec70b34..4f77a9d12 100644 --- a/mvvmfx/src/main/java/de/saxsys/mvvmfx/InjectScope.java +++ b/mvvmfx/src/main/java/de/saxsys/mvvmfx/InjectScope.java @@ -6,18 +6,11 @@ import java.lang.annotation.Target; /** + * This annotation is used to inject a {@link Scope} object into a {@link ViewModel}. * * @author alexander.casall - * */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface InjectScope { - - /** - * the id of the scope. - * Default is "" which means that the scope is global. - */ - String value() default ""; - } \ No newline at end of file From 4cc4d34faf8b3562d2e794b9f69dfae55cd035c7 Mon Sep 17 00:00:00 2001 From: Manuel Mauky Date: Mon, 25 Sep 2017 12:53:26 +0200 Subject: [PATCH 49/51] Add support for multiple resourceBundles in FluentViewLoader #518 --- .../de/saxsys/mvvmfx/FluentViewLoader.java | 45 ++++++++++++------ .../viewloader/ResourceBundleManager.java | 47 ++++++++++++++++++- .../global/GlobalResourceBundleTest.java | 29 +++++++++++- .../resourcebundle/global/third.properties | 2 + 4 files changed, 105 insertions(+), 18 deletions(-) create mode 100644 mvvmfx/src/test/resources/de/saxsys/mvvmfx/resourcebundle/global/third.properties diff --git a/mvvmfx/src/main/java/de/saxsys/mvvmfx/FluentViewLoader.java b/mvvmfx/src/main/java/de/saxsys/mvvmfx/FluentViewLoader.java index 058a1e99a..aa8cb29ad 100644 --- a/mvvmfx/src/main/java/de/saxsys/mvvmfx/FluentViewLoader.java +++ b/mvvmfx/src/main/java/de/saxsys/mvvmfx/FluentViewLoader.java @@ -63,7 +63,7 @@ public class FluentViewLoader { public static class JavaViewStep, ViewModelType extends ViewModel> { private final Class viewType; - private ResourceBundle resourceBundle; + private List resourceBundles; private ViewModelType viewModel; private ViewType codeBehind; @@ -96,20 +96,27 @@ public JavaViewStep providedScopes(Collection pr * view. Note: It is possible to provide a global application-wide * resourceBundle via * {@link MvvmFX#setGlobalResourceBundle(ResourceBundle)} method. - * + *

    * If there is a global resourceBundle set it will be merged with the * resourceBundle provided by this builder method. The resourceBundle * provided by this method will have a higher priority then the global * one which means that if there are duplicate keys, the values of the * global resourceBundle will be overwritten and the values of this * resourceBundle will be used. + *

    + * It is possible to add multiple resourceBundles by invoking this builder method + * multiple times. In this case the last provided resourceBundle will have the + * highest priority when it comes to overwriting values with the same keys. * * @param resourceBundle * the resource bundle that is used while loading the view. * @return this instance of the builder step. */ public JavaViewStep resourceBundle(ResourceBundle resourceBundle) { - this.resourceBundle = resourceBundle; + if(resourceBundles == null) { + resourceBundles = new ArrayList<>(); + } + resourceBundles.add(resourceBundle); return this; } @@ -154,12 +161,12 @@ public JavaViewStep codeBehind(ViewType codeBehind) { public ViewTuple load() { JavaViewLoader javaViewLoader = new JavaViewLoader(); - return javaViewLoader.loadJavaViewTuple(viewType, - ResourceBundleManager.getInstance().mergeWithGlobal(resourceBundle), viewModel, codeBehind, context, - providedScopes); - } + final ResourceBundle bundle = ResourceBundleManager.getInstance().mergeListWithGlobal(resourceBundles); - } + return javaViewLoader.loadJavaViewTuple(viewType, bundle, viewModel, codeBehind, context, + providedScopes); + } + } /** * This class is the builder step to load a fxml based view. It is accessed @@ -176,7 +183,7 @@ public ViewTuple load() { public static class FxmlViewStep, ViewModelType extends ViewModel> { private final Class viewType; - private ResourceBundle resourceBundle; + private List resourceBundles; private Object root; private ViewType codeBehind; private ViewModelType viewModel; @@ -211,20 +218,27 @@ public FxmlViewStep providedScopes(Collection pr * view. Note: It is possible to provide a global application-wide * resourceBundle via * {@link MvvmFX#setGlobalResourceBundle(ResourceBundle)} method. - * + *

    * If there is a global resourceBundle set it will be merged with the * resourceBundle provided by this builder method. The resourceBundle * provided by this method will have a higher priority then the global * one which means that if there are duplicate keys, the values of the * global resourceBundle will be overwritten and the values of this * resourceBundle will be used. + *

    + * It is possible to add multiple resourceBundles by invoking this builder method + * multiple times. In this case the last provided resourceBundle will have the + * highest priority when it comes to overwriting values with the same keys. * * @param resourceBundle * the resource bundle that is used while loading the view. * @return this instance of the builder step. */ public FxmlViewStep resourceBundle(ResourceBundle resourceBundle) { - this.resourceBundle = resourceBundle; + if(resourceBundles == null) { + resourceBundles = new ArrayList<>(); + } + resourceBundles.add(resourceBundle); return this; } @@ -314,10 +328,11 @@ public FxmlViewStep builderFactory(BuilderFactory build public ViewTuple load() { FxmlViewLoader fxmlViewLoader = new FxmlViewLoader(); - return fxmlViewLoader.loadFxmlViewTuple(viewType, - ResourceBundleManager.getInstance().mergeWithGlobal(resourceBundle), codeBehind, root, viewModel, - context, providedScopes, builderFactories); - } + final ResourceBundle bundle = ResourceBundleManager.getInstance().mergeListWithGlobal(resourceBundles); + + return fxmlViewLoader.loadFxmlViewTuple(viewType, bundle, codeBehind, root, viewModel, + context, providedScopes, builderFactories); + } } /** diff --git a/mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/viewloader/ResourceBundleManager.java b/mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/viewloader/ResourceBundleManager.java index 70de22c47..b9ee75d6a 100644 --- a/mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/viewloader/ResourceBundleManager.java +++ b/mvvmfx/src/main/java/de/saxsys/mvvmfx/internal/viewloader/ResourceBundleManager.java @@ -18,10 +18,12 @@ import eu.lestard.doc.Internal; import sun.util.ResourceBundleEnumeration; +import java.util.ArrayList; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; +import java.util.List; import java.util.ListResourceBundle; import java.util.Locale; import java.util.Map; @@ -87,9 +89,50 @@ public ResourceBundle mergeWithGlobal(ResourceBundle resourceBundle) { return merge(resourceBundle, globalResourceBundle); } } - } + } + + + private static ResourceBundle reduce(List bundles) { + return bundles.stream() + .filter(Objects::nonNull) + .reduce(EMPTY_RESOURCE_BUNDLE, (a, b) -> merge(b, a)); + } + + /** + * Merges the list of ResourceBundles with the global one (if any). + *

    + * The global resourceBundle has a lower priority then the provided ones. If there is the same key defined in the + * global and in one of the provided resourceBundles, the value from the provided resourceBundle will be used. + *

    + * The order of resourceBundles in the list defines the priority for resourceBundles. + * ResourceBundles at the start of the list have a lower priority compared to bundles + * at the end of the list. This means that the last resourceBundle will overwrite values from previous resourceBundles + * (including the global resourceBundle (if any)). * + * + * @param bundles + * a list of resourceBundles that will be merged. null is accepted. + * @return the merged resourceBundle. + */ + public ResourceBundle mergeListWithGlobal(List bundles) { + if (globalResourceBundle == null) { + if (bundles == null) { + return EMPTY_RESOURCE_BUNDLE; + } else { + return reduce(bundles); + } + } else { + if (bundles == null) { + return new ResourceBundleWrapper(globalResourceBundle); + } else { + final List resourceBundles = new ArrayList<>(); + resourceBundles.add(globalResourceBundle); + resourceBundles.addAll(bundles); + return reduce(resourceBundles); + } + } + } - private ResourceBundle merge(ResourceBundle highPriority, ResourceBundle lowPriority) { + private static ResourceBundle merge(ResourceBundle highPriority, ResourceBundle lowPriority) { return new MergedResourceBundle(highPriority, lowPriority); } diff --git a/mvvmfx/src/test/java/de/saxsys/mvvmfx/resourcebundle/global/GlobalResourceBundleTest.java b/mvvmfx/src/test/java/de/saxsys/mvvmfx/resourcebundle/global/GlobalResourceBundleTest.java index 11af3e656..3a51877bc 100644 --- a/mvvmfx/src/test/java/de/saxsys/mvvmfx/resourcebundle/global/GlobalResourceBundleTest.java +++ b/mvvmfx/src/test/java/de/saxsys/mvvmfx/resourcebundle/global/GlobalResourceBundleTest.java @@ -39,12 +39,14 @@ public class GlobalResourceBundleTest { private ResourceBundle global; private ResourceBundle other; + private ResourceBundle third; @BeforeEach public void setup(){ MvvmFX.setGlobalResourceBundle(null); global = ResourceBundle.getBundle(this.getClass().getPackage().getName() + ".global"); other = ResourceBundle.getBundle(this.getClass().getPackage().getName() + ".other"); + third = ResourceBundle.getBundle(this.getClass().getPackage().getName() + ".third"); } @AfterEach @@ -68,6 +70,31 @@ public void test() { // in this case "other" has the higher priority and overwrites the value defined in "global" assertThat(codeBehind.label.getText()).isEqualTo("other"); } - + + /** + * It's possible to use more then one resourceBundle with {@link FluentViewLoader}. + */ + @Test + public void testThreeResourceBundles() { + MvvmFX.setGlobalResourceBundle(global); + + final ViewTuple viewTuple = FluentViewLoader.fxmlView(TestView.class) + .resourceBundle(other) + .resourceBundle(third) + .load(); + final TestView codeBehind = viewTuple.getCodeBehind(); + + assertThat(codeBehind.resources).isNotNull(); + + assertThat(codeBehind.global_label.getText()).isEqualTo("global"); + assertThat(codeBehind.other_label.getText()).isEqualTo("other"); + + // the "third" bundle was added last in the fluent API. + // for this reason it has the highest priority and overwrites other values + assertThat(codeBehind.label.getText()).isEqualTo("third"); + + assertThat(codeBehind.resources.getString("third_label")).isEqualTo("third"); + + } } diff --git a/mvvmfx/src/test/resources/de/saxsys/mvvmfx/resourcebundle/global/third.properties b/mvvmfx/src/test/resources/de/saxsys/mvvmfx/resourcebundle/global/third.properties new file mode 100644 index 000000000..7cae60549 --- /dev/null +++ b/mvvmfx/src/test/resources/de/saxsys/mvvmfx/resourcebundle/global/third.properties @@ -0,0 +1,2 @@ +third_label=third +label=third \ No newline at end of file From 5a5db2ad4684705311ccbb7ad54f2ff1ddc746ee Mon Sep 17 00:00:00 2001 From: Manuel Mauky Date: Mon, 25 Sep 2017 15:43:38 +0200 Subject: [PATCH 50/51] Add example to show FxmlPath annotation for custom fxml paths #249 --- .../helloworld-custom-fxml-path/README.md | 10 +++++++ .../helloworld-custom-fxml-path/pom.xml | 22 ++++++++++++++ .../mvvmfx/examples/helloworld/HelloView.java | 20 +++++++++++++ .../examples/helloworld/HelloViewModel.java | 23 ++++++++++++++ .../mvvmfx/examples/helloworld/Starter.java | 30 +++++++++++++++++++ .../resources/some/other/path/HalloWelt.fxml | 16 ++++++++++ examples/mini-examples/pom.xml | 1 + 7 files changed, 122 insertions(+) create mode 100644 examples/mini-examples/helloworld-custom-fxml-path/README.md create mode 100644 examples/mini-examples/helloworld-custom-fxml-path/pom.xml create mode 100644 examples/mini-examples/helloworld-custom-fxml-path/src/main/java/de/saxsys/mvvmfx/examples/helloworld/HelloView.java create mode 100644 examples/mini-examples/helloworld-custom-fxml-path/src/main/java/de/saxsys/mvvmfx/examples/helloworld/HelloViewModel.java create mode 100644 examples/mini-examples/helloworld-custom-fxml-path/src/main/java/de/saxsys/mvvmfx/examples/helloworld/Starter.java create mode 100644 examples/mini-examples/helloworld-custom-fxml-path/src/main/resources/some/other/path/HalloWelt.fxml diff --git a/examples/mini-examples/helloworld-custom-fxml-path/README.md b/examples/mini-examples/helloworld-custom-fxml-path/README.md new file mode 100644 index 000000000..70309cd65 --- /dev/null +++ b/examples/mini-examples/helloworld-custom-fxml-path/README.md @@ -0,0 +1,10 @@ +# MvvmFX HelloWorld with custom FXML path + +This module is an example of a simple HelloWorld app that shows +how to develop a View with a CodeBehind class that has another location then +the FXML file. Normally both files should have the same name to be found by mvvmFX +by following a set of [naming conventions](https://github.com/sialcasa/mvvmFX/wiki/Naming-Conventions). + +However, it is also possible to use the `@FxmlPath` annotation +to define a custom path for the FXML file. See [the Wiki](https://github.com/sialcasa/mvvmFX/wiki/Deviating-FXML-location) +for a detailed description. \ No newline at end of file diff --git a/examples/mini-examples/helloworld-custom-fxml-path/pom.xml b/examples/mini-examples/helloworld-custom-fxml-path/pom.xml new file mode 100644 index 000000000..492474d9a --- /dev/null +++ b/examples/mini-examples/helloworld-custom-fxml-path/pom.xml @@ -0,0 +1,22 @@ + + + + mini-examples + de.saxsys.mvvmfx + 1.7.0-SNAPSHOT + + 4.0.0 + + helloworld-custom-fxml-path + HelloWorld with custom FXML path + + + + de.saxsys + mvvmfx + + + + \ No newline at end of file diff --git a/examples/mini-examples/helloworld-custom-fxml-path/src/main/java/de/saxsys/mvvmfx/examples/helloworld/HelloView.java b/examples/mini-examples/helloworld-custom-fxml-path/src/main/java/de/saxsys/mvvmfx/examples/helloworld/HelloView.java new file mode 100644 index 000000000..dc1f9feb4 --- /dev/null +++ b/examples/mini-examples/helloworld-custom-fxml-path/src/main/java/de/saxsys/mvvmfx/examples/helloworld/HelloView.java @@ -0,0 +1,20 @@ +package de.saxsys.mvvmfx.examples.helloworld; + +import de.saxsys.mvvmfx.FxmlPath; +import de.saxsys.mvvmfx.FxmlView; +import de.saxsys.mvvmfx.InjectViewModel; +import javafx.fxml.FXML; +import javafx.scene.control.Label; + +@FxmlPath("/some/other/path/HalloWelt.fxml") +public class HelloView implements FxmlView { + @FXML + public Label helloLabel; + + @InjectViewModel + private HelloViewModel viewModel; + + public void initialize() { + helloLabel.textProperty().bind(viewModel.helloMessage()); + } +} diff --git a/examples/mini-examples/helloworld-custom-fxml-path/src/main/java/de/saxsys/mvvmfx/examples/helloworld/HelloViewModel.java b/examples/mini-examples/helloworld-custom-fxml-path/src/main/java/de/saxsys/mvvmfx/examples/helloworld/HelloViewModel.java new file mode 100644 index 000000000..0bbcddeb8 --- /dev/null +++ b/examples/mini-examples/helloworld-custom-fxml-path/src/main/java/de/saxsys/mvvmfx/examples/helloworld/HelloViewModel.java @@ -0,0 +1,23 @@ +package de.saxsys.mvvmfx.examples.helloworld; + +import de.saxsys.mvvmfx.ViewModel; +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; + +public class HelloViewModel implements ViewModel { + + private final StringProperty helloMessage = new SimpleStringProperty("Hello World"); + + public StringProperty helloMessage() { + return helloMessage; + } + + public String getHelloMessage() { + return helloMessage.get(); + } + + public void setHelloMessage(String message) { + helloMessage.set(message); + } + +} diff --git a/examples/mini-examples/helloworld-custom-fxml-path/src/main/java/de/saxsys/mvvmfx/examples/helloworld/Starter.java b/examples/mini-examples/helloworld-custom-fxml-path/src/main/java/de/saxsys/mvvmfx/examples/helloworld/Starter.java new file mode 100644 index 000000000..93db24da3 --- /dev/null +++ b/examples/mini-examples/helloworld-custom-fxml-path/src/main/java/de/saxsys/mvvmfx/examples/helloworld/Starter.java @@ -0,0 +1,30 @@ +package de.saxsys.mvvmfx.examples.helloworld; + +import de.saxsys.mvvmfx.FluentViewLoader; +import de.saxsys.mvvmfx.ViewTuple; +import javafx.application.Application; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.stage.Stage; + +public class Starter extends Application { + + public static void main(String... args) { + Application.launch(args); + } + + @Override + public void start(Stage stage) throws Exception { + stage.setTitle("Hello World Application"); + + final ViewTuple viewTuple = FluentViewLoader + .fxmlView(HelloView.class) + .load(); + + final Parent root = viewTuple.getView(); + stage.setScene(new Scene(root)); + stage.show(); + } + +} + diff --git a/examples/mini-examples/helloworld-custom-fxml-path/src/main/resources/some/other/path/HalloWelt.fxml b/examples/mini-examples/helloworld-custom-fxml-path/src/main/resources/some/other/path/HalloWelt.fxml new file mode 100644 index 000000000..32e158f5b --- /dev/null +++ b/examples/mini-examples/helloworld-custom-fxml-path/src/main/resources/some/other/path/HalloWelt.fxml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + diff --git a/examples/mini-examples/pom.xml b/examples/mini-examples/pom.xml index 6c212c43e..7dd251a97 100644 --- a/examples/mini-examples/pom.xml +++ b/examples/mini-examples/pom.xml @@ -22,6 +22,7 @@ welcome-example scopes-example async-todoapp-futures + helloworld-custom-fxml-path From 66e5c22721b8d0cac7c21e08f8057d1af2b3d9e8 Mon Sep 17 00:00:00 2001 From: Manuel Mauky Date: Mon, 25 Sep 2017 16:28:04 +0200 Subject: [PATCH 51/51] Update version to 1.7.0 to prepare for release --- README.md | 20 +++++++++---------- examples/books-example/pom.xml | 2 +- examples/contacts-example/pom.xml | 2 +- .../async-todoapp-futures/pom.xml | 2 +- .../mini-examples/fx-root-example/pom.xml | 2 +- .../helloworld-custom-fxml-path/pom.xml | 2 +- .../helloworld-without-fxml/pom.xml | 2 +- examples/mini-examples/helloworld/pom.xml | 2 +- examples/mini-examples/pom.xml | 2 +- examples/mini-examples/scopes-example/pom.xml | 2 +- .../synchronizefx-example/pom.xml | 2 +- .../mini-examples/welcome-example/pom.xml | 2 +- examples/pom.xml | 2 +- examples/todomvc-example/pom.xml | 2 +- mvvmfx-archetype/pom.xml | 2 +- .../resources/archetype-resources/pom.xml | 2 +- mvvmfx-cdi/pom.xml | 2 +- mvvmfx-easydi/pom.xml | 2 +- mvvmfx-guice/pom.xml | 2 +- mvvmfx-testing-utils/pom.xml | 2 +- mvvmfx-utils/pom.xml | 2 +- mvvmfx-validation/pom.xml | 2 +- mvvmfx/pom.xml | 2 +- pom.xml | 2 +- 24 files changed, 33 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index 12807877a..91203998a 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ This is the stable release that can be used in production. de.saxsys mvvmfx - 1.6.0 + 1.7.0 ``` @@ -32,7 +32,7 @@ Here we make bug fixes for the current stable release. de.saxsys mvvmfx - 1.6.1-SNAPSHOT + 1.7.1-SNAPSHOT ``` @@ -44,7 +44,7 @@ Here we develop new features. This release is unstable and shouldn't be used in de.saxsys mvvmfx - 1.7.0-SNAPSHOT + 1.8.0-SNAPSHOT ``` @@ -56,11 +56,11 @@ The best way to get help with mvvmFX is to either ask questions on StackOverflow ### Links - [Project Page](http://sialcasa.github.io/mvvmFX/) -- [javadoc mvvmfx core](http://sialcasa.github.io/mvvmFX/javadoc/1.6.0/mvvmfx/) -- [javadoc mvvmfx-cdi](http://sialcasa.github.io/mvvmFX/javadoc/1.6.0/mvvmfx-cdi/) -- [javadoc mvvmfx-guice](http://sialcasa.github.io/mvvmFX/javadoc/1.6.0/mvvmfx-guice/) -- [javadoc mvvmfx-easydi](http://sialcasa.github.io/mvvmFX/javadoc/1.6.0/mvvmfx-easydi/) -- [javadoc mvvmfx-validation](http://sialcasa.github.io/mvvmFX/javadoc/1.6.0/mvvmfx-validation/) -- [javadoc mvvmfx-utils](http://sialcasa.github.io/mvvmFX/javadoc/1.6.0/mvvmfx-utils/) -- [javadoc mvvmfx-testing-utils](http://sialcasa.github.io/mvvmFX/javadoc/1.6.0/mvvmfx-testing-utils/) +- [javadoc mvvmfx core](http://sialcasa.github.io/mvvmFX/javadoc/1.7.0/mvvmfx/) +- [javadoc mvvmfx-cdi](http://sialcasa.github.io/mvvmFX/javadoc/1.7.0/mvvmfx-cdi/) +- [javadoc mvvmfx-guice](http://sialcasa.github.io/mvvmFX/javadoc/1.7.0/mvvmfx-guice/) +- [javadoc mvvmfx-easydi](http://sialcasa.github.io/mvvmFX/javadoc/1.7.0/mvvmfx-easydi/) +- [javadoc mvvmfx-validation](http://sialcasa.github.io/mvvmFX/javadoc/1.7.0/mvvmfx-validation/) +- [javadoc mvvmfx-utils](http://sialcasa.github.io/mvvmFX/javadoc/1.7.0/mvvmfx-utils/) +- [javadoc mvvmfx-testing-utils](http://sialcasa.github.io/mvvmFX/javadoc/1.7.0/mvvmfx-testing-utils/) diff --git a/examples/books-example/pom.xml b/examples/books-example/pom.xml index fb4100c4e..cf8880c8d 100644 --- a/examples/books-example/pom.xml +++ b/examples/books-example/pom.xml @@ -5,7 +5,7 @@ de.saxsys.mvvmfx examples - 1.7.0-SNAPSHOT + 1.7.0 4.0.0 diff --git a/examples/contacts-example/pom.xml b/examples/contacts-example/pom.xml index af99c72fd..3a16299e8 100644 --- a/examples/contacts-example/pom.xml +++ b/examples/contacts-example/pom.xml @@ -6,7 +6,7 @@ de.saxsys.mvvmfx examples - 1.7.0-SNAPSHOT + 1.7.0 contacts-example diff --git a/examples/mini-examples/async-todoapp-futures/pom.xml b/examples/mini-examples/async-todoapp-futures/pom.xml index 7ba67114f..5612129b0 100644 --- a/examples/mini-examples/async-todoapp-futures/pom.xml +++ b/examples/mini-examples/async-todoapp-futures/pom.xml @@ -5,7 +5,7 @@ mini-examples de.saxsys.mvvmfx - 1.7.0-SNAPSHOT + 1.7.0 4.0.0 diff --git a/examples/mini-examples/fx-root-example/pom.xml b/examples/mini-examples/fx-root-example/pom.xml index c295139ee..374ff224e 100644 --- a/examples/mini-examples/fx-root-example/pom.xml +++ b/examples/mini-examples/fx-root-example/pom.xml @@ -5,7 +5,7 @@ mini-examples de.saxsys.mvvmfx - 1.7.0-SNAPSHOT + 1.7.0 4.0.0 diff --git a/examples/mini-examples/helloworld-custom-fxml-path/pom.xml b/examples/mini-examples/helloworld-custom-fxml-path/pom.xml index 492474d9a..17b49d009 100644 --- a/examples/mini-examples/helloworld-custom-fxml-path/pom.xml +++ b/examples/mini-examples/helloworld-custom-fxml-path/pom.xml @@ -5,7 +5,7 @@ mini-examples de.saxsys.mvvmfx - 1.7.0-SNAPSHOT + 1.7.0 4.0.0 diff --git a/examples/mini-examples/helloworld-without-fxml/pom.xml b/examples/mini-examples/helloworld-without-fxml/pom.xml index e1eef73f5..e1a6b7305 100644 --- a/examples/mini-examples/helloworld-without-fxml/pom.xml +++ b/examples/mini-examples/helloworld-without-fxml/pom.xml @@ -5,7 +5,7 @@ mini-examples de.saxsys.mvvmfx - 1.7.0-SNAPSHOT + 1.7.0 4.0.0 diff --git a/examples/mini-examples/helloworld/pom.xml b/examples/mini-examples/helloworld/pom.xml index a4b2cf136..590904ae8 100644 --- a/examples/mini-examples/helloworld/pom.xml +++ b/examples/mini-examples/helloworld/pom.xml @@ -5,7 +5,7 @@ mini-examples de.saxsys.mvvmfx - 1.7.0-SNAPSHOT + 1.7.0 4.0.0 diff --git a/examples/mini-examples/pom.xml b/examples/mini-examples/pom.xml index 7dd251a97..5c20b3e74 100644 --- a/examples/mini-examples/pom.xml +++ b/examples/mini-examples/pom.xml @@ -5,7 +5,7 @@ examples de.saxsys.mvvmfx - 1.7.0-SNAPSHOT + 1.7.0 4.0.0 pom diff --git a/examples/mini-examples/scopes-example/pom.xml b/examples/mini-examples/scopes-example/pom.xml index c3c29124d..45f01a170 100644 --- a/examples/mini-examples/scopes-example/pom.xml +++ b/examples/mini-examples/scopes-example/pom.xml @@ -5,7 +5,7 @@ mini-examples de.saxsys.mvvmfx - 1.7.0-SNAPSHOT + 1.7.0 4.0.0 diff --git a/examples/mini-examples/synchronizefx-example/pom.xml b/examples/mini-examples/synchronizefx-example/pom.xml index 355e80d9a..ca43e6d02 100644 --- a/examples/mini-examples/synchronizefx-example/pom.xml +++ b/examples/mini-examples/synchronizefx-example/pom.xml @@ -5,7 +5,7 @@ mini-examples de.saxsys.mvvmfx - 1.7.0-SNAPSHOT + 1.7.0 4.0.0 diff --git a/examples/mini-examples/welcome-example/pom.xml b/examples/mini-examples/welcome-example/pom.xml index e94737685..e0c6779b9 100644 --- a/examples/mini-examples/welcome-example/pom.xml +++ b/examples/mini-examples/welcome-example/pom.xml @@ -5,7 +5,7 @@ mini-examples de.saxsys.mvvmfx - 1.7.0-SNAPSHOT + 1.7.0 4.0.0 diff --git a/examples/pom.xml b/examples/pom.xml index 0666e89fa..5627fb827 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -5,7 +5,7 @@ de.saxsys mvvmfx-parent - 1.7.0-SNAPSHOT + 1.7.0 de.saxsys.mvvmfx diff --git a/examples/todomvc-example/pom.xml b/examples/todomvc-example/pom.xml index 895fb3409..bb48a25d7 100644 --- a/examples/todomvc-example/pom.xml +++ b/examples/todomvc-example/pom.xml @@ -5,7 +5,7 @@ de.saxsys.mvvmfx examples - 1.7.0-SNAPSHOT + 1.7.0 4.0.0 diff --git a/mvvmfx-archetype/pom.xml b/mvvmfx-archetype/pom.xml index b49afbe93..9f0b56d8e 100644 --- a/mvvmfx-archetype/pom.xml +++ b/mvvmfx-archetype/pom.xml @@ -5,7 +5,7 @@ de.saxsys mvvmfx-parent - 1.7.0-SNAPSHOT + 1.7.0 diff --git a/mvvmfx-archetype/src/main/resources/archetype-resources/pom.xml b/mvvmfx-archetype/src/main/resources/archetype-resources/pom.xml index c7a975808..1626dddcb 100644 --- a/mvvmfx-archetype/src/main/resources/archetype-resources/pom.xml +++ b/mvvmfx-archetype/src/main/resources/archetype-resources/pom.xml @@ -16,7 +16,7 @@ de.saxsys mvvmfx-parent - 1.7.0-SNAPSHOT + 1.7.0 pom import diff --git a/mvvmfx-cdi/pom.xml b/mvvmfx-cdi/pom.xml index 9c162ea6c..1eddb5484 100644 --- a/mvvmfx-cdi/pom.xml +++ b/mvvmfx-cdi/pom.xml @@ -20,7 +20,7 @@ de.saxsys mvvmfx-parent - 1.7.0-SNAPSHOT + 1.7.0 mvvmfx-cdi diff --git a/mvvmfx-easydi/pom.xml b/mvvmfx-easydi/pom.xml index 7f1af2dc0..8c028eece 100644 --- a/mvvmfx-easydi/pom.xml +++ b/mvvmfx-easydi/pom.xml @@ -5,7 +5,7 @@ mvvmfx-parent de.saxsys - 1.7.0-SNAPSHOT + 1.7.0 4.0.0 diff --git a/mvvmfx-guice/pom.xml b/mvvmfx-guice/pom.xml index 3083ed10a..13073b5cb 100644 --- a/mvvmfx-guice/pom.xml +++ b/mvvmfx-guice/pom.xml @@ -20,7 +20,7 @@ de.saxsys mvvmfx-parent - 1.7.0-SNAPSHOT + 1.7.0 mvvmfx-guice diff --git a/mvvmfx-testing-utils/pom.xml b/mvvmfx-testing-utils/pom.xml index fa2e99fa6..2c9d3b4c2 100644 --- a/mvvmfx-testing-utils/pom.xml +++ b/mvvmfx-testing-utils/pom.xml @@ -5,7 +5,7 @@ mvvmfx-parent de.saxsys - 1.7.0-SNAPSHOT + 1.7.0 4.0.0 diff --git a/mvvmfx-utils/pom.xml b/mvvmfx-utils/pom.xml index a7a6c4de9..45906dca0 100644 --- a/mvvmfx-utils/pom.xml +++ b/mvvmfx-utils/pom.xml @@ -5,7 +5,7 @@ mvvmfx-parent de.saxsys - 1.7.0-SNAPSHOT + 1.7.0 4.0.0 diff --git a/mvvmfx-validation/pom.xml b/mvvmfx-validation/pom.xml index 9527c7d76..a73159445 100644 --- a/mvvmfx-validation/pom.xml +++ b/mvvmfx-validation/pom.xml @@ -20,7 +20,7 @@ de.saxsys mvvmfx-parent - 1.7.0-SNAPSHOT + 1.7.0 mvvmfx-validation diff --git a/mvvmfx/pom.xml b/mvvmfx/pom.xml index 1ed563cbe..6ca6c9219 100644 --- a/mvvmfx/pom.xml +++ b/mvvmfx/pom.xml @@ -20,7 +20,7 @@ de.saxsys mvvmfx-parent - 1.7.0-SNAPSHOT + 1.7.0 mvvmfx diff --git a/pom.xml b/pom.xml index 5cadfd63d..fef7fd199 100644 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,7 @@ de.saxsys mvvmfx-parent pom - 1.7.0-SNAPSHOT + 1.7.0 mvvmFX parent Application Framework for MVVM with JavaFX. http://www.saxsys.de